diff options
author | 2023-08-10 13:32:04 -0700 | |
---|---|---|
committer | 2023-08-10 13:32:04 -0700 | |
commit | e25833d00946fdd400abd3c25e8aa1c3e3d2e355 (patch) | |
tree | 71f6f046b75f6dbafd90e96443b1a3e8a29ad07d | |
parent | e65535cc054f8bfff98648f0605537a9d734e225 (diff) | |
download | bun-e25833d00946fdd400abd3c25e8aa1c3e3d2e355.tar.gz bun-e25833d00946fdd400abd3c25e8aa1c3e3d2e355.tar.zst bun-e25833d00946fdd400abd3c25e8aa1c3e3d2e355.zip |
Fixes #4062 (#4106)
* Fixes #4062
* Update encoding.zig
* Use faster C++ impl
* Update wtf-bindings.cpp
* undo
* Fixup
---------
Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
-rwxr-xr-x | bun.lockb | bin | 72574 -> 72574 bytes | |||
-rw-r--r-- | src/base64/base64.zig | 29 | ||||
-rw-r--r-- | src/bun.js/bindings/BunString.cpp | 10 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 8 | ||||
-rw-r--r-- | src/bun.js/bindings/wtf-bindings.cpp | 53 | ||||
-rw-r--r-- | src/bun.js/bindings/wtf-bindings.h | 1 | ||||
-rw-r--r-- | src/bun.js/webcore/encoding.zig | 5 | ||||
-rw-r--r-- | src/string.zig | 7 |
8 files changed, 87 insertions, 26 deletions
Binary files differ diff --git a/src/base64/base64.zig b/src/base64/base64.zig index e56e15877..a483f5ca5 100644 --- a/src/base64/base64.zig +++ b/src/base64/base64.zig @@ -70,8 +70,15 @@ pub fn encodeLen(source: anytype) usize { return zig_base64.standard.Encoder.calcSize(source.len); } -// This is just std.base64 copy-pasted -// with support for returning how many bytes were decoded +pub fn urlSafeEncodeLen(source: anytype) usize { + // Copied from WebKit + return ((source.len * 4) + 2) / 3; +} +extern fn WTF__base64URLEncode(input: [*]const u8, input_len: usize, output: [*]u8, output_len: usize) usize; +pub fn encodeURLSafe(dest: []u8, source: []const u8) usize { + return WTF__base64URLEncode(source.ptr, source.len, dest.ptr, dest.len); +} + const zig_base64 = struct { const assert = std.debug.assert; const testing = std.testing; @@ -167,6 +174,7 @@ const zig_base64 = struct { } /// Compute the encoded length + /// Note: this is wrong for base64url encoding. Do not use it for that. pub fn calcSize(encoder: *const Base64Encoder, source_len: usize) usize { if (encoder.pad_char != null) { return @divTrunc(source_len + 2, 3) * 4; @@ -181,6 +189,16 @@ const zig_base64 = struct { const out_len = encoder.calcSize(source.len); assert(dest.len >= out_len); + const out_idx = encoder.encodeWithoutSizeCheck(dest, source); + if (encoder.pad_char) |pad_char| { + for (dest[out_idx..out_len]) |*pad| { + pad.* = pad_char; + } + } + return dest[0..out_len]; + } + + pub fn encodeWithoutSizeCheck(encoder: *const Base64Encoder, dest: []u8, source: []const u8) usize { var acc: u12 = 0; var acc_len: u4 = 0; var out_idx: usize = 0; @@ -197,12 +215,7 @@ const zig_base64 = struct { dest[out_idx] = encoder.alphabet_chars[@as(u6, @truncate((acc << 6 - acc_len)))]; out_idx += 1; } - if (encoder.pad_char) |pad_char| { - for (dest[out_idx..out_len]) |*pad| { - pad.* = pad_char; - } - } - return dest[0..out_len]; + return out_idx; } }; diff --git a/src/bun.js/bindings/BunString.cpp b/src/bun.js/bindings/BunString.cpp index 09b545cba..5f312a28f 100644 --- a/src/bun.js/bindings/BunString.cpp +++ b/src/bun.js/bindings/BunString.cpp @@ -60,6 +60,11 @@ JSC::JSValue toJS(JSC::JSGlobalObject* globalObject, BunString bunString) return JSValue(Zig::toJSStringGC(bunString.impl.zig, globalObject)); } +JSC::JSValue toJS(JSC::JSGlobalObject* globalObject, BunString bunString, size_t length) +{ + return jsSubstring(globalObject, jsUndefined(), Bun::toWTFString(bunString), 0, length); +} + WTF::String toWTFString(const BunString& bunString) { if (bunString.tag == BunStringTag::ZigString) { @@ -195,6 +200,11 @@ extern "C" JSC::EncodedJSValue BunString__toJS(JSC::JSGlobalObject* globalObject return JSValue::encode(Bun::toJS(globalObject, *bunString)); } +extern "C" JSC::EncodedJSValue BunString__toJSWithLength(JSC::JSGlobalObject* globalObject, BunString* bunString, size_t length) +{ + return JSValue::encode(Bun::toJS(globalObject, *bunString, length)); +} + extern "C" BunString BunString__fromUTF16Unitialized(size_t length) { unsigned utf16Length = length; diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 3b6116a0d..0b787ee42 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -5514,7 +5514,6 @@ pub const URLSearchParams = opaque { pub const WTF = struct { extern fn WTF__copyLCharsFromUCharSource(dest: [*]u8, source: *const anyopaque, len: usize) void; - extern fn WTF__toBase64URLStringValue(bytes: [*]const u8, length: usize, globalObject: *JSGlobalObject) JSValue; extern fn WTF__parseDouble(bytes: [*]const u8, length: usize, counted: *usize) f64; pub fn parseDouble(buf: []const u8) !f64 { @@ -5540,13 +5539,6 @@ pub const WTF = struct { WTF__copyLCharsFromUCharSource(destination, source.ptr, source.len); } - /// Encode a byte array to a URL-safe base64 string for use with JS - /// Memory is managed by JavaScriptCore instead of us - pub fn toBase64URLStringValue(bytes: []const u8, globalObject: *JSGlobalObject) JSValue { - JSC.markBinding(@src()); - - return WTF__toBase64URLStringValue(bytes.ptr, bytes.len, globalObject); - } }; pub const Callback = struct { diff --git a/src/bun.js/bindings/wtf-bindings.cpp b/src/bun.js/bindings/wtf-bindings.cpp index 5c0e593d7..ccf71f8eb 100644 --- a/src/bun.js/bindings/wtf-bindings.cpp +++ b/src/bun.js/bindings/wtf-bindings.cpp @@ -1,5 +1,4 @@ #include "wtf-bindings.h" -#include "wtf/text/Base64.h" #include "wtf/StackTrace.h" #include "wtf/dtoa.h" @@ -14,13 +13,6 @@ extern "C" void WTF__copyLCharsFromUCharSource(LChar* destination, const UChar* WTF::StringImpl::copyCharacters(destination, source, length); } -extern "C" JSC::EncodedJSValue WTF__toBase64URLStringValue(const uint8_t* bytes, size_t length, JSC::JSGlobalObject* globalObject) -{ - WTF::String string = WTF::base64URLEncodeToString(reinterpret_cast<const LChar*>(bytes), static_cast<unsigned int>(length)); - string.impl()->ref(); - return JSC::JSValue::encode(JSC::jsString(globalObject->vm(), string)); -} - extern "C" void Bun__crashReportWrite(void* ctx, const char* message, size_t length); extern "C" void Bun__crashReportDumpStackTrace(void* ctx) { @@ -51,4 +43,49 @@ extern "C" void Bun__crashReportDumpStackTrace(void* ctx) auto str = out.toCString(); Bun__crashReportWrite(ctx, str.data(), str.length()); } +} + +// For whatever reason +// Doing this in C++/C is 2x faster than doing it in Zig. +// However, it's still slower than it should be. +static constexpr size_t encodeMapSize = 64; +static constexpr char base64URLEncMap[encodeMapSize] = { + 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, + 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, + 0x57, 0x58, 0x59, 0x5A, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x30, 0x31, 0x32, + 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x2D, 0x5F +}; + +extern "C" size_t WTF__base64URLEncode(const unsigned char* __restrict inputDataBuffer, size_t inputDataBufferSize, + unsigned char* __restrict destinationDataBuffer, + size_t destinationDataBufferSize) +{ + size_t sidx = 0; + size_t didx = 0; + + if (inputDataBufferSize > 1) { + while (sidx < inputDataBufferSize - 2) { + destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx] >> 2) & 077]; + destinationDataBuffer[didx++] = base64URLEncMap[((inputDataBuffer[sidx + 1] >> 4) & 017) | ((inputDataBuffer[sidx] << 4) & 077)]; + destinationDataBuffer[didx++] = base64URLEncMap[((inputDataBuffer[sidx + 2] >> 6) & 003) | ((inputDataBuffer[sidx + 1] << 2) & 077)]; + destinationDataBuffer[didx++] = base64URLEncMap[inputDataBuffer[sidx + 2] & 077]; + sidx += 3; + } + } + + if (sidx < inputDataBufferSize) { + destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx] >> 2) & 077]; + if (sidx < inputDataBufferSize - 1) { + destinationDataBuffer[didx++] = base64URLEncMap[((inputDataBuffer[sidx + 1] >> 4) & 017) | ((inputDataBuffer[sidx] << 4) & 077)]; + destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx + 1] << 2) & 077]; + } else + destinationDataBuffer[didx++] = base64URLEncMap[(inputDataBuffer[sidx] << 4) & 077]; + } + + while (didx < destinationDataBufferSize) + destinationDataBuffer[didx++] = '='; + + return destinationDataBufferSize; }
\ No newline at end of file diff --git a/src/bun.js/bindings/wtf-bindings.h b/src/bun.js/bindings/wtf-bindings.h index 2abd398fe..1721b0e1c 100644 --- a/src/bun.js/bindings/wtf-bindings.h +++ b/src/bun.js/bindings/wtf-bindings.h @@ -4,4 +4,3 @@ #include "wtf/text/ASCIIFastPath.h" extern "C" void WTF__copyLCharsFromUCharSource(LChar* destination, const UChar* source, size_t length); -extern "C" JSC::EncodedJSValue WTF__toBase64URLStringValue(const uint8_t* bytes, size_t length, JSC::JSGlobalObject* globalObject);
\ No newline at end of file diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index 9b1de838c..0e6b2526f 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -909,7 +909,10 @@ pub const Encoder = struct { }, .base64url => { - return JSC.WTF.toBase64URLStringValue(input, global); + var out = bun.String.createUninitialized(.latin1, bun.base64.urlSafeEncodeLen(input)) orelse return ZigString.init("Out of memory").toErrorInstance(global); + defer out.deref(); + const outlen = bun.base64.encodeURLSafe(@constCast(out.latin1()), input); + return out.toJSWithLength(global, outlen); }, .base64 => { diff --git a/src/string.zig b/src/string.zig index 9002234cf..332a1f583 100644 --- a/src/string.zig +++ b/src/string.zig @@ -503,6 +503,12 @@ pub const String = extern struct { return BunString__toJS(globalObject, this); } + pub fn toJSWithLength(this: *String, globalObject: *bun.JSC.JSGlobalObject, len: usize) JSC.JSValue { + JSC.markBinding(@src()); + + return BunString__toJSWithLength(globalObject, this, len); + } + pub fn toJSConst(this: *const String, globalObject: *bun.JSC.JSGlobalObject) JSC.JSValue { JSC.markBinding(@src()); var a = this.*; @@ -685,6 +691,7 @@ pub const String = extern struct { extern fn BunString__fromJS(globalObject: *JSC.JSGlobalObject, value: bun.JSC.JSValue, out: *String) bool; extern fn BunString__toJS(globalObject: *JSC.JSGlobalObject, in: *String) JSC.JSValue; + extern fn BunString__toJSWithLength(globalObject: *JSC.JSGlobalObject, in: *String, usize) JSC.JSValue; extern fn BunString__toWTFString(this: *String) void; pub fn ref(this: String) void { |