aboutsummaryrefslogtreecommitdiff
path: root/src/string.zig
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2023-07-17 04:15:13 -0700
committerGravatar GitHub <noreply@github.com> 2023-07-17 04:15:13 -0700
commit36a25c358044b0c9a56a06d8246ae2b5098b3ae4 (patch)
treea869888ad4d39e9a5a6dc0b9f7d5e8b1c7528691 /src/string.zig
parent13b5d9d4de4e9fe897d373a6ed7fdf2c9884c71f (diff)
downloadbun-36a25c358044b0c9a56a06d8246ae2b5098b3ae4.tar.gz
bun-36a25c358044b0c9a56a06d8246ae2b5098b3ae4.tar.zst
bun-36a25c358044b0c9a56a06d8246ae2b5098b3ae4.zip
Fix memory leak in `await new Response(latin1String).arrayBuffer()` and `await Response.json(obj).json()` (#3656)
❯ mem bun --smol response-arrayBuffer.mjs cpu: Apple M1 Max runtime: bun 0.6.15 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p995 --------------------------------------------------------------------------------------------------- ----------------------------- new Response().arrayBuffer() (new string each call, latin1) 12.9 µs/iter (625 ns … 4.18 ms) 1 µs 567.17 µs 711.79 µs new Response().arrayBuffer() (new string each call, utf16) 12.85 µs/iter (1.67 µs … 1.56 ms) 2.17 µs 462.75 µs 621.13 µs new Response().arrayBuffer() (existing string, latin1) 6.53 µs/iter (6.21 µs … 7.07 µs) 6.64 µs 7.07 µs 7.07 µs Peak memory usage: 49 MB bun on  jarred/memory-leak-fix took 2s ❯ mem bun response-arrayBuffer.mjs cpu: Apple M1 Max runtime: bun 0.6.15 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p995 --------------------------------------------------------------------------------------------------- ----------------------------- new Response().arrayBuffer() (new string each call, latin1) 1.2 µs/iter (1.04 µs … 1.42 µs) 1.22 µs 1.42 µs 1.42 µs new Response().arrayBuffer() (new string each call, utf16) 2.74 µs/iter (2.42 µs … 6.37 µs) 2.68 µs 6.37 µs 6.37 µs new Response().arrayBuffer() (existing string, latin1) 746.37 ns/iter (643.82 ns … 1.04 µs) 776.11 ns 1.04 µs 1.04 µs Peak memory usage: 104 MB bun on  jarred/memory-leak-fix took 2s ❯ mem ~/.bun/bin/bun response-arrayBuffer.mjs cpu: Apple M1 Max runtime: bun 0.6.15 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p995 --------------------------------------------------------------------------------------------------- ----------------------------- new Response().arrayBuffer() (new string each call, latin1) 1.69 µs/iter (1.56 µs … 2.1 µs) 1.73 µs 2.1 µs 2.1 µs new Response().arrayBuffer() (new string each call, utf16) 2.65 µs/iter (2.47 µs … 3.17 µs) 2.69 µs 3.17 µs 3.17 µs new Response().arrayBuffer() (existing string, latin1) 667.67 ns/iter (547.67 ns … 1.28 µs) 694.21 ns 1.28 µs 1.28 µs Peak memory usage: 2735 MB bun on  jarred/memory-leak-fix took 2s ❯ mem ~/.bun/bin/bun --smol response-arrayBuffer.mjs cpu: Apple M1 Max runtime: bun 0.6.15 (arm64-darwin) benchmark time (avg) (min … max) p75 p99 p995 --------------------------------------------------------------------------------------------------- ----------------------------- new Response().arrayBuffer() (new string each call, latin1) 13.51 µs/iter (541 ns … 3.2 ms) 1.92 µs 553.42 µs 709.92 µs new Response().arrayBuffer() (new string each call, utf16) 13.07 µs/iter (1.71 µs … 3.43 ms) 2.13 µs 451.21 µs 651.67 µs new Response().arrayBuffer() (existing string, latin1) 6.25 µs/iter (5.79 µs … 6.81 µs) 6.4 µs 6.81 µs 6.81 µs Peak memory usage: 292 MB Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
Diffstat (limited to 'src/string.zig')
-rw-r--r--src/string.zig43
1 files changed, 43 insertions, 0 deletions
diff --git a/src/string.zig b/src/string.zig
index 5f107197f..26cd86d8c 100644
--- a/src/string.zig
+++ b/src/string.zig
@@ -128,6 +128,22 @@ pub const WTFStringImplStruct = extern struct {
return .{};
}
+ pub fn toUTF8WithoutRef(this: WTFStringImpl, allocator: std.mem.Allocator) ZigString.Slice {
+ if (this.is8Bit()) {
+ if (bun.strings.toUTF8FromLatin1(allocator, this.latin1Slice()) catch null) |utf8| {
+ return ZigString.Slice.init(allocator, utf8.items);
+ }
+
+ return ZigString.Slice.fromUTF8NeverFree(this.latin1Slice());
+ }
+
+ if (bun.strings.toUTF8Alloc(allocator, this.utf16Slice()) catch null) |utf8| {
+ return ZigString.Slice.init(allocator, utf8);
+ }
+
+ return .{};
+ }
+
pub fn toUTF8IfNeeded(this: WTFStringImpl, allocator: std.mem.Allocator) ?ZigString.Slice {
if (this.is8Bit()) {
if (bun.strings.toUTF8FromLatin1(allocator, this.latin1Slice()) catch null) |utf8| {
@@ -533,6 +549,16 @@ pub const String = extern struct {
return false;
}
+ extern fn BunString__toJSON(
+ globalObject: *bun.JSC.JSGlobalObject,
+ this: *String,
+ ) JSC.JSValue;
+
+ pub fn toJSForParseJSON(self: *String, globalObject: *JSC.JSGlobalObject) JSC.JSValue {
+ JSC.markBinding(@src());
+ return BunString__toJSON(globalObject, self);
+ }
+
pub fn encodeInto(self: String, out: []u8, comptime enc: JSC.Node.Encoding) !usize {
if (self.isUTF16()) {
return JSC.WebCore.Encoder.encodeIntoFrom16(self.utf16(), out, enc, true);
@@ -581,6 +607,23 @@ pub const String = extern struct {
return ZigString.Slice.empty;
}
+ /// This is the same as toUTF8, but it doesn't increment the reference count for latin1 strings
+ pub fn toUTF8WithoutRef(this: String, allocator: std.mem.Allocator) ZigString.Slice {
+ if (this.tag == .WTFStringImpl) {
+ return this.value.WTFStringImpl.toUTF8WithoutRef(allocator);
+ }
+
+ if (this.tag == .ZigString) {
+ return this.value.ZigString.toSlice(allocator);
+ }
+
+ if (this.tag == .StaticZigString) {
+ return ZigString.Slice.fromUTF8NeverFree(this.value.StaticZigString.slice());
+ }
+
+ return ZigString.Slice.empty;
+ }
+
pub fn toSlice(this: String, allocator: std.mem.Allocator) SliceWithUnderlyingString {
return SliceWithUnderlyingString{
.utf8 = this.toUTF8(allocator),