diff --git a/src/baby_list.zig b/src/baby_list.zig index 0df33b2dce74c1..2ebb383e6592ed 100644 --- a/src/baby_list.zig +++ b/src/baby_list.zig @@ -177,7 +177,7 @@ pub fn BabyList(comptime Type: type) type { const orig_len = list_.items.len; var slice_ = list_.items.ptr[orig_len..list_.capacity]; - const result = strings.copyUTF16IntoUTF8WithBuffer(slice_, []const u16, remain, trimmed, out_len); + const result = strings.copyUTF16IntoUTF8WithBuffer(slice_, []const u16, remain, trimmed, out_len, true); remain = remain[result.read..]; list_.items.len += @as(usize, result.written); if (result.read == 0 or result.written == 0) break; diff --git a/src/base64/base64.zig b/src/base64/base64.zig index daf5b228ecaa18..0cd16fb8ca1681 100644 --- a/src/base64/base64.zig +++ b/src/base64/base64.zig @@ -5,34 +5,28 @@ pub const DecodeResult = struct { fail: bool = false, }; -pub fn decode(destination: []u8, source: []const u8) DecodeResult { - var wrote: usize = 0; - const result = zig_base64.standard.decoderWithIgnore(&[_]u8{ - ' ', - '\n', - '\r', - '\t', +const mixed_decoder = brk: { + var decoder = zig_base64.standard.decoderWithIgnore("\xff \t\r\n" ++ [_]u8{ std.ascii.control_code.vt, - }).decode(destination, source, &wrote) catch { - return .{ - .written = wrote, - .fail = true, - }; - }; + std.ascii.control_code.ff, + }); - return .{ .written = result, .fail = false }; -} + for (zig_base64.url_safe_alphabet_chars[62..], 62..) |c, i| { + decoder.decoder.char_to_index[c] = @intCast(u8, i); + } -pub fn decodeURLSafe(destination: []u8, source: []const u8) DecodeResult { + break :brk decoder; +}; + +pub fn decode(destination: []u8, source: []const u8) DecodeResult { var wrote: usize = 0; - const result = urlsafe.decode(destination, source, &wrote) catch { + mixed_decoder.decode(destination, source, &wrote) catch { return .{ .written = wrote, .fail = true, }; }; - - return .{ .written = result, .fail = false }; + return .{ .written = wrote, .fail = false }; } pub fn encode(destination: []u8, source: []const u8) usize { @@ -58,14 +52,6 @@ pub fn encodeLen(source: anytype) usize { return zig_base64.standard.Encoder.calcSize(source.len); } -pub const urlsafe = zig_base64.Base64DecoderWithIgnore.init( - zig_base64.url_safe_alphabet_chars, - null, - "= \t\r\n" ++ [_]u8{ std.ascii.control_code.vt, std.ascii.control_code.ff }, -); - -pub const urlsafeEncoder = zig_base64.url_safe_no_pad.Encoder; - // This is just std.base64 copy-pasted // with support for returning how many bytes were decoded const zig_base64 = struct { @@ -118,7 +104,7 @@ const zig_base64 = struct { pub const url_safe_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; fn urlSafeBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore { - return Base64DecoderWithIgnore.init(url_safe_alphabet_chars, null, ignore); + return Base64DecoderWithIgnore.init(url_safe_alphabet_chars, '=', ignore); } /// URL-safe Base64 codecs, with padding @@ -316,7 +302,7 @@ const zig_base64 = struct { /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding /// `InvalidPadding` is returned if the input length is not valid. - pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize { + pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) usize { var result = source_len / 4 * 3; if (decoder_with_ignore.decoder.pad_char == null) { const leftover = source_len % 4; @@ -329,7 +315,7 @@ const zig_base64 = struct { /// Invalid padding results in error.InvalidPadding. /// Decoding more data than can fit in dest results in error.NoSpaceLeft. See also ::calcSizeUpperBound. /// Returns the number of bytes written to dest. - pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8, wrote: *usize) Error!usize { + pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8, wrote: *usize) Error!void { const decoder = &decoder_with_ignore.decoder; var acc: u12 = 0; var acc_len: u4 = 0; @@ -337,16 +323,21 @@ const zig_base64 = struct { var leftover_idx: ?usize = null; defer { - wrote.* = leftover_idx orelse dest_idx; + wrote.* = dest_idx; } for (source, 0..) |c, src_idx| { if (decoder_with_ignore.char_is_ignored[c]) continue; const d = decoder.char_to_index[c]; if (d == Base64Decoder.invalid_char) { - if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter; - leftover_idx = src_idx; - break; + if (decoder.pad_char) |pad_char| { + if (c == pad_char) { + leftover_idx = src_idx; + break; + } + } + if (decoder_with_ignore.char_is_ignored[Base64Decoder.invalid_char]) continue; + return error.InvalidCharacter; } acc = (acc << 6) + d; acc_len += 6; @@ -357,27 +348,26 @@ const zig_base64 = struct { dest_idx += 1; } } - if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) { - return error.InvalidPadding; - } - const padding_len = acc_len / 2; - if (leftover_idx == null) { - if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding; - return dest_idx; - } - var leftover = source[leftover_idx.?..]; + if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) return error.InvalidPadding; + if (decoder.pad_char) |pad_char| { - var padding_chars: usize = 0; - for (leftover) |c| { - if (decoder_with_ignore.char_is_ignored[c]) continue; - if (c != pad_char) { - return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding; + const padding_len = acc_len / 2; + + if (leftover_idx) |idx| { + var leftover = source[idx..]; + var padding_chars: usize = 0; + for (leftover) |c| { + if (decoder_with_ignore.char_is_ignored[c]) continue; + if (c != pad_char) { + return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding; + } + padding_chars += 1; } - padding_chars += 1; + if (padding_chars != padding_len) return error.InvalidPadding; + } else if (padding_len > 0) { + return error.InvalidPadding; } - if (padding_chars != padding_len) return error.InvalidPadding; } - return dest_idx; } }; diff --git a/src/bun.js/bindings/Buffer.h b/src/bun.js/bindings/Buffer.h index fecc627a1c3b2f..4f15835135ee8f 100644 --- a/src/bun.js/bindings/Buffer.h +++ b/src/bun.js/bindings/Buffer.h @@ -16,7 +16,7 @@ extern "C" JSC::EncodedJSValue JSBuffer__bufferFromLength(JSC::JSGlobalObject* l extern "C" JSC::EncodedJSValue JSBuffer__bufferFromPointerAndLengthAndDeinit(JSC::JSGlobalObject* lexicalGlobalObject, char* ptr, size_t length, void* ctx, JSTypedArrayBytesDeallocator bytesDeallocator); extern "C" JSC::EncodedJSValue Bun__encoding__toString(const uint8_t* input, size_t len, JSC::JSGlobalObject* globalObject, Encoding encoding); extern "C" JSC::EncodedJSValue Bun__encoding__toStringUTF8(const uint8_t* input, size_t len, JSC::JSGlobalObject* globalObject); -extern "C" void Bun__Buffer_fill(ZigString*, void*, size_t, WebCore::BufferEncodingType); +extern "C" bool Bun__Buffer_fill(ZigString*, void*, size_t, WebCore::BufferEncodingType); namespace WebCore { diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp index 2ce07617aecd30..9b3854b95410a1 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -133,6 +133,35 @@ static int normalizeCompareVal(int val, size_t a_length, size_t b_length) return val; } +static inline uint32_t parseIndex(JSC::JSGlobalObject* lexicalGlobalObject, JSC::ThrowScope& scope, JSValue arg) +{ + if (auto num = arg.tryGetAsUint32Index()) + return num.value(); + + if (arg.isNumber()) + throwRangeError(lexicalGlobalObject, scope, "Invalid array length"_s); + else + throwTypeError(lexicalGlobalObject, scope, "Expected number"_s); + + return 0; +} + +static inline WebCore::BufferEncodingType parseEncoding(JSC::JSGlobalObject* lexicalGlobalObject, JSC::ThrowScope& scope, JSValue arg) +{ + if (UNLIKELY(!arg.isString())) { + throwTypeError(lexicalGlobalObject, scope, "Expected string"_s); + return WebCore::BufferEncodingType::utf8; + } + + std::optional encoded = parseEnumeration(*lexicalGlobalObject, arg); + if (UNLIKELY(!encoded)) { + throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); + return WebCore::BufferEncodingType::utf8; + } + + return encoded.value(); +} + namespace WebCore { using namespace JSC; @@ -190,8 +219,11 @@ using namespace JSC; static inline EncodedJSValue writeToBuffer(JSC::JSGlobalObject* lexicalGlobalObject, JSArrayBufferView* castedThis, JSString* str, uint32_t offset, uint32_t length, BufferEncodingType encoding) { + if (UNLIKELY(str->length() == 0)) + return JSC::JSValue::encode(JSC::jsNumber(0)); + auto view = str->tryGetValue(lexicalGlobalObject); - int64_t written = 0; + size_t written = 0; switch (encoding) { case WebCore::BufferEncodingType::utf8: @@ -358,9 +390,6 @@ static inline JSC::EncodedJSValue constructBufferFromStringAndEncoding(JSC::JSGl RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); - if (str->length() == 0) - return constructBufferEmpty(lexicalGlobalObject); - if (arg1 && arg1.isString()) { std::optional encoded = parseEnumeration(*lexicalGlobalObject, arg1); if (!encoded) { @@ -371,6 +400,9 @@ static inline JSC::EncodedJSValue constructBufferFromStringAndEncoding(JSC::JSGl encoding = encoded.value(); } + if (str->length() == 0) + return constructBufferEmpty(lexicalGlobalObject); + JSC::EncodedJSValue result = constructFromEncoding(lexicalGlobalObject, str, encoding); RELEASE_AND_RETURN(scope, result); @@ -380,7 +412,12 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG { VM& vm = lexicalGlobalObject->vm(); auto scope = DECLARE_THROW_SCOPE(vm); - auto length = callFrame->uncheckedArgument(0).toInt32(lexicalGlobalObject); + auto lengthArg = callFrame->uncheckedArgument(0); + if (UNLIKELY(!lengthArg.isNumber())) { + throwTypeError(lexicalGlobalObject, scope, "Expected number"_s); + return JSValue::encode(jsUndefined()); + } + auto length = lengthArg.toInt32(lexicalGlobalObject); if (length < 0) { throwRangeError(lexicalGlobalObject, scope, "Invalid array length"_s); return JSValue::encode(jsUndefined()); @@ -392,10 +429,56 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG // fill argument if (UNLIKELY(callFrame->argumentCount() > 1)) { auto* uint8Array = JSC::JSUint8Array::createUninitialized(lexicalGlobalObject, subclassStructure, length); - auto value = callFrame->argument(1); - if (!value.isString()) { + if (value.isString()) { + size_t length = uint8Array->byteLength(); + size_t start = 0; + size_t end = length; + WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; + if (callFrame->argumentCount() > 2) { + EnsureStillAliveScope arg2 = callFrame->uncheckedArgument(2); + if (!arg2.value().isUndefined()) { + encoding = parseEncoding(lexicalGlobalObject, scope, arg2.value()); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); + } + } + auto startPtr = uint8Array->typedVector() + start; + auto str_ = value.toWTFString(lexicalGlobalObject); + ZigString str = Zig::toZigString(str_); + + if (UNLIKELY(!Bun__Buffer_fill(&str, startPtr, end - start, encoding))) { + throwTypeError(lexicalGlobalObject, scope, "Failed to decode value"_s); + return JSC::JSValue::encode(jsUndefined()); + } + } else if (auto* view = JSC::jsDynamicCast(value)) { + if (UNLIKELY(view->isDetached())) { + throwVMTypeError(lexicalGlobalObject, scope, "Uint8Array is detached"_s); + return JSValue::encode(jsUndefined()); + } + + size_t length = view->byteLength(); + if (UNLIKELY(length == 0)) { + throwTypeError(lexicalGlobalObject, scope, "Buffer cannot be empty"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + auto* start = uint8Array->typedVector(); + auto* head = start; + size_t remain = uint8Array->byteLength(); + memmove(head, view->vector(), length); + remain -= length; + head += length; + while (remain >= length) { + memcpy(head, start, length); + remain -= length; + head += length; + length <<= 1; + } + if (remain > 0) { + memcpy(head, start, remain); + } + } else { auto value_ = value.toInt32(lexicalGlobalObject) & 0xFF; auto value_uint8 = static_cast(value_); @@ -408,33 +491,9 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_allocBody(JSC::JSG auto startPtr = uint8Array->typedVector() + start; auto endPtr = uint8Array->typedVector() + end; memset(startPtr, value_uint8, endPtr - startPtr); - RELEASE_AND_RETURN(scope, JSC::JSValue::encode(uint8Array)); } - { - size_t length = uint8Array->byteLength(); - size_t start = 0; - size_t end = length; - WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; - if (callFrame->argumentCount() > 2) { - EnsureStillAliveScope arg2 = callFrame->uncheckedArgument(2); - if (arg2.value().isString()) { - std::optional encoded = parseEnumeration(*lexicalGlobalObject, arg2.value()); - if (!encoded) { - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); - return JSC::JSValue::encode(jsUndefined()); - } - - encoding = encoded.value(); - } - } - auto startPtr = uint8Array->typedVector() + start; - auto str_ = value.toWTFString(lexicalGlobalObject); - ZigString str = Zig::toZigString(str_); - - Bun__Buffer_fill(&str, startPtr, end - start, encoding); - RELEASE_AND_RETURN(scope, JSC::JSValue::encode(uint8Array)); - } + RELEASE_AND_RETURN(scope, JSC::JSValue::encode(uint8Array)); } else { auto* uint8Array = JSC::JSUint8Array::create(lexicalGlobalObject, subclassStructure, length); if (UNLIKELY(!uint8Array)) { @@ -585,6 +644,10 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (first argument)"_s); return JSValue::encode(jsUndefined()); } + if (UNLIKELY(castedThis->isDetached())) { + throwVMTypeError(lexicalGlobalObject, throwScope, "Uint8Array (first argument) is detached"_s); + return JSValue::encode(jsUndefined()); + } auto buffer = callFrame->uncheckedArgument(1); JSC::JSArrayBufferView* view = JSC::jsDynamicCast(buffer); @@ -592,13 +655,7 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J throwVMTypeError(lexicalGlobalObject, throwScope, "Expected Buffer (2nd argument)"_s); return JSValue::encode(jsUndefined()); } - if (UNLIKELY(view->isDetached())) { - throwVMTypeError(lexicalGlobalObject, throwScope, "Uint8Array (first argument) is detached"_s); - return JSValue::encode(jsUndefined()); - } - - if (UNLIKELY(castedThis->isDetached())) { throwVMTypeError(lexicalGlobalObject, throwScope, "Uint8Array (second argument) is detached"_s); return JSValue::encode(jsUndefined()); } @@ -611,43 +668,27 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_compareBody(JSC::J size_t sourceEndInit = castedThis->byteLength(); size_t sourceEnd = sourceEndInit; - if (callFrame->argumentCount() > 2) { - if (auto targetEnd_ = callFrame->uncheckedArgument(2).tryGetAsUint32Index()) { - targetStart = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - - if (callFrame->argumentCount() > 3) { - auto targetEndArgument = callFrame->uncheckedArgument(3); - if (auto targetEnd_ = targetEndArgument.tryGetAsUint32Index()) { - targetEnd = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } - - if (callFrame->argumentCount() > 4) { - auto targetEndArgument = callFrame->uncheckedArgument(4); - if (auto targetEnd_ = targetEndArgument.tryGetAsUint32Index()) { - sourceStart = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } - - if (callFrame->argumentCount() > 5) { - auto targetEndArgument = callFrame->uncheckedArgument(5); - if (auto targetEnd_ = targetEndArgument.tryGetAsUint32Index()) { - sourceEnd = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } + switch (callFrame->argumentCount()) { + default: + sourceEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(5)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 5: + sourceStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(4)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 4: + targetEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(3)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 3: + targetStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(2)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + break; + case 2: + case 1: + case 0: + break; } targetStart = std::min(targetStart, std::min(targetEnd, targetEndInit)); @@ -822,43 +863,26 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_compareBody(JSC::JSG size_t sourceEndInit = castedThis->byteLength(); size_t sourceEnd = sourceEndInit; - if (callFrame->argumentCount() > 1) { - if (auto targetEnd_ = callFrame->uncheckedArgument(1).tryGetAsUint32Index()) { - targetStart = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - - if (callFrame->argumentCount() > 2) { - auto targetEndArgument = callFrame->uncheckedArgument(2); - if (auto targetEnd_ = targetEndArgument.tryGetAsUint32Index()) { - targetEnd = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } - - if (callFrame->argumentCount() > 3) { - auto targetEndArgument = callFrame->uncheckedArgument(3); - if (auto targetEnd_ = targetEndArgument.tryGetAsUint32Index()) { - sourceStart = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } - - if (callFrame->argumentCount() > 4) { - auto targetEndArgument = callFrame->uncheckedArgument(4); - if (auto targetEnd_ = targetEndArgument.tryGetAsUint32Index()) { - sourceEnd = targetEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } + switch (callFrame->argumentCount()) { + default: + sourceEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(4)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 4: + sourceStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(3)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 3: + targetEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(2)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 2: + targetStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(1)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + break; + case 1: + case 0: + break; } targetStart = std::min(targetStart, std::min(targetEnd, targetEndInit)); @@ -905,31 +929,22 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_copyBody(JSC::JSGlob size_t sourceEndInit = castedThis->byteLength(); size_t sourceEnd = sourceEndInit; - if (callFrame->argumentCount() > 1) { - if (auto targetStart_ = callFrame->uncheckedArgument(1).tryGetAsUint32Index()) { - targetStart = targetStart_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - - if (callFrame->argumentCount() > 2) { - if (auto sourceStart_ = callFrame->uncheckedArgument(2).tryGetAsUint32Index()) { - sourceStart = sourceStart_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } - - if (callFrame->argumentCount() > 3) { - if (auto sourceEnd_ = callFrame->uncheckedArgument(3).tryGetAsUint32Index()) { - sourceEnd = sourceEnd_.value(); - } else { - throwVMTypeError(lexicalGlobalObject, throwScope, "Expected number"_s); - return JSValue::encode(jsUndefined()); - } - } + switch (callFrame->argumentCount()) { + default: + sourceEnd = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(3)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 3: + sourceStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(2)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + FALLTHROUGH; + case 2: + targetStart = parseIndex(lexicalGlobalObject, throwScope, callFrame->uncheckedArgument(1)); + RETURN_IF_EXCEPTION(throwScope, JSValue::encode(jsUndefined())); + break; + case 1: + case 0: + break; } targetStart = std::min(targetStart, targetEnd); @@ -993,120 +1008,111 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob } auto value = callFrame->uncheckedArgument(0); + const size_t limit = castedThis->byteLength(); + size_t start = 0; + size_t end = limit; + WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; + JSValue encodingValue = jsUndefined(); + JSValue offsetValue = jsUndefined(); + JSValue lengthValue = jsUndefined(); - if (!value.isString()) { - auto value_ = value.toInt32(lexicalGlobalObject) & 0xFF; + switch (callFrame->argumentCount()) { + case 4: + encodingValue = callFrame->uncheckedArgument(3); + FALLTHROUGH; + case 3: + lengthValue = callFrame->uncheckedArgument(2); + FALLTHROUGH; + case 2: + offsetValue = callFrame->uncheckedArgument(1); + FALLTHROUGH; + default: + break; + } - auto value_uint8 = static_cast(value_); + if (offsetValue.isUndefined() || offsetValue.isString()) { + encodingValue = offsetValue; + offsetValue = jsUndefined(); + } else if (lengthValue.isString()) { + encodingValue = lengthValue; + lengthValue = jsUndefined(); + } + + if (!encodingValue.isUndefined()) { + encoding = parseEncoding(lexicalGlobalObject, scope, encodingValue); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); + } - auto length = castedThis->byteLength(); - auto start = 0; - auto end = length; - if (callFrame->argumentCount() > 1) { - if (auto start_ = callFrame->uncheckedArgument(1).tryGetAsUint32Index()) { - start = start_.value(); - } else { - return throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "start out of range"_s)); - } - if (callFrame->argumentCount() > 2) { - if (auto end_ = callFrame->uncheckedArgument(2).tryGetAsUint32Index()) { - end = end_.value(); - } else { - return throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "end out of range"_s)); - } - } - } - if (start > end) { - return throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "start out of range"_s)); - } - if (end > length) { - return throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "end out of range"_s)); - } - auto startPtr = castedThis->typedVector() + start; - auto endPtr = castedThis->typedVector() + end; - memset(startPtr, value_uint8, endPtr - startPtr); - return JSValue::encode(castedThis); + if (!offsetValue.isUndefined()) { + start = parseIndex(lexicalGlobalObject, scope, offsetValue); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); } - { - EnsureStillAliveScope value_ = callFrame->argument(0); - - size_t length = castedThis->byteLength(); - size_t start = 0; - size_t end = length; - WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8; - - JSValue encodingValue = jsUndefined(); - JSValue offsetValue = jsUndefined(); - JSValue lengthValue = jsUndefined(); - - switch (callFrame->argumentCount()) { - case 4: - encodingValue = callFrame->uncheckedArgument(3); - FALLTHROUGH; - case 3: - lengthValue = callFrame->uncheckedArgument(2); - FALLTHROUGH; - case 2: - offsetValue = callFrame->uncheckedArgument(1); - FALLTHROUGH; - default: - break; - } + if (!lengthValue.isUndefined()) { + end = parseIndex(lexicalGlobalObject, scope, lengthValue); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); + } - if (offsetValue.isUndefined() || offsetValue.isString()) { - encodingValue = offsetValue; - offsetValue = jsUndefined(); - } else if (lengthValue.isString()) { - encodingValue = lengthValue; - lengthValue = jsUndefined(); - } + if (start >= end) { + RELEASE_AND_RETURN(scope, JSValue::encode(castedThis)); + } - if (encodingValue.isString()) { - std::optional encoded = parseEnumeration(*lexicalGlobalObject, encodingValue); - if (!encoded) { - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); - return JSC::JSValue::encode(jsUndefined()); - } + if (UNLIKELY(end > limit)) { + throwRangeError(lexicalGlobalObject, scope, "end out of range"_s); + return JSC::JSValue::encode(jsUndefined()); + } - encoding = encoded.value(); - } + if (value.isString()) { + auto startPtr = castedThis->typedVector() + start; + auto str_ = value.toWTFString(lexicalGlobalObject); + ZigString str = Zig::toZigString(str_); - if (!offsetValue.isUndefined()) { - if (auto offset = offsetValue.tryGetAsUint32Index()) { - start = offset.value(); - } else { - throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "start out of range"_s)); - return JSC::JSValue::encode(jsUndefined()); - } + if (str.len == 0) { + memset(startPtr, 0, end - start); + } else if (UNLIKELY(!Bun__Buffer_fill(&str, startPtr, end - start, encoding))) { + throwTypeError(lexicalGlobalObject, scope, "Failed to decode value"_s); + return JSC::JSValue::encode(jsUndefined()); } + } else if (auto* view = JSC::jsDynamicCast(value)) { + auto* startPtr = castedThis->typedVector() + start; + auto* head = startPtr; + size_t remain = end - start; - if (!lengthValue.isUndefined()) { - if (auto length = lengthValue.tryGetAsUint32Index()) { - end = std::min(static_cast(length.value()), end); - } else { - throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "end out of range"_s)); - return JSC::JSValue::encode(jsUndefined()); - } + if (UNLIKELY(view->isDetached())) { + throwVMTypeError(lexicalGlobalObject, scope, "Uint8Array is detached"_s); + return JSValue::encode(jsUndefined()); } - if (start >= end) { - return JSValue::encode(castedThis); + size_t length = view->byteLength(); + if (UNLIKELY(length == 0)) { + throwTypeError(lexicalGlobalObject, scope, "Buffer cannot be empty"_s); + return JSC::JSValue::encode(jsUndefined()); } - auto startPtr = castedThis->typedVector() + start; - auto str_ = value.toWTFString(lexicalGlobalObject); - ZigString str = Zig::toZigString(str_); - - if (str.len > 0) { - Bun__Buffer_fill(&str, startPtr, end - start, encoding); - } else { - memset(startPtr, 0, end - start); + memmove(head, view->vector(), length); + remain -= length; + head += length; + while (remain >= length) { + memcpy(head, startPtr, length); + remain -= length; + head += length; + length <<= 1; + } + if (remain > 0) { + memcpy(head, startPtr, remain); } + } else { + auto value_ = value.toInt32(lexicalGlobalObject) & 0xFF; - RELEASE_AND_RETURN(scope, JSValue::encode(castedThis)); + auto value_uint8 = static_cast(value_); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); + + auto startPtr = castedThis->typedVector() + start; + auto endPtr = castedThis->typedVector() + end; + memset(startPtr, value_uint8, endPtr - startPtr); } + + RELEASE_AND_RETURN(scope, JSValue::encode(castedThis)); } static int64_t indexOf(const uint8_t* thisPtr, int64_t thisLength, const uint8_t* valuePtr, int64_t valueLength, int64_t byteOffset) @@ -1150,42 +1156,42 @@ static int64_t indexOf(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* int64_t byteOffset = last ? length - 1 : 0; if (callFrame->argumentCount() > 1) { - auto byteOffset_ = callFrame->uncheckedArgument(1).toNumber(lexicalGlobalObject); - RETURN_IF_EXCEPTION(scope, -1); - - if (std::isnan(byteOffset_) || std::isinf(byteOffset_)) { - byteOffset = last ? length - 1 : 0; - } else if (byteOffset_ < 0) { - byteOffset = length + static_cast(byteOffset_); + EnsureStillAliveScope arg1 = callFrame->uncheckedArgument(1); + if (arg1.value().isString()) { + encoding = parseEncoding(lexicalGlobalObject, scope, arg1.value()); + RETURN_IF_EXCEPTION(scope, -1); } else { - byteOffset = static_cast(byteOffset_); - } + auto byteOffset_ = arg1.value().toNumber(lexicalGlobalObject); + RETURN_IF_EXCEPTION(scope, -1); - if (last) { - if (byteOffset < 0) { - return -1; - } else if (byteOffset > length - 1) { - byteOffset = length - 1; - } - } else { - if (byteOffset <= 0) { - byteOffset = 0; - } else if (byteOffset > length - 1) { - return -1; + if (std::isnan(byteOffset_) || std::isinf(byteOffset_)) { + byteOffset = last ? length - 1 : 0; + } else if (byteOffset_ < 0) { + byteOffset = length + static_cast(byteOffset_); + } else { + byteOffset = static_cast(byteOffset_); } - } - if (callFrame->argumentCount() > 2) { - EnsureStillAliveScope encodingValue = callFrame->uncheckedArgument(2); - - if (encodingValue.value().isString()) { - std::optional encoded = parseEnumeration(*lexicalGlobalObject, encodingValue.value()); - if (!encoded) { - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); - return JSC::JSValue::encode(jsUndefined()); + if (last) { + if (byteOffset < 0) { + return -1; + } else if (byteOffset > length - 1) { + byteOffset = length - 1; + } + } else { + if (byteOffset <= 0) { + byteOffset = 0; + } else if (byteOffset > length - 1) { + return -1; } + } - encoding = encoded.value(); + if (callFrame->argumentCount() > 2) { + EnsureStillAliveScope encodingValue = callFrame->uncheckedArgument(2); + if (!encodingValue.value().isUndefined()) { + encoding = parseEncoding(lexicalGlobalObject, scope, encodingValue.value()); + RETURN_IF_EXCEPTION(scope, -1); + } } } } @@ -1420,6 +1426,7 @@ static inline JSC::EncodedJSValue jsBufferToString(JSC::VM& vm, JSC::JSGlobalObj static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); + auto scope = DECLARE_THROW_SCOPE(vm); uint32_t offset = 0; uint32_t length = castedThis->length(); uint32_t byteLength = length; @@ -1436,18 +1443,10 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS case 3: case 1: { EnsureStillAliveScope arg1 = callFrame->uncheckedArgument(0); - if (arg1.value().isString()) { - std::optional encoded = parseEnumeration(*lexicalGlobalObject, arg1.value()); - if (!encoded) { - auto scope = DECLARE_THROW_SCOPE(vm); - - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); - return JSC::JSValue::encode(jsUndefined()); - } - - encoding = encoded.value(); + if (!arg1.value().isUndefined()) { + encoding = parseEncoding(lexicalGlobalObject, scope, arg1.value()); + RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); } - if (callFrame->argumentCount() == 1) break; } @@ -1456,8 +1455,6 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS JSC::JSValue arg2 = callFrame->uncheckedArgument(1); int32_t ioffset = arg2.toInt32(lexicalGlobalObject); if (ioffset < 0) { - auto scope = DECLARE_THROW_SCOPE(vm); - throwTypeError(lexicalGlobalObject, scope, "Offset must be a positive integer"_s); return JSC::JSValue::encode(jsUndefined()); } @@ -1524,9 +1521,6 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlo return JSC::JSValue::encode(jsUndefined()); } - if (str->length() == 0) - return JSC::JSValue::encode(JSC::jsNumber(0)); - JSValue offsetValue = jsUndefined(); JSValue lengthValue = jsUndefined(); JSValue encodingValue = jsUndefined(); @@ -1546,14 +1540,8 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlo } auto setEncoding = [&]() { - if (encodingValue.isString()) { - std::optional encoded = parseEnumeration(*lexicalGlobalObject, encodingValue); - if (!encoded) { - throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s); - return; - } - - encoding = encoded.value(); + if (!encodingValue.isUndefined()) { + encoding = parseEncoding(lexicalGlobalObject, scope, encodingValue); } }; @@ -1570,6 +1558,11 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlo RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding)); } + if (UNLIKELY(!offsetValue.isNumber())) { + throwTypeError(lexicalGlobalObject, scope, "Invalid offset"_s); + return JSC::JSValue::encode(jsUndefined()); + } + int32_t userOffset = offsetValue.toInt32(lexicalGlobalObject); RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined())); if (userOffset < 0 || userOffset > max) { @@ -1845,11 +1838,15 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "readInt32BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadInt32BECodeGenerator, 1 } }, { "readInt32LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadInt32LECodeGenerator, 1 } }, { "readInt8"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadInt8CodeGenerator, 2 } }, + { "readIntBE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadIntBECodeGenerator, 1 } }, + { "readIntLE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadIntLECodeGenerator, 1 } }, { "readUInt16BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16BECodeGenerator, 1 } }, { "readUInt16LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16LECodeGenerator, 1 } }, { "readUInt32BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt32BECodeGenerator, 1 } }, { "readUInt32LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt32LECodeGenerator, 1 } }, { "readUInt8"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt8CodeGenerator, 1 } }, + { "readUIntBE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUIntBECodeGenerator, 1 } }, + { "readUIntLE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUIntLECodeGenerator, 1 } }, { "readUint16BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16BECodeGenerator, 1 } }, { "readUint16LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16LECodeGenerator, 1 } }, { "readUint32BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt32BECodeGenerator, 1 } }, @@ -1861,6 +1858,7 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "swap32"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap32, 0 } }, { "swap64"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap64, 0 } }, { "toJSON"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeToJSONCodeGenerator, 1 } }, + { "toLocaleString"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } }, { "toString"_s, static_cast(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } }, { "ucs2Slice"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUcs2SliceCodeGenerator, 2 } }, { "ucs2Write"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUcs2WriteCodeGenerator, 1 } }, @@ -1886,6 +1884,8 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "writeInt32BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteInt32BECodeGenerator, 1 } }, { "writeInt32LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteInt32LECodeGenerator, 1 } }, { "writeInt8"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteInt8CodeGenerator, 1 } }, + { "writeIntBE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteIntBECodeGenerator, 1 } }, + { "writeIntLE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteIntLECodeGenerator, 1 } }, { "writeUInt16"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16LECodeGenerator, 1 } }, { "writeUInt16BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16BECodeGenerator, 1 } }, { "writeUInt16LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16LECodeGenerator, 1 } }, @@ -1893,6 +1893,8 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "writeUInt32BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt32BECodeGenerator, 1 } }, { "writeUInt32LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt32LECodeGenerator, 1 } }, { "writeUInt8"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt8CodeGenerator, 1 } }, + { "writeUIntBE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUIntBECodeGenerator, 1 } }, + { "writeUIntLE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUIntLECodeGenerator, 1 } }, { "writeUint16"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16LECodeGenerator, 1 } }, { "writeUint16BE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16BECodeGenerator, 1 } }, { "writeUint16LE"_s, static_cast(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16LECodeGenerator, 1 } }, diff --git a/src/bun.js/bindings/JSBufferEncodingType.cpp b/src/bun.js/bindings/JSBufferEncodingType.cpp index 8d99dd4db0ecdc..dde9e1be2725ff 100644 --- a/src/bun.js/bindings/JSBufferEncodingType.cpp +++ b/src/bun.js/bindings/JSBufferEncodingType.cpp @@ -39,7 +39,6 @@ static const NeverDestroyed values[] = { MAKE_STATIC_STRING_IMPL("base64"), MAKE_STATIC_STRING_IMPL("base64url"), MAKE_STATIC_STRING_IMPL("hex"), - MAKE_STATIC_STRING_IMPL("buffer"), }; String convertEnumerationToString(BufferEncodingType enumerationValue) @@ -104,8 +103,6 @@ template<> std::optional parseEnumeration std::optional parseEnumeration const char* expectedEnumerationValues() { - return "\"utf8\", \"ucs2\", \"utf16le\", \"latin1\", \"ascii\", \"base64\", \"base64url\", \"hex\", \"buffer\""; + return "\"utf8\", \"ucs2\", \"utf16le\", \"latin1\", \"ascii\", \"base64\", \"base64url\", \"hex\""; } } // namespace WebCore diff --git a/src/bun.js/bindings/headers-handwritten.h b/src/bun.js/bindings/headers-handwritten.h index 8f925e84f26eb4..d4e1e72af27364 100644 --- a/src/bun.js/bindings/headers-handwritten.h +++ b/src/bun.js/bindings/headers-handwritten.h @@ -268,8 +268,8 @@ extern "C" const char* Bun__version_sha; extern "C" void ZigString__free_global(const unsigned char* ptr, size_t len); -extern "C" int64_t Bun__encoding__writeLatin1(const unsigned char* ptr, size_t len, unsigned char* to, size_t other_len, Encoding encoding); -extern "C" int64_t Bun__encoding__writeUTF16(const UChar* ptr, size_t len, unsigned char* to, size_t other_len, Encoding encoding); +extern "C" size_t Bun__encoding__writeLatin1(const unsigned char* ptr, size_t len, unsigned char* to, size_t other_len, Encoding encoding); +extern "C" size_t Bun__encoding__writeUTF16(const UChar* ptr, size_t len, unsigned char* to, size_t other_len, Encoding encoding); extern "C" size_t Bun__encoding__byteLengthLatin1(const unsigned char* ptr, size_t len, Encoding encoding); extern "C" size_t Bun__encoding__byteLengthUTF16(const UChar* ptr, size_t len, Encoding encoding); diff --git a/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.cpp b/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.cpp index 8aff610648851b..1ce441bb6026bc 100644 --- a/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.cpp +++ b/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.cpp @@ -180,6 +180,146 @@ const char* const s_jsBufferPrototypeReadUInt32BECode = "})\n" \ ; +const JSC::ConstructAbility s_jsBufferPrototypeReadIntLECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeReadIntLECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeReadIntLECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeReadIntLECodeLength = 882; +static const JSC::Intrinsic s_jsBufferPrototypeReadIntLECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeReadIntLECode = + "(function (offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " return view.getInt8(offset);\n" \ + " }\n" \ + " case 2: {\n" \ + " return view.getInt16(offset, true);\n" \ + " }\n" \ + " case 3: {\n" \ + " const val = view.getUint16(offset, true) + view.getUint8(offset + 2) * 2 ** 16;\n" \ + " return val | (val & 2 ** 23) * 0x1fe;\n" \ + " }\n" \ + " case 4: {\n" \ + " return view.getInt32(offset, true);\n" \ + " }\n" \ + " case 5: {\n" \ + " const last = view.getUint8(offset + 4);\n" \ + " return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + view.getUint32(offset, true);\n" \ + " }\n" \ + " case 6: {\n" \ + " const last = view.getUint16(offset + 4, true);\n" \ + " return (last | (last & 2 ** 15) * 0x1fffe) * 2 ** 32 + view.getUint32(offset, true);\n" \ + " }\n" \ + " }\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + "})\n" \ +; + +const JSC::ConstructAbility s_jsBufferPrototypeReadIntBECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeReadIntBECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeReadIntBECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeReadIntBECodeLength = 888; +static const JSC::Intrinsic s_jsBufferPrototypeReadIntBECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeReadIntBECode = + "(function (offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " return view.getInt8(offset);\n" \ + " }\n" \ + " case 2: {\n" \ + " return view.getInt16(offset, false);\n" \ + " }\n" \ + " case 3: {\n" \ + " const val = view.getUint16(offset + 1, false) + view.getUint8(offset) * 2 ** 16;\n" \ + " return val | (val & 2 ** 23) * 0x1fe;\n" \ + " }\n" \ + " case 4: {\n" \ + " return view.getInt32(offset, false);\n" \ + " }\n" \ + " case 5: {\n" \ + " const last = view.getUint8(offset);\n" \ + " return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + view.getUint32(offset + 1, false);\n" \ + " }\n" \ + " case 6: {\n" \ + " const last = view.getUint16(offset, false);\n" \ + " return (last | (last & 2 ** 15) * 0x1fffe) * 2 ** 32 + view.getUint32(offset + 2, false);\n" \ + " }\n" \ + " }\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + "})\n" \ +; + +const JSC::ConstructAbility s_jsBufferPrototypeReadUIntLECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeReadUIntLECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeReadUIntLECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeReadUIntLECodeLength = 723; +static const JSC::Intrinsic s_jsBufferPrototypeReadUIntLECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeReadUIntLECode = + "(function (offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " return view.getUint8(offset);\n" \ + " }\n" \ + " case 2: {\n" \ + " return view.getUint16(offset, true);\n" \ + " }\n" \ + " case 3: {\n" \ + " return view.getUint16(offset, true) + view.getUint8(offset + 2) * 2 ** 16;\n" \ + " }\n" \ + " case 4: {\n" \ + " return view.getUint32(offset, true);\n" \ + " }\n" \ + " case 5: {\n" \ + " return view.getUint8(offset + 4) * 2 ** 32 + view.getUint32(offset, true);\n" \ + " }\n" \ + " case 6: {\n" \ + " return view.getUint16(offset + 4, true) * 2 ** 32 + view.getUint32(offset, true);\n" \ + " }\n" \ + " }\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + "})\n" \ +; + +const JSC::ConstructAbility s_jsBufferPrototypeReadUIntBECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeReadUIntBECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeReadUIntBECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeReadUIntBECodeLength = 842; +static const JSC::Intrinsic s_jsBufferPrototypeReadUIntBECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeReadUIntBECode = + "(function (offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " return view.getUint8(offset);\n" \ + " }\n" \ + " case 2: {\n" \ + " return view.getUint16(offset, false);\n" \ + " }\n" \ + " case 3: {\n" \ + " return view.getUint16(offset + 1, false) + view.getUint8(offset) * 2 ** 16;\n" \ + " }\n" \ + " case 4: {\n" \ + " return view.getUint32(offset, false);\n" \ + " }\n" \ + " case 5: {\n" \ + " const last = view.getUint8(offset);\n" \ + " return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + view.getUint32(offset + 1, false);\n" \ + " }\n" \ + " case 6: {\n" \ + " const last = view.getUint16(offset, false);\n" \ + " return (last | (last & 2 ** 15) * 0x1fffe) * 2 ** 32 + view.getUint32(offset + 2, false);\n" \ + " }\n" \ + " }\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + "})\n" \ +; + const JSC::ConstructAbility s_jsBufferPrototypeReadFloatLECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_jsBufferPrototypeReadFloatLECodeConstructorKind = JSC::ConstructorKind::None; const JSC::ImplementationVisibility s_jsBufferPrototypeReadFloatLECodeImplementationVisibility = JSC::ImplementationVisibility::Public; @@ -406,6 +546,186 @@ const char* const s_jsBufferPrototypeWriteUInt32BECode = "})\n" \ ; +const JSC::ConstructAbility s_jsBufferPrototypeWriteIntLECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeWriteIntLECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeWriteIntLECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeWriteIntLECodeLength = 949; +static const JSC::Intrinsic s_jsBufferPrototypeWriteIntLECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeWriteIntLECode = + "(function (value, offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " view.setInt8(offset, value);\n" \ + " break;\n" \ + " }\n" \ + " case 2: {\n" \ + " view.setInt16(offset, value, true);\n" \ + " break;\n" \ + " }\n" \ + " case 3: {\n" \ + " view.setUint16(offset, value & 0xFFFF, true);\n" \ + " view.setInt8(offset + 2, Math.floor(value * 2 ** -16));\n" \ + " break;\n" \ + " }\n" \ + " case 4: {\n" \ + " view.setInt32(offset, value, true);\n" \ + " break;\n" \ + " }\n" \ + " case 5: {\n" \ + " view.setUint32(offset, value | 0, true);\n" \ + " view.setInt8(offset + 4, Math.floor(value * 2 ** -32));\n" \ + " break;\n" \ + " }\n" \ + " case 6: {\n" \ + " view.setUint32(offset, value | 0, true);\n" \ + " view.setInt16(offset + 4, Math.floor(value * 2 ** -32), true);\n" \ + " break;\n" \ + " }\n" \ + " default: {\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + " }\n" \ + " }\n" \ + " return offset + byteLength;\n" \ + "})\n" \ +; + +const JSC::ConstructAbility s_jsBufferPrototypeWriteIntBECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeWriteIntBECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeWriteIntBECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeWriteIntBECodeLength = 955; +static const JSC::Intrinsic s_jsBufferPrototypeWriteIntBECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeWriteIntBECode = + "(function (value, offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " view.setInt8(offset, value);\n" \ + " break;\n" \ + " }\n" \ + " case 2: {\n" \ + " view.setInt16(offset, value, false);\n" \ + " break;\n" \ + " }\n" \ + " case 3: {\n" \ + " view.setUint16(offset + 1, value & 0xFFFF, false);\n" \ + " view.setInt8(offset, Math.floor(value * 2 ** -16));\n" \ + " break;\n" \ + " }\n" \ + " case 4: {\n" \ + " view.setInt32(offset, value, false);\n" \ + " break;\n" \ + " }\n" \ + " case 5: {\n" \ + " view.setUint32(offset + 1, value | 0, false);\n" \ + " view.setInt8(offset, Math.floor(value * 2 ** -32));\n" \ + " break;\n" \ + " }\n" \ + " case 6: {\n" \ + " view.setUint32(offset + 2, value | 0, false);\n" \ + " view.setInt16(offset, Math.floor(value * 2 ** -32), false);\n" \ + " break;\n" \ + " }\n" \ + " default: {\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + " }\n" \ + " }\n" \ + " return offset + byteLength;\n" \ + "})\n" \ +; + +const JSC::ConstructAbility s_jsBufferPrototypeWriteUIntLECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeWriteUIntLECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeWriteUIntLECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeWriteUIntLECodeLength = 955; +static const JSC::Intrinsic s_jsBufferPrototypeWriteUIntLECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeWriteUIntLECode = + "(function (value, offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " view.setUint8(offset, value);\n" \ + " break;\n" \ + " }\n" \ + " case 2: {\n" \ + " view.setUint16(offset, value, true);\n" \ + " break;\n" \ + " }\n" \ + " case 3: {\n" \ + " view.setUint16(offset, value & 0xFFFF, true);\n" \ + " view.setUint8(offset + 2, Math.floor(value * 2 ** -16));\n" \ + " break;\n" \ + " }\n" \ + " case 4: {\n" \ + " view.setUint32(offset, value, true);\n" \ + " break;\n" \ + " }\n" \ + " case 5: {\n" \ + " view.setUint32(offset, value | 0, true);\n" \ + " view.setUint8(offset + 4, Math.floor(value * 2 ** -32));\n" \ + " break;\n" \ + " }\n" \ + " case 6: {\n" \ + " view.setUint32(offset, value | 0, true);\n" \ + " view.setUint16(offset + 4, Math.floor(value * 2 ** -32), true);\n" \ + " break;\n" \ + " }\n" \ + " default: {\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + " }\n" \ + " }\n" \ + " return offset + byteLength;\n" \ + "})\n" \ +; + +const JSC::ConstructAbility s_jsBufferPrototypeWriteUIntBECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; +const JSC::ConstructorKind s_jsBufferPrototypeWriteUIntBECodeConstructorKind = JSC::ConstructorKind::None; +const JSC::ImplementationVisibility s_jsBufferPrototypeWriteUIntBECodeImplementationVisibility = JSC::ImplementationVisibility::Public; +const int s_jsBufferPrototypeWriteUIntBECodeLength = 961; +static const JSC::Intrinsic s_jsBufferPrototypeWriteUIntBECodeIntrinsic = JSC::NoIntrinsic; +const char* const s_jsBufferPrototypeWriteUIntBECode = + "(function (value, offset, byteLength) {\n" \ + " \"use strict\";\n" \ + " const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength);\n" \ + " switch (byteLength) {\n" \ + " case 1: {\n" \ + " view.setUint8(offset, value);\n" \ + " break;\n" \ + " }\n" \ + " case 2: {\n" \ + " view.setUint16(offset, value, false);\n" \ + " break;\n" \ + " }\n" \ + " case 3: {\n" \ + " view.setUint16(offset + 1, value & 0xFFFF, false);\n" \ + " view.setUint8(offset, Math.floor(value * 2 ** -16));\n" \ + " break;\n" \ + " }\n" \ + " case 4: {\n" \ + " view.setUint32(offset, value, false);\n" \ + " break;\n" \ + " }\n" \ + " case 5: {\n" \ + " view.setUint32(offset + 1, value | 0, false);\n" \ + " view.setUint8(offset, Math.floor(value * 2 ** -32));\n" \ + " break;\n" \ + " }\n" \ + " case 6: {\n" \ + " view.setUint32(offset + 2, value | 0, false);\n" \ + " view.setUint16(offset, Math.floor(value * 2 ** -32), false);\n" \ + " break;\n" \ + " }\n" \ + " default: {\n" \ + " @throwRangeError(\"byteLength must be >= 1 and <= 6\");\n" \ + " }\n" \ + " }\n" \ + " return offset + byteLength;\n" \ + "})\n" \ +; + const JSC::ConstructAbility s_jsBufferPrototypeWriteFloatLECodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_jsBufferPrototypeWriteFloatLECodeConstructorKind = JSC::ConstructorKind::None; const JSC::ImplementationVisibility s_jsBufferPrototypeWriteFloatLECodeImplementationVisibility = JSC::ImplementationVisibility::Public; @@ -719,7 +1039,7 @@ const char* const s_jsBufferPrototypeToJSONCode = const JSC::ConstructAbility s_jsBufferPrototypeSliceCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_jsBufferPrototypeSliceCodeConstructorKind = JSC::ConstructorKind::None; const JSC::ImplementationVisibility s_jsBufferPrototypeSliceCodeImplementationVisibility = JSC::ImplementationVisibility::Public; -const int s_jsBufferPrototypeSliceCodeLength = 612; +const int s_jsBufferPrototypeSliceCodeLength = 613; static const JSC::Intrinsic s_jsBufferPrototypeSliceCodeIntrinsic = JSC::NoIntrinsic; const char* const s_jsBufferPrototypeSliceCode = "(function (start, end) {\n" \ @@ -741,7 +1061,7 @@ const char* const s_jsBufferPrototypeSliceCode = " }\n" \ "\n" \ " var start_ = adjustOffset(start, byteLength);\n" \ - " var end_ = end !== undefined ? adjustOffset(end, byteLength) : byteLength;\n" \ + " var end_ = end !== @undefined ? adjustOffset(end, byteLength) : byteLength;\n" \ " return new Buffer(buffer, byteOffset + start_, end_ > start_ ? (end_ - start_) : 0);\n" \ "})\n" \ ; @@ -749,37 +1069,24 @@ const char* const s_jsBufferPrototypeSliceCode = const JSC::ConstructAbility s_jsBufferPrototypeParentCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_jsBufferPrototypeParentCodeConstructorKind = JSC::ConstructorKind::None; const JSC::ImplementationVisibility s_jsBufferPrototypeParentCodeImplementationVisibility = JSC::ImplementationVisibility::Public; -const int s_jsBufferPrototypeParentCodeLength = 57; +const int s_jsBufferPrototypeParentCodeLength = 114; static const JSC::Intrinsic s_jsBufferPrototypeParentCodeIntrinsic = JSC::NoIntrinsic; const char* const s_jsBufferPrototypeParentCode = "(function () {\n" \ " \"use strict\";\n" \ - " return this?.buffer;\n" \ + " return @isObject(this) && this instanceof @Buffer ? this.buffer : @undefined;\n" \ "})\n" \ ; const JSC::ConstructAbility s_jsBufferPrototypeOffsetCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; const JSC::ConstructorKind s_jsBufferPrototypeOffsetCodeConstructorKind = JSC::ConstructorKind::None; const JSC::ImplementationVisibility s_jsBufferPrototypeOffsetCodeImplementationVisibility = JSC::ImplementationVisibility::Public; -const int s_jsBufferPrototypeOffsetCodeLength = 61; +const int s_jsBufferPrototypeOffsetCodeLength = 118; static const JSC::Intrinsic s_jsBufferPrototypeOffsetCodeIntrinsic = JSC::NoIntrinsic; const char* const s_jsBufferPrototypeOffsetCode = "(function () {\n" \ " \"use strict\";\n" \ - " return this?.byteOffset;\n" \ - "})\n" \ -; - -const JSC::ConstructAbility s_jsBufferPrototypeInitializeBunBufferCodeConstructAbility = JSC::ConstructAbility::CannotConstruct; -const JSC::ConstructorKind s_jsBufferPrototypeInitializeBunBufferCodeConstructorKind = JSC::ConstructorKind::None; -const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBufferCodeImplementationVisibility = JSC::ImplementationVisibility::Public; -const int s_jsBufferPrototypeInitializeBunBufferCodeLength = 45; -static const JSC::Intrinsic s_jsBufferPrototypeInitializeBunBufferCodeIntrinsic = JSC::NoIntrinsic; -const char* const s_jsBufferPrototypeInitializeBunBufferCode = - "(function (parameters)\n" \ - "{\n" \ - " \"use strict\";\n" \ - "\n" \ + " return @isObject(this) && this instanceof @Buffer ? this.byteOffset : @undefined;\n" \ "})\n" \ ; diff --git a/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.h b/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.h index f7a3364deaabb9..60c00bedf60c50 100644 --- a/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.h +++ b/src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.h @@ -102,6 +102,26 @@ extern const int s_jsBufferPrototypeReadUInt32BECodeLength; extern const JSC::ConstructAbility s_jsBufferPrototypeReadUInt32BECodeConstructAbility; extern const JSC::ConstructorKind s_jsBufferPrototypeReadUInt32BECodeConstructorKind; extern const JSC::ImplementationVisibility s_jsBufferPrototypeReadUInt32BECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeReadIntLECode; +extern const int s_jsBufferPrototypeReadIntLECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeReadIntLECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeReadIntLECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeReadIntLECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeReadIntBECode; +extern const int s_jsBufferPrototypeReadIntBECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeReadIntBECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeReadIntBECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeReadIntBECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeReadUIntLECode; +extern const int s_jsBufferPrototypeReadUIntLECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeReadUIntLECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeReadUIntLECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeReadUIntLECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeReadUIntBECode; +extern const int s_jsBufferPrototypeReadUIntBECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeReadUIntBECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeReadUIntBECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeReadUIntBECodeImplementationVisibility; extern const char* const s_jsBufferPrototypeReadFloatLECode; extern const int s_jsBufferPrototypeReadFloatLECodeLength; extern const JSC::ConstructAbility s_jsBufferPrototypeReadFloatLECodeConstructAbility; @@ -192,6 +212,26 @@ extern const int s_jsBufferPrototypeWriteUInt32BECodeLength; extern const JSC::ConstructAbility s_jsBufferPrototypeWriteUInt32BECodeConstructAbility; extern const JSC::ConstructorKind s_jsBufferPrototypeWriteUInt32BECodeConstructorKind; extern const JSC::ImplementationVisibility s_jsBufferPrototypeWriteUInt32BECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeWriteIntLECode; +extern const int s_jsBufferPrototypeWriteIntLECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeWriteIntLECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeWriteIntLECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeWriteIntLECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeWriteIntBECode; +extern const int s_jsBufferPrototypeWriteIntBECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeWriteIntBECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeWriteIntBECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeWriteIntBECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeWriteUIntLECode; +extern const int s_jsBufferPrototypeWriteUIntLECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeWriteUIntLECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeWriteUIntLECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeWriteUIntLECodeImplementationVisibility; +extern const char* const s_jsBufferPrototypeWriteUIntBECode; +extern const int s_jsBufferPrototypeWriteUIntBECodeLength; +extern const JSC::ConstructAbility s_jsBufferPrototypeWriteUIntBECodeConstructAbility; +extern const JSC::ConstructorKind s_jsBufferPrototypeWriteUIntBECodeConstructorKind; +extern const JSC::ImplementationVisibility s_jsBufferPrototypeWriteUIntBECodeImplementationVisibility; extern const char* const s_jsBufferPrototypeWriteFloatLECode; extern const int s_jsBufferPrototypeWriteFloatLECodeLength; extern const JSC::ConstructAbility s_jsBufferPrototypeWriteFloatLECodeConstructAbility; @@ -332,11 +372,6 @@ extern const int s_jsBufferPrototypeOffsetCodeLength; extern const JSC::ConstructAbility s_jsBufferPrototypeOffsetCodeConstructAbility; extern const JSC::ConstructorKind s_jsBufferPrototypeOffsetCodeConstructorKind; extern const JSC::ImplementationVisibility s_jsBufferPrototypeOffsetCodeImplementationVisibility; -extern const char* const s_jsBufferPrototypeInitializeBunBufferCode; -extern const int s_jsBufferPrototypeInitializeBunBufferCodeLength; -extern const JSC::ConstructAbility s_jsBufferPrototypeInitializeBunBufferCodeConstructAbility; -extern const JSC::ConstructorKind s_jsBufferPrototypeInitializeBunBufferCodeConstructorKind; -extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBufferCodeImplementationVisibility; #define WEBCORE_FOREACH_JSBUFFERPROTOTYPE_BUILTIN_DATA(macro) \ macro(setBigUint64, jsBufferPrototypeSetBigUint64, 3) \ @@ -350,6 +385,10 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(readInt32BE, jsBufferPrototypeReadInt32BE, 1) \ macro(readUInt32LE, jsBufferPrototypeReadUInt32LE, 1) \ macro(readUInt32BE, jsBufferPrototypeReadUInt32BE, 1) \ + macro(readIntLE, jsBufferPrototypeReadIntLE, 2) \ + macro(readIntBE, jsBufferPrototypeReadIntBE, 2) \ + macro(readUIntLE, jsBufferPrototypeReadUIntLE, 2) \ + macro(readUIntBE, jsBufferPrototypeReadUIntBE, 2) \ macro(readFloatLE, jsBufferPrototypeReadFloatLE, 1) \ macro(readFloatBE, jsBufferPrototypeReadFloatBE, 1) \ macro(readDoubleLE, jsBufferPrototypeReadDoubleLE, 1) \ @@ -368,6 +407,10 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(writeInt32BE, jsBufferPrototypeWriteInt32BE, 2) \ macro(writeUInt32LE, jsBufferPrototypeWriteUInt32LE, 2) \ macro(writeUInt32BE, jsBufferPrototypeWriteUInt32BE, 2) \ + macro(writeIntLE, jsBufferPrototypeWriteIntLE, 3) \ + macro(writeIntBE, jsBufferPrototypeWriteIntBE, 3) \ + macro(writeUIntLE, jsBufferPrototypeWriteUIntLE, 3) \ + macro(writeUIntBE, jsBufferPrototypeWriteUIntBE, 3) \ macro(writeFloatLE, jsBufferPrototypeWriteFloatLE, 2) \ macro(writeFloatBE, jsBufferPrototypeWriteFloatBE, 2) \ macro(writeDoubleLE, jsBufferPrototypeWriteDoubleLE, 2) \ @@ -396,7 +439,6 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(slice, jsBufferPrototypeSlice, 2) \ macro(parent, jsBufferPrototypeParent, 0) \ macro(offset, jsBufferPrototypeOffset, 0) \ - macro(initializeBunBuffer, jsBufferPrototypeInitializeBunBuffer, 1) \ #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_SETBIGUINT64 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READINT8 1 @@ -409,6 +451,10 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READINT32BE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READUINT32LE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READUINT32BE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READINTLE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READINTBE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READUINTLE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READUINTBE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READFLOATLE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READFLOATBE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_READDOUBLELE 1 @@ -427,6 +473,10 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEINT32BE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEUINT32LE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEUINT32BE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEINTLE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEINTBE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEUINTLE 1 +#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEUINTBE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEFLOATLE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEFLOATBE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_WRITEDOUBLELE 1 @@ -455,7 +505,6 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_SLICE 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_PARENT 1 #define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_OFFSET 1 -#define WEBCORE_BUILTIN_JSBUFFERPROTOTYPE_INITIALIZEBUNBUFFER 1 #define WEBCORE_FOREACH_JSBUFFERPROTOTYPE_BUILTIN_CODE(macro) \ macro(jsBufferPrototypeSetBigUint64Code, setBigUint64, ASCIILiteral(), s_jsBufferPrototypeSetBigUint64CodeLength) \ @@ -469,6 +518,10 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(jsBufferPrototypeReadInt32BECode, readInt32BE, ASCIILiteral(), s_jsBufferPrototypeReadInt32BECodeLength) \ macro(jsBufferPrototypeReadUInt32LECode, readUInt32LE, ASCIILiteral(), s_jsBufferPrototypeReadUInt32LECodeLength) \ macro(jsBufferPrototypeReadUInt32BECode, readUInt32BE, ASCIILiteral(), s_jsBufferPrototypeReadUInt32BECodeLength) \ + macro(jsBufferPrototypeReadIntLECode, readIntLE, ASCIILiteral(), s_jsBufferPrototypeReadIntLECodeLength) \ + macro(jsBufferPrototypeReadIntBECode, readIntBE, ASCIILiteral(), s_jsBufferPrototypeReadIntBECodeLength) \ + macro(jsBufferPrototypeReadUIntLECode, readUIntLE, ASCIILiteral(), s_jsBufferPrototypeReadUIntLECodeLength) \ + macro(jsBufferPrototypeReadUIntBECode, readUIntBE, ASCIILiteral(), s_jsBufferPrototypeReadUIntBECodeLength) \ macro(jsBufferPrototypeReadFloatLECode, readFloatLE, ASCIILiteral(), s_jsBufferPrototypeReadFloatLECodeLength) \ macro(jsBufferPrototypeReadFloatBECode, readFloatBE, ASCIILiteral(), s_jsBufferPrototypeReadFloatBECodeLength) \ macro(jsBufferPrototypeReadDoubleLECode, readDoubleLE, ASCIILiteral(), s_jsBufferPrototypeReadDoubleLECodeLength) \ @@ -487,6 +540,10 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(jsBufferPrototypeWriteInt32BECode, writeInt32BE, ASCIILiteral(), s_jsBufferPrototypeWriteInt32BECodeLength) \ macro(jsBufferPrototypeWriteUInt32LECode, writeUInt32LE, ASCIILiteral(), s_jsBufferPrototypeWriteUInt32LECodeLength) \ macro(jsBufferPrototypeWriteUInt32BECode, writeUInt32BE, ASCIILiteral(), s_jsBufferPrototypeWriteUInt32BECodeLength) \ + macro(jsBufferPrototypeWriteIntLECode, writeIntLE, ASCIILiteral(), s_jsBufferPrototypeWriteIntLECodeLength) \ + macro(jsBufferPrototypeWriteIntBECode, writeIntBE, ASCIILiteral(), s_jsBufferPrototypeWriteIntBECodeLength) \ + macro(jsBufferPrototypeWriteUIntLECode, writeUIntLE, ASCIILiteral(), s_jsBufferPrototypeWriteUIntLECodeLength) \ + macro(jsBufferPrototypeWriteUIntBECode, writeUIntBE, ASCIILiteral(), s_jsBufferPrototypeWriteUIntBECodeLength) \ macro(jsBufferPrototypeWriteFloatLECode, writeFloatLE, ASCIILiteral(), s_jsBufferPrototypeWriteFloatLECodeLength) \ macro(jsBufferPrototypeWriteFloatBECode, writeFloatBE, ASCIILiteral(), s_jsBufferPrototypeWriteFloatBECodeLength) \ macro(jsBufferPrototypeWriteDoubleLECode, writeDoubleLE, ASCIILiteral(), s_jsBufferPrototypeWriteDoubleLECodeLength) \ @@ -515,7 +572,6 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(jsBufferPrototypeSliceCode, slice, ASCIILiteral(), s_jsBufferPrototypeSliceCodeLength) \ macro(jsBufferPrototypeParentCode, parent, "get parent"_s, s_jsBufferPrototypeParentCodeLength) \ macro(jsBufferPrototypeOffsetCode, offset, "get offset"_s, s_jsBufferPrototypeOffsetCodeLength) \ - macro(jsBufferPrototypeInitializeBunBufferCode, initializeBunBuffer, ASCIILiteral(), s_jsBufferPrototypeInitializeBunBufferCodeLength) \ #define WEBCORE_FOREACH_JSBUFFERPROTOTYPE_BUILTIN_FUNCTION_NAME(macro) \ macro(asciiSlice) \ @@ -526,7 +582,6 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(base64urlWrite) \ macro(hexSlice) \ macro(hexWrite) \ - macro(initializeBunBuffer) \ macro(latin1Slice) \ macro(latin1Write) \ macro(offset) \ @@ -544,11 +599,15 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(readInt32BE) \ macro(readInt32LE) \ macro(readInt8) \ + macro(readIntBE) \ + macro(readIntLE) \ macro(readUInt16BE) \ macro(readUInt16LE) \ macro(readUInt32BE) \ macro(readUInt32LE) \ macro(readUInt8) \ + macro(readUIntBE) \ + macro(readUIntLE) \ macro(setBigUint64) \ macro(slice) \ macro(toJSON) \ @@ -571,11 +630,15 @@ extern const JSC::ImplementationVisibility s_jsBufferPrototypeInitializeBunBuffe macro(writeInt32BE) \ macro(writeInt32LE) \ macro(writeInt8) \ + macro(writeIntBE) \ + macro(writeIntLE) \ macro(writeUInt16BE) \ macro(writeUInt16LE) \ macro(writeUInt32BE) \ macro(writeUInt32LE) \ macro(writeUInt8) \ + macro(writeUIntBE) \ + macro(writeUIntLE) \ #define DECLARE_BUILTIN_GENERATOR(codeName, functionName, overriddenName, argumentCount) \ JSC::FunctionExecutable* codeName##Generator(JSC::VM&); diff --git a/src/bun.js/builtins/js/JSBufferPrototype.js b/src/bun.js/builtins/js/JSBufferPrototype.js index 827e613d84c040..32aebb3f2ce98a 100644 --- a/src/bun.js/builtins/js/JSBufferPrototype.js +++ b/src/bun.js/builtins/js/JSBufferPrototype.js @@ -73,6 +73,116 @@ function readUInt32BE(offset) { "use strict"; return (this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getUint32(offset, false); } + +function readIntLE(offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + return view.getInt8(offset); + } + case 2: { + return view.getInt16(offset, true); + } + case 3: { + const val = view.getUint16(offset, true) + view.getUint8(offset + 2) * 2 ** 16; + return val | (val & 2 ** 23) * 0x1fe; + } + case 4: { + return view.getInt32(offset, true); + } + case 5: { + const last = view.getUint8(offset + 4); + return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + view.getUint32(offset, true); + } + case 6: { + const last = view.getUint16(offset + 4, true); + return (last | (last & 2 ** 15) * 0x1fffe) * 2 ** 32 + view.getUint32(offset, true); + } + } + @throwRangeError("byteLength must be >= 1 and <= 6"); +} +function readIntBE(offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + return view.getInt8(offset); + } + case 2: { + return view.getInt16(offset, false); + } + case 3: { + const val = view.getUint16(offset + 1, false) + view.getUint8(offset) * 2 ** 16; + return val | (val & 2 ** 23) * 0x1fe; + } + case 4: { + return view.getInt32(offset, false); + } + case 5: { + const last = view.getUint8(offset); + return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + view.getUint32(offset + 1, false); + } + case 6: { + const last = view.getUint16(offset, false); + return (last | (last & 2 ** 15) * 0x1fffe) * 2 ** 32 + view.getUint32(offset + 2, false); + } + } + @throwRangeError("byteLength must be >= 1 and <= 6"); +} +function readUIntLE(offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + return view.getUint8(offset); + } + case 2: { + return view.getUint16(offset, true); + } + case 3: { + return view.getUint16(offset, true) + view.getUint8(offset + 2) * 2 ** 16; + } + case 4: { + return view.getUint32(offset, true); + } + case 5: { + return view.getUint8(offset + 4) * 2 ** 32 + view.getUint32(offset, true); + } + case 6: { + return view.getUint16(offset + 4, true) * 2 ** 32 + view.getUint32(offset, true); + } + } + @throwRangeError("byteLength must be >= 1 and <= 6"); +} +function readUIntBE(offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + return view.getUint8(offset); + } + case 2: { + return view.getUint16(offset, false); + } + case 3: { + return view.getUint16(offset + 1, false) + view.getUint8(offset) * 2 ** 16; + } + case 4: { + return view.getUint32(offset, false); + } + case 5: { + const last = view.getUint8(offset); + return (last | (last & 2 ** 7) * 0x1fffffe) * 2 ** 32 + view.getUint32(offset + 1, false); + } + case 6: { + const last = view.getUint16(offset, false); + return (last | (last & 2 ** 15) * 0x1fffe) * 2 ** 32 + view.getUint32(offset + 2, false); + } + } + @throwRangeError("byteLength must be >= 1 and <= 6"); +} + function readFloatLE(offset) { "use strict"; return (this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getFloat32(offset, true); @@ -105,6 +215,7 @@ function readBigUInt64BE(offset) { "use strict"; return (this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).getBigUint64(offset, false); } + function writeInt8(value, offset) { "use strict"; (this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setInt8(offset, value); @@ -156,6 +267,155 @@ function writeUInt32BE(value, offset) { return offset + 4; } +function writeIntLE(value, offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + view.setInt8(offset, value); + break; + } + case 2: { + view.setInt16(offset, value, true); + break; + } + case 3: { + view.setUint16(offset, value & 0xFFFF, true); + view.setInt8(offset + 2, Math.floor(value * 2 ** -16)); + break; + } + case 4: { + view.setInt32(offset, value, true); + break; + } + case 5: { + view.setUint32(offset, value | 0, true); + view.setInt8(offset + 4, Math.floor(value * 2 ** -32)); + break; + } + case 6: { + view.setUint32(offset, value | 0, true); + view.setInt16(offset + 4, Math.floor(value * 2 ** -32), true); + break; + } + default: { + @throwRangeError("byteLength must be >= 1 and <= 6"); + } + } + return offset + byteLength; +} +function writeIntBE(value, offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + view.setInt8(offset, value); + break; + } + case 2: { + view.setInt16(offset, value, false); + break; + } + case 3: { + view.setUint16(offset + 1, value & 0xFFFF, false); + view.setInt8(offset, Math.floor(value * 2 ** -16)); + break; + } + case 4: { + view.setInt32(offset, value, false); + break; + } + case 5: { + view.setUint32(offset + 1, value | 0, false); + view.setInt8(offset, Math.floor(value * 2 ** -32)); + break; + } + case 6: { + view.setUint32(offset + 2, value | 0, false); + view.setInt16(offset, Math.floor(value * 2 ** -32), false); + break; + } + default: { + @throwRangeError("byteLength must be >= 1 and <= 6"); + } + } + return offset + byteLength; +} +function writeUIntLE(value, offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + view.setUint8(offset, value); + break; + } + case 2: { + view.setUint16(offset, value, true); + break; + } + case 3: { + view.setUint16(offset, value & 0xFFFF, true); + view.setUint8(offset + 2, Math.floor(value * 2 ** -16)); + break; + } + case 4: { + view.setUint32(offset, value, true); + break; + } + case 5: { + view.setUint32(offset, value | 0, true); + view.setUint8(offset + 4, Math.floor(value * 2 ** -32)); + break; + } + case 6: { + view.setUint32(offset, value | 0, true); + view.setUint16(offset + 4, Math.floor(value * 2 ** -32), true); + break; + } + default: { + @throwRangeError("byteLength must be >= 1 and <= 6"); + } + } + return offset + byteLength; +} +function writeUIntBE(value, offset, byteLength) { + "use strict"; + const view = this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength); + switch (byteLength) { + case 1: { + view.setUint8(offset, value); + break; + } + case 2: { + view.setUint16(offset, value, false); + break; + } + case 3: { + view.setUint16(offset + 1, value & 0xFFFF, false); + view.setUint8(offset, Math.floor(value * 2 ** -16)); + break; + } + case 4: { + view.setUint32(offset, value, false); + break; + } + case 5: { + view.setUint32(offset + 1, value | 0, false); + view.setUint8(offset, Math.floor(value * 2 ** -32)); + break; + } + case 6: { + view.setUint32(offset + 2, value | 0, false); + view.setUint16(offset, Math.floor(value * 2 ** -32), false); + break; + } + default: { + @throwRangeError("byteLength must be >= 1 and <= 6"); + } + } + return offset + byteLength; +} + function writeFloatLE(value, offset) { "use strict"; (this.@dataView ||= new DataView(this.buffer, this.byteOffset, this.byteLength)).setFloat32(offset, value, true); @@ -296,24 +556,18 @@ function slice(start, end) { } var start_ = adjustOffset(start, byteLength); - var end_ = end !== undefined ? adjustOffset(end, byteLength) : byteLength; + var end_ = end !== @undefined ? adjustOffset(end, byteLength) : byteLength; return new Buffer(buffer, byteOffset + start_, end_ > start_ ? (end_ - start_) : 0); } @getter function parent() { "use strict"; - return this?.buffer; + return @isObject(this) && this instanceof @Buffer ? this.buffer : @undefined; } @getter function offset() { "use strict"; - return this?.byteOffset; -} - -function initializeBunBuffer(parameters) -{ - "use strict"; - + return @isObject(this) && this instanceof @Buffer ? this.byteOffset : @undefined; } diff --git a/src/bun.js/node/buffer.zig b/src/bun.js/node/buffer.zig index 5a7d64955087aa..f3cc3c2c290993 100644 --- a/src/bun.js/node/buffer.zig +++ b/src/bun.js/node/buffer.zig @@ -1,96 +1,72 @@ -const std = @import("std"); const bun = @import("bun"); -const strings = bun.strings; -const string = bun.string; -const AsyncIO = @import("bun").AsyncIO; -const JSC = @import("bun").JSC; -const PathString = JSC.PathString; -const Environment = bun.Environment; -const C = bun.C; -const Syscall = @import("./syscall.zig"); -const os = std.os; - -const JSGlobalObject = JSC.JSGlobalObject; -const ArgumentsSlice = JSC.Node.ArgumentsSlice; +const JSC = bun.JSC; +const Encoder = JSC.WebCore.Encoder; pub const BufferVectorized = struct { - extern fn memset_pattern16(b: *anyopaque, pattern16: *const anyopaque, len: usize) void; - pub fn fill( str: *JSC.ZigString, buf_ptr: [*]u8, fill_length: usize, encoding: JSC.Node.Encoding, - ) callconv(.C) void { - if (str.len == 0) return; + ) callconv(.C) bool { + if (str.len == 0) return true; var buf = buf_ptr[0..fill_length]; const written = switch (encoding) { - JSC.Node.Encoding.utf8 => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.utf8, true) + .utf8 => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .utf8, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.utf8), - JSC.Node.Encoding.ascii => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.ascii, true) + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .utf8), + .ascii => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .ascii, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.ascii), - JSC.Node.Encoding.latin1 => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.latin1, true) + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .ascii), + .latin1 => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .latin1, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.latin1), - JSC.Node.Encoding.buffer => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.buffer, true) + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .latin1), + .buffer => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .buffer, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.buffer), - JSC.Node.Encoding.utf16le, - JSC.Node.Encoding.ucs2, - => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.utf16le, true) + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .buffer), + .utf16le, .ucs2 => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .utf16le, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.utf16le), - JSC.Node.Encoding.base64 => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.base64, true) + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .utf16le), + .base64 => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .base64, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.base64), - JSC.Node.Encoding.base64url => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.base64url, true) + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .base64), + .base64url => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .base64url, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.base64url), - JSC.Node.Encoding.hex => if (str.is16Bit()) - JSC.WebCore.Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, JSC.Node.Encoding.hex, true) + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .base64url), + .hex => if (str.is16Bit()) + Encoder.writeU16(str.utf16SliceAligned().ptr, str.utf16SliceAligned().len, buf.ptr, buf.len, .hex, true) else - JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.hex), - }; - - if (written <= 0) { - return; - } + Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, .hex), + } catch return false; - var contents = buf[0..@intCast(usize, written)]; - buf = buf[@intCast(usize, written)..]; + switch (written) { + 0 => {}, + 1 => @memset(buf.ptr, buf[0], buf.len), + else => { + var contents = buf[0..written]; + buf = buf[written..]; - if (contents.len == 1) { - @memset(buf.ptr, contents[0], buf.len); - return; - } - - const minimum_contents = contents; - while (buf.len >= contents.len) { - const min_len = @min(contents.len, buf.len); - bun.copy(u8, buf, contents[0..min_len]); - if (buf.len <= contents.len) { - break; - } - buf = buf[min_len..]; - contents.len *= 2; - } + while (buf.len >= contents.len) { + bun.copy(u8, buf, contents); + buf = buf[contents.len..]; + contents.len *= 2; + } - while (buf.len > 0) { - const to_fill = @min(minimum_contents.len, buf.len); - bun.copy(u8, buf, minimum_contents[0..to_fill]); - buf = buf[to_fill..]; + if (buf.len > 0) { + bun.copy(u8, buf, contents[0..buf.len]); + } + }, } + return true; } }; diff --git a/src/bun.js/webcore/base64.zig b/src/bun.js/webcore/base64.zig deleted file mode 100644 index aad1c471b7fed0..00000000000000 --- a/src/bun.js/webcore/base64.zig +++ /dev/null @@ -1,445 +0,0 @@ -// this is ripped from zig's stdlib -const std = @import("std"); -const assert = std.debug.assert; -const testing = std.testing; -const mem = std.mem; - -pub const Error = error{ - InvalidCharacter, - InvalidPadding, - NoSpaceLeft, -}; - -/// Base64 codecs -pub const Codecs = struct { - alphabet_chars: [64]u8, - pad_char: ?u8, - decoderWithIgnore: fn (ignore: []const u8) Base64DecoderWithIgnore, - Encoder: Base64Encoder, - Decoder: Base64Decoder, -}; - -pub const standard_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".*; -fn standardBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore { - return Base64DecoderWithIgnore.init(standard_alphabet_chars, '=', ignore); -} - -/// Standard Base64 codecs, with padding -pub const standard = Codecs{ - .alphabet_chars = standard_alphabet_chars, - .pad_char = '=', - .decoderWithIgnore = standardBase64DecoderWithIgnore, - .Encoder = Base64Encoder.init(standard_alphabet_chars, '='), - .Decoder = Base64Decoder.init(standard_alphabet_chars, '='), -}; - -/// Standard Base64 codecs, without padding -pub const standard_no_pad = Codecs{ - .alphabet_chars = standard_alphabet_chars, - .pad_char = null, - .decoderWithIgnore = standardBase64DecoderWithIgnore, - .Encoder = Base64Encoder.init(standard_alphabet_chars, null), - .Decoder = Base64Decoder.init(standard_alphabet_chars, null), -}; - -pub const url_safe_alphabet_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".*; -fn urlSafeBase64DecoderWithIgnore(ignore: []const u8) Base64DecoderWithIgnore { - return Base64DecoderWithIgnore.init(url_safe_alphabet_chars, null, ignore); -} - -/// URL-safe Base64 codecs, with padding -pub const url_safe = Codecs{ - .alphabet_chars = url_safe_alphabet_chars, - .pad_char = '=', - .decoderWithIgnore = urlSafeBase64DecoderWithIgnore, - .Encoder = Base64Encoder.init(url_safe_alphabet_chars, '='), - .Decoder = Base64Decoder.init(url_safe_alphabet_chars, '='), -}; - -/// URL-safe Base64 codecs, without padding -pub const url_safe_no_pad = Codecs{ - .alphabet_chars = url_safe_alphabet_chars, - .pad_char = null, - .decoderWithIgnore = urlSafeBase64DecoderWithIgnore, - .Encoder = Base64Encoder.init(url_safe_alphabet_chars, null), - .Decoder = Base64Decoder.init(url_safe_alphabet_chars, null), -}; - -pub const standard_pad_char = @compileError("deprecated; use standard.pad_char"); -pub const standard_encoder = @compileError("deprecated; use standard.Encoder"); -pub const standard_decoder = @compileError("deprecated; use standard.Decoder"); - -pub const Base64Encoder = struct { - alphabet_chars: [64]u8, - pad_char: ?u8, - - /// A bunch of assertions, then simply pass the data right through. - pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Encoder { - assert(alphabet_chars.len == 64); - var char_in_alphabet = [_]bool{false} ** 256; - for (alphabet_chars) |c| { - assert(!char_in_alphabet[c]); - assert(pad_char == null or c != pad_char.?); - char_in_alphabet[c] = true; - } - return Base64Encoder{ - .alphabet_chars = alphabet_chars, - .pad_char = pad_char, - }; - } - - /// Compute the encoded length - pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize { - if (encoder.pad_char != null) { - return @divTrunc(source_len + 2, 3) * 4; - } else { - const leftover = source_len % 3; - return @divTrunc(source_len, 3) * 4 + @divTrunc(leftover * 4 + 2, 3); - } - } - - /// dest.len must at least be what you get from ::calcSize. - pub fn encode(encoder: *const Base64Encoder, dest: []u8, source: []const u8) []const u8 { - const out_len = encoder.calcSize(source.len); - assert(dest.len >= out_len); - - var acc: u12 = 0; - var acc_len: u4 = 0; - var out_idx: usize = 0; - for (source) |v| { - acc = (acc << 8) + v; - acc_len += 8; - while (acc_len >= 6) { - acc_len -= 6; - dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc >> acc_len))]; - out_idx += 1; - } - } - if (acc_len > 0) { - dest[out_idx] = encoder.alphabet_chars[@truncate(u6, (acc << 6 - acc_len))]; - out_idx += 1; - } - if (encoder.pad_char) |pad_char| { - for (&dest[out_idx..]) |*pad| { - pad.* = pad_char; - } - } - return dest[0..out_len]; - } -}; - -pub const Base64Decoder = struct { - const invalid_char: u8 = 0xff; - - /// e.g. 'A' => 0. - /// `invalid_char` for any value not in the 64 alphabet chars. - char_to_index: [256]u8, - pad_char: ?u8, - - pub fn init(alphabet_chars: [64]u8, pad_char: ?u8) Base64Decoder { - var result = Base64Decoder{ - .char_to_index = [_]u8{invalid_char} ** 256, - .pad_char = pad_char, - }; - - var char_in_alphabet = [_]bool{false} ** 256; - for (alphabet_chars, 0..) |c, i| { - assert(!char_in_alphabet[c]); - assert(pad_char == null or c != pad_char.?); - - result.char_to_index[c] = @intCast(u8, i); - char_in_alphabet[c] = true; - } - return result; - } - - /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding. - /// `InvalidPadding` is returned if the input length is not valid. - pub fn calcSizeUpperBound(decoder: *const Base64Decoder, source_len: usize) Error!usize { - var result = source_len / 4 * 3; - const leftover = source_len % 4; - if (decoder.pad_char != null) { - if (leftover % 4 != 0) return error.InvalidPadding; - } else { - if (leftover % 4 == 1) return error.InvalidPadding; - result += leftover * 3 / 4; - } - return result; - } - - /// Return the exact decoded size for a slice. - /// `InvalidPadding` is returned if the input length is not valid. - pub fn calcSizeForSlice(decoder: *const Base64Decoder, source: []const u8) Error!usize { - const source_len = source.len; - var result = try decoder.calcSizeUpperBound(source_len); - if (decoder.pad_char) |pad_char| { - if (source_len >= 1 and source[source_len - 1] == pad_char) result -= 1; - if (source_len >= 2 and source[source_len - 2] == pad_char) result -= 1; - } - return result; - } - - /// dest.len must be what you get from ::calcSize. - /// invalid characters result in error.InvalidCharacter. - /// invalid padding results in error.InvalidPadding. - pub fn decode(decoder: *const Base64Decoder, dest: []u8, source: []const u8) Error!void { - if (decoder.pad_char != null and source.len % 4 != 0) return error.InvalidPadding; - var acc: u12 = 0; - var acc_len: u4 = 0; - var dest_idx: usize = 0; - var leftover_idx: ?usize = null; - for (source, 0..) |c, src_idx| { - const d = decoder.char_to_index[c]; - if (d == invalid_char) { - if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter; - leftover_idx = src_idx; - break; - } - acc = (acc << 6) + d; - acc_len += 6; - if (acc_len >= 8) { - acc_len -= 8; - dest[dest_idx] = @truncate(u8, acc >> acc_len); - dest_idx += 1; - } - } - if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) { - return error.InvalidPadding; - } - if (leftover_idx == null) return; - var leftover = source[leftover_idx.?..]; - if (decoder.pad_char) |pad_char| { - const padding_len = acc_len / 2; - var padding_chars: usize = 0; - for (leftover) |c| { - if (c != pad_char) { - return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding; - } - padding_chars += 1; - } - if (padding_chars != padding_len) return error.InvalidPadding; - } - } -}; - -pub const Base64DecoderWithIgnore = struct { - decoder: Base64Decoder, - char_is_ignored: [256]bool, - - pub fn init(alphabet_chars: [64]u8, pad_char: ?u8, ignore_chars: []const u8) Base64DecoderWithIgnore { - var result = Base64DecoderWithIgnore{ - .decoder = Base64Decoder.init(alphabet_chars, pad_char), - .char_is_ignored = [_]bool{false} ** 256, - }; - for (ignore_chars) |c| { - assert(result.decoder.char_to_index[c] == Base64Decoder.invalid_char); - assert(!result.char_is_ignored[c]); - assert(result.decoder.pad_char != c); - result.char_is_ignored[c] = true; - } - return result; - } - - /// Return the maximum possible decoded size for a given input length - The actual length may be less if the input includes padding - /// `InvalidPadding` is returned if the input length is not valid. - pub fn calcSizeUpperBound(decoder_with_ignore: *const Base64DecoderWithIgnore, source_len: usize) Error!usize { - var result = source_len / 4 * 3; - if (decoder_with_ignore.decoder.pad_char == null) { - const leftover = source_len % 4; - result += leftover * 3 / 4; - } - return result; - } - - /// Invalid characters that are not ignored result in error.InvalidCharacter. - /// Invalid padding results in error.InvalidPadding. - /// Decoding more data than can fit in dest results in error.NoSpaceLeft. See also ::calcSizeUpperBound. - /// Returns the number of bytes written to dest. - pub fn decode(decoder_with_ignore: *const Base64DecoderWithIgnore, dest: []u8, source: []const u8) Error!usize { - const decoder = &decoder_with_ignore.decoder; - var acc: u12 = 0; - var acc_len: u4 = 0; - var dest_idx: usize = 0; - var leftover_idx: ?usize = null; - for (source, 0..) |c, src_idx| { - if (decoder_with_ignore.char_is_ignored[c]) continue; - const d = decoder.char_to_index[c]; - if (d == Base64Decoder.invalid_char) { - if (decoder.pad_char == null or c != decoder.pad_char.?) return error.InvalidCharacter; - leftover_idx = src_idx; - break; - } - acc = (acc << 6) + d; - acc_len += 6; - if (acc_len >= 8) { - if (dest_idx == dest.len) return error.NoSpaceLeft; - acc_len -= 8; - dest[dest_idx] = @truncate(u8, acc >> acc_len); - dest_idx += 1; - } - } - if (acc_len > 4 or (acc & (@as(u12, 1) << acc_len) - 1) != 0) { - return error.InvalidPadding; - } - const padding_len = acc_len / 2; - if (leftover_idx == null) { - if (decoder.pad_char != null and padding_len != 0) return error.InvalidPadding; - return dest_idx; - } - var leftover = source[leftover_idx.?..]; - if (decoder.pad_char) |pad_char| { - var padding_chars: usize = 0; - for (leftover) |c| { - if (decoder_with_ignore.char_is_ignored[c]) continue; - if (c != pad_char) { - return if (c == Base64Decoder.invalid_char) error.InvalidCharacter else error.InvalidPadding; - } - padding_chars += 1; - } - if (padding_chars != padding_len) return error.InvalidPadding; - } - return dest_idx; - } -}; - -test "base64" { - @setEvalBranchQuota(8000); - try testBase64(); - comptime try testAllApis(standard, "comptime", "Y29tcHRpbWU="); -} - -test "base64 url_safe_no_pad" { - @setEvalBranchQuota(8000); - try testBase64UrlSafeNoPad(); - comptime try testAllApis(url_safe_no_pad, "comptime", "Y29tcHRpbWU"); -} - -fn testBase64() !void { - const codecs = standard; - - try testAllApis(codecs, "", ""); - try testAllApis(codecs, "f", "Zg=="); - try testAllApis(codecs, "fo", "Zm8="); - try testAllApis(codecs, "foo", "Zm9v"); - try testAllApis(codecs, "foob", "Zm9vYg=="); - try testAllApis(codecs, "fooba", "Zm9vYmE="); - try testAllApis(codecs, "foobar", "Zm9vYmFy"); - - try testDecodeIgnoreSpace(codecs, "", " "); - try testDecodeIgnoreSpace(codecs, "f", "Z g= ="); - try testDecodeIgnoreSpace(codecs, "fo", " Zm8="); - try testDecodeIgnoreSpace(codecs, "foo", "Zm9v "); - try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg = = "); - try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE="); - try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y "); - - // test getting some api errors - try testError(codecs, "A", error.InvalidPadding); - try testError(codecs, "AA", error.InvalidPadding); - try testError(codecs, "AAA", error.InvalidPadding); - try testError(codecs, "A..A", error.InvalidCharacter); - try testError(codecs, "AA=A", error.InvalidPadding); - try testError(codecs, "AA/=", error.InvalidPadding); - try testError(codecs, "A/==", error.InvalidPadding); - try testError(codecs, "A===", error.InvalidPadding); - try testError(codecs, "====", error.InvalidPadding); - - try testNoSpaceLeftError(codecs, "AA=="); - try testNoSpaceLeftError(codecs, "AAA="); - try testNoSpaceLeftError(codecs, "AAAA"); - try testNoSpaceLeftError(codecs, "AAAAAA=="); -} - -fn testBase64UrlSafeNoPad() !void { - const codecs = url_safe_no_pad; - - try testAllApis(codecs, "", ""); - try testAllApis(codecs, "f", "Zg"); - try testAllApis(codecs, "fo", "Zm8"); - try testAllApis(codecs, "foo", "Zm9v"); - try testAllApis(codecs, "foob", "Zm9vYg"); - try testAllApis(codecs, "fooba", "Zm9vYmE"); - try testAllApis(codecs, "foobar", "Zm9vYmFy"); - - try testDecodeIgnoreSpace(codecs, "", " "); - try testDecodeIgnoreSpace(codecs, "f", "Z g "); - try testDecodeIgnoreSpace(codecs, "fo", " Zm8"); - try testDecodeIgnoreSpace(codecs, "foo", "Zm9v "); - try testDecodeIgnoreSpace(codecs, "foob", "Zm9vYg "); - try testDecodeIgnoreSpace(codecs, "fooba", "Zm9v YmE"); - try testDecodeIgnoreSpace(codecs, "foobar", " Z m 9 v Y m F y "); - - // test getting some api errors - try testError(codecs, "A", error.InvalidPadding); - try testError(codecs, "AAA=", error.InvalidCharacter); - try testError(codecs, "A..A", error.InvalidCharacter); - try testError(codecs, "AA=A", error.InvalidCharacter); - try testError(codecs, "AA/=", error.InvalidCharacter); - try testError(codecs, "A/==", error.InvalidCharacter); - try testError(codecs, "A===", error.InvalidCharacter); - try testError(codecs, "====", error.InvalidCharacter); - - try testNoSpaceLeftError(codecs, "AA"); - try testNoSpaceLeftError(codecs, "AAA"); - try testNoSpaceLeftError(codecs, "AAAA"); - try testNoSpaceLeftError(codecs, "AAAAAA"); -} - -fn testAllApis(codecs: Codecs, expected_decoded: []const u8, expected_encoded: []const u8) !void { - // Base64Encoder - { - var buffer: [0x100]u8 = undefined; - const encoded = codecs.Encoder.encode(&buffer, expected_decoded); - try testing.expectEqualSlices(u8, expected_encoded, encoded); - } - - // Base64Decoder - { - var buffer: [0x100]u8 = undefined; - var decoded = buffer[0..try codecs.Decoder.calcSizeForSlice(expected_encoded)]; - try codecs.Decoder.decode(decoded, expected_encoded); - try testing.expectEqualSlices(u8, expected_decoded, decoded); - } - - // Base64DecoderWithIgnore - { - const decoder_ignore_nothing = codecs.decoderWithIgnore(""); - var buffer: [0x100]u8 = undefined; - var decoded = buffer[0..try decoder_ignore_nothing.calcSizeUpperBound(expected_encoded.len)]; - var written = try decoder_ignore_nothing.decode(decoded, expected_encoded); - try testing.expect(written <= decoded.len); - try testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); - } -} - -fn testDecodeIgnoreSpace(codecs: Codecs, expected_decoded: []const u8, encoded: []const u8) !void { - const decoder_ignore_space = codecs.decoderWithIgnore(" "); - var buffer: [0x100]u8 = undefined; - var decoded = buffer[0..try decoder_ignore_space.calcSizeUpperBound(encoded.len)]; - var written = try decoder_ignore_space.decode(decoded, encoded); - try testing.expectEqualSlices(u8, expected_decoded, decoded[0..written]); -} - -fn testError(codecs: Codecs, encoded: []const u8, expected_err: anyerror) !void { - const decoder_ignore_space = codecs.decoderWithIgnore(" "); - var buffer: [0x100]u8 = undefined; - if (codecs.Decoder.calcSizeForSlice(encoded)) |decoded_size| { - var decoded = buffer[0..decoded_size]; - if (codecs.Decoder.decode(decoded, encoded)) |_| { - return error.ExpectedError; - } else |err| if (err != expected_err) return err; - } else |err| if (err != expected_err) return err; - - if (decoder_ignore_space.decode(buffer[0..], encoded)) |_| { - return error.ExpectedError; - } else |err| if (err != expected_err) return err; -} - -fn testNoSpaceLeftError(codecs: Codecs, encoded: []const u8) !void { - const decoder_ignore_space = codecs.decoderWithIgnore(" "); - var buffer: [0x100]u8 = undefined; - var decoded = buffer[0 .. (try codecs.Decoder.calcSizeForSlice(encoded)) - 1]; - if (decoder_ignore_space.decode(decoded, encoded)) |_| { - return error.ExpectedError; - } else |err| if (err != error.NoSpaceLeft) return err; -} diff --git a/src/bun.js/webcore/body.zig b/src/bun.js/webcore/body.zig index 97c9f78748da7b..d4bd490c174b9e 100644 --- a/src/bun.js/webcore/body.zig +++ b/src/bun.js/webcore/body.zig @@ -477,6 +477,7 @@ pub const Body = struct { // blob.bytes[0..blob.bytes.len], // []const u16, // str.utf16SliceAligned(), + // true, // ); // blob.len = @intCast(InlineBlob.IntSize, result.written); // std.debug.assert(@as(usize, result.read) == str.len); diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index deab624d0bdf0a..d58513c1e8111f 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -98,7 +98,7 @@ pub const TextEncoder = struct { // max utf16 -> utf8 length if (slice.len <= buf.len / 4) { - const result = strings.copyUTF16IntoUTF8(&buf, @TypeOf(slice), slice); + const result = strings.copyUTF16IntoUTF8(&buf, @TypeOf(slice), slice, true); if (result.read == 0 or result.written == 0) { const uint8array = JSC.JSValue.createUninitializedUint8Array(globalThis, 3); const array_buffer = uint8array.asArrayBuffer(globalThis).?; @@ -221,7 +221,7 @@ pub const TextEncoder = struct { ) u64 { var output = buf_ptr[0..buf_len]; const input = input_ptr[0..input_len]; - const result: strings.EncodeIntoResult = strings.copyUTF16IntoUTF8(output, []const u16, input); + const result: strings.EncodeIntoResult = strings.copyUTF16IntoUTF8(output, []const u16, input, true); if (result.read == 0 or result.written == 0) { const replacement_char = [_]u8{ 239, 191, 189 }; @memcpy(buf_ptr, &replacement_char, replacement_char.len); @@ -683,7 +683,7 @@ pub const TextDecoder = struct { }; pub const Encoder = struct { - export fn Bun__encoding__writeLatin1(input: [*]const u8, len: usize, to: [*]u8, to_len: usize, encoding: u8) i64 { + export fn Bun__encoding__writeLatin1(input: [*]const u8, len: usize, to: [*]u8, to_len: usize, encoding: u8) usize { return switch (@intToEnum(JSC.Node.Encoding, encoding)) { .utf8 => writeU8(input, len, to, to_len, .utf8), .latin1 => writeU8(input, len, to, to_len, .ascii), @@ -694,9 +694,9 @@ pub const Encoder = struct { .base64url => writeU8(input, len, to, to_len, .base64url), .hex => writeU8(input, len, to, to_len, .hex), else => unreachable, - }; + } catch 0; } - export fn Bun__encoding__writeUTF16(input: [*]const u16, len: usize, to: [*]u8, to_len: usize, encoding: u8) i64 { + export fn Bun__encoding__writeUTF16(input: [*]const u16, len: usize, to: [*]u8, to_len: usize, encoding: u8) usize { return switch (@intToEnum(JSC.Node.Encoding, encoding)) { .utf8 => writeU16(input, len, to, to_len, .utf8, false), .latin1 => writeU16(input, len, to, to_len, .ascii, false), @@ -707,7 +707,7 @@ pub const Encoder = struct { .base64url => writeU16(input, len, to, to_len, .base64url, false), .hex => writeU16(input, len, to, to_len, .hex, false), else => unreachable, - }; + } catch 0; } export fn Bun__encoding__byteLengthLatin1(input: [*]const u8, len: usize, encoding: u8) usize { return switch (@intToEnum(JSC.Node.Encoding, encoding)) { @@ -783,7 +783,7 @@ pub const Encoder = struct { } // pub fn writeUTF16AsUTF8(utf16: [*]const u16, len: usize, to: [*]u8, to_len: usize) callconv(.C) i32 { - // return @intCast(i32, strings.copyUTF16IntoUTF8(to[0..to_len], []const u16, utf16[0..len]).written); + // return @intCast(i32, strings.copyUTF16IntoUTF8(to[0..to_len], []const u16, utf16[0..len], true).written); // } pub fn toString(input_ptr: [*]const u8, len: usize, global: *JSGlobalObject, comptime encoding: JSC.Node.Encoding) JSValue { @@ -839,7 +839,7 @@ pub const Encoder = struct { return ZigString.toExternalU16(output.ptr, output.len, global); }, - JSC.Node.Encoding.hex => { + .hex => { var output = allocator.alloc(u8, input.len * 2) catch return ZigString.init("Out of memory").toErrorInstance(global); const wrote = strings.encodeBytesToHex(output, input); std.debug.assert(wrote == output.len); @@ -848,11 +848,11 @@ pub const Encoder = struct { return val.toExternalValue(global); }, - JSC.Node.Encoding.base64url => { + .base64url => { return JSC.WTF.toBase64URLStringValue(input, global); }, - JSC.Node.Encoding.base64 => { + .base64 => { const to_len = bun.base64.encodeLen(input); var to = allocator.alloc(u8, to_len) catch return ZigString.init("Out of memory").toErrorInstance(global); const wrote = bun.base64.encode(to, input); @@ -861,7 +861,7 @@ pub const Encoder = struct { } } - pub fn writeU8(input: [*]const u8, len: usize, to_ptr: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding) i64 { + pub fn writeU8(input: [*]const u8, len: usize, to_ptr: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding) !usize { if (len == 0 or to_len == 0) return 0; @@ -873,11 +873,11 @@ pub const Encoder = struct { // if (comptime encoding.isBinaryToText()) {} switch (comptime encoding) { - JSC.Node.Encoding.buffer => { + .buffer => { const written = @min(len, to_len); @memcpy(to_ptr, input, written); - return @intCast(i64, written); + return written; }, .latin1, .ascii => { const written = @min(len, to_len); @@ -891,14 +891,14 @@ pub const Encoder = struct { strings.copyLatin1IntoASCII(to, remain); } - return @intCast(i64, written); + return written; }, .utf8 => { // need to encode - return @intCast(i64, strings.copyLatin1IntoUTF8(to_ptr[0..to_len], []const u8, input[0..len]).written); + return strings.copyLatin1IntoUTF8(to_ptr[0..to_len], []const u8, input[0..len]).written; }, // encode latin1 into UTF16 - JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => { + .ucs2, .utf16le => { if (to_len < 2) return 0; @@ -917,29 +917,13 @@ pub const Encoder = struct { } }, - JSC.Node.Encoding.hex => { - return @intCast(i64, strings.decodeHexToBytes(to_ptr[0..to_len], u8, input[0..len])); - }, - - JSC.Node.Encoding.base64url => { - var slice = strings.trim(input[0..len], "\r\n\t " ++ [_]u8{std.ascii.control_code.vt}); - if (slice.len == 0) - return 0; - - if (strings.endsWithComptime(slice, "==")) { - slice = slice[0 .. slice.len - 2]; - } else if (slice[slice.len - 1] == '=') { - slice = slice[0 .. slice.len - 1]; - } - - const wrote = bun.base64.decodeURLSafe(to_ptr[0..to_len], slice).written; - return @intCast(i64, wrote); + .hex => { + return strings.decodeHexToBytes(to_ptr[0..to_len], u8, input[0..len]); }, - JSC.Node.Encoding.base64 => { - return @intCast(i64, bun.base64.decode(to_ptr[0..to_len], input[0..len]).written); + .base64, .base64url => { + return bun.base64.decode(to_ptr[0..to_len], input[0..len]).written; }, - // else => return 0, } } @@ -952,46 +936,46 @@ pub const Encoder = struct { return strings.elementLengthLatin1IntoUTF8([]const u8, input[0..len]); }, - .latin1, JSC.Node.Encoding.ascii, JSC.Node.Encoding.buffer => { + .latin1, .ascii, .buffer => { return len; }, - JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => { + .ucs2, .utf16le => { return strings.elementLengthUTF8IntoUTF16([]const u8, input[0..len]) * 2; }, - JSC.Node.Encoding.hex => { + .hex => { return len / 2; }, - JSC.Node.Encoding.base64, JSC.Node.Encoding.base64url => { + .base64, .base64url => { return bun.base64.decodeLen(input[0..len]); }, // else => return &[_]u8{}; } } - pub fn writeU16(input: [*]const u16, len: usize, to: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding, comptime allow_partial_write: bool) i64 { + pub fn writeU16(input: [*]const u16, len: usize, to: [*]u8, to_len: usize, comptime encoding: JSC.Node.Encoding, comptime allow_partial_write: bool) !usize { if (len == 0) return 0; switch (comptime encoding) { .utf8 => { - return @intCast(i32, strings.copyUTF16IntoUTF8(to[0..to_len], []const u16, input[0..len]).written); + return strings.copyUTF16IntoUTF8(to[0..to_len], []const u16, input[0..len], allow_partial_write).written; }, - .latin1, JSC.Node.Encoding.ascii, JSC.Node.Encoding.buffer => { + .latin1, .ascii, .buffer => { const out = @min(len, to_len); strings.copyU16IntoU8(to[0..to_len], []const u16, input[0..out]); - return @intCast(i64, out); + return out; }, // string is already encoded, just need to copy the data - JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => { + .ucs2, .utf16le => { if (allow_partial_write) { const bytes_input_len = len * 2; const written = @min(bytes_input_len, to_len); const input_u8 = @ptrCast([*]const u8, input); strings.copyU16IntoU8(to[0..written], []const u8, input_u8[0..written]); - return @intCast(i64, written); + return written; } else { const bytes_input_len = len * 2; const written = @min(bytes_input_len, to_len); @@ -1000,15 +984,15 @@ pub const Encoder = struct { const fixed_len = (written / 2) * 2; const input_u8 = @ptrCast([*]const u8, input); strings.copyU16IntoU8(to[0..written], []const u8, input_u8[0..fixed_len]); - return @intCast(i64, fixed_len); + return fixed_len; } }, - JSC.Node.Encoding.hex => { - return @intCast(i64, strings.decodeHexToBytes(to[0..to_len], u16, input[0..len])); + .hex => { + return strings.decodeHexToBytes(to[0..to_len], u16, input[0..len]); }, - JSC.Node.Encoding.base64, JSC.Node.Encoding.base64url => { + .base64, .base64url => { if (to_len < 2 or len == 0) return 0; @@ -1033,15 +1017,15 @@ pub const Encoder = struct { .ascii, .latin1, .utf8 => { return strings.elementLengthUTF16IntoUTF8([]const u16, input[0..len]); }, - JSC.Node.Encoding.ucs2, JSC.Node.Encoding.buffer, JSC.Node.Encoding.utf16le => { + .ucs2, .buffer, .utf16le => { return len * 2; }, - JSC.Node.Encoding.hex => { + .hex => { return len / 2; }, - JSC.Node.Encoding.base64, JSC.Node.Encoding.base64url => { + .base64, .base64url => { return bun.base64.decodeLenUpperBound(len); }, // else => return &[_]u8{}; @@ -1057,13 +1041,12 @@ pub const Encoder = struct { } pub fn constructFromU8(input: [*]const u8, len: usize, comptime encoding: JSC.Node.Encoding) []u8 { - if (len == 0) - return &[_]u8{}; + if (len == 0) return &[_]u8{}; const allocator = VirtualMachine.get().allocator; switch (comptime encoding) { - JSC.Node.Encoding.buffer => { + .buffer => { var to = allocator.alloc(u8, len) catch return &[_]u8{}; @memcpy(to.ptr, input, len); @@ -1082,52 +1065,35 @@ pub const Encoder = struct { }, // encode latin1 into UTF16 // return as bytes - JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => { + .ucs2, .utf16le => { var to = allocator.alloc(u16, len) catch return &[_]u8{}; _ = strings.copyLatin1IntoUTF16([]u16, to, []const u8, input[0..len]); return std.mem.sliceAsBytes(to[0..len]); }, - JSC.Node.Encoding.hex => { + .hex => { if (len < 2) return &[_]u8{}; var to = allocator.alloc(u8, len / 2) catch return &[_]u8{}; - return to[0..strings.decodeHexToBytes(to, u8, input[0..len])]; + return to[0..strings.decodeHexToBytesTruncate(to, u8, input[0..len])]; }, - JSC.Node.Encoding.base64url => { - var slice = strings.trim(input[0..len], "\r\n\t " ++ [_]u8{std.ascii.control_code.vt}); - if (slice.len == 0) - return &[_]u8{}; + .base64, .base64url => { + const slice = strings.trim(input[0..len], "\r\n\t " ++ [_]u8{std.ascii.control_code.vt}); + if (slice.len == 0) return &[_]u8{}; - if (strings.endsWithComptime(slice, "==")) { - slice = slice[0 .. slice.len - 2]; - } else if (slice[slice.len - 1] == '=') { - slice = slice[0 .. slice.len - 1]; - } + const outlen = bun.base64.decodeLen(slice); + const to = allocator.alloc(u8, outlen) catch return &[_]u8{}; - const to_len = bun.base64.urlsafe.decoder.calcSizeForSlice(slice) catch unreachable; - var to = allocator.alloc(u8, to_len) catch return &[_]u8{}; - const wrote = bun.base64.decodeURLSafe(to[0..to_len], slice).written; + const wrote = bun.base64.decode(to[0..outlen], slice).written; return to[0..wrote]; }, - - JSC.Node.Encoding.base64 => { - var slice = strings.trim(input[0..len], "\r\n\t " ++ [_]u8{std.ascii.control_code.vt}); - var outlen = bun.base64.decodeLen(slice); - - var to = allocator.alloc(u8, outlen) catch return &[_]u8{}; - const written = bun.base64.decode(to[0..outlen], slice).written; - return to[0..@min(written, outlen)]; - }, - // else => return 0, } } pub fn constructFromU16(input: [*]const u16, len: usize, comptime encoding: JSC.Node.Encoding) []u8 { - if (len == 0) - return &[_]u8{}; + if (len == 0) return &[_]u8{}; const allocator = VirtualMachine.get().allocator; @@ -1135,7 +1101,7 @@ pub const Encoder = struct { .utf8 => { return strings.toUTF8AllocWithType(allocator, []const u16, input[0..len]) catch return &[_]u8{}; }, - JSC.Node.Encoding.latin1, JSC.Node.Encoding.buffer, JSC.Node.Encoding.ascii => { + .latin1, .buffer, .ascii => { var to = allocator.alloc(u8, len) catch return &[_]u8{}; var input_bytes = std.mem.sliceAsBytes(input[0..len]); @memcpy(to.ptr, input_bytes.ptr, input_bytes.len); @@ -1146,34 +1112,24 @@ pub const Encoder = struct { return to; }, // string is already encoded, just need to copy the data - JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => { + .ucs2, .utf16le => { var to = std.mem.sliceAsBytes(allocator.alloc(u16, len * 2) catch return &[_]u8{}); @memcpy(to.ptr, std.mem.sliceAsBytes(input[0..len]).ptr, std.mem.sliceAsBytes(input[0..len]).len); return to; }, - JSC.Node.Encoding.hex => { + .hex => { var to = allocator.alloc(u8, len * 2) catch return &[_]u8{}; - return to[0..strings.decodeHexToBytes(to, u16, input[0..len])]; + return to[0..strings.decodeHexToBytesTruncate(to, u16, input[0..len])]; }, - JSC.Node.Encoding.base64 => { - // very very slow case! - // shouldn't really happen though - var transcoded = strings.toUTF8Alloc(allocator, input[0..len]) catch return &[_]u8{}; - defer allocator.free(transcoded); - return constructFromU8(transcoded.ptr, transcoded.len, .base64); - }, - - JSC.Node.Encoding.base64url => { - + .base64, .base64url => { // very very slow case! // shouldn't really happen though var transcoded = strings.toUTF8Alloc(allocator, input[0..len]) catch return &[_]u8{}; defer allocator.free(transcoded); - return constructFromU8(transcoded.ptr, transcoded.len, .base64url); + return constructFromU8(transcoded.ptr, transcoded.len, encoding); }, - // else => return 0, } } diff --git a/src/bun.js/webcore/streams.zig b/src/bun.js/webcore/streams.zig index 1de209b693d956..748b4583c69eb9 100644 --- a/src/bun.js/webcore/streams.zig +++ b/src/bun.js/webcore/streams.zig @@ -983,7 +983,7 @@ pub const Sink = struct { if (stack_size >= str.len * 2) { var buf: [stack_size]u8 = undefined; - const copied = strings.copyUTF16IntoUTF8(&buf, []const u16, str); + const copied = strings.copyUTF16IntoUTF8(&buf, []const u16, str, true); std.debug.assert(copied.written <= stack_size); std.debug.assert(copied.read <= stack_size); if (input.isDone()) { diff --git a/src/http/websocket_http_client.zig b/src/http/websocket_http_client.zig index a4f9284d389418..a5be7faab5d113 100644 --- a/src/http/websocket_http_client.zig +++ b/src/http/websocket_http_client.zig @@ -776,7 +776,7 @@ const Copy = union(enum) { switch (this) { .utf16 => |utf16| { header.len = WebsocketHeader.packLength(content_byte_len); - const encode_into_result = strings.copyUTF16IntoUTF8(to_mask, []const u16, utf16); + const encode_into_result = strings.copyUTF16IntoUTF8(to_mask, []const u16, utf16, true); std.debug.assert(@as(usize, encode_into_result.written) == content_byte_len); std.debug.assert(@as(usize, encode_into_result.read) == utf16.len); header.len = WebsocketHeader.packLength(encode_into_result.written); diff --git a/src/napi/napi.zig b/src/napi/napi.zig index b06e25d7e54849..99d7c82fbf5ddf 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -384,11 +384,8 @@ pub export fn napi_get_value_string_latin1(env: napi_env, value: napi_value, buf if (zig_str.is16Bit()) { const utf16 = zig_str.utf16SliceAligned(); - const wrote = JSC.WebCore.Encoder.writeU16(utf16.ptr, utf16.len, buf, buf_.len, .latin1, false); - if (wrote < 0) { - return genericFailure(); - } - maybeAppendNull(&buf[@intCast(usize, wrote)], bufsize == 0); + const wrote = JSC.WebCore.Encoder.writeU16(utf16.ptr, utf16.len, buf, buf_.len, .latin1, false) catch return genericFailure(); + maybeAppendNull(&buf[wrote], bufsize == 0); // if zero terminated, report the length of the string without the null result.* = @intCast(@TypeOf(result.*), wrote); return .ok; @@ -448,11 +445,8 @@ pub export fn napi_get_value_string_utf8(env: napi_env, value: napi_value, buf_p if (zig_str.is16Bit()) { const utf16 = zig_str.utf16SliceAligned(); - const wrote = JSC.WebCore.Encoder.writeU16(utf16.ptr, utf16.len, buf, buf_.len, .utf8, false); - if (wrote < 0) { - return genericFailure(); - } - buf[@intCast(usize, wrote)] = 0; + const wrote = JSC.WebCore.Encoder.writeU16(utf16.ptr, utf16.len, buf, buf_.len, .utf8, false) catch return genericFailure(); + buf[wrote] = 0; if (result_ptr) |result| { result.* = @intCast(@TypeOf(result.*), wrote); } diff --git a/src/string_immutable.zig b/src/string_immutable.zig index e85fc4ef813d93..7bdd7cfe54366e 100644 --- a/src/string_immutable.zig +++ b/src/string_immutable.zig @@ -2550,7 +2550,7 @@ pub fn latin1ToCodepointBytesAssumeNotASCII16(char: u32) u16 { return latin1_to_utf16_conversion_table[@truncate(u8, char)]; } -pub fn copyUTF16IntoUTF8(buf: []u8, comptime Type: type, utf16: Type) EncodeIntoResult { +pub fn copyUTF16IntoUTF8(buf: []u8, comptime Type: type, utf16: Type, comptime allow_partial_write: bool) EncodeIntoResult { if (comptime Type == []const u16) { if (bun.FeatureFlags.use_simdutf) { if (utf16.len == 0) @@ -2564,14 +2564,14 @@ pub fn copyUTF16IntoUTF8(buf: []u8, comptime Type: type, utf16: Type) EncodeInto else buf.len; - return copyUTF16IntoUTF8WithBuffer(buf, Type, utf16, trimmed, out_len); + return copyUTF16IntoUTF8WithBuffer(buf, Type, utf16, trimmed, out_len, allow_partial_write); } } - return copyUTF16IntoUTF8WithBuffer(buf, Type, utf16, utf16, utf16.len); + return copyUTF16IntoUTF8WithBuffer(buf, Type, utf16, utf16, utf16.len, allow_partial_write); } -pub fn copyUTF16IntoUTF8WithBuffer(buf: []u8, comptime Type: type, utf16: Type, trimmed: Type, out_len: usize) EncodeIntoResult { +pub fn copyUTF16IntoUTF8WithBuffer(buf: []u8, comptime Type: type, utf16: Type, trimmed: Type, out_len: usize, comptime allow_partial_write: bool) EncodeIntoResult { var remaining = buf; var utf16_remaining = utf16; var ended_on_non_ascii = false; @@ -2604,7 +2604,7 @@ pub fn copyUTF16IntoUTF8WithBuffer(buf: []u8, comptime Type: type, utf16: Type, const width: usize = replacement.utf8Width(); if (width > remaining.len) { ended_on_non_ascii = width > 1; - switch (width) { + if (comptime allow_partial_write) switch (width) { 2 => { if (remaining.len > 0) { //only first will be written @@ -2650,7 +2650,7 @@ pub fn copyUTF16IntoUTF8WithBuffer(buf: []u8, comptime Type: type, utf16: Type, }, else => {}, - } + }; break; } @@ -3257,8 +3257,9 @@ pub fn indexOfNotChar(slice: []const u8, char: u8) ?u32 { return null; } +const invalid_char: u8 = 0xff; const hex_table: [255]u8 = brk: { - var values: [255]u8 = [_]u8{0} ** 255; + var values: [255]u8 = [_]u8{invalid_char} ** 255; values['0'] = 0; values['1'] = 1; values['2'] = 2; @@ -3285,22 +3286,41 @@ const hex_table: [255]u8 = brk: { break :brk values; }; -pub fn decodeHexToBytes(destination: []u8, comptime Char: type, source: []const Char) usize { +pub fn decodeHexToBytes(destination: []u8, comptime Char: type, source: []const Char) !usize { + return _decodeHexToBytes(destination, Char, source, false); +} + +pub fn decodeHexToBytesTruncate(destination: []u8, comptime Char: type, source: []const Char) usize { + return _decodeHexToBytes(destination, Char, source, true) catch 0; +} + +inline fn _decodeHexToBytes(destination: []u8, comptime Char: type, source: []const Char, comptime truncate: bool) !usize { var remain = destination; var input = source; - while (input.len > 1 and remain.len > 0) { + while (remain.len > 0 and input.len > 1) { const int = input[0..2].*; + if (comptime @sizeOf(Char) > 1) { + if (int[0] > std.math.maxInt(u8) or int[1] > std.math.maxInt(u8)) { + if (comptime truncate) break; + return error.InvalidByteSequence; + } + } const a = hex_table[@truncate(u8, int[0])]; const b = hex_table[@truncate(u8, int[1])]; - if (a == 255 or b == 255) { - break; + if (a == invalid_char or b == invalid_char) { + if (comptime truncate) break; + return error.InvalidByteSequence; } remain[0] = a << 4 | b; remain = remain[1..]; input = input[2..]; } + if (comptime !truncate) { + if (remain.len > 0 and input.len > 0) return error.InvalidByteSequence; + } + return destination.len - remain.len; } @@ -3615,7 +3635,7 @@ pub fn formatUTF16Type(comptime Slice: type, slice_: Slice, writer: anytype) !vo var slice = slice_; while (slice.len > 0) { - const result = strings.copyUTF16IntoUTF8(chunk, Slice, slice); + const result = strings.copyUTF16IntoUTF8(chunk, Slice, slice, true); if (result.read == 0 or result.written == 0) break; try writer.writeAll(chunk[0..result.written]); diff --git a/src/string_mutable.zig b/src/string_mutable.zig index d7b0cf9308ee40..129ea1481f1203 100644 --- a/src/string_mutable.zig +++ b/src/string_mutable.zig @@ -327,6 +327,7 @@ pub const MutableString = struct { this.remain()[0 .. bytes.len * 2], []const u16, bytes, + true, ); this.context.list.items.len += @as(usize, decoded.written); return pending.len; @@ -340,6 +341,7 @@ pub const MutableString = struct { this.remain()[0 .. bytes.len * 2], []const u16, bytes, + true, ); this.pos += @as(usize, decoded.written); } diff --git a/test/js/node/buffer.test.js b/test/js/node/buffer.test.js index 568bf8d4458463..f0295b08face5a 100644 --- a/test/js/node/buffer.test.js +++ b/test/js/node/buffer.test.js @@ -1,4 +1,5 @@ -import { describe, it, expect, beforeEach, afterEach, test } from "bun:test"; +import { Buffer, SlowBuffer } from "buffer"; +import { describe, it, expect, beforeEach, afterEach } from "bun:test"; import { gc } from "harness"; const BufferModule = await import("buffer"); @@ -6,25 +7,6 @@ const BufferModule = await import("buffer"); beforeEach(() => gc()); afterEach(() => gc()); -function assert(a) { - expect(a).toBeTruthy(); -} - -Object.assign(assert, { - ok(a) { - expect(a).toBeTruthy(); - }, - deepStrictEqual(a, b) { - expect(b).toStrictEqual(a); - }, - strictEqual(a, b) { - expect(a).toBe(b); - }, - throws(a, b) { - expect(a).toThrow(); - }, -}); - // https://github.com/oven-sh/bun/issues/2052 it("Buffer global is settable", () => { var prevBuffer = globalThis.Buffer; @@ -35,68 +17,72 @@ it("Buffer global is settable", () => { expect(globalThis.Buffer).toBe(prevBuffer); }); -it("Buffer.alloc", () => { +it("length overflow", () => { // Verify the maximum Uint8Array size. There is no concrete limit by spec. The // internal limits should be updated if this fails. - assert.throws(() => new Uint8Array(2 ** 32 + 1), { - message: "Invalid typed array length: 4294967297", - }); + expect(() => new Uint8Array(2 ** 32 + 1)).toThrow(/length/); +}); +it("truncate input values", () => { const b = Buffer.allocUnsafe(1024); - assert.strictEqual(b.length, 1024); + expect(b.length).toBe(1024); b[0] = -1; - assert.strictEqual(b[0], 255); + expect(b[0]).toBe(255); for (let i = 0; i < 1024; i++) { - b[i] = i % 256; + b[i] = i; } for (let i = 0; i < 1024; i++) { - assert.strictEqual(i % 256, b[i]); + expect(i % 256).toBe(b[i]); } +}); +it("Buffer.allocUnsafe()", () => { const c = Buffer.allocUnsafe(512); - assert.strictEqual(c.length, 512); + expect(c.length).toBe(512); +}); +it("Buffer.from()", () => { const d = Buffer.from([]); - assert.strictEqual(d.length, 0); + expect(d.length).toBe(0); +}); - // Test offset properties - { - const b = Buffer.alloc(128); - assert.strictEqual(b.length, 128); - assert.strictEqual(b.byteOffset, 0); - assert.strictEqual(b.offset, 0); - } +it("offset properties", () => { + const b = Buffer.alloc(128); + expect(b.length).toBe(128); + expect(b.byteOffset).toBe(0); + expect(b.offset).toBe(0); +}); - // Test creating a Buffer from a Uint32Array - { - const ui32 = new Uint32Array(4).fill(42); - const e = Buffer.from(ui32); - for (const [index, value] of e.entries()) { - assert.strictEqual(value, ui32[index]); - } +it("creating a Buffer from a Uint32Array", () => { + const ui32 = new Uint32Array(4).fill(42); + const e = Buffer.from(ui32); + for (const [index, value] of e.entries()) { + expect(value).toBe(ui32[index]); } - // Test creating a Buffer from a Uint32Array (old constructor) - { - const ui32 = new Uint32Array(4).fill(42); - const e = Buffer(ui32); - for (const [key, value] of e.entries()) { - assert.deepStrictEqual(value, ui32[key]); - } +}); + +it("creating a Buffer from a Uint32Array (old constructor)", () => { + const ui32 = new Uint32Array(4).fill(42); + const e = Buffer(ui32); + for (const [key, value] of e.entries()) { + expect(value).toBe(ui32[key]); } +}); +it("invalid encoding", () => { + const b = Buffer.allocUnsafe(64); // Test invalid encoding for Buffer.toString - assert.throws(() => b.toString("invalid"), /Unknown encoding: invalid/); + expect(() => b.toString("invalid")).toThrow(/encoding/); // Invalid encoding for Buffer.write - assert.throws(() => b.write("test string", 0, 5, "invalid"), /Unknown encoding: invalid/); + expect(() => b.write("test string", 0, 5, "invalid")).toThrow(/encoding/); // Unsupported arguments for Buffer.write - // assert.throws(() => b.write("test", "utf8", 0), { - // code: "ERR_INVALID_ARG_TYPE", - // }); + expect(() => b.write("test", "utf8", 0)).toThrow(/invalid/i); +}); - // Try to create 0-length buffers. Should not throw. +it("create 0-length buffers", () => { Buffer.from(""); Buffer.from("", "ascii"); Buffer.from("", "latin1"); @@ -107,490 +93,445 @@ it("Buffer.alloc", () => { new Buffer("", "latin1"); new Buffer("", "binary"); Buffer(0); +}); - const outOfRangeError = { - code: "ERR_OUT_OF_RANGE", - name: "RangeError", - }; - +it("write() beyond end of buffer", () => { + const b = Buffer.allocUnsafe(64); // Try to write a 0-length string beyond the end of b - // assert.throws(() => b.write("", 2048), outOfRangeError); - - // // Throw when writing to negative offset - // assert.throws(() => b.write("a", -1), outOfRangeError); - - // // Throw when writing past bounds from the pool - // assert.throws(() => b.write("a", 2048), outOfRangeError); - - // // Throw when writing to negative offset - // assert.throws(() => b.write("a", -1), outOfRangeError); + expect(() => b.write("", 2048)).toThrow(RangeError); + // Throw when writing to negative offset + expect(() => b.write("a", -1)).toThrow(RangeError); + // Throw when writing past bounds from the pool + expect(() => b.write("a", 2048)).toThrow(RangeError); + // Throw when writing to negative offset + expect(() => b.write("a", -1)).toThrow(RangeError); + // Offset points to the end of the buffer and does not throw. + // (see https://github.com/nodejs/node/issues/8127). + Buffer.alloc(1).write("", 1, 0); +}); +it("copy() beyond end of buffer", () => { + const b = Buffer.allocUnsafe(64); // Try to copy 0 bytes worth of data into an empty buffer b.copy(Buffer.alloc(0), 0, 0, 0); - // Try to copy 0 bytes past the end of the target buffer b.copy(Buffer.alloc(0), 1, 1, 1); b.copy(Buffer.alloc(1), 1, 1, 1); - // Try to copy 0 bytes from past the end of the source buffer b.copy(Buffer.alloc(1), 0, 2048, 2048); +}); - // Testing for smart defaults and ability to pass string values as offset - { - const writeTest = Buffer.from("abcdes"); - writeTest.write("n", "ascii"); - assert.throws(() => writeTest.write("o", "1", "ascii"), { - code: "ERR_INVALID_ARG_TYPE", - }); - writeTest.write("o", 1, "ascii"); - writeTest.write("d", 2, "ascii"); - writeTest.write("e", 3, "ascii"); - writeTest.write("j", 4, "ascii"); - assert.strictEqual(writeTest.toString(), "nodejs"); - } - - // Offset points to the end of the buffer and does not throw. - // (see https://github.com/nodejs/node/issues/8127). - Buffer.alloc(1).write("", 1, 0); - - // ASCII slice test - { - const asciiString = "hello world"; - - for (let i = 0; i < asciiString.length; i++) { - b[i] = asciiString.charCodeAt(i); - } - const asciiSlice = b.toString("ascii", 0, asciiString.length); - assert.strictEqual(asciiString, asciiSlice); - } - - { - const asciiString = "hello world"; - const offset = 100; +it("smart defaults and ability to pass string values as offset", () => { + const writeTest = Buffer.from("abcdes"); + writeTest.write("n", "ascii"); + expect(() => writeTest.write("o", "1", "ascii")).toThrow(/offset/); + writeTest.write("o", 1, "ascii"); + writeTest.write("d", 2, "ascii"); + writeTest.write("e", 3, "ascii"); + writeTest.write("j", 4, "ascii"); + expect(writeTest.toString()).toBe("nodejs"); +}); - assert.strictEqual(asciiString.length, b.write(asciiString, offset, "ascii")); - const asciiSlice = b.toString("ascii", offset, offset + asciiString.length); - assert.strictEqual(asciiString, asciiSlice); +it("ASCII slice", () => { + const buf = Buffer.allocUnsafe(256); + const str = "hello world"; + for (let i = 0; i < str.length; i++) { + buf[i] = str.charCodeAt(i); } + expect(buf.toString("ascii", 0, str.length)).toBe(str); - { - const asciiString = "hello world"; - const offset = 100; + const offset = 100; + expect(buf.write(str, offset, "ascii")).toBe(str.length); + expect(buf.toString("ascii", offset, offset + str.length)).toBe(str); - const sliceA = b.slice(offset, offset + asciiString.length); - const sliceB = b.slice(offset, offset + asciiString.length); - for (let i = 0; i < asciiString.length; i++) { - assert.strictEqual(sliceA[i], sliceB[i]); - } + const slice1 = buf.slice(offset, offset + str.length); + const slice2 = buf.slice(offset, offset + str.length); + for (let i = 0; i < str.length; i++) { + expect(slice1[i]).toBe(slice2[i]); } +}); - // UTF-8 slice test - { - const utf8String = "¡hέlló wôrld!"; - const offset = 100; +it("UTF-8 slice", () => { + const b = Buffer.allocUnsafe(256); + const utf8String = "¡hέlló wôrld!"; + const offset = 100; - b.write(utf8String, 0, Buffer.byteLength(utf8String), "utf8"); - let utf8Slice = b.toString("utf8", 0, Buffer.byteLength(utf8String)); - assert.strictEqual(utf8String, utf8Slice); + b.write(utf8String, 0, Buffer.byteLength(utf8String), "utf8"); + expect(b.toString("utf8", 0, Buffer.byteLength(utf8String))).toBe(utf8String); - assert.strictEqual(Buffer.byteLength(utf8String), b.write(utf8String, offset, "utf8")); - utf8Slice = b.toString("utf8", offset, offset + Buffer.byteLength(utf8String)); - assert.strictEqual(utf8String, utf8Slice); + expect(b.write(utf8String, offset, "utf8")).toBe(Buffer.byteLength(utf8String)); + expect(b.toString("utf8", offset, offset + Buffer.byteLength(utf8String))).toBe(utf8String); - const sliceA = b.slice(offset, offset + Buffer.byteLength(utf8String)); - const sliceB = b.slice(offset, offset + Buffer.byteLength(utf8String)); - for (let i = 0; i < Buffer.byteLength(utf8String); i++) { - assert.strictEqual(sliceA[i], sliceB[i]); - } + const sliceA = b.slice(offset, offset + Buffer.byteLength(utf8String)); + const sliceB = b.slice(offset, offset + Buffer.byteLength(utf8String)); + for (let i = 0; i < Buffer.byteLength(utf8String); i++) { + expect(sliceA[i]).toBe(sliceB[i]); } - { - const slice = b.slice(100, 150); - assert.strictEqual(slice.length, 50); - for (let i = 0; i < 50; i++) { - assert.strictEqual(b[100 + i], slice[i]); - } + const slice = b.slice(100, 150); + expect(slice.length).toBe(50); + for (let i = 0; i < 50; i++) { + expect(b[100 + i]).toBe(slice[i]); } +}); - { - // Make sure only top level parent propagates from allocPool - const b = Buffer.allocUnsafe(5); - const c = b.slice(0, 4); - const d = c.slice(0, 2); - assert.strictEqual(b.parent, c.parent); - assert.strictEqual(b.parent, d.parent); - } +it("only top level parent propagates from allocPool", () => { + const b = Buffer.allocUnsafe(5); + const c = b.slice(0, 4); + const d = c.slice(0, 2); + expect(b.parent).toBe(c.parent); + expect(b.parent).toBe(d.parent); +}); - { - // Also from a non-pooled instance - const b = Buffer.allocUnsafeSlow(5); - const c = b.slice(0, 4); - const d = c.slice(0, 2); - assert.strictEqual(c.parent, d.parent); - } +it("only top level parent propagates from a non-pooled instance", () => { + const b = Buffer.allocUnsafeSlow(5); + const c = b.slice(0, 4); + const d = c.slice(0, 2); + expect(c.parent).toBe(d.parent); +}); - { - // Bug regression test - const testValue = "\u00F6\u65E5\u672C\u8A9E"; // ö日本語 - const buffer = Buffer.allocUnsafe(32); - const size = buffer.write(testValue, 0, "utf8"); - const slice = buffer.toString("utf8", 0, size); - assert.strictEqual(slice, testValue); - } +it("UTF-8 write() & slice()", () => { + const testValue = "\u00F6\u65E5\u672C\u8A9E"; // ö日本語 + const buffer = Buffer.allocUnsafe(32); + const size = buffer.write(testValue, 0, "utf8"); + const slice = buffer.toString("utf8", 0, size); + expect(slice).toBe(testValue); +}); - { - // Test triple slice - const a = Buffer.allocUnsafe(8); - for (let i = 0; i < 8; i++) a[i] = i; - const b = a.slice(4, 8); - assert.strictEqual(b[0], 4); - assert.strictEqual(b[1], 5); - assert.strictEqual(b[2], 6); - assert.strictEqual(b[3], 7); - const c = b.slice(2, 4); - assert.strictEqual(c[0], 6); - assert.strictEqual(c[1], 7); - } +it("triple slice", () => { + const a = Buffer.allocUnsafe(8); + for (let i = 0; i < 8; i++) a[i] = i; + const b = a.slice(4, 8); + expect(b[0]).toBe(4); + expect(b[1]).toBe(5); + expect(b[2]).toBe(6); + expect(b[3]).toBe(7); + const c = b.slice(2, 4); + expect(c[0]).toBe(6); + expect(c[1]).toBe(7); +}); - { - const d = Buffer.from([23, 42, 255]); - assert.strictEqual(d.length, 3); - assert.strictEqual(d[0], 23); - assert.strictEqual(d[1], 42); - assert.strictEqual(d[2], 255); - assert.deepStrictEqual(d, Buffer.from(d)); - } +it("Buffer.from() with encoding", () => { + const b = Buffer.from([23, 42, 255]); + expect(b.length).toBe(3); + expect(b[0]).toBe(23); + expect(b[1]).toBe(42); + expect(b[2]).toBe(255); + expect(Buffer.from(b)).toStrictEqual(b); - { - // Test for proper UTF-8 Encoding - const e = Buffer.from("über"); - assert.deepStrictEqual(e, Buffer.from([195, 188, 98, 101, 114])); - } + // Test for proper UTF-8 Encoding + expect(Buffer.from("über")).toStrictEqual(Buffer.from([195, 188, 98, 101, 114])); - { - // Test for proper ascii Encoding, length should be 4 - const f = Buffer.from("über", "ascii"); - assert.deepStrictEqual(f, Buffer.from([252, 98, 101, 114])); - } + // Test for proper ascii Encoding, length should be 4 + expect(Buffer.from("über", "ascii")).toStrictEqual(Buffer.from([252, 98, 101, 114])); ["ucs2", "ucs-2", "utf16le", "utf-16le"].forEach(encoding => { - { - // Test for proper UTF16LE encoding, length should be 8 - const f = Buffer.from("über", encoding); - assert.deepStrictEqual(f, Buffer.from([252, 0, 98, 0, 101, 0, 114, 0])); - } - - { - // Length should be 12 - const f = Buffer.from("привет", encoding); - assert.deepStrictEqual(f, Buffer.from([63, 4, 64, 4, 56, 4, 50, 4, 53, 4, 66, 4])); - assert.strictEqual(f.toString(encoding), "привет"); - } - - { - const f = Buffer.from([0, 0, 0, 0, 0]); - assert.strictEqual(f.length, 5); - const size = f.write("あいうえお", encoding); - assert.strictEqual(size, 4); - assert.deepStrictEqual(f, Buffer.from([0x42, 0x30, 0x44, 0x30, 0x00])); - } + // Test for proper UTF16LE encoding, length should be 8 + expect(Buffer.from("über", encoding)).toStrictEqual(Buffer.from([252, 0, 98, 0, 101, 0, 114, 0])); + + // Length should be 12 + const b = Buffer.from("привет", encoding); + expect(b).toStrictEqual(Buffer.from([63, 4, 64, 4, 56, 4, 50, 4, 53, 4, 66, 4])); + expect(b.toString(encoding)).toBe("привет"); + + const c = Buffer.from([0, 0, 0, 0, 0]); + expect(c.length).toBe(5); + expect(c.write("あいうえお", encoding)).toBe(4); + expect(c).toStrictEqual(Buffer.from([0x42, 0x30, 0x44, 0x30, 0x00])); }); - { - const f = Buffer.from("\uD83D\uDC4D", "utf-16le"); // THUMBS UP SIGN (U+1F44D) - assert.strictEqual(f.length, 4); - assert.deepStrictEqual(f, Buffer.from("3DD84DDC", "hex")); - } + const c = Buffer.from("\uD83D\uDC4D", "utf-16le"); // THUMBS UP SIGN (U+1F44D) + expect(c.length).toBe(4); + expect(c).toStrictEqual(Buffer.from("3DD84DDC", "hex")); +}); - // Test construction from arrayish object - { - const arrayIsh = { 0: 0, 1: 1, 2: 2, 3: 3, length: 4 }; - let g = Buffer.from(arrayIsh); - assert.deepStrictEqual(g, Buffer.from([0, 1, 2, 3])); - const strArrayIsh = { 0: "0", 1: "1", 2: "2", 3: "3", length: 4 }; - g = Buffer.from(strArrayIsh); - assert.deepStrictEqual(g, Buffer.from([0, 1, 2, 3])); - } +it("construction from arrayish object", () => { + const arrayIsh = { 0: 0, 1: 1, 2: 2, 3: 3, length: 4 }; + expect(Buffer.from(arrayIsh)).toStrictEqual(Buffer.from([0, 1, 2, 3])); + const strArrayIsh = { 0: "0", 1: "1", 2: "2", 3: "3", length: 4 }; + expect(Buffer.from(strArrayIsh)).toStrictEqual(Buffer.from([0, 1, 2, 3])); +}); - // - // Test toString('base64') - // - assert.strictEqual(Buffer.from("Man").toString("base64"), "TWFu"); - assert.strictEqual(Buffer.from("Woman").toString("base64"), "V29tYW4="); +it("toString('base64')", () => { + expect(Buffer.from("Man").toString("base64")).toBe("TWFu"); + expect(Buffer.from("Woman").toString("base64")).toBe("V29tYW4="); +}); - // - // Test toString('base64url') - // - assert.strictEqual(Buffer.from("Man").toString("base64url"), "TWFu"); - assert.strictEqual(Buffer.from("Woman").toString("base64url"), "V29tYW4"); +it("toString('base64url')", () => { + expect(Buffer.from("Man").toString("base64url")).toBe("TWFu"); + expect(Buffer.from("Woman").toString("base64url")).toBe("V29tYW4"); +}); - { - // Test that regular and URL-safe base64 both work both ways - const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff]; - assert.deepStrictEqual(Buffer.from("//++/++/++//", "base64"), Buffer.from(expected)); - assert.deepStrictEqual(Buffer.from("__--_--_--__", "base64"), Buffer.from(expected)); - assert.deepStrictEqual(Buffer.from("//++/++/++//", "base64url"), Buffer.from(expected)); - assert.deepStrictEqual(Buffer.from("__--_--_--__", "base64url"), Buffer.from(expected)); - } +it("regular and URL-safe base64 work both ways", () => { + const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff]; + expect(Buffer.from("//++/++/++//", "base64")).toStrictEqual(Buffer.from(expected)); + expect(Buffer.from("__--_--_--__", "base64")).toStrictEqual(Buffer.from(expected)); + expect(Buffer.from("//++/++/++//", "base64url")).toStrictEqual(Buffer.from(expected)); + expect(Buffer.from("__--_--_--__", "base64url")).toStrictEqual(Buffer.from(expected)); +}); - const base64flavors = ["base64", "base64url"]; +it("regular and URL-safe base64 work both ways with padding", () => { + const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff, 0xfb]; + expect(Buffer.from("//++/++/++//+w==", "base64")).toStrictEqual(Buffer.from(expected)); + expect(Buffer.from("//++/++/++//+w==", "base64")).toStrictEqual(Buffer.from(expected)); + expect(Buffer.from("//++/++/++//+w==", "base64url")).toStrictEqual(Buffer.from(expected)); + expect(Buffer.from("//++/++/++//+w==", "base64url")).toStrictEqual(Buffer.from(expected)); +}); - { - // Test that regular and URL-safe base64 both work both ways with padding - const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff, 0xfb]; - assert.deepStrictEqual(Buffer.from("//++/++/++//+w==", "base64"), Buffer.from(expected)); - assert.deepStrictEqual(Buffer.from("//++/++/++//+w==", "base64"), Buffer.from(expected)); - assert.deepStrictEqual(Buffer.from("//++/++/++//+w==", "base64url"), Buffer.from(expected)); - assert.deepStrictEqual(Buffer.from("//++/++/++//+w==", "base64url"), Buffer.from(expected)); - } +it("big example (base64 & base64url)", () => { + const quote = + "Man is distinguished, not only by his reason, but by this " + + "singular passion from other animals, which is a lust " + + "of the mind, that by a perseverance of delight in the " + + "continued and indefatigable generation of knowledge, " + + "exceeds the short vehemence of any carnal pleasure."; + const expected = + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb" + + "24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlci" + + "BhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQ" + + "gYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" + + "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZ" + + "GdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm" + + "5hbCBwbGVhc3VyZS4="; + + expect(Buffer.from(quote).toString("base64")).toBe(expected); + expect(Buffer.from(quote).toString("base64url")).toBe( + expected.replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", ""), + ); +}); - { - // big example - const quote = - "Man is distinguished, not only by his reason, but by this " + - "singular passion from other animals, which is a lust " + - "of the mind, that by a perseverance of delight in the " + - "continued and indefatigable generation of knowledge, " + - "exceeds the short vehemence of any carnal pleasure."; - const expected = - "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb" + - "24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlci" + - "BhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQ" + - "gYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" + - "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZ" + - "GdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm" + - "5hbCBwbGVhc3VyZS4="; - assert.strictEqual(Buffer.from(quote).toString("base64"), expected); - assert.strictEqual( - Buffer.from(quote).toString("base64url"), - expected.replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", ""), - ); +function forEachBase64(label, test) { + ["base64", "base64url"].forEach(encoding => it(`${label} (${encoding})`, test.bind(null, encoding))); +} - base64flavors.forEach(encoding => { - let b = Buffer.allocUnsafe(1024); - let bytesWritten = b.write(expected, 0, encoding); - assert.strictEqual(quote.length, bytesWritten); - assert.strictEqual(quote, b.toString("ascii", 0, quote.length)); - - // Check that the base64 decoder ignores whitespace - const expectedWhite = - `${expected.slice(0, 60)} \n` + - `${expected.slice(60, 120)} \n` + - `${expected.slice(120, 180)} \n` + - `${expected.slice(180, 240)} \n` + - `${expected.slice(240, 300)}\n` + - `${expected.slice(300, 360)}\n`; - b = Buffer.allocUnsafe(1024); - bytesWritten = b.write(expectedWhite, 0, encoding); - assert.strictEqual(quote.length, bytesWritten); - assert.strictEqual(quote, b.toString("ascii", 0, quote.length)); - - // Check that the base64 decoder on the constructor works - // even in the presence of whitespace. - b = Buffer.from(expectedWhite, encoding); - assert.strictEqual(quote.length, b.length); - assert.strictEqual(quote, b.toString("ascii", 0, quote.length)); - - // Check that the base64 decoder ignores illegal chars - const expectedIllegal = - expected.slice(0, 60) + - " \x80" + - expected.slice(60, 120) + - " \xff" + - expected.slice(120, 180) + - " \x00" + - expected.slice(180, 240) + - " \x98" + - expected.slice(240, 300) + - "\x03" + - expected.slice(300, 360); - b = Buffer.from(expectedIllegal, encoding); - assert.strictEqual(quote.length, b.length); - assert.strictEqual(quote, b.toString("ascii", 0, quote.length)); - }); - } +forEachBase64("big example", encoding => { + const quote = + "Man is distinguished, not only by his reason, but by this " + + "singular passion from other animals, which is a lust " + + "of the mind, that by a perseverance of delight in the " + + "continued and indefatigable generation of knowledge, " + + "exceeds the short vehemence of any carnal pleasure."; + const expected = + "TWFuIGlzIGRpc3Rpbmd1aXNoZWQsIG5vdCBvbmx5IGJ5IGhpcyByZWFzb" + + "24sIGJ1dCBieSB0aGlzIHNpbmd1bGFyIHBhc3Npb24gZnJvbSBvdGhlci" + + "BhbmltYWxzLCB3aGljaCBpcyBhIGx1c3Qgb2YgdGhlIG1pbmQsIHRoYXQ" + + "gYnkgYSBwZXJzZXZlcmFuY2Ugb2YgZGVsaWdodCBpbiB0aGUgY29udGlu" + + "dWVkIGFuZCBpbmRlZmF0aWdhYmxlIGdlbmVyYXRpb24gb2Yga25vd2xlZ" + + "GdlLCBleGNlZWRzIHRoZSBzaG9ydCB2ZWhlbWVuY2Ugb2YgYW55IGNhcm" + + "5hbCBwbGVhc3VyZS4="; - base64flavors.forEach(encoding => { - assert.strictEqual(Buffer.from("", encoding).toString(), ""); - assert.strictEqual(Buffer.from("K", encoding).toString(), ""); - - // multiple-of-4 with padding - assert.strictEqual(Buffer.from("Kg==", encoding).toString(), "*"); - assert.strictEqual(Buffer.from("Kio=", encoding).toString(), "*".repeat(2)); - assert.strictEqual(Buffer.from("Kioq", encoding).toString(), "*".repeat(3)); - assert.strictEqual(Buffer.from("KioqKg==", encoding).toString(), "*".repeat(4)); - assert.strictEqual(Buffer.from("KioqKio=", encoding).toString(), "*".repeat(5)); - assert.strictEqual(Buffer.from("KioqKioq", encoding).toString(), "*".repeat(6)); - assert.strictEqual(Buffer.from("KioqKioqKg==", encoding).toString(), "*".repeat(7)); - assert.strictEqual(Buffer.from("KioqKioqKio=", encoding).toString(), "*".repeat(8)); - assert.strictEqual(Buffer.from("KioqKioqKioq", encoding).toString(), "*".repeat(9)); - assert.strictEqual(Buffer.from("KioqKioqKioqKg==", encoding).toString(), "*".repeat(10)); - assert.strictEqual(Buffer.from("KioqKioqKioqKio=", encoding).toString(), "*".repeat(11)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioq", encoding).toString(), "*".repeat(12)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKg==", encoding).toString(), "*".repeat(13)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKio=", encoding).toString(), "*".repeat(14)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioq", encoding).toString(), "*".repeat(15)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKg==", encoding).toString(), "*".repeat(16)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKio=", encoding).toString(), "*".repeat(17)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKioq", encoding).toString(), "*".repeat(18)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKioqKg==", encoding).toString(), "*".repeat(19)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKioqKio=", encoding).toString(), "*".repeat(20)); - - // No padding, not a multiple of 4 - assert.strictEqual(Buffer.from("Kg", encoding).toString(), "*"); - assert.strictEqual(Buffer.from("Kio", encoding).toString(), "*".repeat(2)); - assert.strictEqual(Buffer.from("KioqKg", encoding).toString(), "*".repeat(4)); - assert.strictEqual(Buffer.from("KioqKio", encoding).toString(), "*".repeat(5)); - assert.strictEqual(Buffer.from("KioqKioqKg", encoding).toString(), "*".repeat(7)); - assert.strictEqual(Buffer.from("KioqKioqKio", encoding).toString(), "*".repeat(8)); - assert.strictEqual(Buffer.from("KioqKioqKioqKg", encoding).toString(), "*".repeat(10)); - assert.strictEqual(Buffer.from("KioqKioqKioqKio", encoding).toString(), "*".repeat(11)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKg", encoding).toString(), "*".repeat(13)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKio", encoding).toString(), "*".repeat(14)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKg", encoding).toString(), "*".repeat(16)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKio", encoding).toString(), "*".repeat(17)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKioqKg", encoding).toString(), "*".repeat(19)); - assert.strictEqual(Buffer.from("KioqKioqKioqKioqKioqKioqKio", encoding).toString(), "*".repeat(20)); - }); + const b = Buffer.allocUnsafe(1024); + expect(b.write(expected, 0, encoding)).toBe(quote.length); + expect(b.toString("ascii", 0, quote.length)).toBe(quote); + + // Check that the base64 decoder ignores whitespace + const white = + `${expected.slice(0, 60)} \n` + + `${expected.slice(60, 120)} \n` + + `${expected.slice(120, 180)} \n` + + `${expected.slice(180, 240)} \n` + + `${expected.slice(240, 300)}\n` + + `${expected.slice(300, 360)}\n`; + const c = Buffer.allocUnsafe(1024); + expect(c.write(white, 0, encoding)).toBe(quote.length); + expect(c.toString("ascii", 0, quote.length)).toBe(quote); + + // Check that the base64 decoder on the constructor works + // even in the presence of whitespace. + const d = Buffer.from(white, encoding); + expect(d.length).toBe(quote.length); + expect(d.toString("ascii", 0, quote.length)).toBe(quote); + + // Check that the base64 decoder ignores illegal chars + const illegal = + expected.slice(0, 60) + + " \x80" + + expected.slice(60, 120) + + " \xff" + + expected.slice(120, 180) + + " \x00" + + expected.slice(180, 240) + + " \x98" + + expected.slice(240, 300) + + "\x03" + + expected.slice(300, 360); + const e = Buffer.from(illegal, encoding); + expect(e.length).toBe(quote.length); + expect(e.toString("ascii", 0, quote.length)).toBe(quote); +}); +forEachBase64("padding", encoding => { + expect(Buffer.from("", encoding).toString()).toBe(""); + expect(Buffer.from("K", encoding).toString()).toBe(""); + // multiple-of-4 with padding + expect(Buffer.from("Kg==", encoding).toString()).toBe("*"); + expect(Buffer.from("Kio=", encoding).toString()).toBe("*".repeat(2)); + expect(Buffer.from("Kioq", encoding).toString()).toBe("*".repeat(3)); + expect(Buffer.from("KioqKg==", encoding).toString()).toBe("*".repeat(4)); + expect(Buffer.from("KioqKio=", encoding).toString()).toBe("*".repeat(5)); + expect(Buffer.from("KioqKioq", encoding).toString()).toBe("*".repeat(6)); + expect(Buffer.from("KioqKioqKg==", encoding).toString()).toBe("*".repeat(7)); + expect(Buffer.from("KioqKioqKio=", encoding).toString()).toBe("*".repeat(8)); + expect(Buffer.from("KioqKioqKioq", encoding).toString()).toBe("*".repeat(9)); + expect(Buffer.from("KioqKioqKioqKg==", encoding).toString()).toBe("*".repeat(10)); + expect(Buffer.from("KioqKioqKioqKio=", encoding).toString()).toBe("*".repeat(11)); + expect(Buffer.from("KioqKioqKioqKioq", encoding).toString()).toBe("*".repeat(12)); + expect(Buffer.from("KioqKioqKioqKioqKg==", encoding).toString()).toBe("*".repeat(13)); + expect(Buffer.from("KioqKioqKioqKioqKio=", encoding).toString()).toBe("*".repeat(14)); + expect(Buffer.from("KioqKioqKioqKioqKioq", encoding).toString()).toBe("*".repeat(15)); + expect(Buffer.from("KioqKioqKioqKioqKioqKg==", encoding).toString()).toBe("*".repeat(16)); + expect(Buffer.from("KioqKioqKioqKioqKioqKio=", encoding).toString()).toBe("*".repeat(17)); + expect(Buffer.from("KioqKioqKioqKioqKioqKioq", encoding).toString()).toBe("*".repeat(18)); + expect(Buffer.from("KioqKioqKioqKioqKioqKioqKg==", encoding).toString()).toBe("*".repeat(19)); + expect(Buffer.from("KioqKioqKioqKioqKioqKioqKio=", encoding).toString()).toBe("*".repeat(20)); + // No padding, not a multiple of 4 + expect(Buffer.from("Kg", encoding).toString()).toBe("*"); + expect(Buffer.from("Kio", encoding).toString()).toBe("*".repeat(2)); + expect(Buffer.from("KioqKg", encoding).toString()).toBe("*".repeat(4)); + expect(Buffer.from("KioqKio", encoding).toString()).toBe("*".repeat(5)); + expect(Buffer.from("KioqKioqKg", encoding).toString()).toBe("*".repeat(7)); + expect(Buffer.from("KioqKioqKio", encoding).toString()).toBe("*".repeat(8)); + expect(Buffer.from("KioqKioqKioqKg", encoding).toString()).toBe("*".repeat(10)); + expect(Buffer.from("KioqKioqKioqKio", encoding).toString()).toBe("*".repeat(11)); + expect(Buffer.from("KioqKioqKioqKioqKg", encoding).toString()).toBe("*".repeat(13)); + expect(Buffer.from("KioqKioqKioqKioqKio", encoding).toString()).toBe("*".repeat(14)); + expect(Buffer.from("KioqKioqKioqKioqKioqKg", encoding).toString()).toBe("*".repeat(16)); + expect(Buffer.from("KioqKioqKioqKioqKioqKio", encoding).toString()).toBe("*".repeat(17)); + expect(Buffer.from("KioqKioqKioqKioqKioqKioqKg", encoding).toString()).toBe("*".repeat(19)); + expect(Buffer.from("KioqKioqKioqKioqKioqKioqKio", encoding).toString()).toBe("*".repeat(20)); // Handle padding graciously, multiple-of-4 or not - assert.strictEqual(Buffer.from("72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw==", "base64").length, 32); - assert.strictEqual(Buffer.from("72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw==", "base64url").length, 32); - assert.strictEqual(Buffer.from("72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw=", "base64").length, 32); - assert.strictEqual(Buffer.from("72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw=", "base64url").length, 32); - assert.strictEqual(Buffer.from("72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw", "base64").length, 32); - assert.strictEqual(Buffer.from("72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw", "base64url").length, 32); - assert.strictEqual(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==", "base64").length, 31); - assert.strictEqual(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==", "base64url").length, 31); - assert.strictEqual(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=", "base64").length, 31); - assert.strictEqual(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=", "base64url").length, 31); - assert.strictEqual(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg", "base64").length, 31); - assert.strictEqual(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg", "base64url").length, 31); + expect(Buffer.from("72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw==", encoding).length).toBe(32); + expect(Buffer.from("72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw==", encoding).length).toBe(32); + expect(Buffer.from("72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw=", encoding).length).toBe(32); + expect(Buffer.from("72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw=", encoding).length).toBe(32); + expect(Buffer.from("72INjkR5fchcxk9+VgdGPFJDxUBFR5/rMFsghgxADiw", encoding).length).toBe(32); + expect(Buffer.from("72INjkR5fchcxk9-VgdGPFJDxUBFR5_rMFsghgxADiw", encoding).length).toBe(32); + expect(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg==", encoding).length).toBe(31); + expect(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg=", encoding).length).toBe(31); + expect(Buffer.from("w69jACy6BgZmaFvv96HG6MYksWytuZu3T1FvGnulPg", encoding).length).toBe(31); +}); - { - // This string encodes single '.' character in UTF-16 - const dot = Buffer.from("//4uAA==", "base64"); - assert.strictEqual(dot[0], 0xff); - assert.strictEqual(dot[1], 0xfe); - assert.strictEqual(dot[2], 0x2e); - assert.strictEqual(dot[3], 0x00); - assert.strictEqual(dot.toString("base64"), "//4uAA=="); - } +it("encodes single '.' character in UTF-16", () => { + const padded = Buffer.from("//4uAA==", "base64"); + expect(padded[0]).toBe(0xff); + expect(padded[1]).toBe(0xfe); + expect(padded[2]).toBe(0x2e); + expect(padded[3]).toBe(0x00); + expect(padded.toString("base64")).toBe("//4uAA=="); + + const dot = Buffer.from("//4uAA", "base64url"); + expect(dot[0]).toBe(0xff); + expect(dot[1]).toBe(0xfe); + expect(dot[2]).toBe(0x2e); + expect(dot[3]).toBe(0x00); + expect(dot.toString("base64url")).toBe("__4uAA"); +}); - { - // This string encodes single '.' character in UTF-16 - const dot = Buffer.from("//4uAA", "base64url"); - assert.strictEqual(dot[0], 0xff); - assert.strictEqual(dot[1], 0xfe); - assert.strictEqual(dot[2], 0x2e); - assert.strictEqual(dot[3], 0x00); - assert.strictEqual(dot.toString("base64url"), "__4uAA"); - } +// https://github.com/joyent/node/issues/402 +it("writing base64 at a position > 0 should not mangle the result", () => { + const segments = ["TWFkbmVzcz8h", "IFRoaXM=", "IGlz", "IG5vZGUuanMh"]; + const b = Buffer.allocUnsafe(64); + let pos = 0; - { - // Writing base64 at a position > 0 should not mangle the result. - // - // https://github.com/joyent/node/issues/402 - const segments = ["TWFkbmVzcz8h", "IFRoaXM=", "IGlz", "IG5vZGUuanMh"]; - const b = Buffer.allocUnsafe(64); - let pos = 0; - - for (let i = 0; i < segments.length; ++i) { - pos += b.write(segments[i], pos, "base64"); - } - assert.strictEqual(b.toString("latin1", 0, pos), "Madness?! This is node.js!"); + for (let i = 0; i < segments.length; ++i) { + pos += b.write(segments[i], pos, "base64"); } + expect(b.toString("latin1", 0, pos)).toBe("Madness?! This is node.js!"); +}); - { - // Writing base64url at a position > 0 should not mangle the result. - // - // https://github.com/joyent/node/issues/402 - const segments = ["TWFkbmVzcz8h", "IFRoaXM", "IGlz", "IG5vZGUuanMh"]; - const b = Buffer.allocUnsafe(64); - let pos = 0; - - for (let i = 0; i < segments.length; ++i) { - pos += b.write(segments[i], pos, "base64url"); - } - assert.strictEqual(b.toString("latin1", 0, pos), "Madness?! This is node.js!"); +// https://github.com/joyent/node/issues/402 +it("writing base64url at a position > 0 should not mangle the result", () => { + const segments = ["TWFkbmVzcz8h", "IFRoaXM", "IGlz", "IG5vZGUuanMh"]; + const b = Buffer.allocUnsafe(64); + let pos = 0; + + for (let i = 0; i < segments.length; ++i) { + pos += b.write(segments[i], pos, "base64url"); } + expect(b.toString("latin1", 0, pos)).toBe("Madness?! This is node.js!"); +}); +it("regression tests from Node.js", () => { // Regression test for https://github.com/nodejs/node/issues/3496. - assert.strictEqual(Buffer.from("=bad".repeat(1e4), "base64").length, 0); - + expect(Buffer.from("=bad".repeat(1e4), "base64").length).toBe(0); // Regression test for https://github.com/nodejs/node/issues/11987. - assert.deepStrictEqual(Buffer.from("w0 ", "base64"), Buffer.from("w0", "base64")); - + expect(Buffer.from("w0 ", "base64")).toStrictEqual(Buffer.from("w0", "base64")); // Regression test for https://github.com/nodejs/node/issues/13657. - assert.deepStrictEqual(Buffer.from(" YWJvcnVtLg", "base64"), Buffer.from("YWJvcnVtLg", "base64")); - - { - // Creating buffers larger than pool size. - const l = Buffer.poolSize + 5; - const s = "h".repeat(l); - const b = Buffer.from(s); + expect(Buffer.from(" YWJvcnVtLg", "base64")).toStrictEqual(Buffer.from("YWJvcnVtLg", "base64")); + // issue GH-3416 + Buffer.from(Buffer.allocUnsafe(0), 0, 0); + // Regression test for https://github.com/nodejs/node-v0.x-archive/issues/5482: + // should throw but not assert in C++ land. + expect(() => Buffer.from("", "buffer")).toThrow(/encoding/); +}); - for (let i = 0; i < l; i++) { - assert.strictEqual(b[i], "h".charCodeAt(0)); - } +it("creating buffers larger than pool size", () => { + const l = Buffer.poolSize + 5; + const s = "h".repeat(l); + const b = Buffer.from(s); - const sb = b.toString(); - assert.strictEqual(sb.length, s.length); - assert.strictEqual(sb, s); + for (let i = 0; i < l; i++) { + expect(b[i]).toBe("h".charCodeAt(0)); } - { - // test hex toString - const hexb = Buffer.allocUnsafe(256); - for (let i = 0; i < 256; i++) { - hexb[i] = i; - } - const hexStr = hexb.toString("hex"); - assert.strictEqual( - hexStr, - "000102030405060708090a0b0c0d0e0f" + - "101112131415161718191a1b1c1d1e1f" + - "202122232425262728292a2b2c2d2e2f" + - "303132333435363738393a3b3c3d3e3f" + - "404142434445464748494a4b4c4d4e4f" + - "505152535455565758595a5b5c5d5e5f" + - "606162636465666768696a6b6c6d6e6f" + - "707172737475767778797a7b7c7d7e7f" + - "808182838485868788898a8b8c8d8e8f" + - "909192939495969798999a9b9c9d9e9f" + - "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + - "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + - "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + - "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + - "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + - "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - ); + const sb = b.toString(); + expect(sb.length).toBe(s.length); + expect(sb).toBe(s); +}); - const hexb2 = Buffer.from(hexStr, "hex"); - for (let i = 0; i < 256; i++) { - assert.strictEqual(hexb2[i], hexb[i]); - } +it("hex toString()", () => { + const hexb = Buffer.allocUnsafe(256); + for (let i = 0; i < 256; i++) { + hexb[i] = i; + } + const hexStr = hexb.toString("hex"); + expect(hexStr).toBe( + "000102030405060708090a0b0c0d0e0f" + + "101112131415161718191a1b1c1d1e1f" + + "202122232425262728292a2b2c2d2e2f" + + "303132333435363738393a3b3c3d3e3f" + + "404142434445464748494a4b4c4d4e4f" + + "505152535455565758595a5b5c5d5e5f" + + "606162636465666768696a6b6c6d6e6f" + + "707172737475767778797a7b7c7d7e7f" + + "808182838485868788898a8b8c8d8e8f" + + "909192939495969798999a9b9c9d9e9f" + + "a0a1a2a3a4a5a6a7a8a9aaabacadaeaf" + + "b0b1b2b3b4b5b6b7b8b9babbbcbdbebf" + + "c0c1c2c3c4c5c6c7c8c9cacbcccdcecf" + + "d0d1d2d3d4d5d6d7d8d9dadbdcdddedf" + + "e0e1e2e3e4e5e6e7e8e9eaebecedeeef" + + "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", + ); + + const hexb2 = Buffer.from(hexStr, "hex"); + for (let i = 0; i < 256; i++) { + expect(hexb2[i]).toBe(hexb[i]); } +}); - // Test single hex character is discarded. - assert.strictEqual(Buffer.from("A", "hex").length, 0); +it("single hex character is discarded", () => { + expect(Buffer.from("A", "hex").length).toBe(0); +}); - // Test that if a trailing character is discarded, rest of string is processed. - assert.deepStrictEqual(Buffer.from("Abx", "hex"), Buffer.from("Ab", "hex")); +it("if a trailing character is discarded, rest of string is processed", () => { + expect(Buffer.from("Abx", "hex")).toEqual(Buffer.from("Ab", "hex")); +}); - // Test single base64 char encodes as 0. - assert.strictEqual(Buffer.from("A", "base64").length, 0); +it("single base64 char encodes as 0", () => { + expect(Buffer.from("A", "base64").length).toBe(0); +}); - { - // Test an invalid slice end. - const b = Buffer.from([1, 2, 3, 4, 5]); - const b2 = b.toString("hex", 1, 10000); - const b3 = b.toString("hex", 1, 5); - const b4 = b.toString("hex", 1); - assert.strictEqual(b2, b3); - assert.strictEqual(b2, b4); - } +it("invalid slice end", () => { + const b = Buffer.from([1, 2, 3, 4, 5]); + const b2 = b.toString("hex", 1, 10000); + const b3 = b.toString("hex", 1, 5); + const b4 = b.toString("hex", 1); + expect(b2).toBe(b3); + expect(b2).toBe(b4); +}); +it("slice()", () => { function buildBuffer(data) { if (Array.isArray(data)) { const buffer = Buffer.allocUnsafe(data.length); @@ -601,522 +542,419 @@ it("Buffer.alloc", () => { } const x = buildBuffer([0x81, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72]); + expect(x).toStrictEqual(Buffer.from([0x81, 0xa3, 0x66, 0x6f, 0x6f, 0xa3, 0x62, 0x61, 0x72])); + + const a = x.slice(4); + expect(a.length).toBe(5); + expect(a[0]).toBe(0x6f); + expect(a[1]).toBe(0xa3); + expect(a[2]).toBe(0x62); + expect(a[3]).toBe(0x61); + expect(a[4]).toBe(0x72); + + const b = x.slice(0); + expect(b.length).toBe(x.length); + + const c = x.slice(0, 4); + expect(c.length).toBe(4); + expect(c[0]).toBe(0x81); + expect(c[1]).toBe(0xa3); + + const d = x.slice(0, 9); + expect(d.length).toBe(9); + + const e = x.slice(1, 4); + expect(e.length).toBe(3); + expect(e[0]).toBe(0xa3); + + const f = x.slice(2, 4); + expect(f.length).toBe(2); + expect(f[0]).toBe(0x66); + expect(f[1]).toBe(0x6f); +}); - // assert.strictEqual(x.inspect(), ""); +function forEachUnicode(label, test) { + ["ucs2", "ucs-2", "utf16le", "utf-16le"].forEach(encoding => it(`${label} (${encoding})`, test.bind(null, encoding))); +} - { - const z = x.slice(4); - assert.strictEqual(z.length, 5); - assert.strictEqual(z[0], 0x6f); - assert.strictEqual(z[1], 0xa3); - assert.strictEqual(z[2], 0x62); - assert.strictEqual(z[3], 0x61); - assert.strictEqual(z[4], 0x72); - } +forEachUnicode("write()", encoding => { + const b = Buffer.allocUnsafe(10); + b.write("あいうえお", encoding); + expect(b.toString(encoding)).toBe("あいうえお"); +}); - { - const z = x.slice(0); - assert.strictEqual(z.length, x.length); - } +forEachUnicode("write() with offset", encoding => { + const b = Buffer.allocUnsafe(11); + b.write("あいうえお", 1, encoding); + expect(b.toString(encoding, 1)).toBe("あいうえお"); +}); - { - const z = x.slice(0, 4); - assert.strictEqual(z.length, 4); - assert.strictEqual(z[0], 0x81); - assert.strictEqual(z[1], 0xa3); - } +it("latin1 encoding should write only one byte per character", () => { + const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); + b.write(String.fromCharCode(0xffff), 0, "latin1"); + expect(b[0]).toBe(0xff); + expect(b[1]).toBe(0xad); + expect(b[2]).toBe(0xbe); + expect(b[3]).toBe(0xef); + b.write(String.fromCharCode(0xaaee), 0, "latin1"); + expect(b[0]).toBe(0xee); + expect(b[1]).toBe(0xad); + expect(b[2]).toBe(0xbe); + expect(b[3]).toBe(0xef); +}); - { - const z = x.slice(0, 9); - assert.strictEqual(z.length, 9); - } +it("binary encoding should write only one byte per character", () => { + const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); + b.write(String.fromCharCode(0xffff), 0, "latin1"); + expect(b[0]).toBe(0xff); + expect(b[1]).toBe(0xad); + expect(b[2]).toBe(0xbe); + expect(b[3]).toBe(0xef); + b.write(String.fromCharCode(0xaaee), 0, "latin1"); + expect(b[0]).toBe(0xee); + expect(b[1]).toBe(0xad); + expect(b[2]).toBe(0xbe); + expect(b[3]).toBe(0xef); +}); - { - const z = x.slice(1, 4); - assert.strictEqual(z.length, 3); - assert.strictEqual(z[0], 0xa3); - } +it("UTF-8 string includes null character", () => { + // https://github.com/nodejs/node-v0.x-archive/pull/1210 + expect(Buffer.from("\0").length).toBe(1); + expect(Buffer.from("\0\0").length).toBe(2); +}); - { - const z = x.slice(2, 4); - assert.strictEqual(z.length, 2); - assert.strictEqual(z[0], 0x66); - assert.strictEqual(z[1], 0x6f); - } +it("truncate write() at character boundary", () => { + const buf = Buffer.allocUnsafe(2); + expect(buf.write("")).toBe(0); // 0bytes + expect(buf.write("\0")).toBe(1); // 1byte (v8 adds null terminator) + expect(buf.write("a\0")).toBe(2); // 1byte * 2 + expect(buf.write("あ")).toBe(0); // 3bytes + expect(buf.write("\0あ")).toBe(1); // 1byte + 3bytes + expect(buf.write("\0\0あ")).toBe(2); // 1byte * 2 + 3bytes + + const buf2 = Buffer.allocUnsafe(10); + expect(buf2.write("あいう")).toBe(9); // 3bytes * 3 (v8 adds null term.) + expect(buf2.write("あいう\0")).toBe(10); // 3bytes * 3 + 1byte +}); - ["ucs2", "ucs-2", "utf16le", "utf-16le"].forEach(encoding => { - const b = Buffer.allocUnsafe(10); - b.write("あいうえお", encoding); - assert.strictEqual(b.toString(encoding), "あいうえお"); - }); +it("write() with maxLength", () => { + // https://github.com/nodejs/node-v0.x-archive/issues/243 + const buf = Buffer.allocUnsafe(4); + buf.fill(0xff); + expect(buf.write("abcd", 1, 2, "utf8")).toBe(2); + expect(buf[0]).toBe(0xff); + expect(buf[1]).toBe(0x61); + expect(buf[2]).toBe(0x62); + expect(buf[3]).toBe(0xff); + + buf.fill(0xff); + expect(buf.write("abcd", 1, 4)).toBe(3); + expect(buf[0]).toBe(0xff); + expect(buf[1]).toBe(0x61); + expect(buf[2]).toBe(0x62); + expect(buf[3]).toBe(0x63); + + buf.fill(0xff); + expect(buf.write("abcd", 1, 2, "utf8")).toBe(2); + expect(buf[0]).toBe(0xff); + expect(buf[1]).toBe(0x61); + expect(buf[2]).toBe(0x62); + expect(buf[3]).toBe(0xff); + + buf.fill(0xff); + expect(buf.write("abcdef", 1, 2, "hex")).toBe(2); + expect(buf[0]).toBe(0xff); + expect(buf[1]).toBe(0xab); + expect(buf[2]).toBe(0xcd); + expect(buf[3]).toBe(0xff); ["ucs2", "ucs-2", "utf16le", "utf-16le"].forEach(encoding => { - const b = Buffer.allocUnsafe(11); - b.write("あいうえお", 1, encoding); - assert.strictEqual(b.toString(encoding, 1), "あいうえお"); - }); - - { - // latin1 encoding should write only one byte per character. - const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); - let s = String.fromCharCode(0xffff); - b.write(s, 0, "latin1"); - assert.strictEqual(b[0], 0xff); - assert.strictEqual(b[1], 0xad); - assert.strictEqual(b[2], 0xbe); - assert.strictEqual(b[3], 0xef); - s = String.fromCharCode(0xaaee); - b.write(s, 0, "latin1"); - assert.strictEqual(b[0], 0xee); - assert.strictEqual(b[1], 0xad); - assert.strictEqual(b[2], 0xbe); - assert.strictEqual(b[3], 0xef); - } - - { - // Binary encoding should write only one byte per character. - const b = Buffer.from([0xde, 0xad, 0xbe, 0xef]); - let s = String.fromCharCode(0xffff); - b.write(s, 0, "latin1"); - assert.strictEqual(b[0], 0xff); - assert.strictEqual(b[1], 0xad); - assert.strictEqual(b[2], 0xbe); - assert.strictEqual(b[3], 0xef); - s = String.fromCharCode(0xaaee); - b.write(s, 0, "latin1"); - assert.strictEqual(b[0], 0xee); - assert.strictEqual(b[1], 0xad); - assert.strictEqual(b[2], 0xbe); - assert.strictEqual(b[3], 0xef); - } - - { - // https://github.com/nodejs/node-v0.x-archive/pull/1210 - // Test UTF-8 string includes null character - let buf = Buffer.from("\0"); - assert.strictEqual(buf.length, 1); - buf = Buffer.from("\0\0"); - assert.strictEqual(buf.length, 2); - } - - { - const buf = Buffer.allocUnsafe(2); - assert.strictEqual(buf.write(""), 0); // 0bytes - assert.strictEqual(buf.write("\0"), 1); // 1byte (v8 adds null terminator) - assert.strictEqual(buf.write("a\0"), 2); // 1byte * 2 - assert.strictEqual(buf.write("あ"), 0); // 3bytes - assert.strictEqual(buf.write("\0あ"), 1); // 1byte + 3bytes - assert.strictEqual(buf.write("\0\0あ"), 2); // 1byte * 2 + 3bytes - } - - { - const buf = Buffer.allocUnsafe(10); - assert.strictEqual(buf.write("あいう"), 9); // 3bytes * 3 (v8 adds null term.) - assert.strictEqual(buf.write("あいう\0"), 10); // 3bytes * 3 + 1byte - } - - { - // https://github.com/nodejs/node-v0.x-archive/issues/243 - // Test write() with maxLength - const buf = Buffer.allocUnsafe(4); - buf.fill(0xff); - assert.strictEqual(buf.write("abcd", 1, 2, "utf8"), 2); - assert.strictEqual(buf[0], 0xff); - assert.strictEqual(buf[1], 0x61); - assert.strictEqual(buf[2], 0x62); - assert.strictEqual(buf[3], 0xff); - - buf.fill(0xff); - assert.strictEqual(buf.write("abcd", 1, 4), 3); - assert.strictEqual(buf[0], 0xff); - assert.strictEqual(buf[1], 0x61); - assert.strictEqual(buf[2], 0x62); - assert.strictEqual(buf[3], 0x63); - - buf.fill(0xff); - assert.strictEqual(buf.write("abcd", 1, 2, "utf8"), 2); - assert.strictEqual(buf[0], 0xff); - assert.strictEqual(buf[1], 0x61); - assert.strictEqual(buf[2], 0x62); - assert.strictEqual(buf[3], 0xff); - buf.fill(0xff); - assert.strictEqual(buf.write("abcdef", 1, 2, "hex"), 2); - assert.strictEqual(buf[0], 0xff); - assert.strictEqual(buf[1], 0xab); - assert.strictEqual(buf[2], 0xcd); - assert.strictEqual(buf[3], 0xff); - - ["ucs2", "ucs-2", "utf16le", "utf-16le"].forEach(encoding => { - buf.fill(0xff); - assert.strictEqual(buf.write("abcd", 0, 2, encoding), 2); - assert.strictEqual(buf[0], 0x61); - assert.strictEqual(buf[1], 0x00); - assert.strictEqual(buf[2], 0xff); - assert.strictEqual(buf[3], 0xff); - }); - } - - { - // Test offset returns are correct - const b = Buffer.allocUnsafe(16); - assert.strictEqual(b.writeUInt32LE(0, 0), 4); - assert.strictEqual(b.writeUInt16LE(0, 4), 6); - assert.strictEqual(b.writeUInt8(0, 6), 7); - assert.strictEqual(b.writeInt8(0, 7), 8); - assert.strictEqual(b.writeDoubleLE(0, 8), 16); - } - - { - // Test unmatched surrogates not producing invalid utf8 output - // ef bf bd = utf-8 representation of unicode replacement character - // see https://codereview.chromium.org/121173009/ - let buf = Buffer.from("ab\ud800cd", "utf8"); - assert.strictEqual(buf[0], 0x61); - assert.strictEqual(buf[1], 0x62); - assert.strictEqual(buf[2], 0xef); - assert.strictEqual(buf[3], 0xbf); - assert.strictEqual(buf[4], 0xbd); - assert.strictEqual(buf[5], 0x63); - assert.strictEqual(buf[6], 0x64); - - buf = Buffer.from("abcd\ud800", "utf8"); + expect(buf.write("abcd", 0, 2, encoding)).toBe(2); expect(buf[0]).toBe(0x61); - expect(buf[1]).toBe(0x62); - expect(buf[2]).toBe(0x63); - expect(buf[3]).toBe(0x64); - expect(buf[4]).toBe(0xef); - expect(buf[5]).toBe(0xbf); - expect(buf[6]).toBe(0xbd); - - buf = Buffer.from("\ud800abcd", "utf8"); - expect(buf[0]).toBe(0xef); - expect(buf[1]).toBe(0xbf); - expect(buf[2]).toBe(0xbd); - expect(buf[3]).toBe(0x61); - expect(buf[4]).toBe(0x62); - expect(buf[5]).toBe(0x63); - expect(buf[6]).toBe(0x64); - } + expect(buf[1]).toBe(0x00); + expect(buf[2]).toBe(0xff); + expect(buf[3]).toBe(0xff); + }); +}); - { - // Test for buffer overrun - const buf = Buffer.from([0, 0, 0, 0, 0]); // length: 5 - const sub = buf.slice(0, 4); // length: 4 - assert.strictEqual(sub.write("12345", "latin1"), 4); - assert.strictEqual(buf[4], 0); - assert.strictEqual(sub.write("12345", "binary"), 4); - assert.strictEqual(buf[4], 0); - } +it("offset returns are correct", () => { + const b = Buffer.allocUnsafe(16); + expect(b.writeUInt32LE(0, 0)).toBe(4); + expect(b.writeUInt16LE(0, 4)).toBe(6); + expect(b.writeUInt8(0, 6)).toBe(7); + expect(b.writeInt8(0, 7)).toBe(8); + expect(b.writeDoubleLE(0, 8)).toBe(16); +}); - { - // Test alloc with fill option - const buf = Buffer.alloc(5, "800A", "hex"); - assert.strictEqual(buf[0], 128); - assert.strictEqual(buf[1], 10); - assert.strictEqual(buf[2], 128); - assert.strictEqual(buf[3], 10); - assert.strictEqual(buf[4], 128); - } +it("unmatched surrogates should not produce invalid utf8 output", () => { + // ef bf bd = utf-8 representation of unicode replacement character + // see https://codereview.chromium.org/121173009/ + let buf = Buffer.from("ab\ud800cd", "utf8"); + expect(buf[0]).toBe(0x61); + expect(buf[1]).toBe(0x62); + expect(buf[2]).toBe(0xef); + expect(buf[3]).toBe(0xbf); + expect(buf[4]).toBe(0xbd); + expect(buf[5]).toBe(0x63); + expect(buf[6]).toBe(0x64); + + buf = Buffer.from("abcd\ud800", "utf8"); + expect(buf[0]).toBe(0x61); + expect(buf[1]).toBe(0x62); + expect(buf[2]).toBe(0x63); + expect(buf[3]).toBe(0x64); + expect(buf[4]).toBe(0xef); + expect(buf[5]).toBe(0xbf); + expect(buf[6]).toBe(0xbd); + + buf = Buffer.from("\ud800abcd", "utf8"); + expect(buf[0]).toBe(0xef); + expect(buf[1]).toBe(0xbf); + expect(buf[2]).toBe(0xbd); + expect(buf[3]).toBe(0x61); + expect(buf[4]).toBe(0x62); + expect(buf[5]).toBe(0x63); + expect(buf[6]).toBe(0x64); +}); - // Check for fractional length args, junk length args, etc. - // https://github.com/joyent/node/issues/1758 +it("buffer overrun", () => { + const buf = Buffer.from([0, 0, 0, 0, 0]); // length: 5 + const sub = buf.slice(0, 4); // length: 4 + expect(sub.write("12345", "latin1")).toBe(4); + expect(buf[4]).toBe(0); + expect(sub.write("12345", "binary")).toBe(4); + expect(buf[4]).toBe(0); +}); +it("alloc with fill option", () => { + const buf = Buffer.alloc(5, "800A", "hex"); + expect(buf[0]).toBe(128); + expect(buf[1]).toBe(10); + expect(buf[2]).toBe(128); + expect(buf[3]).toBe(10); + expect(buf[4]).toBe(128); +}); + +// https://github.com/joyent/node/issues/1758 +it("check for fractional length args, junk length args, etc.", () => { // Call .fill() first, stops valgrind warning about uninitialized memory reads. Buffer.allocUnsafe(3.3).fill().toString(); // Throws bad argument error in commit 43cb4ec Buffer.alloc(3.3).fill().toString(); - assert.strictEqual(Buffer.allocUnsafe(3.3).length, 3); - assert.strictEqual(Buffer.from({ length: 3.3 }).length, 3); - assert.strictEqual(Buffer.from({ length: "BAM" }).length, 0); - + expect(Buffer.allocUnsafe(3.3).length).toBe(3); + expect(Buffer.from({ length: 3.3 }).length).toBe(3); + expect(Buffer.from({ length: "BAM" }).length).toBe(0); // Make sure that strings are not coerced to numbers. - assert.strictEqual(Buffer.from("99").length, 2); - assert.strictEqual(Buffer.from("13.37").length, 5); - + expect(Buffer.from("99").length).toBe(2); + expect(Buffer.from("13.37").length).toBe(5); // Ensure that the length argument is respected. ["ascii", "utf8", "hex", "base64", "latin1", "binary"].forEach(enc => { - assert.strictEqual(Buffer.allocUnsafe(1).write("aaaaaa", 0, 1, enc), 1); + expect(Buffer.allocUnsafe(1).write("aaaaaa", 0, 1, enc)).toBe(1); }); + // Regression test, guard against buffer overrun in the base64 decoder. + const a = Buffer.allocUnsafe(3); + const b = Buffer.from("xxx"); + a.write("aaaaaaaa", "base64"); + expect(b.toString()).toBe("xxx"); +}); - { - // Regression test, guard against buffer overrun in the base64 decoder. - const a = Buffer.allocUnsafe(3); - const b = Buffer.from("xxx"); - a.write("aaaaaaaa", "base64"); - assert.strictEqual(b.toString(), "xxx"); - } - - // issue GH-3416 - Buffer.from(Buffer.allocUnsafe(0), 0, 0); - +it("buffer overflow", () => { // issue GH-5587 - assert.throws(() => Buffer.alloc(8).writeFloatLE(0, 5), outOfRangeError); - assert.throws(() => Buffer.alloc(16).writeDoubleLE(0, 9), outOfRangeError); - + expect(() => Buffer.alloc(8).writeFloatLE(0, 5)).toThrow(RangeError); + expect(() => Buffer.alloc(16).writeDoubleLE(0, 9)).toThrow(RangeError); // Attempt to overflow buffers, similar to previous bug in array buffers - assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff), outOfRangeError); - assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff), outOfRangeError); - + expect(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff)).toThrow(RangeError); + expect(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, 0xffffffff)).toThrow(RangeError); // Ensure negative values can't get past offset - assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1), outOfRangeError); - assert.throws(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1), outOfRangeError); - - // Test for common write(U)IntLE/BE - { - let buf = Buffer.allocUnsafe(3); - buf.writeUIntLE(0x123456, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); - assert.strictEqual(buf.readUIntLE(0, 3), 0x123456); - - buf.fill(0xff); - buf.writeUIntBE(0x123456, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); - assert.strictEqual(buf.readUIntBE(0, 3), 0x123456); - - buf.fill(0xff); - buf.writeIntLE(0x123456, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0x56, 0x34, 0x12]); - assert.strictEqual(buf.readIntLE(0, 3), 0x123456); - - buf.fill(0xff); - buf.writeIntBE(0x123456, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56]); - assert.strictEqual(buf.readIntBE(0, 3), 0x123456); - - buf.fill(0xff); - buf.writeIntLE(-0x123456, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0xaa, 0xcb, 0xed]); - assert.strictEqual(buf.readIntLE(0, 3), -0x123456); - - buf.fill(0xff); - buf.writeIntBE(-0x123456, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xaa]); - assert.strictEqual(buf.readIntBE(0, 3), -0x123456); - - buf.fill(0xff); - buf.writeIntLE(-0x123400, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0x00, 0xcc, 0xed]); - assert.strictEqual(buf.readIntLE(0, 3), -0x123400); - - buf.fill(0xff); - buf.writeIntBE(-0x123400, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcc, 0x00]); - assert.strictEqual(buf.readIntBE(0, 3), -0x123400); - - buf.fill(0xff); - buf.writeIntLE(-0x120000, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0xee]); - assert.strictEqual(buf.readIntLE(0, 3), -0x120000); - - buf.fill(0xff); - buf.writeIntBE(-0x120000, 0, 3); - assert.deepStrictEqual(buf.toJSON().data, [0xee, 0x00, 0x00]); - assert.strictEqual(buf.readIntBE(0, 3), -0x120000); - - buf = Buffer.allocUnsafe(5); - buf.writeUIntLE(0x1234567890, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); - assert.strictEqual(buf.readUIntLE(0, 5), 0x1234567890); - - buf.fill(0xff); - buf.writeUIntBE(0x1234567890, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); - assert.strictEqual(buf.readUIntBE(0, 5), 0x1234567890); - - buf.fill(0xff); - buf.writeIntLE(0x1234567890, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0x90, 0x78, 0x56, 0x34, 0x12]); - assert.strictEqual(buf.readIntLE(0, 5), 0x1234567890); - - buf.fill(0xff); - buf.writeIntBE(0x1234567890, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0x12, 0x34, 0x56, 0x78, 0x90]); - assert.strictEqual(buf.readIntBE(0, 5), 0x1234567890); - - buf.fill(0xff); - buf.writeIntLE(-0x1234567890, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0x70, 0x87, 0xa9, 0xcb, 0xed]); - assert.strictEqual(buf.readIntLE(0, 5), -0x1234567890); - - buf.fill(0xff); - buf.writeIntBE(-0x1234567890, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0xed, 0xcb, 0xa9, 0x87, 0x70]); - assert.strictEqual(buf.readIntBE(0, 5), -0x1234567890); - - buf.fill(0xff); - buf.writeIntLE(-0x0012000000, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0x00, 0x00, 0x00, 0xee, 0xff]); - assert.strictEqual(buf.readIntLE(0, 5), -0x0012000000); - - buf.fill(0xff); - buf.writeIntBE(-0x0012000000, 0, 5); - assert.deepStrictEqual(buf.toJSON().data, [0xff, 0xee, 0x00, 0x00, 0x00]); - assert.strictEqual(buf.readIntBE(0, 5), -0x0012000000); - } + expect(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1)).toThrow(RangeError); + expect(() => Buffer.allocUnsafe(8).writeFloatLE(0.0, -1)).toThrow(RangeError); +}); - // Regression test for https://github.com/nodejs/node-v0.x-archive/issues/5482: - // should throw but not assert in C++ land. - assert.throws(() => Buffer.from("", "buffer"), { - code: "ERR_UNKNOWN_ENCODING", - name: "TypeError", - message: "Unknown encoding: buffer", - }); +it("common write{U}IntLE/BE()", () => { + let buf = Buffer.allocUnsafe(3); + buf.writeUIntLE(0x123456, 0, 3); + expect(buf.toJSON().data).toEqual([0x56, 0x34, 0x12]); + expect(buf.readUIntLE(0, 3)).toBe(0x123456); + + buf.fill(0xff); + buf.writeUIntBE(0x123456, 0, 3); + expect(buf.toJSON().data).toEqual([0x12, 0x34, 0x56]); + expect(buf.readUIntBE(0, 3)).toBe(0x123456); + + buf.fill(0xff); + buf.writeIntLE(0x123456, 0, 3); + expect(buf.toJSON().data).toEqual([0x56, 0x34, 0x12]); + expect(buf.readIntLE(0, 3)).toBe(0x123456); + + buf.fill(0xff); + buf.writeIntBE(0x123456, 0, 3); + expect(buf.toJSON().data).toEqual([0x12, 0x34, 0x56]); + expect(buf.readIntBE(0, 3)).toBe(0x123456); + + buf.fill(0xff); + buf.writeIntLE(-0x123456, 0, 3); + expect(buf.toJSON().data).toEqual([0xaa, 0xcb, 0xed]); + expect(buf.readIntLE(0, 3)).toBe(-0x123456); + + buf.fill(0xff); + buf.writeIntBE(-0x123456, 0, 3); + expect(buf.toJSON().data).toEqual([0xed, 0xcb, 0xaa]); + expect(buf.readIntBE(0, 3)).toBe(-0x123456); + + buf.fill(0xff); + buf.writeIntLE(-0x123400, 0, 3); + expect(buf.toJSON().data).toEqual([0x00, 0xcc, 0xed]); + expect(buf.readIntLE(0, 3)).toBe(-0x123400); + + buf.fill(0xff); + buf.writeIntBE(-0x123400, 0, 3); + expect(buf.toJSON().data).toEqual([0xed, 0xcc, 0x00]); + expect(buf.readIntBE(0, 3)).toBe(-0x123400); + + buf.fill(0xff); + buf.writeIntLE(-0x120000, 0, 3); + expect(buf.toJSON().data).toEqual([0x00, 0x00, 0xee]); + expect(buf.readIntLE(0, 3)).toBe(-0x120000); + + buf.fill(0xff); + buf.writeIntBE(-0x120000, 0, 3); + expect(buf.toJSON().data).toEqual([0xee, 0x00, 0x00]); + expect(buf.readIntBE(0, 3)).toBe(-0x120000); + + buf = Buffer.allocUnsafe(5); + buf.writeUIntLE(0x1234567890, 0, 5); + expect(buf.toJSON().data).toEqual([0x90, 0x78, 0x56, 0x34, 0x12]); + expect(buf.readUIntLE(0, 5)).toBe(0x1234567890); + + buf.fill(0xff); + buf.writeUIntBE(0x1234567890, 0, 5); + expect(buf.toJSON().data).toEqual([0x12, 0x34, 0x56, 0x78, 0x90]); + expect(buf.readUIntBE(0, 5)).toBe(0x1234567890); + + buf.fill(0xff); + buf.writeIntLE(0x1234567890, 0, 5); + expect(buf.toJSON().data).toEqual([0x90, 0x78, 0x56, 0x34, 0x12]); + expect(buf.readIntLE(0, 5)).toBe(0x1234567890); + + buf.fill(0xff); + buf.writeIntBE(0x1234567890, 0, 5); + expect(buf.toJSON().data).toEqual([0x12, 0x34, 0x56, 0x78, 0x90]); + expect(buf.readIntBE(0, 5)).toBe(0x1234567890); + + buf.fill(0xff); + buf.writeIntLE(-0x1234567890, 0, 5); + expect(buf.toJSON().data).toEqual([0x70, 0x87, 0xa9, 0xcb, 0xed]); + expect(buf.readIntLE(0, 5)).toBe(-0x1234567890); + + buf.fill(0xff); + buf.writeIntBE(-0x1234567890, 0, 5); + expect(buf.toJSON().data).toEqual([0xed, 0xcb, 0xa9, 0x87, 0x70]); + expect(buf.readIntBE(0, 5)).toBe(-0x1234567890); + + buf.fill(0xff); + buf.writeIntLE(-0x0012000000, 0, 5); + expect(buf.toJSON().data).toEqual([0x00, 0x00, 0x00, 0xee, 0xff]); + expect(buf.readIntLE(0, 5)).toBe(-0x0012000000); + + buf.fill(0xff); + buf.writeIntBE(-0x0012000000, 0, 5); + expect(buf.toJSON().data).toEqual([0xff, 0xee, 0x00, 0x00, 0x00]); + expect(buf.readIntBE(0, 5)).toBe(-0x0012000000); +}); +it("construct buffer from buffer", () => { // Regression test for https://github.com/nodejs/node-v0.x-archive/issues/6111. // Constructing a buffer from another buffer should a) work, and b) not corrupt // the source buffer. - { - const a = [...Array(128).keys()]; // [0, 1, 2, 3, ... 126, 127] - const b = Buffer.from(a); - const c = Buffer.from(b); - assert.strictEqual(b.length, a.length); - assert.strictEqual(c.length, a.length); - for (let i = 0, k = a.length; i < k; ++i) { - assert.strictEqual(a[i], i); - assert.strictEqual(b[i], i); - assert.strictEqual(c[i], i); - } + const a = [...Array(128).keys()]; // [0, 1, 2, 3, ... 126, 127] + const b = Buffer.from(a); + const c = Buffer.from(b); + expect(b.length).toBe(a.length); + expect(c.length).toBe(a.length); + for (let i = 0, k = a.length; i < k; ++i) { + expect(a[i]).toBe(i); + expect(b[i]).toBe(i); + expect(c[i]).toBe(i); } +}); - // if (common.hasCrypto) { - // eslint-disable-line node-core/crypto-check - // Test truncation after decode +it("truncation after decode", () => { const crypto = require("crypto"); - const b1 = Buffer.from("YW55=======", "base64"); - const b2 = Buffer.from("YW55", "base64"); - - assert.strictEqual( - crypto.createHash("sha1").update(b1).digest("hex"), - crypto.createHash("sha1").update(b2).digest("hex"), + expect(crypto.createHash("sha1").update(Buffer.from("YW55=======", "base64")).digest("hex")).toBe( + crypto.createHash("sha1").update(Buffer.from("YW55", "base64")).digest("hex"), ); - // } else { - // common.printSkipMessage("missing crypto"); - // } +}); +it("Buffer,poolSize", () => { const ps = Buffer.poolSize; Buffer.poolSize = 0; - assert(Buffer.allocUnsafe(1).parent instanceof ArrayBuffer); + expect(Buffer.allocUnsafe(1).parent instanceof ArrayBuffer).toBe(true); Buffer.poolSize = ps; - assert.throws(() => Buffer.allocUnsafe(10).copy(), { - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - message: 'The "target" argument must be an instance of Buffer or ' + "Uint8Array. Received undefined", - }); + expect(() => Buffer.allocUnsafe(10).copy()).toThrow(TypeError); - assert.throws(() => Buffer.from(), { - name: "TypeError", - message: - "The first argument must be of type string or an instance of " + - "Buffer, ArrayBuffer, or Array or an Array-like Object. Received undefined", - }); - assert.throws(() => Buffer.from(null), { - name: "TypeError", - message: - "The first argument must be of type string or an instance of " + - "Buffer, ArrayBuffer, or Array or an Array-like Object. Received null", - }); + expect(() => Buffer.from()).toThrow(TypeError); + expect(() => Buffer.from(null)).toThrow(TypeError); +}); - // Test prototype getters don't throw - assert.strictEqual(Buffer.prototype.parent, undefined); - assert.strictEqual(Buffer.prototype.offset, undefined); - assert.strictEqual(SlowBuffer.prototype.parent, undefined); - assert.strictEqual(SlowBuffer.prototype.offset, undefined); +it("prototype getters should not throw", () => { + expect(Buffer.prototype.parent).toBeUndefined(); + expect(Buffer.prototype.offset).toBeUndefined(); + expect(SlowBuffer.prototype.parent).toBeUndefined(); + expect(SlowBuffer.prototype.offset).toBeUndefined(); +}); - { - // Test that large negative Buffer length inputs don't affect the pool offset. - // Use the fromArrayLike() variant here because it's more lenient - // about its input and passes the length directly to allocate(). - assert.deepStrictEqual(Buffer.from({ length: -Buffer.poolSize }), Buffer.from("")); - assert.deepStrictEqual(Buffer.from({ length: -100 }), Buffer.from("")); - - // Check pool offset after that by trying to write string into the pool. - Buffer.from("abc"); - } +it("large negative Buffer length inputs should not affect pool offset", () => { + // Use the fromArrayLike() variant here because it's more lenient + // about its input and passes the length directly to allocate(). + expect(Buffer.from({ length: -Buffer.poolSize })).toStrictEqual(Buffer.from("")); + expect(Buffer.from({ length: -100 })).toStrictEqual(Buffer.from("")); - // Test that ParseArrayIndex handles full uint32 - { - const errMsg = common.expectsError({ - code: "ERR_BUFFER_OUT_OF_BOUNDS", - name: "RangeError", - message: '"offset" is outside of buffer bounds', - }); - assert.throws(() => Buffer.from(new ArrayBuffer(0), -1 >>> 0), errMsg); - } + // Check pool offset after that by trying to write string into the pool. + Buffer.from("abc"); +}); + +it("ParseArrayIndex() should handle full uint32", () => { + expect(() => Buffer.from(new ArrayBuffer(0), -1 >>> 0)).toThrow(RangeError); +}); - // ParseArrayIndex() should reject values that don't fit in a 32 bits size_t. - assert.throws(() => { +it("ParseArrayIndex() should reject values that don't fit in a 32 bits size_t", () => { + expect(() => { const a = Buffer.alloc(1); const b = Buffer.alloc(1); a.copy(b, 0, 0x100000000, 0x100000001); - }, outOfRangeError); + }).toThrow(RangeError); +}); - // Unpooled buffer (replaces SlowBuffer) - { - const ubuf = Buffer.allocUnsafeSlow(10); - assert(ubuf); - assert(ubuf.buffer); - assert.strictEqual(ubuf.buffer.byteLength, 10); - } +it("unpooled buffer (replaces SlowBuffer)", () => { + const ubuf = Buffer.allocUnsafeSlow(10); + expect(ubuf).toBeTruthy(); + expect(ubuf.buffer).toBeTruthy(); + expect(ubuf.buffer.byteLength).toBe(10); +}); - // Regression test to verify that an empty ArrayBuffer does not throw. +it("verify that an empty ArrayBuffer does not throw", () => { Buffer.from(new ArrayBuffer()); +}); - // Test that ArrayBuffer from a different context is detected correctly. - // const arrayBuf = vm.runInNewContext("new ArrayBuffer()"); - // Buffer.from(arrayBuf); - // Buffer.from({ buffer: arrayBuf }); - - assert.throws(() => Buffer.alloc({ valueOf: () => 1 }), /"size" argument must be of type number/); - assert.throws(() => Buffer.alloc({ valueOf: () => -1 }), /"size" argument must be of type number/); - - assert.strictEqual(Buffer.prototype.toLocaleString, Buffer.prototype.toString); - { - const buf = Buffer.from("test"); - assert.strictEqual(buf.toLocaleString(), buf.toString()); - } - - assert.throws( - () => { - Buffer.alloc(0x1000, "This is not correctly encoded", "hex"); - }, - { - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }, - ); - - assert.throws( - () => { - Buffer.alloc(0x1000, "c", "hex"); - }, - { - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }, - ); +it("alloc() should throw on non-numeric size", () => { + expect(() => Buffer.alloc({ valueOf: () => 1 })).toThrow(TypeError); + expect(() => Buffer.alloc({ valueOf: () => -1 })).toThrow(TypeError); +}); - assert.throws( - () => { - Buffer.alloc(1, Buffer.alloc(0)); - }, - { - code: "ERR_INVALID_ARG_VALUE", - name: "TypeError", - }, - ); +it("toLocaleString()", () => { + const buf = Buffer.from("test"); + expect(buf.toLocaleString()).toBe(buf.toString()); + // expect(Buffer.prototype.toLocaleString).toBe(Buffer.prototype.toString); +}); - assert.throws( - () => { - Buffer.alloc(40, "x", 20); - }, - { - code: "ERR_INVALID_ARG_TYPE", - name: "TypeError", - }, - ); +it("alloc() should throw on invalid data", () => { + expect(() => Buffer.alloc(0x1000, "This is not correctly encoded", "hex")).toThrow(TypeError); + expect(() => Buffer.alloc(0x1000, "c", "hex")).toThrow(TypeError); + expect(() => Buffer.alloc(1, Buffer.alloc(0))).toThrow(TypeError); + expect(() => Buffer.alloc(40, "x", 20)).toThrow(TypeError); }); it("Buffer.toJSON()", () => { @@ -2064,8 +1902,58 @@ it("Buffer.fill (Node.js tests)", () => { const buf1 = Buffer.allocUnsafe(SIZE); const buf2 = Buffer.allocUnsafe(SIZE); - function assertEqual(a, b) { - expect(a).toEqual(b); + function bufReset() { + buf1.fill(0); + buf2.fill(0); + } + + // This is mostly accurate. Except write() won't write partial bytes to the + // string while fill() blindly copies bytes into memory. To account for that an + // error will be thrown if not all the data can be written, and the SIZE has + // been massaged to work with the input characters. + function writeToFill(string, offset, end, encoding) { + if (typeof offset === "string") { + encoding = offset; + offset = 0; + end = buf2.length; + } else if (typeof end === "string") { + encoding = end; + end = buf2.length; + } else if (end === undefined) { + end = buf2.length; + } + + // Should never be reached. + if (offset < 0 || end > buf2.length) throw new ERR_OUT_OF_RANGE(); + + if (end <= offset) return buf2; + + offset >>>= 0; + end >>>= 0; + expect(offset <= buf2.length).toBe(true); + + // Convert "end" to "length" (which write understands). + const length = end - offset < 0 ? 0 : end - offset; + + let wasZero = false; + do { + const written = buf2.write(string, offset, length, encoding); + offset += written; + // Safety check in case write falls into infinite loop. + if (written === 0) { + if (wasZero) throw new Error("Could not write all data to Buffer at " + offset); + else wasZero = true; + } + } while (offset < buf2.length); + + return buf2; + } + + function testBufs(string, offset, length, encoding) { + bufReset(); + buf1.fill.apply(buf1, arguments); + // Swap bytes on BE archs for ucs2 encoding. + expect(buf1.fill.apply(buf1, arguments)).toStrictEqual(writeToFill.apply(null, arguments)); } // Default encoding @@ -2101,7 +1989,7 @@ it("Buffer.fill (Node.js tests)", () => { testBufs("\u0222aa", 8, 1, "utf8"); testBufs("a\u0234b\u0235c\u0236", 4, 1, "utf8"); testBufs("a\u0234b\u0235c\u0236", 12, 1, "utf8"); - assertEqual(Buffer.allocUnsafe(1).fill(0).fill("\u0222")[0], 0xc8); + expect(Buffer.allocUnsafe(1).fill(0).fill("\u0222")[0]).toBe(0xc8); // BINARY testBufs("abc", "binary"); @@ -2152,7 +2040,7 @@ it("Buffer.fill (Node.js tests)", () => { testBufs("\u0222aa", 8, 1, "ucs2"); testBufs("a\u0234b\u0235c\u0236", 4, 1, "ucs2"); testBufs("a\u0234b\u0235c\u0236", 12, 1, "ucs2"); - assertEqual(Buffer.allocUnsafe(1).fill("\u0222", "ucs2")[0], 0x22); + expect(Buffer.allocUnsafe(1).fill("\u0222", "ucs2")[0]).toBe(0x22); // HEX testBufs("616263", "hex"); @@ -2214,199 +2102,136 @@ it("Buffer.fill (Node.js tests)", () => { testBufs("yKJhYQ", 8, 1, "base64url"); testBufs("Yci0Ysi1Y8i2", 4, 1, "base64url"); testBufs("Yci0Ysi1Y8i2", 12, 1, "base64url"); +}); - // Buffer - function deepStrictEqualValues(buf, arr) { - for (const [index, value] of buf.entries()) { - expect(value).toStrictEqual(arr[index]); - } - } - - const buf2Fill = Buffer.allocUnsafe(1).fill(2); - deepStrictEqualValues(genBuffer(4, [buf2Fill]), [2, 2, 2, 2]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1]), [0, 2, 2, 2]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 3]), [0, 2, 2, 0]); - deepStrictEqualValues(genBuffer(4, [buf2Fill, 1, 1]), [0, 0, 0, 0]); - const hexBufFill = Buffer.allocUnsafe(2).fill(0).fill("0102", "hex"); - deepStrictEqualValues(genBuffer(4, [hexBufFill]), [1, 2, 1, 2]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1]), [0, 1, 2, 1]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 3]), [0, 1, 2, 0]); - deepStrictEqualValues(genBuffer(4, [hexBufFill, 1, 1]), [0, 0, 0, 0]); - - // Check exceptions - [ - [0, -1], - [0, 0, buf1.length + 1], - ["", -1], - ["", 0, buf1.length + 1], - ["", 1, -1], - ].forEach(args => { - expect(() => buf1.fill(...args)).toThrow(); - }); - - expect(() => buf1.fill("a", 0, buf1.length, "node rocks!")).toThrow(); - - [ - ["a", 0, 0, NaN], - ["a", 0, 0, false], - ].forEach(args => { - expect(() => buf1.fill(...args)).toThrow(); - }); - - expect(() => buf1.fill("a", 0, 0, "foo")).toThrow(); - +it("fill() repeat pattern", () => { function genBuffer(size, args) { const b = Buffer.allocUnsafe(size); return b.fill(0).fill.apply(b, args); } - function bufReset() { - buf1.fill(0); - buf2.fill(0); - } - - // This is mostly accurate. Except write() won't write partial bytes to the - // string while fill() blindly copies bytes into memory. To account for that an - // error will be thrown if not all the data can be written, and the SIZE has - // been massaged to work with the input characters. - function writeToFill(string, offset, end, encoding) { - if (typeof offset === "string") { - encoding = offset; - offset = 0; - end = buf2.length; - } else if (typeof end === "string") { - encoding = end; - end = buf2.length; - } else if (end === undefined) { - end = buf2.length; - } - - // Should never be reached. - if (offset < 0 || end > buf2.length) throw new ERR_OUT_OF_RANGE(); - - if (end <= offset) return buf2; - - offset >>>= 0; - end >>>= 0; - expect(offset <= buf2.length).toBe(true); - - // Convert "end" to "length" (which write understands). - const length = end - offset < 0 ? 0 : end - offset; - - let wasZero = false; - do { - const written = buf2.write(string, offset, length, encoding); - offset += written; - // Safety check in case write falls into infinite loop. - if (written === 0) { - if (wasZero) throw new Error("Could not write all data to Buffer at " + offset); - else wasZero = true; - } - } while (offset < buf2.length); - - return buf2; - } + const buf2Fill = Buffer.allocUnsafe(1).fill(2); + expect(genBuffer(4, [buf2Fill])).toStrictEqual(Buffer.from([2, 2, 2, 2])); + expect(genBuffer(4, [buf2Fill, 1])).toStrictEqual(Buffer.from([0, 2, 2, 2])); + expect(genBuffer(4, [buf2Fill, 1, 3])).toStrictEqual(Buffer.from([0, 2, 2, 0])); + expect(genBuffer(4, [buf2Fill, 1, 1])).toStrictEqual(Buffer.from([0, 0, 0, 0])); + const hexBufFill = Buffer.allocUnsafe(2).fill(0).fill("0102", "hex"); + expect(genBuffer(4, [hexBufFill])).toStrictEqual(Buffer.from([1, 2, 1, 2])); + expect(genBuffer(4, [hexBufFill, 1])).toStrictEqual(Buffer.from([0, 1, 2, 1])); + expect(genBuffer(4, [hexBufFill, 1, 3])).toStrictEqual(Buffer.from([0, 1, 2, 0])); + expect(genBuffer(4, [hexBufFill, 1, 1])).toStrictEqual(Buffer.from([0, 0, 0, 0])); +}); - function testBufs(string, offset, length, encoding) { - bufReset(); - buf1.fill.apply(buf1, arguments); - // Swap bytes on BE archs for ucs2 encoding. - expect(buf1.fill.apply(buf1, arguments)).toStrictEqual(writeToFill.apply(null, arguments)); - } +it("fill() should throw on invalid arguments", () => { + // Check exceptions + const buf = Buffer.allocUnsafe(16); + expect(() => buf.fill(0, -1)).toThrow(RangeError); + expect(() => buf.fill(0, 0, buf.length + 1)).toThrow(RangeError); + expect(() => buf.fill("", -1)).toThrow(RangeError); + expect(() => buf.fill("", 0, buf.length + 1)).toThrow(RangeError); + expect(() => buf.fill("", 1, -1)).toThrow(RangeError); + expect(() => buf.fill("a", 0, buf.length, "node rocks!")).toThrow(TypeError); + expect(() => buf.fill("a", 0, 0, NaN)).toThrow(TypeError); + expect(() => buf.fill("a", 0, 0, false)).toThrow(TypeError); + expect(() => buf.fill("a", 0, 0, "foo")).toThrow(TypeError); // Make sure these throw. expect(() => Buffer.allocUnsafe(8).fill("a", -1)).toThrow(); expect(() => Buffer.allocUnsafe(8).fill("a", 0, 9)).toThrow(); +}); +it("fill() should not hang indefinitely", () => { // Make sure this doesn't hang indefinitely. Buffer.allocUnsafe(8).fill(""); Buffer.alloc(8, ""); +}); - { - const buf = Buffer.alloc(64, 10); - for (let i = 0; i < buf.length; i++) assertEqual(buf[i], 10); +it("fill() repeat byte", () => { + const buf = Buffer.alloc(64, 10); + for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(10); - buf.fill(11, 0, buf.length >> 1); - for (let i = 0; i < buf.length >> 1; i++) assertEqual(buf[i], 11); - for (let i = (buf.length >> 1) + 1; i < buf.length; i++) assertEqual(buf[i], 10); + buf.fill(11, 0, buf.length >> 1); + for (let i = 0; i < buf.length >> 1; i++) expect(buf[i]).toBe(11); + for (let i = (buf.length >> 1) + 1; i < buf.length; i++) expect(buf[i]).toBe(10); - buf.fill("h"); - for (let i = 0; i < buf.length; i++) assertEqual(buf[i], "h".charCodeAt(0)); + buf.fill("h"); + for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe("h".charCodeAt(0)); - buf.fill(0); - for (let i = 0; i < buf.length; i++) assertEqual(buf[i], 0); + buf.fill(0); + for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(0); - buf.fill(null); - for (let i = 0; i < buf.length; i++) assertEqual(buf[i], 0); + buf.fill(null); + for (let i = 0; i < buf.length; i++) expect(buf[i]).toBe(0); - buf.fill(1, 16, 32); - for (let i = 0; i < 16; i++) assertEqual(buf[i], 0); - for (let i = 16; i < 32; i++) assertEqual(buf[i], 1); - for (let i = 32; i < buf.length; i++) assertEqual(buf[i], 0); - } + buf.fill(1, 16, 32); + for (let i = 0; i < 16; i++) expect(buf[i]).toBe(0); + for (let i = 16; i < 32; i++) expect(buf[i]).toBe(1); + for (let i = 32; i < buf.length; i++) expect(buf[i]).toBe(0); +}); - { - const buf = Buffer.alloc(10, "abc"); - assertEqual(buf.toString(), "abcabcabca"); - buf.fill("է"); - assertEqual(buf.toString(), "էէէէէ"); - } +it("alloc() repeat pattern", () => { + const buf = Buffer.alloc(10, "abc"); + expect(buf.toString()).toBe("abcabcabca"); + buf.fill("է"); + expect(buf.toString()).toBe("էէէէէ"); +}); +it("fill() should properly check `start` & `end`", () => { // // Testing process.binding. Make sure "start" is properly checked for range // // errors. - // assert.throws( - // () => { - // internalBinding("buffer").fill(Buffer.alloc(1), 1, -1, 0, 1); - // }, - // { code: "ERR_OUT_OF_RANGE" }, - // ); + // expect(() => internalBinding("buffer").fill(Buffer.alloc(1), 1, -1, 0, 1)).toThrow(RangeError); // Make sure "end" is properly checked, even if it's magically mangled using // Symbol.toPrimitive. - { - expect(() => { - const end = { - [Symbol.toPrimitive]() { - return 1; - }, - }; - Buffer.alloc(1).fill(Buffer.alloc(1), 0, end); - }).toThrow(); - } + expect(() => { + const end = { + [Symbol.toPrimitive]() { + return 1; + }, + }; + Buffer.alloc(1).fill(Buffer.alloc(1), 0, end); + }).toThrow(TypeError); // Testing process.binding. Make sure "end" is properly checked for range // errors. - // assert.throws( - // () => { - // internalBinding("buffer").fill(Buffer.alloc(1), 1, 1, -2, 1); - // }, - // { code: "ERR_OUT_OF_RANGE" }, - // ); - - // Test that bypassing 'length' won't cause an abort. - expect(() => { - const buf = Buffer.from("w00t"); - Object.defineProperty(buf, "length", { - value: 1337, - enumerable: true, - }); - buf.fill(""); - }).toThrow(); + // expect(() => internalBinding("buffer").fill(Buffer.alloc(1), 1, 1, -2, 1)).toThrow(RangeError); +}); + +it("bypassing `length` should not cause an abort", () => { + const buf = Buffer.from("w00t"); + expect(buf).toStrictEqual(Buffer.from([119, 48, 48, 116])); + Object.defineProperty(buf, "length", { + value: 1337, + enumerable: true, + }); + // Node.js throws here, but we can handle it just fine + buf.fill(""); + expect(buf).toStrictEqual(Buffer.from([0, 0, 0, 0])); +}); - assertEqual(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le"), Buffer.from("61006200610062006100620061006200", "hex")); +it("allocUnsafeSlow().fill()", () => { + expect(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le")).toStrictEqual( + Buffer.from("61006200610062006100620061006200", "hex"), + ); - assertEqual(Buffer.allocUnsafeSlow(15).fill("ab", "utf16le"), Buffer.from("610062006100620061006200610062", "hex")); + expect(Buffer.allocUnsafeSlow(15).fill("ab", "utf16le")).toStrictEqual( + Buffer.from("610062006100620061006200610062", "hex"), + ); - assertEqual(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le"), Buffer.from("61006200610062006100620061006200", "hex")); - assertEqual(Buffer.allocUnsafeSlow(16).fill("a", "utf16le"), Buffer.from("61006100610061006100610061006100", "hex")); + expect(Buffer.allocUnsafeSlow(16).fill("ab", "utf16le")).toStrictEqual( + Buffer.from("61006200610062006100620061006200", "hex"), + ); + expect(Buffer.allocUnsafeSlow(16).fill("a", "utf16le")).toStrictEqual( + Buffer.from("61006100610061006100610061006100", "hex"), + ); - assertEqual(Buffer.allocUnsafeSlow(16).fill("a", "utf16le").toString("utf16le"), "a".repeat(8)); - assertEqual(Buffer.allocUnsafeSlow(16).fill("a", "latin1").toString("latin1"), "a".repeat(16)); - assertEqual(Buffer.allocUnsafeSlow(16).fill("a", "utf8").toString("utf8"), "a".repeat(16)); + expect(Buffer.allocUnsafeSlow(16).fill("a", "utf16le").toString("utf16le")).toBe("a".repeat(8)); + expect(Buffer.allocUnsafeSlow(16).fill("a", "latin1").toString("latin1")).toBe("a".repeat(16)); + expect(Buffer.allocUnsafeSlow(16).fill("a", "utf8").toString("utf8")).toBe("a".repeat(16)); - assertEqual(Buffer.allocUnsafeSlow(16).fill("Љ", "utf16le").toString("utf16le"), "Љ".repeat(8)); - assertEqual(Buffer.allocUnsafeSlow(16).fill("Љ", "latin1").toString("latin1"), "\t".repeat(16)); - assertEqual(Buffer.allocUnsafeSlow(16).fill("Љ", "utf8").toString("utf8"), "Љ".repeat(8)); + expect(Buffer.allocUnsafeSlow(16).fill("Љ", "utf16le").toString("utf16le")).toBe("Љ".repeat(8)); + expect(Buffer.allocUnsafeSlow(16).fill("Љ", "latin1").toString("latin1")).toBe("\t".repeat(16)); + expect(Buffer.allocUnsafeSlow(16).fill("Љ", "utf8").toString("utf8")).toBe("Љ".repeat(8)); expect(() => { const buf = Buffer.from("a".repeat(1000)); @@ -2415,115 +2240,116 @@ it("Buffer.fill (Node.js tests)", () => { }).toThrow(); }); -test("Buffer.byteLength", () => { - const SlowBuffer = require("buffer").SlowBuffer; - - [[32, "latin1"], [NaN, "utf8"], [{}, "latin1"], []].forEach(args => { - assert.throws(() => Buffer.byteLength(...args)); - }); +it("ArrayBuffer.isView()", () => { + expect(ArrayBuffer.isView(new Buffer(10))).toBe(true); + expect(ArrayBuffer.isView(new SlowBuffer(10))).toBe(true); + expect(ArrayBuffer.isView(Buffer.alloc(10))).toBe(true); + expect(ArrayBuffer.isView(Buffer.allocUnsafe(10))).toBe(true); + expect(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10))).toBe(true); + expect(ArrayBuffer.isView(Buffer.from(""))).toBe(true); +}); - assert.strictEqual(Buffer.byteLength("", undefined, true), 0); +it("Buffer.byteLength()", () => { + expect(() => Buffer.byteLength(32, "latin1")).toThrow(TypeError); + expect(() => Buffer.byteLength(NaN, "utf8")).toThrow(TypeError); + expect(() => Buffer.byteLength({}, "latin1")).toThrow(TypeError); + expect(() => Buffer.byteLength()).toThrow(TypeError); - assert(ArrayBuffer.isView(new Buffer(10))); - assert(ArrayBuffer.isView(new SlowBuffer(10))); - assert(ArrayBuffer.isView(Buffer.alloc(10))); - assert(ArrayBuffer.isView(Buffer.allocUnsafe(10))); - assert(ArrayBuffer.isView(Buffer.allocUnsafeSlow(10))); - assert(ArrayBuffer.isView(Buffer.from(""))); + expect(Buffer.byteLength("", undefined, true)).toBe(0); // buffer const incomplete = Buffer.from([0xe4, 0xb8, 0xad, 0xe6, 0x96]); - assert.strictEqual(Buffer.byteLength(incomplete), 5); + expect(Buffer.byteLength(incomplete)).toBe(5); const ascii = Buffer.from("abc"); - assert.strictEqual(Buffer.byteLength(ascii), 3); + expect(Buffer.byteLength(ascii)).toBe(3); // ArrayBuffer const buffer = new ArrayBuffer(8); - assert.strictEqual(Buffer.byteLength(buffer), 8); + expect(Buffer.byteLength(buffer)).toBe(8); // TypedArray const int8 = new Int8Array(8); - assert.strictEqual(Buffer.byteLength(int8), 8); + expect(Buffer.byteLength(int8)).toBe(8); const uint8 = new Uint8Array(8); - assert.strictEqual(Buffer.byteLength(uint8), 8); + expect(Buffer.byteLength(uint8)).toBe(8); const uintc8 = new Uint8ClampedArray(2); - assert.strictEqual(Buffer.byteLength(uintc8), 2); + expect(Buffer.byteLength(uintc8)).toBe(2); const int16 = new Int16Array(8); - assert.strictEqual(Buffer.byteLength(int16), 16); + expect(Buffer.byteLength(int16)).toBe(16); const uint16 = new Uint16Array(8); - assert.strictEqual(Buffer.byteLength(uint16), 16); + expect(Buffer.byteLength(uint16)).toBe(16); const int32 = new Int32Array(8); - assert.strictEqual(Buffer.byteLength(int32), 32); + expect(Buffer.byteLength(int32)).toBe(32); const uint32 = new Uint32Array(8); - assert.strictEqual(Buffer.byteLength(uint32), 32); + expect(Buffer.byteLength(uint32)).toBe(32); const float32 = new Float32Array(8); - assert.strictEqual(Buffer.byteLength(float32), 32); + expect(Buffer.byteLength(float32)).toBe(32); const float64 = new Float64Array(8); - assert.strictEqual(Buffer.byteLength(float64), 64); + expect(Buffer.byteLength(float64)).toBe(64); // DataView const dv = new DataView(new ArrayBuffer(2)); - assert.strictEqual(Buffer.byteLength(dv), 2); + expect(Buffer.byteLength(dv)).toBe(2); // Special case: zero length string - assert.strictEqual(Buffer.byteLength("", "ascii"), 0); - assert.strictEqual(Buffer.byteLength("", "HeX"), 0); + expect(Buffer.byteLength("", "ascii")).toBe(0); + expect(Buffer.byteLength("", "HeX")).toBe(0); // utf8 - assert.strictEqual(Buffer.byteLength("∑éllö wørl∂!", "utf-8"), 19); - assert.strictEqual(Buffer.byteLength("κλμνξο", "utf8"), 12); - assert.strictEqual(Buffer.byteLength("挵挶挷挸挹", "utf-8"), 15); - assert.strictEqual(Buffer.byteLength("𠝹𠱓𠱸", "UTF8"), 12); + expect(Buffer.byteLength("∑éllö wørl∂!", "utf-8")).toBe(19); + expect(Buffer.byteLength("κλμνξο", "utf8")).toBe(12); + expect(Buffer.byteLength("挵挶挷挸挹", "utf-8")).toBe(15); + expect(Buffer.byteLength("𠝹𠱓𠱸", "UTF8")).toBe(12); // Without an encoding, utf8 should be assumed - assert.strictEqual(Buffer.byteLength("hey there"), 9); - assert.strictEqual(Buffer.byteLength("𠱸挶νξ#xx :)"), 17); - assert.strictEqual(Buffer.byteLength("hello world", ""), 11); + expect(Buffer.byteLength("hey there")).toBe(9); + expect(Buffer.byteLength("𠱸挶νξ#xx :)")).toBe(17); + expect(Buffer.byteLength("hello world", "")).toBe(11); // It should also be assumed with unrecognized encoding - assert.strictEqual(Buffer.byteLength("hello world", "abc"), 11); - assert.strictEqual(Buffer.byteLength("ßœ∑≈", "unkn0wn enc0ding"), 10); + expect(Buffer.byteLength("hello world", "abc")).toBe(11); + expect(Buffer.byteLength("ßœ∑≈", "unkn0wn enc0ding")).toBe(10); // base64 - assert.strictEqual(Buffer.byteLength("aGVsbG8gd29ybGQ=", "base64"), 11); - assert.strictEqual(Buffer.byteLength("aGVsbG8gd29ybGQ=", "BASE64"), 11); - assert.strictEqual(Buffer.byteLength("bm9kZS5qcyByb2NrcyE=", "base64"), 14); - assert.strictEqual(Buffer.byteLength("aGkk", "base64"), 3); - assert.strictEqual(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==", "base64"), 25); + expect(Buffer.byteLength("aGVsbG8gd29ybGQ=", "base64")).toBe(11); + expect(Buffer.byteLength("aGVsbG8gd29ybGQ=", "BASE64")).toBe(11); + expect(Buffer.byteLength("bm9kZS5qcyByb2NrcyE=", "base64")).toBe(14); + expect(Buffer.byteLength("aGkk", "base64")).toBe(3); + expect(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw==", "base64")).toBe(25); // base64url - assert.strictEqual(Buffer.byteLength("aGVsbG8gd29ybGQ", "base64url"), 11); - assert.strictEqual(Buffer.byteLength("aGVsbG8gd29ybGQ", "BASE64URL"), 11); - assert.strictEqual(Buffer.byteLength("bm9kZS5qcyByb2NrcyE", "base64url"), 14); - assert.strictEqual(Buffer.byteLength("aGkk", "base64url"), 3); - assert.strictEqual(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw", "base64url"), 25); + expect(Buffer.byteLength("aGVsbG8gd29ybGQ", "base64url")).toBe(11); + expect(Buffer.byteLength("aGVsbG8gd29ybGQ", "BASE64URL")).toBe(11); + expect(Buffer.byteLength("bm9kZS5qcyByb2NrcyE", "base64url")).toBe(14); + expect(Buffer.byteLength("aGkk", "base64url")).toBe(3); + expect(Buffer.byteLength("bHNrZGZsa3NqZmtsc2xrZmFqc2RsZmtqcw", "base64url")).toBe(25); // special padding - assert.strictEqual(Buffer.byteLength("aaa=", "base64"), 2); - assert.strictEqual(Buffer.byteLength("aaaa==", "base64"), 3); - assert.strictEqual(Buffer.byteLength("aaa=", "base64url"), 2); - assert.strictEqual(Buffer.byteLength("aaaa==", "base64url"), 3); - assert.strictEqual(Buffer.byteLength("Il était tué", "utf8"), 14); - assert.strictEqual(Buffer.byteLength("Il était tué"), 14); + expect(Buffer.byteLength("aaa=", "base64")).toBe(2); + expect(Buffer.byteLength("aaaa==", "base64")).toBe(3); + expect(Buffer.byteLength("aaa=", "base64url")).toBe(2); + expect(Buffer.byteLength("aaaa==", "base64url")).toBe(3); + expect(Buffer.byteLength("Il était tué", "utf8")).toBe(14); + expect(Buffer.byteLength("Il était tué")).toBe(14); ["ascii", "latin1", "binary"] .reduce((es, e) => es.concat(e, e.toUpperCase()), []) .forEach(encoding => { - assert.strictEqual(Buffer.byteLength("Il était tué", encoding), 12); + expect(Buffer.byteLength("Il était tué", encoding)).toBe(12); }); ["ucs2", "ucs-2", "utf16le", "utf-16le"] .reduce((es, e) => es.concat(e, e.toUpperCase()), []) .forEach(encoding => { - assert.strictEqual(Buffer.byteLength("Il était tué", encoding), 24); + expect(Buffer.byteLength("Il était tué", encoding)).toBe(24); }); // Test that ArrayBuffer from a different context is detected correctly // const arrayBuf = vm.runInNewContext("new ArrayBuffer()"); - // assert.strictEqual(Buffer.byteLength(arrayBuf), 0); + // expect(Buffer.byteLength(arrayBuf)).toBe(0); // Verify that invalid encodings are treated as utf8 for (let i = 1; i < 10; i++) { const encoding = String(i).repeat(i); - assert.ok(!Buffer.isEncoding(encoding)); - assert.strictEqual(Buffer.byteLength("foo", encoding), Buffer.byteLength("foo", "utf8")); + expect(Buffer.isEncoding(encoding)).toBe(false); + expect(Buffer.byteLength("foo", encoding)).toBe(Buffer.byteLength("foo", "utf8")); } });