mirror of
https://github.com/oven-sh/bun
synced 2026-02-09 10:28:47 +00:00
Add string support to Buffer.fill
This commit is contained in:
@@ -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 {
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -2431,7 +2431,6 @@ pub const JSValue = enum(JSValueReprInt) {
|
||||
.JSImmutableButterfly,
|
||||
.JSSourceCode,
|
||||
.JSScriptFetcher,
|
||||
.InternalFunction,
|
||||
.JSScriptFetchParameters,
|
||||
=> true,
|
||||
else => false,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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..];
|
||||
if (written <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
while (remain.len > 0) {
|
||||
for (slice[0..]) |a| {
|
||||
remain[0] = a;
|
||||
remain = remain[1..];
|
||||
}
|
||||
}
|
||||
var contents = buf[0..@intCast(usize, written)];
|
||||
buf = buf[@intCast(usize, written)..];
|
||||
|
||||
memset_pattern16(buf.ptr, &pattern, buf.len);
|
||||
return;
|
||||
}
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
if (contents.len == 1) {
|
||||
@memset(buf.ptr, contents[0], buf.len);
|
||||
return;
|
||||
}
|
||||
|
||||
var in_there = @minimum(slice.len, buf.len);
|
||||
@memcpy(buf.ptr, slice.ptr, in_there);
|
||||
if (in_there < slice.len) {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// var ptr = buf.ptr + @as(usize, start) + slice.len;
|
||||
|
||||
// const fill_length = @as(usize, end) - @as(usize, start);
|
||||
|
||||
// // 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" });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user