aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-08-10 13:32:04 -0700
committerGravatar GitHub <noreply@github.com> 2023-08-10 13:32:04 -0700
commite25833d00946fdd400abd3c25e8aa1c3e3d2e355 (patch)
tree71f6f046b75f6dbafd90e96443b1a3e8a29ad07d
parente65535cc054f8bfff98648f0605537a9d734e225 (diff)
downloadbun-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-xbun.lockbbin72574 -> 72574 bytes
-rw-r--r--src/base64/base64.zig29
-rw-r--r--src/bun.js/bindings/BunString.cpp10
-rw-r--r--src/bun.js/bindings/bindings.zig8
-rw-r--r--src/bun.js/bindings/wtf-bindings.cpp53
-rw-r--r--src/bun.js/bindings/wtf-bindings.h1
-rw-r--r--src/bun.js/webcore/encoding.zig5
-rw-r--r--src/string.zig7
8 files changed, 87 insertions, 26 deletions
diff --git a/bun.lockb b/bun.lockb
index da36ca965..fe74c1e04 100755
--- a/bun.lockb
+++ b/bun.lockb
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 {