diff options
-rw-r--r-- | src/bun.js/bindings/Buffer.h | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/JSBuffer.cpp | 13 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 1 | ||||
-rw-r--r-- | src/bun.js/builtins/js/JSBufferPrototype.js | 11 | ||||
-rw-r--r-- | src/bun.js/node/buffer.zig | 121 | ||||
-rw-r--r-- | src/napi/napi.zig | 3 | ||||
-rw-r--r-- | test/bun.js/buffer.test.js | 45 |
7 files changed, 119 insertions, 77 deletions
diff --git a/src/bun.js/bindings/Buffer.h b/src/bun.js/bindings/Buffer.h index 3dc828c87..086646d5f 100644 --- a/src/bun.js/bindings/Buffer.h +++ b/src/bun.js/bindings/Buffer.h @@ -20,7 +20,7 @@ extern "C" JSC::EncodedJSValue Bun__encoding__toStringLatin1(const uint8_t* inpu extern "C" JSC::EncodedJSValue Bun__encoding__toStringHex(const uint8_t* input, size_t len, JSC::JSGlobalObject* globalObject); extern "C" JSC::EncodedJSValue Bun__encoding__toStringBase64(const uint8_t* input, size_t len, JSC::JSGlobalObject* globalObject); extern "C" JSC::EncodedJSValue Bun__encoding__toStringURLSafeBase64(const uint8_t* input, size_t len, JSC::JSGlobalObject* globalObject); -extern "C" void Bun__Buffer_fill(JSC::JSGlobalObject*, Bun__ArrayBuffer*, ZigString*, uint32_t, uint32_t, WebCore::BufferEncodingType); +extern "C" void 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 85f2d17b3..9533076cf 100644 --- a/src/bun.js/bindings/JSBuffer.cpp +++ b/src/bun.js/bindings/JSBuffer.cpp @@ -986,17 +986,15 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_fillBody(JSC::JSGlob } auto startPtr = castedThis->typedVector() + start; + auto str_ = value.toWTFString(lexicalGlobalObject); + ZigString str = Zig::toZigString(str_); - // ZigString str = Zig::toString(value.toString(lexicalGlobalObject)); - - // Bun__ArrayBuffer buf; - // JSC__JSValue__asArrayBuffer_(JSC::JSValue::encode(castedThis), lexicalGlobalObject, - // &buf); - // Bun__Buffer_fill(lexicalGlobalObject, &buf, &str, start, end, encoding); + Bun__Buffer_fill(&str, startPtr, end - start, encoding); RELEASE_AND_RETURN(throwScope, JSValue::encode(castedThis)); } } + static inline JSC::EncodedJSValue jsBufferPrototypeFunction_includesBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); @@ -1004,7 +1002,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_includesBody(JSC::JS UNUSED_PARAM(throwScope); UNUSED_PARAM(callFrame); - return JSC::JSValue::encode(JSC::JSValue(reinterpret_cast<uint8_t*>(castedThis->vector())[0])); + return jsBufferPrototypeFunction_indexOfBody(lexicalGlobalObject, callFrame, castedThis) } static inline JSC::EncodedJSValue jsBufferPrototypeFunction_indexOfBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) { @@ -1031,6 +1029,7 @@ static inline JSC::EncodedJSValue jsBufferPrototypeFunction_swap64Body(JSC::JSGl auto& vm = JSC::getVM(lexicalGlobalObject); return JSC::JSValue::encode(jsUndefined()); } + static inline JSC::EncodedJSValue jsBufferPrototypeFunction_toStringBody(JSC::JSGlobalObject* lexicalGlobalObject, JSC::CallFrame* callFrame, typename IDLOperation<JSBuffer>::ClassParameter castedThis) { auto& vm = JSC::getVM(lexicalGlobalObject); diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 36c22ae2f..a22271593 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -2431,7 +2431,6 @@ pub const JSValue = enum(JSValueReprInt) { .JSImmutableButterfly, .JSSourceCode, .JSScriptFetcher, - .InternalFunction, .JSScriptFetchParameters, => true, else => false, diff --git a/src/bun.js/builtins/js/JSBufferPrototype.js b/src/bun.js/builtins/js/JSBufferPrototype.js index c841bcd6c..de80450b1 100644 --- a/src/bun.js/builtins/js/JSBufferPrototype.js +++ b/src/bun.js/builtins/js/JSBufferPrototype.js @@ -204,17 +204,6 @@ function writeBigUInt64BE(value, offset) { return offset + 8; } -function slice(start, end) { - "use strict"; - if (start === undefined && end === undefined) { - return this; - } - - Buffer[Symbol.species] ||= Buffer; - - return new Buffer(this.buffer, this.byteOffset + (start || 0), (end || this.byteLength) - (start || 0)); -} - function utf8Write(text, offset, length) { "use strict"; return this.write(text, offset, length, "utf8"); diff --git a/src/bun.js/node/buffer.zig b/src/bun.js/node/buffer.zig index 412c61722..541079a0b 100644 --- a/src/bun.js/node/buffer.zig +++ b/src/bun.js/node/buffer.zig @@ -17,78 +17,85 @@ pub const BufferVectorized = struct { extern fn memset_pattern16(b: *anyopaque, pattern16: *const anyopaque, len: usize) void; pub fn fill( - globalObject: *JSGlobalObject, - this: *JSC.ArrayBuffer, str: *JSC.ZigString, - start: u32, - end: u32, + buf_ptr: [*]u8, + fill_length: usize, encoding: JSC.Node.Encoding, ) callconv(.C) void { - const allocator = JSC.VirtualMachine.vm.allocator; - var stack_fallback = std.heap.stackFallback(512, allocator); - var stack_fallback_allocator = stack_fallback.get(); - var input_string = str.toSlice(stack_fallback_allocator); - if (input_string.len == 0) return; + if (str.len == 0) return; - defer input_string.deinit(); + var buf = buf_ptr[0..fill_length]; - var buf = this.slice()[start..end]; + 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) + 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) + 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) + 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) + 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) + 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) + 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) + 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) + else + JSC.WebCore.Encoder.writeU8(str.slice().ptr, str.slice().len, buf.ptr, buf.len, JSC.Node.Encoding.hex), + }; - var slice = input_string.slice(); - switch (encoding) { - JSC.Node.Encoding.utf8, - JSC.Node.Encoding.ascii, - JSC.Node.Encoding.latin1, - JSC.Node.Encoding.buffer, - => { - switch (slice.len) { - 0 => unreachable, - 1 => { - @memset(buf.ptr, slice[0], 1); - return; - }, - 2...16 => { - if (comptime Environment.isMac) { - var pattern: [16]u8 = undefined; - var remain: []u8 = pattern[0..]; - - while (remain.len > 0) { - for (slice[0..]) |a| { - remain[0] = a; - remain = remain[1..]; - } - } - - memset_pattern16(buf.ptr, &pattern, buf.len); - return; - } - }, - else => {}, - } + if (written <= 0) { + return; + } - var in_there = @minimum(slice.len, buf.len); - @memcpy(buf.ptr, slice.ptr, in_there); - if (in_there < slice.len) { - return; - } + var contents = buf[0..@intCast(usize, written)]; + buf = buf[@intCast(usize, written)..]; - // var ptr = buf.ptr + @as(usize, start) + slice.len; + if (contents.len == 1) { + @memset(buf.ptr, contents[0], buf.len); + return; + } - // const fill_length = @as(usize, end) - @as(usize, start); + const minimum_contents = contents; + while (buf.len >= contents.len) { + const min_len = @minimum(contents.len, buf.len); + std.mem.copy(u8, buf[0..min_len], contents[0..min_len]); + if (buf.len <= contents.len) { + break; + } + buf = buf[min_len..]; + contents.len *= 2; + } - // // while (in_there < fill_length - in_there) { - // // std.mem.copy(ptr) - // // ptr += in_there; - // // in_there *= 2; - // // } - }, - else => {}, + while (buf.len > 0) { + const to_fill = @minimum(minimum_contents.len, buf.len); + std.mem.copy(u8, buf[0..to_fill], minimum_contents[0..to_fill]); + buf = buf[to_fill..]; } } }; comptime { if (!JSC.is_bindgen) { - @export(BufferVectorized, .{ .name = "Bun__Buffer__fill" }); + @export(BufferVectorized.fill, .{ .name = "Bun__Buffer_fill" }); } } diff --git a/src/napi/napi.zig b/src/napi/napi.zig index 7fd1818ac..64fb8b153 100644 --- a/src/napi/napi.zig +++ b/src/napi/napi.zig @@ -1462,6 +1462,8 @@ pub fn fixDeadCodeElimination() void { std.mem.doNotOptimizeAway(&napi_ref_threadsafe_function); std.mem.doNotOptimizeAway(&napi_add_async_cleanup_hook); std.mem.doNotOptimizeAway(&napi_remove_async_cleanup_hook); + + std.mem.doNotOptimizeAway(&@import("../bun.js/node/buffer.zig").BufferVectorized.fill); } comptime { @@ -1552,5 +1554,6 @@ comptime { _ = napi_ref_threadsafe_function; _ = napi_add_async_cleanup_hook; _ = napi_remove_async_cleanup_hook; + _ = @import("../bun.js/node/buffer.zig").BufferVectorized.fill; } } diff --git a/test/bun.js/buffer.test.js b/test/bun.js/buffer.test.js index 7a587a0a5..87937e65e 100644 --- a/test/bun.js/buffer.test.js +++ b/test/bun.js/buffer.test.js @@ -211,6 +211,51 @@ it("Buffer.copy", () => { expect(array1.join("")).toBe(array2.join("")); }); +export function fillRepeating(dstBuffer, start, end) { + let len = dstBuffer.length, // important: use indices length, not byte-length + sLen = end - start, + p = sLen; // set initial position = source sequence length + + // step 2: copy existing data doubling segment length per iteration + while (p < len) { + if (p + sLen > len) sLen = len - p; // if not power of 2, truncate last segment + dstBuffer.copyWithin(p, start, sLen); // internal copy + p += sLen; // add current length to offset + sLen <<= 1; // double length for next segment + } +} + +describe("Buffer.fill string", () => { + for (let text of [ + "hello world", + "1234567890", + "\uD83D\uDE00", + "ππππππ
ππ€£βΊοΈπππ", + ]) { + it(text, () => { + var input = new Buffer(1024); + input.fill(text); + var demo = new Uint8Array(1024); + var encoded = new TextEncoder().encode(text); + + demo.set(encoded); + fillRepeating(demo, 0, encoded.length); + expect(input.join("")).toBe(demo.join("")); + }); + } +}); + +it("Buffer.fill 1 char string", () => { + var input = new Buffer(1024); + input.fill("h"); + var demo = new Uint8Array(1024); + var encoded = new TextEncoder().encode("h"); + + demo.set(encoded); + fillRepeating(demo, 0, encoded.length); + expect(input.join("")).toBe(demo.join("")); +}); + it("Buffer.concat", () => { var array1 = new Uint8Array(128); array1.fill(100); |