diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/baby_list.zig | 2 | ||||
-rw-r--r-- | src/base64/base64.zig | 92 | ||||
-rw-r--r-- | src/bun.js/bindings/Buffer.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBuffer.cpp | 574 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBufferEncodingType.cpp | 5 | ||||
-rw-r--r-- | src/bun.js/bindings/headers-handwritten.h | 4 | ||||
-rw-r--r-- | src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.cpp | 345 | ||||
-rw-r--r-- | src/bun.js/builtins/cpp/JSBufferPrototypeBuiltins.h | 81 | ||||
-rw-r--r-- | src/bun.js/builtins/js/JSBufferPrototype.js | 272 | ||||
-rw-r--r-- | src/bun.js/node/buffer.zig | 114 | ||||
-rw-r--r-- | src/bun.js/webcore/base64.zig | 445 | ||||
-rw-r--r-- | src/bun.js/webcore/body.zig | 1 | ||||
-rw-r--r-- | src/bun.js/webcore/encoding.zig | 154 | ||||
-rw-r--r-- | src/bun.js/webcore/streams.zig | 2 | ||||
-rw-r--r-- | src/http/websocket_http_client.zig | 2 | ||||
-rw-r--r-- | src/napi/napi.zig | 14 | ||||
-rw-r--r-- | src/string_immutable.zig | 44 | ||||
-rw-r--r-- | src/string_mutable.zig | 2 |
18 files changed, 1136 insertions, 1019 deletions
diff --git a/src/baby_list.zig b/src/baby_list.zig index 0df33b2dc..2ebb383e6 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 daf5b228e..0cd16fb8c 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 fecc627a1..4f1583513 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 2ce07617a..9b3854b95 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<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*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<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*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<JSC::JSArrayBufferView*>(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<uint8_t>(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<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*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<JSC::JSArrayBufferView*>(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<uint8_t>(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<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*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<JSC::JSArrayBufferView*>(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<size_t>(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<uint8_t>(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<int64_t>(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<int64_t>(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<int64_t>(byteOffset_); + } else { + byteOffset = static_cast<int64_t>(byteOffset_); } - } - if (callFrame->argumentCount() > 2) { - EnsureStillAliveScope encodingValue = callFrame->uncheckedArgument(2); - - if (encodingValue.value().isString()) { - std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*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<JSArrayBufferView>::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<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*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<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*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<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadInt32BECodeGenerator, 1 } }, { "readInt32LE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadInt32LECodeGenerator, 1 } }, { "readInt8"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadInt8CodeGenerator, 2 } }, + { "readIntBE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadIntBECodeGenerator, 1 } }, + { "readIntLE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadIntLECodeGenerator, 1 } }, { "readUInt16BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16BECodeGenerator, 1 } }, { "readUInt16LE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16LECodeGenerator, 1 } }, { "readUInt32BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt32BECodeGenerator, 1 } }, { "readUInt32LE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt32LECodeGenerator, 1 } }, { "readUInt8"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt8CodeGenerator, 1 } }, + { "readUIntBE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUIntBECodeGenerator, 1 } }, + { "readUIntLE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUIntLECodeGenerator, 1 } }, { "readUint16BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16BECodeGenerator, 1 } }, { "readUint16LE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt16LECodeGenerator, 1 } }, { "readUint32BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeReadUInt32BECodeGenerator, 1 } }, @@ -1861,6 +1858,7 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "swap32"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap32, 0 } }, { "swap64"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_swap64, 0 } }, { "toJSON"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeToJSONCodeGenerator, 1 } }, + { "toLocaleString"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } }, { "toString"_s, static_cast<unsigned>(JSC::PropertyAttribute::Function), NoIntrinsic, { HashTableValue::NativeFunctionType, jsBufferPrototypeFunction_toString, 4 } }, { "ucs2Slice"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUcs2SliceCodeGenerator, 2 } }, { "ucs2Write"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeUcs2WriteCodeGenerator, 1 } }, @@ -1886,6 +1884,8 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "writeInt32BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteInt32BECodeGenerator, 1 } }, { "writeInt32LE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteInt32LECodeGenerator, 1 } }, { "writeInt8"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteInt8CodeGenerator, 1 } }, + { "writeIntBE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteIntBECodeGenerator, 1 } }, + { "writeIntLE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteIntLECodeGenerator, 1 } }, { "writeUInt16"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16LECodeGenerator, 1 } }, { "writeUInt16BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16BECodeGenerator, 1 } }, { "writeUInt16LE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16LECodeGenerator, 1 } }, @@ -1893,6 +1893,8 @@ static const HashTableValue JSBufferPrototypeTableValues[] { "writeUInt32BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt32BECodeGenerator, 1 } }, { "writeUInt32LE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt32LECodeGenerator, 1 } }, { "writeUInt8"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt8CodeGenerator, 1 } }, + { "writeUIntBE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUIntBECodeGenerator, 1 } }, + { "writeUIntLE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUIntLECodeGenerator, 1 } }, { "writeUint16"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16LECodeGenerator, 1 } }, { "writeUint16BE"_s, static_cast<unsigned>(JSC::PropertyAttribute::Builtin), NoIntrinsic, { HashTableValue::BuiltinGeneratorType, jsBufferPrototypeWriteUInt16BECodeGenerator, 1 } }, { "writeUint16LE"_s, static_cast<unsigned>(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 8d99dd4db..dde9e1be2 100644 --- a/src/bun.js/bindings/JSBufferEncodingType.cpp +++ b/src/bun.js/bindings/JSBufferEncodingType.cpp @@ -39,7 +39,6 @@ static const NeverDestroyed<String> 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<BufferEncodingType> parseEnumeration<BufferEncodingType case 'B': { if (WTF::equalIgnoringASCIICase(encoding, "binary"_s)) return BufferEncodingType::latin1; // BINARY is a deprecated alias of LATIN1. - if (WTF::equalIgnoringASCIICase(encoding, "buffer"_s)) - return BufferEncodingType::buffer; if (WTF::equalIgnoringASCIICase(encoding, "base64"_s)) return BufferEncodingType::base64; if (WTF::equalIgnoringASCIICase(encoding, "base64url"_s)) @@ -135,7 +132,7 @@ template<> std::optional<BufferEncodingType> parseEnumeration<BufferEncodingType } template<> const char* expectedEnumerationValues<BufferEncodingType>() { - 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 8f925e84f..d4e1e72af 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 8aff61064..1ce441bb6 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 f7a3364de..60c00bedf 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 827e613d8..32aebb3f2 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 5a7d64955..f3cc3c2c2 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 aad1c471b..000000000 --- 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 97c9f7874..d4bd490c1 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 deab624d0..d58513c1e 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 1de209b69..748b4583c 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 a4f9284d3..a5be7faab 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 b06e25d7e..99d7c82fb 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 e85fc4ef8..7bdd7cfe5 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 d7b0cf930..129ea1481 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); } |