diff options
author | 2022-10-27 23:25:32 -0500 | |
---|---|---|
committer | 2022-10-27 21:25:32 -0700 | |
commit | 0bfd00e734ebe6093fd69badbc1d733d039f3d6a (patch) | |
tree | 5b7550c900eabe4e365104e5def486ec5d199449 | |
parent | 0b1d4ff81bbb19383cba7932e88359a6ccfb6899 (diff) | |
download | bun-0bfd00e734ebe6093fd69badbc1d733d039f3d6a.tar.gz bun-0bfd00e734ebe6093fd69badbc1d733d039f3d6a.tar.zst bun-0bfd00e734ebe6093fd69badbc1d733d039f3d6a.zip |
feat(core): optimize zig slice (#1408)
* feat(core): optimize zig slice
* address concerns
-rw-r--r-- | src/bun.js/api/bun/socket.zig | 8 | ||||
-rw-r--r-- | src/bun.js/api/transpiler.zig | 2 | ||||
-rw-r--r-- | src/bun.js/bindings/bindings.zig | 68 | ||||
-rw-r--r-- | src/bun.js/bindings/exports.zig | 2 | ||||
-rw-r--r-- | src/bun.js/node/types.zig | 4 | ||||
-rw-r--r-- | src/bun.js/test/jest.zig | 10 | ||||
-rw-r--r-- | src/bun.js/webcore/encoding.zig | 2 | ||||
-rw-r--r-- | src/bun.js/webcore/response.zig | 44 | ||||
-rw-r--r-- | src/nullable_allocator.zig | 31 |
9 files changed, 96 insertions, 75 deletions
diff --git a/src/bun.js/api/bun/socket.zig b/src/bun.js/api/bun/socket.zig index 38b6837b3..8851ff47b 100644 --- a/src/bun.js/api/bun/socket.zig +++ b/src/bun.js/api/bun/socket.zig @@ -379,9 +379,9 @@ pub const Listener = struct { var socket = Listener{ .handlers = handlers, .connection = if (port) |port_| .{ - .host = .{ .host = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), .port = port_ }, + .host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), .port = port_ }, } else .{ - .unix = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), + .unix = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), }, .ssl = ssl_enabled, }; @@ -660,9 +660,9 @@ pub const Listener = struct { var socket_context = uws.us_create_socket_context(@boolToInt(ssl_enabled), uws.Loop.get().?, @sizeOf(usize), ctx_opts).?; var connection: Listener.UnixOrHost = if (port) |port_| .{ - .host = .{ .host = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), .port = port_ }, + .host = .{ .host = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), .port = port_ }, } else .{ - .unix = (hostname_or_unix.cloneIfNeeded() catch unreachable).slice(), + .unix = (hostname_or_unix.cloneIfNeeded(bun.default_allocator) catch unreachable).slice(), }; if (ssl_enabled) { diff --git a/src/bun.js/api/transpiler.zig b/src/bun.js/api/transpiler.zig index 6ce7c70de..c48f1f093 100644 --- a/src/bun.js/api/transpiler.zig +++ b/src/bun.js/api/transpiler.zig @@ -683,7 +683,7 @@ fn transformOptionsFromJSC(ctx: JSC.C.JSContextRef, temp_allocator: std.mem.Allo const replacementValue = JSC.JSObject.getIndex(value, globalThis, 1); if (exportReplacementValue(replacementValue, globalThis)) |to_replace| { const replacementKey = JSC.JSObject.getIndex(value, globalThis, 0); - var slice = (try replacementKey.toSlice(globalThis, bun.default_allocator).cloneIfNeeded()); + var slice = (try replacementKey.toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator)); var replacement_name = slice.slice(); if (!JSLexer.isIdentifier(replacement_name)) { diff --git a/src/bun.js/bindings/bindings.zig b/src/bun.js/bindings/bindings.zig index 6d5f87662..7aa148c4d 100644 --- a/src/bun.js/bindings/bindings.zig +++ b/src/bun.js/bindings/bindings.zig @@ -16,6 +16,8 @@ const ArrayBuffer = @import("../base.zig").ArrayBuffer; const JSC = @import("../../jsc.zig"); const Shimmer = JSC.Shimmer; const FFI = @import("./FFI.zig"); +const NullableAllocator = @import("../../nullable_allocator.zig").NullableAllocator; + pub const JSObject = extern struct { pub const shim = Shimmer("JSC", "JSObject", @This()); bytes: shim.Bytes, @@ -91,7 +93,7 @@ pub const ZigString = extern struct { pub fn clone(this: ZigString, allocator: std.mem.Allocator) !ZigString { var sliced = this.toSlice(allocator); - if (!sliced.allocated) { + if (!sliced.isAllocated()) { var str = ZigString.init(try allocator.dupe(u8, sliced.slice())); str.mark(); str.markUTF8(); @@ -197,47 +199,49 @@ pub const ZigString = extern struct { pub const shim = Shimmer("", "ZigString", @This()); pub const Slice = struct { - allocator: std.mem.Allocator, + allocator: NullableAllocator = .{}, ptr: [*]const u8, len: u32, - allocated: bool = false, pub fn fromUTF8(input: []const u8) Slice { return .{ .ptr = input.ptr, .len = @truncate(u32, input.len), - .allocated = false, - .allocator = bun.default_allocator, + .allocator = NullableAllocator.init(bun.default_allocator), }; } - pub const empty = Slice{ .allocator = bun.default_allocator, .ptr = undefined, .len = 0, .allocated = false }; + pub const empty = Slice{ .ptr = undefined, .len = 0 }; + + pub inline fn isAllocated(this: Slice) bool { + return !this.allocator.isNull(); + } pub fn clone(this: Slice, allocator: std.mem.Allocator) !Slice { - if (!this.allocated) { - return Slice{ .allocator = allocator, .ptr = this.ptr, .len = this.len, .allocated = false }; + if (this.isAllocated()) { + return Slice{ .allocator = this.allocator, .ptr = this.ptr, .len = this.len }; } var duped = try allocator.dupe(u8, this.ptr[0..this.len]); - return Slice{ .allocator = allocator, .ptr = duped.ptr, .len = this.len, .allocated = true }; + return Slice{ .allocator = NullableAllocator.init(allocator), .ptr = duped.ptr, .len = this.len }; } - pub fn cloneIfNeeded(this: Slice) !Slice { - if (this.allocated) { + pub fn cloneIfNeeded(this: Slice, allocator: std.mem.Allocator) !Slice { + if (this.isAllocated()) { return this; } - var duped = try this.allocator.dupe(u8, this.ptr[0..this.len]); - return Slice{ .allocator = this.allocator, .ptr = duped.ptr, .len = this.len, .allocated = true }; + var duped = try allocator.dupe(u8, this.ptr[0..this.len]); + return Slice{ .allocator = NullableAllocator.init(allocator), .ptr = duped.ptr, .len = this.len }; } pub fn cloneZ(this: Slice, allocator: std.mem.Allocator) !Slice { - if (this.allocated or this.len == 0) { + if (this.isAllocated() or this.len == 0) { return this; } var duped = try allocator.dupeZ(u8, this.ptr[0..this.len]); - return Slice{ .allocator = allocator, .ptr = duped.ptr, .len = this.len, .allocated = true }; + return Slice{ .allocator = NullableAllocator.init(allocator), .ptr = duped.ptr, .len = this.len }; } pub fn slice(this: Slice) []const u8 { @@ -272,11 +276,9 @@ pub const ZigString = extern struct { /// Does nothing if the slice is not allocated pub fn deinit(this: *const Slice) void { - if (!this.allocated) { - return; + if (this.allocator.get()) |allocator| { + allocator.free(this.slice()); } - - this.allocator.free(this.slice()); } }; @@ -445,22 +447,19 @@ pub const ZigString = extern struct { pub fn toSliceFast(this: ZigString, allocator: std.mem.Allocator) Slice { if (this.len == 0) - return Slice{ .ptr = "", .len = 0, .allocator = allocator, .allocated = false }; + return Slice.empty; if (is16Bit(&this)) { var buffer = this.toOwnedSlice(allocator) catch unreachable; return Slice{ .ptr = buffer.ptr, .len = @truncate(u32, buffer.len), - .allocated = true, - .allocator = allocator, + .allocator = NullableAllocator.init(allocator), }; } return Slice{ .ptr = untagged(this.ptr), .len = @truncate(u32, this.len), - .allocated = false, - .allocator = allocator, }; } @@ -468,54 +467,43 @@ pub const ZigString = extern struct { /// It is slow but safer when the input is from JavaScript pub fn toSlice(this: ZigString, allocator: std.mem.Allocator) Slice { if (this.len == 0) - return Slice{ .ptr = "", .len = 0, .allocator = allocator, .allocated = false }; + return Slice.empty; if (is16Bit(&this)) { var buffer = this.toOwnedSlice(allocator) catch unreachable; return Slice{ + .allocator = NullableAllocator.init(allocator), .ptr = buffer.ptr, .len = @truncate(u32, buffer.len), - .allocated = true, - .allocator = allocator, }; } if (!this.isUTF8() and !strings.isAllASCII(untagged(this.ptr)[0..this.len])) { var buffer = this.toOwnedSlice(allocator) catch unreachable; - return Slice{ - .ptr = buffer.ptr, - .len = @truncate(u32, buffer.len), - .allocated = true, - .allocator = allocator, - }; + return Slice.fromUTF8(buffer); } return Slice{ .ptr = untagged(this.ptr), .len = @truncate(u32, this.len), - .allocated = false, - .allocator = allocator, }; } pub fn toSliceZ(this: ZigString, allocator: std.mem.Allocator) Slice { if (this.len == 0) - return Slice{ .ptr = "", .len = 0, .allocator = allocator, .allocated = false }; + return Slice.empty; if (is16Bit(&this)) { var buffer = this.toOwnedSliceZ(allocator) catch unreachable; return Slice{ .ptr = buffer.ptr, .len = @truncate(u32, buffer.len), - .allocated = true, - .allocator = allocator, + .allocator = NullableAllocator.init(allocator), }; } return Slice{ .ptr = untagged(this.ptr), .len = @truncate(u32, this.len), - .allocated = false, - .allocator = allocator, }; } diff --git a/src/bun.js/bindings/exports.zig b/src/bun.js/bindings/exports.zig index fb2d45c45..eb4f33001 100644 --- a/src/bun.js/bindings/exports.zig +++ b/src/bun.js/bindings/exports.zig @@ -1999,7 +1999,7 @@ pub const ZigConsoleClient = struct { var tag_name_slice: ZigString.Slice = ZigString.Slice.empty; var is_tag_kind_primitive = false; - defer if (tag_name_slice.allocated) tag_name_slice.deinit(); + defer if (tag_name_slice.isAllocated()) tag_name_slice.deinit(); if (value.get(this.globalThis, "type")) |type_value| { const _tag = Tag.get(type_value, this.globalThis); diff --git a/src/bun.js/node/types.zig b/src/bun.js/node/types.zig index 526e33f29..144274b9b 100644 --- a/src/bun.js/node/types.zig +++ b/src/bun.js/node/types.zig @@ -1675,7 +1675,7 @@ pub const Path = struct { PathHandler.normalizeStringNode(str, &buf, .windows); var out_str = JSC.ZigString.init(out); - if (str_slice.allocated) out_str.setOutputEncoding(); + if (str_slice.isAllocated()) out_str.setOutputEncoding(); return out_str.toValueGC(globalThis); } pub fn parse(globalThis: *JSC.JSGlobalObject, isWindows: bool, args_ptr: [*]JSC.JSValue, args_len: u16) callconv(.C) JSC.JSValue { @@ -1743,7 +1743,7 @@ pub const Path = struct { PathHandler.relativePlatform(from, to, .windows, true); var out_str = JSC.ZigString.init(out); - if (from_slice.allocated or to_slice.allocated) out_str.setOutputEncoding(); + if (from_slice.isAllocated() or to_slice.isAllocated()) out_str.setOutputEncoding(); return out_str.toValueGC(globalThis); } diff --git a/src/bun.js/test/jest.zig b/src/bun.js/test/jest.zig index cab53c1e6..3e962c047 100644 --- a/src/bun.js/test/jest.zig +++ b/src/bun.js/test/jest.zig @@ -707,7 +707,8 @@ pub const TestScope = struct { } if (js.JSValueIsString(ctx, args[0])) { - label = (JSC.JSValue.fromRef(arguments[0]).toSlice(ctx, getAllocator(ctx)).cloneIfNeeded() catch unreachable).slice(); + const allocator = getAllocator(ctx); + label = (JSC.JSValue.fromRef(arguments[0]).toSlice(ctx, allocator).cloneIfNeeded(allocator) catch unreachable).slice(); args = args[1..]; } @@ -972,6 +973,7 @@ pub const DescribeScope = struct { var label = ZigString.init(""); var args = arguments; + const allocator = getAllocator(ctx); if (js.JSValueIsString(ctx, arguments[0])) { JSC.JSValue.fromRef(arguments[0]).toZigString(&label, ctx.ptr()); @@ -979,15 +981,15 @@ pub const DescribeScope = struct { } if (args.len == 0 or !js.JSObjectIsFunction(ctx, args[0])) { - JSError(getAllocator(ctx), "describe() requires a callback function", .{}, ctx, exception); + JSError(allocator, "describe() requires a callback function", .{}, ctx, exception); return js.JSValueMakeUndefined(ctx); } var callback = args[0]; - var scope = getAllocator(ctx).create(DescribeScope) catch unreachable; + var scope = allocator.create(DescribeScope) catch unreachable; scope.* = .{ - .label = (label.toSlice(getAllocator(ctx)).cloneIfNeeded() catch unreachable).slice(), + .label = (label.toSlice(allocator).cloneIfNeeded(allocator) catch unreachable).slice(), .parent = this, .file_id = this.file_id, }; diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index f70071ae3..bf1e8c992 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -666,7 +666,7 @@ pub const TextDecoder = struct { } var str = arguments[0].toSlice(globalThis, default_allocator); - defer if (str.allocated) str.deinit(); + defer if (str.isAllocated()) str.deinit(); encoding = EncodingLabel.which(str.slice()) orelse { globalThis.throwInvalidArguments("Unsupported encoding label \"{s}\"", .{str.slice()}); return null; diff --git a/src/bun.js/webcore/response.zig b/src/bun.js/webcore/response.zig index c47648a5d..43b40b3b6 100644 --- a/src/bun.js/webcore/response.zig +++ b/src/bun.js/webcore/response.zig @@ -36,6 +36,7 @@ const JSPromise = JSC.JSPromise; const JSValue = JSC.JSValue; const JSError = JSC.JSError; const JSGlobalObject = JSC.JSGlobalObject; +const NullableAllocator = @import("../../nullable_allocator.zig").NullableAllocator; const VirtualMachine = @import("../javascript.zig").VirtualMachine; const Task = JSC.Task; @@ -333,15 +334,16 @@ pub const Response = struct { json_value.jsonStringify(globalThis.ptr(), 0, &zig_str); if (zig_str.len > 0) { - var zig_str_slice = zig_str.toSlice(getAllocator(globalThis)); + const allocator = getAllocator(globalThis); + var zig_str_slice = zig_str.toSlice(allocator); - if (zig_str_slice.allocated) { + if (zig_str_slice.isAllocated()) { response.body.value = .{ - .Blob = Blob.initWithAllASCII(zig_str_slice.mut(), zig_str_slice.allocator, globalThis.ptr(), false), + .Blob = Blob.initWithAllASCII(zig_str_slice.mut(), allocator, globalThis.ptr(), false), }; } else { response.body.value = .{ - .Blob = Blob.initWithAllASCII(getAllocator(globalThis).dupe(u8, zig_str_slice.slice()) catch unreachable, zig_str_slice.allocator, globalThis.ptr(), true), + .Blob = Blob.initWithAllASCII(allocator.dupe(u8, zig_str_slice.slice()) catch unreachable, allocator, globalThis.ptr(), true), }; } } @@ -3101,12 +3103,7 @@ pub const Blob = struct { input_path = .{ .fd = store.data.file.pathlike.fd }; } else { input_path = .{ - .path = ZigString.Slice{ - .ptr = store.data.file.pathlike.path.slice().ptr, - .len = @truncate(u32, store.data.file.pathlike.path.slice().len), - .allocated = false, - .allocator = bun.default_allocator, - }, + .path = ZigString.Slice.fromUTF8(store.data.file.pathlike.path.slice()), }; } @@ -3800,10 +3797,10 @@ pub const Blob = struct { JSC.JSValue.JSType.DerivedStringObject, => { var sliced = top_value.toSlice(global, bun.default_allocator); - const is_all_ascii = !sliced.allocated; - if (!sliced.allocated and sliced.len > 0) { + const is_all_ascii = !sliced.isAllocated(); + if (!sliced.isAllocated() and sliced.len > 0) { sliced.ptr = @ptrCast([*]const u8, (try bun.default_allocator.dupe(u8, sliced.slice())).ptr); - sliced.allocated = true; + sliced.allocator = NullableAllocator.init(bun.default_allocator); } return Blob.initWithAllASCII(bun.constStrToU8(sliced.slice()), bun.default_allocator, global, is_all_ascii); @@ -3861,11 +3858,12 @@ pub const Blob = struct { JSC.JSValue.JSType.DerivedStringObject, => { var sliced = current.toSlice(global, bun.default_allocator); - could_have_non_ascii = could_have_non_ascii or sliced.allocated; + const allocator = sliced.allocator.get(); + could_have_non_ascii = could_have_non_ascii or allocator != null; joiner.append( sliced.slice(), 0, - if (sliced.allocated) sliced.allocator else null, + allocator, ); }, @@ -3890,11 +3888,12 @@ pub const Blob = struct { JSC.JSValue.JSType.DerivedStringObject, => { var sliced = item.toSlice(global, bun.default_allocator); - could_have_non_ascii = could_have_non_ascii or sliced.allocated; + const allocator = sliced.allocator.get(); + could_have_non_ascii = could_have_non_ascii or allocator != null; joiner.append( sliced.slice(), 0, - if (sliced.allocated) sliced.allocator else null, + allocator, ); continue; }, @@ -3966,11 +3965,12 @@ pub const Blob = struct { else => { var sliced = current.toSlice(global, bun.default_allocator); - could_have_non_ascii = could_have_non_ascii or sliced.allocated; + const allocator = sliced.allocator.get(); + could_have_non_ascii = could_have_non_ascii or allocator != null; joiner.append( sliced.slice(), 0, - if (sliced.allocated) sliced.allocator else null, + allocator, ); }, } @@ -5399,7 +5399,7 @@ pub const Request = struct { const urlOrObject = arguments[0]; const url_or_object_type = urlOrObject.jsType(); if (url_or_object_type.isStringLike()) { - request.url = (arguments[0].toSlice(globalThis, bun.default_allocator).cloneIfNeeded() catch { + request.url = (arguments[0].toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch { return null; }).slice(); request.url_was_allocated = request.url.len > 0; @@ -5418,7 +5418,7 @@ pub const Request = struct { } if (urlOrObject.fastGet(globalThis, .url)) |url| { - request.url = (url.toSlice(globalThis, bun.default_allocator).cloneIfNeeded() catch { + request.url = (url.toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch { return null; }).slice(); request.url_was_allocated = request.url.len > 0; @@ -5439,7 +5439,7 @@ pub const Request = struct { request.method = req_init.method; } - request.url = (arguments[0].toSlice(globalThis, bun.default_allocator).cloneIfNeeded() catch { + request.url = (arguments[0].toSlice(globalThis, bun.default_allocator).cloneIfNeeded(bun.default_allocator) catch { return null; }).slice(); request.url_was_allocated = request.url.len > 0; diff --git a/src/nullable_allocator.zig b/src/nullable_allocator.zig new file mode 100644 index 000000000..8af65d6bb --- /dev/null +++ b/src/nullable_allocator.zig @@ -0,0 +1,31 @@ +const std = @import("std"); + +/// A nullable allocator the same size as `std.mem.Allocator`. +pub const NullableAllocator = struct { + ptr: *anyopaque = undefined, + // Utilize the null pointer optimization on the vtable instead of + // the regular ptr because some allocator implementations might tag their + // `ptr` property. + vtable: ?*const std.mem.Allocator.VTable = null, + + pub inline fn init(a: std.mem.Allocator) @This() { + return .{ + .ptr = a.ptr, + .vtable = a.vtable, + }; + } + + pub inline fn isNull(this: @This()) bool { + return this.vtable == null; + } + + pub inline fn get(this: @This()) ?std.mem.Allocator { + return if (this.vtable) |vt| std.mem.Allocator{ .ptr = this.ptr, .vtable = vt } else null; + } +}; + +comptime { + if (@sizeOf(NullableAllocator) != @sizeOf(std.mem.Allocator)) { + @compileError("Expected the sizes to be the same."); + } +} |