aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-01-29 21:27:42 -0800
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2023-01-29 21:27:42 -0800
commit552a1940eb6a4c0ee5e9acba80cbd210d499ef3c (patch)
tree9a06f27b4c7392e2472d7b5b2831a97352d04255
parent98becc3538a4f08b7abbe797e2dc14be9290705e (diff)
downloadbun-552a1940eb6a4c0ee5e9acba80cbd210d499ef3c.tar.gz
bun-552a1940eb6a4c0ee5e9acba80cbd210d499ef3c.tar.zst
bun-552a1940eb6a4c0ee5e9acba80cbd210d499ef3c.zip
[buffer] More passing Node.js tests
-rw-r--r--src/bun.js/bindings/JSBuffer.cpp248
-rw-r--r--src/bun.js/webcore/encoding.zig9
2 files changed, 150 insertions, 107 deletions
diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp
index df7433657..8436e24e7 100644
--- a/src/bun.js/bindings/JSBuffer.cpp
+++ b/src/bun.js/bindings/JSBuffer.cpp
@@ -188,6 +188,33 @@ JSC::EncodedJSValue JSBuffer__bufferFromPointerAndLengthAndDeinit(JSC::JSGlobalO
namespace WebCore {
using namespace JSC;
+static inline EncodedJSValue writeToBuffer(JSC::JSGlobalObject* lexicalGlobalObject, JSArrayBufferView* castedThis, JSString* str, uint32_t offset, uint32_t length, BufferEncodingType encoding)
+{
+ auto view = str->tryGetValue(lexicalGlobalObject);
+ int64_t written = 0;
+
+ switch (encoding) {
+ case WebCore::BufferEncodingType::utf8:
+ case WebCore::BufferEncodingType::latin1:
+ case WebCore::BufferEncodingType::ascii:
+ case WebCore::BufferEncodingType::ucs2:
+ case WebCore::BufferEncodingType::utf16le:
+ case WebCore::BufferEncodingType::base64:
+ case WebCore::BufferEncodingType::base64url:
+ case WebCore::BufferEncodingType::hex: {
+
+ if (view.is8Bit()) {
+ written = Bun__encoding__writeLatin1(view.characters8(), view.length(), reinterpret_cast<unsigned char*>(castedThis->vector()) + offset, length, static_cast<uint8_t>(encoding));
+ } else {
+ written = Bun__encoding__writeUTF16(view.characters16(), view.length(), reinterpret_cast<unsigned char*>(castedThis->vector()) + offset, length, static_cast<uint8_t>(encoding));
+ }
+ break;
+ }
+ }
+
+ return JSC::JSValue::encode(JSC::jsNumber(written));
+}
+
static inline JSC::JSUint8Array* JSBuffer__bufferFromLengthAsArray(JSC::JSGlobalObject* lexicalGlobalObject, int64_t length)
{
auto throwScope = DECLARE_THROW_SCOPE(lexicalGlobalObject->vm());
@@ -439,7 +466,7 @@ static inline JSC::EncodedJSValue jsBufferByteLengthFromStringAndEncoding(JSC::J
}
if (str->length() == 0)
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(0)));
+ RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(-1)));
int64_t written = 0;
@@ -514,7 +541,6 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC
}
EnsureStillAliveScope arg0 = callFrame->argument(0);
- auto input = arg0.value();
EnsureStillAliveScope arg1 = callFrame->argument(1);
if (callFrame->argumentCount() > 1) {
@@ -529,14 +555,14 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_byteLengthBody(JSC
}
}
- if (LIKELY(input.isString()))
- return jsBufferFromStringAndEncoding(lexicalGlobalObject, asString(input), encoding);
+ if (LIKELY(arg0.value().isString()))
+ return jsBufferByteLengthFromStringAndEncoding(lexicalGlobalObject, asString(arg0.value()), encoding);
- if (auto* arrayBufferView = jsDynamicCast<JSC::JSArrayBufferView*>(input)) {
+ if (auto* arrayBufferView = jsDynamicCast<JSC::JSArrayBufferView*>(arg0.value())) {
return JSValue::encode(jsNumber(arrayBufferView->byteLength()));
}
- if (auto* arrayBuffer = jsDynamicCast<JSC::JSArrayBuffer*>(input)) {
+ if (auto* arrayBuffer = jsDynamicCast<JSC::JSArrayBuffer*>(arg0.value())) {
return JSValue::encode(jsNumber(arrayBuffer->impl()->byteLength()));
}
@@ -863,7 +889,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_copyBody(JSC::JSGlob
return JSValue::encode(jsUndefined());
}
- JSC::JSUint8Array* view = JSC::jsDynamicCast<JSC::JSUint8Array*>(buffer);
+ JSC::JSArrayBufferView* view = JSC::jsDynamicCast<JSC::JSArrayBufferView*>(buffer);
if (UNLIKELY(!view || view->isDetached())) {
throwVMTypeError(lexicalGlobalObject, throwScope, "Uint8Array is detached"_s);
return JSValue::encode(jsUndefined());
@@ -911,7 +937,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_copyBody(JSC::JSGlob
auto actualLength = std::min(sourceLength, targetLength);
auto sourceStartPtr = castedThis->typedVector() + sourceStart;
- auto targetStartPtr = view->typedVector() + targetStart;
+ auto targetStartPtr = reinterpret_cast<unsigned char*>(view->vector()) + targetStart;
if (actualLength > 0)
memmove(targetStartPtr, sourceStartPtr, actualLength);
@@ -1008,49 +1034,73 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob
size_t end = length;
WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8;
- if (callFrame->argumentCount() > 1) {
- if (auto start_ = callFrame->uncheckedArgument(1).tryGetAsUint32Index()) {
- start = start_.value();
- } else {
- throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "start out of range"_s));
+ 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 (offsetValue.isUndefined() || offsetValue.isString()) {
+ encodingValue = offsetValue;
+ offsetValue = jsUndefined();
+ } else if (lengthValue.isString()) {
+ encodingValue = lengthValue;
+ lengthValue = jsUndefined();
+ }
+
+ 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 (callFrame->argumentCount() > 2) {
- if (auto end_ = callFrame->uncheckedArgument(2).tryGetAsUint32Index()) {
- end = end_.value();
- } else {
- throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "end out of range"_s));
- return JSC::JSValue::encode(jsUndefined());
- }
- }
- if (callFrame->argumentCount() > 3) {
- EnsureStillAliveScope encodingValue = callFrame->uncheckedArgument(3);
- 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());
- }
+ encoding = encoded.value();
+ }
- encoding = encoded.value();
- }
+ 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 (start > end) {
- throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "start out of range"_s));
- return JSC::JSValue::encode(jsUndefined());
+
+ 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 (end > length) {
- throwVMError(lexicalGlobalObject, scope, createRangeError(lexicalGlobalObject, "end out of range"_s));
- return JSC::JSValue::encode(jsUndefined());
+
+ if (start >= end) {
+ return JSValue::encode(castedThis);
}
auto startPtr = castedThis->typedVector() + start;
auto str_ = value.toWTFString(lexicalGlobalObject);
ZigString str = Zig::toZigString(str_);
- Bun__Buffer_fill(&str, startPtr, end - start, encoding);
+ if (str.len > 0) {
+ Bun__Buffer_fill(&str, startPtr, end - start, encoding);
+ } else {
+ memset(startPtr, 0, end - start);
+ }
RELEASE_AND_RETURN(scope, JSValue::encode(castedThis));
}
@@ -1302,6 +1352,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
auto& vm = JSC::getVM(lexicalGlobalObject);
uint32_t offset = 0;
uint32_t length = castedThis->length();
+ uint32_t byteLength = length;
WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8;
if (length == 0)
@@ -1345,7 +1396,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JS
}
default: {
- length = static_cast<uint32_t>(callFrame->argument(2).toInt32(lexicalGlobalObject));
+ length = std::min(byteLength, static_cast<uint32_t>(callFrame->argument(2).toInt32(lexicalGlobalObject)));
break;
}
}
@@ -1411,7 +1462,8 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlo
{
auto& vm = JSC::getVM(lexicalGlobalObject);
uint32_t offset = 0;
- uint32_t length = castedThis->length();
+ uint32_t length = castedThis->byteLength();
+ uint32_t max = length;
WebCore::BufferEncodingType encoding = WebCore::BufferEncodingType::utf8;
auto scope = DECLARE_THROW_SCOPE(vm);
@@ -1428,88 +1480,78 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_writeBody(JSC::JSGlo
return JSC::JSValue::encode(jsUndefined());
}
- EnsureStillAliveScope arg1 = callFrame->argument(1);
-
if (str->length() == 0)
return JSC::JSValue::encode(JSC::jsNumber(0));
- if (callFrame->argumentCount() > 1) {
- if (arg1.value().isAnyInt()) {
- int32_t ioffset = arg1.value().toUInt32(lexicalGlobalObject);
- if (ioffset < 0) {
- throwTypeError(lexicalGlobalObject, scope, "Offset must be a positive integer"_s);
- return JSC::JSValue::encode(jsUndefined());
- }
- offset = ioffset;
- } else if (arg1.value().isString()) {
- std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, arg1.value());
- if (!encoded) {
- throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s);
- return JSC::JSValue::encode(jsUndefined());
- }
- encoding = encoded.value();
- }
- }
+ JSValue offsetValue = jsUndefined();
+ JSValue lengthValue = jsUndefined();
+ JSValue encodingValue = jsUndefined();
- if (UNLIKELY(length < offset)) {
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(0)));
+ switch (callFrame->argumentCount()) {
+ case 4:
+ encodingValue = callFrame->uncheckedArgument(3);
+ FALLTHROUGH;
+ case 3:
+ lengthValue = callFrame->uncheckedArgument(2);
+ FALLTHROUGH;
+ case 2:
+ offsetValue = callFrame->uncheckedArgument(1);
+ break;
+ default:
+ break;
}
- if (callFrame->argumentCount() > 2) {
- uint32_t arg_len = 0;
- EnsureStillAliveScope arg2 = callFrame->argument(2);
- if (arg2.value().isAnyInt()) {
- arg_len = arg2.value().toUInt32(lexicalGlobalObject);
- length = std::min(arg_len, length);
-
- if (callFrame->argumentCount() > 3) {
- EnsureStillAliveScope arg3 = callFrame->argument(3);
- if (arg3.value().isString()) {
- std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, arg3.value());
- if (!encoded) {
- throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s);
- return JSC::JSValue::encode(jsUndefined());
- }
-
- encoding = encoded.value();
- }
- }
- } else if (arg2.value().isString()) {
- std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, arg2.value());
+ auto setEncoding = [&]() {
+ if (encodingValue.isString()) {
+ std::optional<BufferEncodingType> encoded = parseEnumeration<BufferEncodingType>(*lexicalGlobalObject, encodingValue);
if (!encoded) {
throwTypeError(lexicalGlobalObject, scope, "Invalid encoding"_s);
- return JSC::JSValue::encode(jsUndefined());
+ return;
}
encoding = encoded.value();
}
+ };
+
+ if (offsetValue.isUndefined()) {
+ // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L1053
+ RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding));
}
- length = length - offset;
+ if (lengthValue.isUndefined() && offsetValue.isString()) {
+ // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L1056
+ encodingValue = offsetValue;
+ setEncoding();
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined()));
+ RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding));
+ }
- auto view = str->tryGetValue(lexicalGlobalObject);
- int64_t written = 0;
+ int32_t userOffset = offsetValue.toInt32(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined()));
+ if (userOffset < 0 || userOffset > max) {
+ throwRangeError(lexicalGlobalObject, scope, "Offset is out of bounds"_s);
+ return JSC::JSValue::encode(jsUndefined());
+ }
+ offset = static_cast<uint32_t>(userOffset);
+ uint32_t remaining = max - static_cast<uint32_t>(userOffset);
- switch (encoding) {
- case WebCore::BufferEncodingType::utf8:
- case WebCore::BufferEncodingType::latin1:
- case WebCore::BufferEncodingType::ascii:
- case WebCore::BufferEncodingType::ucs2:
- case WebCore::BufferEncodingType::utf16le:
- case WebCore::BufferEncodingType::base64:
- case WebCore::BufferEncodingType::base64url:
- case WebCore::BufferEncodingType::hex: {
+ // https://github.com/nodejs/node/blob/e676942f814915b2d24fc899bb42dc71ae6c8226/lib/buffer.js#L1062-L1077
+ if (lengthValue.isUndefined()) {
+ length = remaining;
+ } else if (lengthValue.isString()) {
+ encodingValue = lengthValue;
+ setEncoding();
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined()));
+ length = remaining;
+ } else {
+ setEncoding();
- if (view.is8Bit()) {
- written = Bun__encoding__writeLatin1(view.characters8(), view.length(), castedThis->typedVector() + offset, length, static_cast<uint8_t>(encoding));
- } else {
- written = Bun__encoding__writeUTF16(view.characters16(), view.length(), castedThis->typedVector() + offset, length, static_cast<uint8_t>(encoding));
- }
- break;
- }
+ int32_t userLength = lengthValue.toInt32(lexicalGlobalObject);
+ RETURN_IF_EXCEPTION(scope, JSC::JSValue::encode(jsUndefined()));
+ length = std::min(static_cast<uint32_t>(userLength), remaining);
}
- RELEASE_AND_RETURN(scope, JSC::JSValue::encode(JSC::jsNumber(written)));
+ RELEASE_AND_RETURN(scope, writeToBuffer(lexicalGlobalObject, castedThis, str, offset, length, encoding));
}
JSC_DEFINE_HOST_FUNCTION(jsBufferConstructorFunction_alloc, (JSGlobalObject * lexicalGlobalObject, CallFrame* callFrame))
diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig
index 4dc5d0bd0..59c3f3866 100644
--- a/src/bun.js/webcore/encoding.zig
+++ b/src/bun.js/webcore/encoding.zig
@@ -963,8 +963,9 @@ pub const Encoder = struct {
return @intCast(i32, strings.copyUTF16IntoUTF8(to[0..to_len], []const u16, input[0..len]).written);
},
.latin1, JSC.Node.Encoding.ascii, JSC.Node.Encoding.buffer => {
- strings.copyU16IntoU8(to[0..to_len], []const u16, input[0..len]);
- return @intCast(i64, @min(len, to_len));
+ const out = @min(len, to_len);
+ strings.copyU16IntoU8(to[0..to_len], []const u16, input[0..out]);
+ return @intCast(i64, out);
},
// string is already encoded, just need to copy the data
JSC.Node.Encoding.ucs2, JSC.Node.Encoding.utf16le => {
@@ -975,7 +976,7 @@ 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, written);
+ return @intCast(i64, fixed_len);
},
JSC.Node.Encoding.hex => {
@@ -1093,7 +1094,7 @@ pub const Encoder = struct {
var to = allocator.alloc(u8, outlen) catch return &[_]u8{};
const written = bun.base64.decode(to[0..outlen], slice).written;
- return to[0..written];
+ return to[0..@min(written, outlen)];
},
// else => return 0,
}