aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-05-01 02:22:13 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-05-01 02:22:13 -0700
commita3b48b3229b4dcdaa7c51762ceb2641d219e9a1c (patch)
treeedbc95b0ef0d51b27ab5f4940a08b3dfea7c0d50
parentd7ef268e183d2592fee6a3f2a2429ec3ad24e0fe (diff)
downloadbun-a3b48b3229b4dcdaa7c51762ceb2641d219e9a1c.tar.gz
bun-a3b48b3229b4dcdaa7c51762ceb2641d219e9a1c.tar.zst
bun-a3b48b3229b4dcdaa7c51762ceb2641d219e9a1c.zip
[bun.js] Implement `Buffer.concat`
-rw-r--r--integration/bunjs-only-snippets/buffer.test.js19
-rw-r--r--src/javascript/jsc/bindings/JSBuffer.cpp60
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)
{