From 16598555f137112a3df2da5d8f2ee8edb496484f Mon Sep 17 00:00:00 2001 From: Silas Rech Date: Tue, 27 Jun 2023 00:35:48 +0200 Subject: `.randomInt()` support (#3357) * Add initial .randomInt() fallback * Add basic .randomInt() test * Attempt creating a native implementation * Switch to JSC.wrapWithHasContainer * Switch to .jsNumberFromUint64(), it seems like using just .jsNumber() causes the number to overflow in some cases * Regenerate out folder after rebasing --- test/js/node/crypto/node-crypto.test.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'test/js/node/crypto/node-crypto.test.js') diff --git a/test/js/node/crypto/node-crypto.test.js b/test/js/node/crypto/node-crypto.test.js index 9e0e7f396..5a68540cf 100644 --- a/test/js/node/crypto/node-crypto.test.js +++ b/test/js/node/crypto/node-crypto.test.js @@ -8,6 +8,27 @@ it("crypto.randomBytes should return a Buffer", () => { expect(Buffer.isBuffer(crypto.randomBytes(1))).toBe(true); }); +it("crypto.randomInt should return a number", () => { + const result = crypto.randomInt(0, 10); + expect(typeof result).toBe("number"); + expect(result).toBeGreaterThanOrEqual(0); + expect(result).toBeLessThanOrEqual(10); +}); + +it("crypto.randomInt with no arguments", () => { + const result = crypto.randomInt(); + expect(typeof result).toBe("number"); + expect(result).toBeGreaterThanOrEqual(0); + expect(result).toBeLessThanOrEqual(Number.MAX_SAFE_INTEGER); +}); + +it("crypto.randomInt with one argument", () => { + const result = crypto.randomInt(100); + expect(typeof result).toBe("number"); + expect(result).toBeGreaterThanOrEqual(0); + expect(result).toBeLessThanOrEqual(100); +}); + // https://github.com/oven-sh/bun/issues/1839 describe("createHash", () => { it("update & digest", () => { -- cgit v1.2.3 From 963d4311e614ac197427104b9cf265bbe2a890af Mon Sep 17 00:00:00 2001 From: Jarred Sumner Date: Sun, 9 Jul 2023 22:36:24 -0700 Subject: Fixes #3530 (#3587) * Fixes #3530 * Handle OOM * Add test --------- Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com> --- src/bun.js/bindings/BunString.cpp | 23 +++++++++++++++++ src/bun.js/node/types.zig | 30 +++++++++++++++++----- src/bun.js/webcore/encoding.zig | 36 ++++++++++++--------------- src/string.zig | 26 +++++++++++++++++++ test/js/node/crypto/node-crypto.test.js | 44 +++++++++++++++++++++++++++++++++ 5 files changed, 133 insertions(+), 26 deletions(-) (limited to 'test/js/node/crypto/node-crypto.test.js') diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index 4c8ff384e..21541d711 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -169,6 +169,29 @@ extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject* globalObject return JSValue::encode(Bun::toJS(globalObject, *bunString)); } +extern "C" BunString BunString__fromUTF16Unitialized(size_t length) +{ + unsigned utf16Length = length; + UChar* ptr; + auto impl = WTF::StringImpl::createUninitialized(utf16Length, ptr); + if (UNLIKELY(!ptr)) + return { BunStringTag::Dead }; + + impl->ref(); + return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; +} + +extern "C" BunString BunString__fromLatin1Unitialized(size_t length) +{ + unsigned latin1Length = length; + LChar* ptr; + auto impl = WTF::StringImpl::createUninitialized(latin1Length, ptr); + if (UNLIKELY(!ptr)) + return { BunStringTag::Dead }; + impl->ref(); + return { BunStringTag::WTFStringImpl, { .wtf = &impl.leakRef() } }; +} + extern "C" BunString BunString__fromUTF8(const char* bytes, size_t length) { if (simdutf::validate_utf8(bytes, length)) { diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 553b292d6..642039ba5 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -541,9 +541,18 @@ pub const Encoding = enum(u8) { const result = JSC.ZigString.init(out).toValueGC(globalThis); return result; }, - else => { - globalThis.throwInvalidArguments("Unexpected encoding", .{}); - return JSC.JSValue.zero; + .buffer => { + return JSC.ArrayBuffer.createBuffer(globalThis, input); + }, + + inline else => |enc| { + const res = JSC.WebCore.Encoder.toString(input.ptr, size, globalThis, enc); + if (res.isError()) { + globalThis.throwValue(res); + return .zero; + } + + return res; }, } } @@ -571,9 +580,18 @@ pub const Encoding = enum(u8) { const result = JSC.ZigString.init(out).toValueGC(globalThis); return result; }, - else => { - globalThis.throwInvalidArguments("Unexpected encoding", .{}); - return JSC.JSValue.zero; + .buffer => { + return JSC.ArrayBuffer.createBuffer(globalThis, input); + }, + inline else => |enc| { + const res = JSC.WebCore.Encoder.toString(input.ptr, input.len, globalThis, enc); + + if (res.isError()) { + globalThis.throwValue(res); + return .zero; + } + + return res; }, } } diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index bb1180acb..dd47ccc29 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -829,23 +829,18 @@ pub const Encoder = struct { return ZigString.init(input).toValueGC(global); } - if (input.len < 512) { - var buf: [512]u8 = undefined; - var to = buf[0..input.len]; - strings.copyLatin1IntoASCII(to, input); - return ZigString.init(to).toValueGC(global); - } - - var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global); - strings.copyLatin1IntoASCII(to, input); - return ZigString.init(to).toExternalValue(global); + var str = bun.String.createUninitialized(.latin1, len) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer str.deref(); + strings.copyLatin1IntoASCII(@constCast(str.latin1()), input); + return str.toJS(global); }, .latin1 => { - var to = allocator.alloc(u8, len) catch return ZigString.init("Out of memory").toErrorInstance(global); + var str = bun.String.createUninitialized(.latin1, len) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer str.deref(); - @memcpy(to, input_ptr[0..to.len]); + @memcpy(@constCast(str.latin1()), input_ptr[0..len]); - return ZigString.init(to).toExternalValue(global); + return str.toJS(global); }, .buffer, .utf8 => { const converted = strings.toUTF16Alloc(allocator, input, false) catch return ZigString.init("Out of memory").toErrorInstance(global); @@ -861,21 +856,22 @@ pub const Encoder = struct { // Avoid incomplete characters if (len / 2 == 0) return ZigString.Empty.toValue(global); - var output = allocator.alloc(u16, len / 2) catch return ZigString.init("Out of memory").toErrorInstance(global); - var output_bytes = std.mem.sliceAsBytes(output); + var output = bun.String.createUninitialized(.utf16, len / 2) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer output.deref(); + var output_bytes = std.mem.sliceAsBytes(@constCast(output.utf16())); output_bytes[output_bytes.len - 1] = 0; @memcpy(output_bytes, input_ptr[0..output_bytes.len]); - return ZigString.toExternalU16(output.ptr, output.len, global); + return output.toJS(global); }, .hex => { - var output = allocator.alloc(u8, input.len * 2) catch return ZigString.init("Out of memory").toErrorInstance(global); + var str = bun.String.createUninitialized(.latin1, len * 2) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer str.deref(); + var output = @constCast(str.latin1()); const wrote = strings.encodeBytesToHex(output, input); std.debug.assert(wrote == output.len); - var val = ZigString.init(output); - val.mark(); - return val.toExternalValue(global); + return str.toJS(global); }, .base64url => { diff --git a/src/string.zig b/src/string.zig index 166a0a6f7..5f107197f 100644 --- a/src/string.zig +++ b/src/string.zig @@ -257,6 +257,8 @@ pub const String = extern struct { extern fn BunString__fromLatin1(bytes: [*]const u8, len: usize) String; extern fn BunString__fromBytes(bytes: [*]const u8, len: usize) String; + extern fn BunString__fromLatin1Unitialized(len: usize) String; + extern fn BunString__fromUTF16Unitialized(len: usize) String; pub fn toOwnedSlice(this: String, allocator: std.mem.Allocator) ![]u8 { switch (this.tag) { @@ -278,6 +280,30 @@ pub const String = extern struct { } } + pub fn createUninitializedLatin1(len: usize) String { + JSC.markBinding(@src()); + return BunString__fromLatin1Unitialized(len); + } + + pub fn createUninitializedUTF16(len: usize) String { + JSC.markBinding(@src()); + return BunString__fromUTF16Unitialized(len); + } + + pub fn createUninitialized(comptime kind: @Type(.EnumLiteral), len: usize) ?String { + const without_check = switch (comptime kind) { + .latin1 => createUninitializedLatin1(len), + .utf16 => createUninitializedUTF16(len), + else => @compileError("Invalid string kind"), + }; + + if (without_check.tag == .Dead) { + return null; + } + + return without_check; + } + pub fn createLatin1(bytes: []const u8) String { JSC.markBinding(@src()); return BunString__fromLatin1(bytes.ptr, bytes.len); diff --git a/test/js/node/crypto/node-crypto.test.js b/test/js/node/crypto/node-crypto.test.js index 5a68540cf..2489f96c7 100644 --- a/test/js/node/crypto/node-crypto.test.js +++ b/test/js/node/crypto/node-crypto.test.js @@ -43,6 +43,50 @@ describe("createHash", () => { expect(Buffer.isBuffer(hash.digest())).toBeTrue(); }); + const otherEncodings = { + ucs2: [ + 11626, 2466, 37699, 38942, 64564, 53010, 48101, 47943, 44761, 18499, 12442, 26994, 46434, 62582, 39395, 20542, + ], + latin1: [ + 106, 45, 162, 9, 67, 147, 30, 152, 52, 252, 18, 207, 229, 187, 71, 187, 217, 174, 67, 72, 154, 48, 114, 105, 98, + 181, 118, 244, 227, 153, 62, 80, + ], + binary: [ + 106, 45, 162, 9, 67, 147, 30, 152, 52, 252, 18, 207, 229, 187, 71, 187, 217, 174, 67, 72, 154, 48, 114, 105, 98, + 181, 118, 244, 227, 153, 62, 80, + ], + base64: [ + 97, 105, 50, 105, 67, 85, 79, 84, 72, 112, 103, 48, 47, 66, 76, 80, 53, 98, 116, 72, 117, 57, 109, 117, 81, 48, + 105, 97, 77, 72, 74, 112, 89, 114, 86, 50, 57, 79, 79, 90, 80, 108, 65, 61, + ], + hex: [ + 54, 97, 50, 100, 97, 50, 48, 57, 52, 51, 57, 51, 49, 101, 57, 56, 51, 52, 102, 99, 49, 50, 99, 102, 101, 53, 98, + 98, 52, 55, 98, 98, 100, 57, 97, 101, 52, 51, 52, 56, 57, 97, 51, 48, 55, 50, 54, 57, 54, 50, 98, 53, 55, 54, 102, + 52, 101, 51, 57, 57, 51, 101, 53, 48, + ], + ascii: [ + 106, 45, 34, 9, 67, 19, 30, 24, 52, 124, 18, 79, 101, 59, 71, 59, 89, 46, 67, 72, 26, 48, 114, 105, 98, 53, 118, + 116, 99, 25, 62, 80, + ], + utf8: [ + 106, 45, 65533, 9, 67, 65533, 30, 65533, 52, 65533, 18, 65533, 65533, 71, 65533, 1646, 67, 72, 65533, 48, 114, + 105, 98, 65533, 118, 65533, 65533, 62, 80, + ], + }; + + for (let encoding in otherEncodings) { + it("digest " + encoding, () => { + const hash = crypto.createHash("sha256"); + hash.update("some data to hash"); + expect( + hash + .digest(encoding) + .split("") + .map(a => a.charCodeAt(0)), + ).toEqual(otherEncodings[encoding]); + }); + } + it("stream (sync)", () => { const hash = crypto.createHash("sha256"); hash.write("some data to hash"); -- cgit v1.2.3