diff options
author | 2023-01-11 02:43:58 +0000 | |
---|---|---|
committer | 2023-01-10 18:43:58 -0800 | |
commit | f1e6ea22541fba2e7354b8440f6efe0f3292c546 (patch) | |
tree | 55c77187daec7d50809accb655987d4df4aabd3c | |
parent | 56a6cf716e3c7c0613245ceb67492b9a2aaf4912 (diff) | |
download | bun-f1e6ea22541fba2e7354b8440f6efe0f3292c546.tar.gz bun-f1e6ea22541fba2e7354b8440f6efe0f3292c546.tar.zst bun-f1e6ea22541fba2e7354b8440f6efe0f3292c546.zip |
Implement Buffer swap16, swap32, swap64 (#1659)
* Implement Buffer swap16, swap32, swap64
* Initial incorporation of feedback
- Use constexpr
- Clean up the indexing
- Check for detached
- Use suggested text for exception text
-rw-r--r-- | src/bun.js/bindings/JSBuffer.cpp | 88 | ||||
-rw-r--r-- | test/bun.js/buffer.test.js | 78 |
2 files changed, 163 insertions, 3 deletions
diff --git a/src/bun.js/bindings/JSBuffer.cpp b/src/bun.js/bindings/JSBuffer.cpp index 623c67bcb..a89492359 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -1114,17 +1114,99 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_lastIndexOfBody(JSC: static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap16Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); - return JSC::JSValue::encode(jsUndefined()); + auto scope = DECLARE_THROW_SCOPE(vm); + + constexpr int elemSize = 2; + int64_t length = static_cast<int64_t>(castedThis->byteLength()); + if (length % elemSize != 0) { + throwRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 16-bits"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + if (UNLIKELY(castedThis->isDetached())) { + throwVMTypeError(lexicalGlobalObject, scope, "Buffer is detached"_s); + return JSValue::encode(jsUndefined()); + } + + uint8_t* typedVector = castedThis->typedVector(); + + for (size_t elem = 0; elem < length; elem += elemSize) { + const size_t right = elem + 1; + + uint8_t temp = typedVector[elem]; + typedVector[elem] = typedVector[right]; + typedVector[right] = temp; + } + + return JSC::JSValue::encode(castedThis); } static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap32Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); - return JSC::JSValue::encode(jsUndefined()); + auto scope = DECLARE_THROW_SCOPE(vm); + + constexpr int elemSize = 4; + int64_t length = static_cast<int64_t>(castedThis->byteLength()); + if (length % elemSize != 0) { + throwRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 32-bits"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + if (UNLIKELY(castedThis->isDetached())) { + throwVMTypeError(lexicalGlobalObject, scope, "Buffer is detached"_s); + return JSValue::encode(jsUndefined()); + } + + uint8_t* typedVector = castedThis->typedVector(); + + constexpr size_t swaps = elemSize/2; + for (size_t elem = 0; elem < length; elem += elemSize) { + const size_t right = elem + elemSize - 1; + for (size_t k = 0; k < swaps; k++) { + const size_t i = right - k; + const size_t j = elem + k; + + uint8_t temp = typedVector[i]; + typedVector[i] = typedVector[j]; + typedVector[j] = temp; + } + } + + return JSC::JSValue::encode(castedThis); } static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap64Body(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); - return JSC::JSValue::encode(jsUndefined()); + auto scope = DECLARE_THROW_SCOPE(vm); + + constexpr size_t elemSize = 8; + int64_t length = static_cast<int64_t>(castedThis->byteLength()); + if (length % elemSize != 0) { + throwRangeError(lexicalGlobalObject, scope, "Buffer size must be a multiple of 64-bits"_s); + return JSC::JSValue::encode(jsUndefined()); + } + + if (UNLIKELY(castedThis->isDetached())) { + throwVMTypeError(lexicalGlobalObject, scope, "Buffer is detached"_s); + return JSValue::encode(jsUndefined()); + } + + uint8_t* typedVector = castedThis->typedVector(); + + constexpr size_t swaps = elemSize/2; + for (size_t elem = 0; elem < length; elem += elemSize) { + const size_t right = elem + elemSize - 1; + for (size_t k = 0; k < swaps; k++) { + const size_t i = right - k; + const size_t j = elem + k; + + uint8_t temp = typedVector[i]; + typedVector[i] = typedVector[j]; + typedVector[j] = temp; + } + } + + return JSC::JSValue::encode(castedThis); } static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSArrayBufferView>::ClassParameter castedThis) diff --git a/test/bun.js/buffer.test.js b/test/bun.js/buffer.test.js index 3e14ead81..4c8603335 100644 --- a/test/bun.js/buffer.test.js +++ b/test/bun.js/buffer.test.js @@ -556,6 +556,84 @@ it("Buffer.from(base64)", () => { ).toBe('console.log("hello world")\n'); }); +it("Buffer.swap16", () => { + const examples = [ + ["", ""], + ["a1", "1a"], + ["a1b2", "1a2b"] + ]; + + for (let i = 0; i < examples.length; i++) { + const input = examples[i][0]; + const output = examples[i][1]; + const buf = Buffer.from(input, "utf-8"); + + const ref = buf.swap16(); + expect(ref instanceof Buffer).toBe(true); + expect(buf.toString()).toBe(output); + } + + const buf = Buffer.from("123", "utf-8"); + try { + buf.swap16(); + expect(false).toBe(true); + } catch (exception) { + expect(exception.message).toBe("Buffer size must be a multiple of 16-bits"); + } +}); + +it("Buffer.swap32", () => { + const examples = [ + ["", ""], + ["a1b2", "2b1a"], + ["a1b2c3d4", "2b1a4d3c"] + ]; + + for (let i = 0; i < examples.length; i++) { + const input = examples[i][0]; + const output = examples[i][1]; + const buf = Buffer.from(input, "utf-8"); + + const ref = buf.swap32(); + expect(ref instanceof Buffer).toBe(true); + expect(buf.toString()).toBe(output); + } + + const buf = Buffer.from("12345", "utf-8"); + try { + buf.swap32(); + expect(false).toBe(true); + } catch (exception) { + expect(exception.message).toBe("Buffer size must be a multiple of 32-bits"); + } +}); + +it("Buffer.swap64", () => { + const examples = [ + ["", ""], + ["a1b2c3d4", "4d3c2b1a"], + ["a1b2c3d4e5f6g7h8", "4d3c2b1a8h7g6f5e"], + ]; + + for (let i = 0; i < examples.length; i++) { + const input = examples[i][0]; + const output = examples[i][1]; + const buf = Buffer.from(input, "utf-8"); + + const ref = buf.swap64(); + expect(ref instanceof Buffer).toBe(true); + expect(buf.toString()).toBe(output); + } + + const buf = Buffer.from("123456789", "utf-8"); + try { + buf.swap64(); + expect(false).toBe(true); + } catch (exception) { + expect(exception.message).toBe("Buffer size must be a multiple of 64-bits"); + } +}); + it("Buffer.toString regessions", () => { expect( Buffer.from([65, 0]) |