diff options
author | 2022-05-01 02:22:13 -0700 | |
---|---|---|
committer | 2022-05-01 02:22:13 -0700 | |
commit | a3b48b3229b4dcdaa7c51762ceb2641d219e9a1c (patch) | |
tree | edbc95b0ef0d51b27ab5f4940a08b3dfea7c0d50 | |
parent | d7ef268e183d2592fee6a3f2a2429ec3ad24e0fe (diff) | |
download | bun-a3b48b3229b4dcdaa7c51762ceb2641d219e9a1c.tar.gz bun-a3b48b3229b4dcdaa7c51762ceb2641d219e9a1c.tar.zst bun-a3b48b3229b4dcdaa7c51762ceb2641d219e9a1c.zip |
[bun.js] Implement `Buffer.concat`
-rw-r--r-- | integration/bunjs-only-snippets/buffer.test.js | 19 | ||||
-rw-r--r-- | src/javascript/jsc/bindings/JSBuffer.cpp | 60 |
2 files changed, 78 insertions, 1 deletions
diff --git a/integration/bunjs-only-snippets/buffer.test.js b/integration/bunjs-only-snippets/buffer.test.js index 18b31cae8..19502b285 100644 --- a/integration/bunjs-only-snippets/buffer.test.js +++ b/integration/bunjs-only-snippets/buffer.test.js @@ -124,6 +124,25 @@ it("Buffer.copy", () => { expect(array1.join("")).toBe(array2.join("")); }); +it("Buffer.concat", () => { + var array1 = new Uint8Array(128); + array1.fill(100); + var array2 = new Uint8Array(128); + array2.fill(200); + var array3 = new Uint8Array(128); + array3.fill(300); + expect(Buffer.concat([array1, array2, array3]).join("")).toBe( + array1.join("") + array2.join("") + array3.join("") + ); + expect(Buffer.concat([array1, array2, array3], 222).length).toBe(222); + expect( + Buffer.concat([array1, array2, array3], 222).subarray(0, 128).join("") + ).toBe("100".repeat(128)); + expect( + Buffer.concat([array1, array2, array3], 222).subarray(129, 222).join("") + ).toBe("200".repeat(222 - 129)); +}); + it("read", () => { var buf = new Buffer(1024); var data = new DataView(buf.buffer); diff --git a/src/javascript/jsc/bindings/JSBuffer.cpp b/src/javascript/jsc/bindings/JSBuffer.cpp index 930a6de68..7a5ea8c7b 100644 --- a/src/javascript/jsc/bindings/JSBuffer.cpp +++ b/src/javascript/jsc/bindings/JSBuffer.cpp @@ -354,7 +354,65 @@ static inline JSC::EncodedJSValue jsBufferConstructorFunction_concatBody(JSC::JS { auto& vm = JSC::getVM(lexicalGlobalObject); - return JSValue::encode(jsUndefined()); + auto throwScope = DECLARE_THROW_SCOPE(vm); + if (callFrame->argumentCount() < 1) { + return constructBufferEmpty(lexicalGlobalObject, callFrame); + } + + auto arrayValue = callFrame->uncheckedArgument(0); + auto array = JSC::jsDynamicCast<JSC::JSArray*>(vm, arrayValue); + if (!array) { + throwTypeError(lexicalGlobalObject, throwScope, "Argument must be an array"_s); + return JSValue::encode(jsUndefined()); + } + + size_t arrayLength = array->length(); + if (arrayLength < 1) { + RELEASE_AND_RETURN(throwScope, constructBufferEmpty(lexicalGlobalObject, callFrame)); + } + + size_t byteLength = 0; + + for (size_t i = 0; i < arrayLength; i++) { + auto element = array->getIndex(lexicalGlobalObject, i); + RETURN_IF_EXCEPTION(throwScope, {}); + if (!element.isObject()) { + throwTypeError(lexicalGlobalObject, throwScope, "Buffer.concat expects Uint8Array"_s); + return JSValue::encode(jsUndefined()); + } + auto* typedArray = JSC::jsDynamicCast<JSC::JSUint8Array*>(vm, element); + if (!typedArray) { + throwTypeError(lexicalGlobalObject, throwScope, "Buffer.concat expects Uint8Array"_s); + return JSValue::encode(jsUndefined()); + } + byteLength += typedArray->length(); + } + + if (callFrame->argumentCount() > 1) { + auto byteLengthValue = callFrame->uncheckedArgument(1); + byteLength = std::min(byteLength, byteLengthValue.toTypedArrayIndex(lexicalGlobalObject, "totalLength must be a valid number"_s)); + RETURN_IF_EXCEPTION(throwScope, {}); + } + + if (byteLength == 0) { + RELEASE_AND_RETURN(throwScope, constructBufferEmpty(lexicalGlobalObject, callFrame)); + } + + JSC::JSUint8Array* outBuffer = JSBuffer__bufferFromLengthAsArray(lexicalGlobalObject, byteLength); + size_t remain = byteLength; + auto* head = outBuffer->typedVector(); + + for (size_t i = 0; i < arrayLength && remain > 0; i++) { + auto element = array->getIndex(lexicalGlobalObject, i); + RETURN_IF_EXCEPTION(throwScope, {}); + auto* typedArray = JSC::jsCast<JSC::JSUint8Array*>(element); + size_t length = std::min(remain, typedArray->length()); + memcpy(head, typedArray->typedVector(), length); + remain -= length; + head += length; + } + + RELEASE_AND_RETURN(throwScope, JSC::JSValue::encode(JSC::JSValue(outBuffer))); } static inline JSC::EncodedJSValue jsBufferConstructorFunction_isBufferBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) { |