aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-08-18 21:09:26 -0700
committerGravatar Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> 2022-08-18 21:09:26 -0700
commit0e4db0b28fb73b0db45bf727e295def014dd691e (patch)
treefc69e772e1b4c141669b54e0b2c5cb3a8cb17293
parentd150a2f4ddc10597e4531fd3c55b62bb0ecbf02c (diff)
downloadbun-0e4db0b28fb73b0db45bf727e295def014dd691e.tar.gz
bun-0e4db0b28fb73b0db45bf727e295def014dd691e.tar.zst
bun-0e4db0b28fb73b0db45bf727e295def014dd691e.zip
Add string support to `Buffer.fill`
-rw-r--r--src/bun.js/bindings/Buffer.h2
-rw-r--r--src/bun.js/bindings/JSBuffer.cpp13
-rw-r--r--src/bun.js/bindings/bindings.zig1
-rw-r--r--src/bun.js/builtins/js/JSBufferPrototype.js11
-rw-r--r--src/bun.js/node/buffer.zig121
-rw-r--r--src/napi/napi.zig3
-rw-r--r--test/bun.js/buffer.test.js45
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);