From ddf614a9d5ed4dbf5e8a58b79fc6bca5bfec76b7 Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Fri, 10 Apr 2026 19:09:18 +0800 Subject: [PATCH] Add arena buckets to ArenaPool ArenaPool previously maintained up to 512 16KB buckets. The 16KB retention is small for things like XHR and scripts, but increasing it to something more reasonably, like 128KB, would use up to 8x more memory. This commit adds 4 buckets: 1KB, 4KB, 16KB and 128KB. Callers can request a tiny, small, medium or large bucket. We end up using less memory peak memory and less allocations. Furthermore, callers can request a specific size. This is particularly useful for WebSocket or Blob where the size could vary greatly (so we'd likely default to a large bucket), but that could needlessly use up a large arena. The bucket sizes were derived from analyzing allocations. A significant number of allocations were very small. Things like ScheduleCallback and FinalizerCallback are always less than 1K and can be generated in the thousands. The 16KB retention was wasteful in these cases...better to have a large number of 1K pools, so that we can have a handful of very large buffers. --- src/App.zig | 2 +- src/ArenaPool.zig | 291 ++++++++++++------ src/browser/HttpClient.zig | 3 +- src/browser/Page.zig | 14 +- src/browser/Runner.zig | 2 +- src/browser/ScriptManager.zig | 6 +- src/browser/Session.zig | 12 +- src/browser/StyleManager.zig | 2 +- src/browser/js/Env.zig | 2 +- src/browser/js/Local.zig | 2 +- src/browser/js/Origin.zig | 2 +- src/browser/js/String.zig | 30 +- src/browser/webapi/Blob.zig | 10 +- src/browser/webapi/DOMParser.zig | 2 +- src/browser/webapi/Document.zig | 2 +- src/browser/webapi/Event.zig | 4 +- src/browser/webapi/File.zig | 2 +- src/browser/webapi/FileReader.zig | 2 +- src/browser/webapi/IntersectionObserver.zig | 4 +- src/browser/webapi/MutationObserver.zig | 8 +- src/browser/webapi/Permissions.zig | 2 +- src/browser/webapi/Range.zig | 4 +- src/browser/webapi/Window.zig | 4 +- src/browser/webapi/animation/Animation.zig | 2 +- src/browser/webapi/collections/ChildNodes.zig | 2 +- src/browser/webapi/css/FontFace.zig | 2 +- src/browser/webapi/css/FontFaceSet.zig | 2 +- src/browser/webapi/element/Html.zig | 2 +- src/browser/webapi/encoding/TextDecoder.zig | 2 +- src/browser/webapi/event/CloseEvent.zig | 4 +- src/browser/webapi/event/CompositionEvent.zig | 2 +- src/browser/webapi/event/CustomEvent.zig | 2 +- src/browser/webapi/event/ErrorEvent.zig | 4 +- src/browser/webapi/event/FocusEvent.zig | 4 +- src/browser/webapi/event/FormDataEvent.zig | 4 +- src/browser/webapi/event/InputEvent.zig | 4 +- src/browser/webapi/event/KeyboardEvent.zig | 4 +- src/browser/webapi/event/MessageEvent.zig | 4 +- src/browser/webapi/event/MouseEvent.zig | 4 +- .../NavigationCurrentEntryChangeEvent.zig | 4 +- .../webapi/event/PageTransitionEvent.zig | 4 +- src/browser/webapi/event/PointerEvent.zig | 2 +- src/browser/webapi/event/PopStateEvent.zig | 4 +- src/browser/webapi/event/ProgressEvent.zig | 4 +- .../webapi/event/PromiseRejectionEvent.zig | 2 +- src/browser/webapi/event/SubmitEvent.zig | 4 +- src/browser/webapi/event/TextEvent.zig | 2 +- src/browser/webapi/event/UIEvent.zig | 2 +- src/browser/webapi/event/WheelEvent.zig | 2 +- src/browser/webapi/net/Response.zig | 4 +- src/browser/webapi/net/WebSocket.zig | 16 +- src/browser/webapi/net/XMLHttpRequest.zig | 2 +- src/browser/webapi/selector/Selector.zig | 2 +- src/cdp/CDP.zig | 4 +- 54 files changed, 314 insertions(+), 204 deletions(-) diff --git a/src/App.zig b/src/App.zig index 8e3fe0c9..9e8741ba 100644 --- a/src/App.zig +++ b/src/App.zig @@ -69,7 +69,7 @@ pub fn init(allocator: Allocator, config: *const Config) !*App { app.telemetry = try Telemetry.init(app, config.mode); errdefer app.telemetry.deinit(allocator); - app.arena_pool = ArenaPool.init(allocator, 512, 1024 * 16); + app.arena_pool = ArenaPool.init(allocator, .{}); errdefer app.arena_pool.deinit(); return app; diff --git a/src/ArenaPool.zig b/src/ArenaPool.zig index 96fb694f..2b501438 100644 --- a/src/ArenaPool.zig +++ b/src/ArenaPool.zig @@ -27,33 +27,52 @@ const ArenaPool = @This(); const IS_DEBUG = builtin.mode == .Debug; -allocator: Allocator, -retain_bytes: usize, -free_list_len: u16 = 0, -free_list: ?*Entry = null, -free_list_max: u16, -entry_pool: std.heap.MemoryPool(Entry), -mutex: std.Thread.Mutex = .{}, -// Debug mode: track acquire/release counts per debug name to detect leaks and double-frees -_leak_track: if (IS_DEBUG) std.StringHashMapUnmanaged(isize) else void = if (IS_DEBUG) .empty else {}, +pub const BucketSize = enum { tiny, small, medium, large }; + +const Bucket = struct { + free_list: ?*Entry = null, + free_list_len: u16 = 0, + free_list_max: u16, + retain_bytes: usize, +}; const Entry = struct { next: ?*Entry, arena: ArenaAllocator, + bucket: *Bucket, debug: if (IS_DEBUG) []const u8 else void = if (IS_DEBUG) "" else {}, }; -pub const DebugInfo = struct { - debug: []const u8 = "", +pub const Config = struct { + tiny: Config.Bucket = .{ .max = 512, .retain = 1024 }, + small: Config.Bucket = .{ .max = 128, .retain = 4 * 1024 }, + medium: Config.Bucket = .{ .max = 64, .retain = 16 * 1024 }, + large: Config.Bucket = .{ .max = 32, .retain = 128 * 1024 }, + + const Bucket = struct { + max: u16, + retain: usize, + }; }; -pub fn init(allocator: Allocator, free_list_max: u16, retain_bytes: usize) ArenaPool { +tiny: Bucket, +small: Bucket, +medium: Bucket, +large: Bucket, +allocator: Allocator, +mutex: std.Thread.Mutex = .{}, +entry_pool: std.heap.MemoryPool(Entry), + +_leak_track: if (IS_DEBUG) std.StringHashMapUnmanaged(isize) else void = if (IS_DEBUG) .empty else {}, + +pub fn init(allocator: Allocator, config: Config) ArenaPool { return .{ .allocator = allocator, - .free_list_max = free_list_max, - .retain_bytes = retain_bytes, .entry_pool = .init(allocator), - ._leak_track = if (IS_DEBUG) .empty else {}, + .tiny = .{ .free_list_max = config.tiny.max, .retain_bytes = config.tiny.retain }, + .small = .{ .free_list_max = config.small.max, .retain_bytes = config.small.retain }, + .medium = .{ .free_list_max = config.medium.max, .retain_bytes = config.medium.retain }, + .large = .{ .free_list_max = config.large.max, .retain_bytes = config.large.retain }, }; } @@ -73,24 +92,49 @@ pub fn deinit(self: *ArenaPool) void { self._leak_track.deinit(self.allocator); } - var entry = self.free_list; - while (entry) |e| { - entry = e.next; - e.arena.deinit(); + // Free all arenas in all buckets + inline for (&[_]*Bucket{ &self.tiny, &self.small, &self.medium, &self.large }) |bucket| { + var entry = bucket.free_list; + while (entry) |e| { + entry = e.next; + e.arena.deinit(); + } } self.entry_pool.deinit(); } -pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator { +// Acquire an arena from the pool. +// - Pass a BucketSize (.tiny, .small, .medium, .large) for explicit bucket selection +// - Pass a usize for automatic bucket selection based on expected size +pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !Allocator { + const bucket = blk: { + const T = @TypeOf(size_or_bucket); + if (T == BucketSize or T == @TypeOf(.enum_literal)) { + break :blk switch (@as(BucketSize, size_or_bucket)) { + .tiny => &self.tiny, + .small => &self.small, + .medium => &self.medium, + .large => &self.large, + }; + } + if (T == usize or T == comptime_int) { + if (size_or_bucket <= self.tiny.retain_bytes) break :blk &self.tiny; + if (size_or_bucket <= self.small.retain_bytes) break :blk &self.small; + if (size_or_bucket <= self.medium.retain_bytes) break :blk &self.medium; + break :blk &self.large; + } + @compileError("acquire expects BucketSize or usize, got " ++ @typeName(T)); + }; + self.mutex.lock(); defer self.mutex.unlock(); - if (self.free_list) |entry| { - self.free_list = entry.next; - self.free_list_len -= 1; + if (bucket.free_list) |entry| { + bucket.free_list = entry.next; + bucket.free_list_len -= 1; if (IS_DEBUG) { - entry.debug = dbg.debug; - const gop = try self._leak_track.getOrPut(self.allocator, dbg.debug); + entry.debug = debug; + const gop = try self._leak_track.getOrPut(self.allocator, debug); if (!gop.found_existing) { gop.value_ptr.* = 0; } @@ -102,12 +146,13 @@ pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator { const entry = try self.entry_pool.create(); entry.* = .{ .next = null, + .bucket = bucket, + .debug = if (IS_DEBUG) debug else {}, .arena = ArenaAllocator.init(self.allocator), - .debug = if (IS_DEBUG) dbg.debug else {}, }; if (IS_DEBUG) { - const gop = try self._leak_track.getOrPut(self.allocator, dbg.debug); + const gop = try self._leak_track.getOrPut(self.allocator, debug); if (!gop.found_existing) { gop.value_ptr.* = 0; } @@ -116,12 +161,14 @@ pub fn acquire(self: *ArenaPool, dbg: DebugInfo) !Allocator { return entry.arena.allocator(); } +// Universal release - determines bucket from the Entry automatically pub fn release(self: *ArenaPool, allocator: Allocator) void { - const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); + const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); const entry: *Entry = @fieldParentPtr("arena", arena); + const bucket = entry.bucket; // Reset the arena before acquiring the lock to minimize lock hold time - _ = arena.reset(.{ .retain_with_limit = self.retain_bytes }); + _ = arena.reset(.{ .retain_with_limit = bucket.retain_bytes }); self.mutex.lock(); defer self.mutex.unlock(); @@ -139,105 +186,113 @@ pub fn release(self: *ArenaPool, allocator: Allocator) void { } } - const free_list_len = self.free_list_len; - if (free_list_len == self.free_list_max) { + if (bucket.free_list_len >= bucket.free_list_max) { arena.deinit(); self.entry_pool.destroy(entry); return; } - entry.next = self.free_list; - self.free_list_len = free_list_len + 1; - self.free_list = entry; + entry.next = bucket.free_list; + bucket.free_list = entry; + bucket.free_list_len += 1; } pub fn reset(_: *const ArenaPool, allocator: Allocator, retain: usize) void { - const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); + const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); _ = arena.reset(.{ .retain_with_limit = retain }); } pub fn resetRetain(_: *const ArenaPool, allocator: Allocator) void { - const arena: *std.heap.ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); + const arena: *ArenaAllocator = @ptrCast(@alignCast(allocator.ptr)); _ = arena.reset(.retain_capacity); } const testing = std.testing; - -test "arena pool - basic acquire and use" { - var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16); +test "ArenaPool: basic acquire and release" { + var pool = ArenaPool.init(testing.allocator, .{}); defer pool.deinit(); - const alloc = try pool.acquire(.{ .debug = "test" }); - const buf = try alloc.alloc(u8, 64); - @memset(buf, 0xAB); - try testing.expectEqual(@as(u8, 0xAB), buf[0]); + const tiny = try pool.acquire(.tiny, "test-tiny"); + const medium = try pool.acquire(.medium, "test-medium"); + const large = try pool.acquire(.large, "test-large"); - pool.release(alloc); + // All three must be distinct arenas + try testing.expect(tiny.ptr != medium.ptr); + try testing.expect(medium.ptr != large.ptr); + + _ = try tiny.alloc(u8, 64); + _ = try medium.alloc(u8, 1024); + _ = try large.alloc(u8, 4096); + + // Universal release works for all buckets + pool.release(tiny); + pool.release(medium); + pool.release(large); + + try testing.expectEqual(1, pool.tiny.free_list_len); + try testing.expectEqual(1, pool.medium.free_list_len); + try testing.expectEqual(1, pool.large.free_list_len); } -test "arena pool - reuse entry after release" { - var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16); +test "ArenaPool: reuse from correct bucket" { + var pool = ArenaPool.init(testing.allocator, .{}); defer pool.deinit(); - const alloc1 = try pool.acquire(.{ .debug = "test" }); - try testing.expectEqual(@as(u16, 0), pool.free_list_len); + const tiny1 = try pool.acquire(.tiny, "test"); + pool.release(tiny1); + try testing.expectEqual(1, pool.tiny.free_list_len); - pool.release(alloc1); - try testing.expectEqual(@as(u16, 1), pool.free_list_len); + // Next acquire with .tiny should reuse from tiny bucket + const tiny2 = try pool.acquire(.tiny, "test"); + try testing.expectEqual(0, pool.tiny.free_list_len); + try testing.expectEqual(tiny1.ptr, tiny2.ptr); - // The same entry should be returned from the free list. - const alloc2 = try pool.acquire(.{ .debug = "test" }); - try testing.expectEqual(@as(u16, 0), pool.free_list_len); - try testing.expectEqual(alloc1.ptr, alloc2.ptr); + // acquire with .medium should NOT get the tiny arena + const medium = try pool.acquire(.medium, "test-medium"); + try testing.expect(medium.ptr != tiny2.ptr); - pool.release(alloc2); + pool.release(tiny2); + pool.release(medium); } -test "arena pool - multiple concurrent arenas" { - var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16); +test "ArenaPool: respects per-bucket max limits" { + var pool = ArenaPool.init(testing.allocator, .{ + .tiny = .{ .max = 1, .retain = 1024 }, + .medium = .{ .max = 2, .retain = 1024 }, + .large = .{ .max = 1, .retain = 1024 }, + }); defer pool.deinit(); - const a1 = try pool.acquire(.{ .debug = "test1" }); - const a2 = try pool.acquire(.{ .debug = "test2" }); - const a3 = try pool.acquire(.{ .debug = "test3" }); + // Acquire 3 tiny arenas + const t1 = try pool.acquire(.tiny, "t1"); + const t2 = try pool.acquire(.tiny, "t2"); + const t3 = try pool.acquire(.tiny, "t3"); - // All three must be distinct arenas. - try testing.expect(a1.ptr != a2.ptr); - try testing.expect(a2.ptr != a3.ptr); - try testing.expect(a1.ptr != a3.ptr); + // Release all 3, but only 1 should be kept (tiny_max = 1) + pool.release(t1); + try testing.expectEqual(1, pool.tiny.free_list_len); + pool.release(t2); + try testing.expectEqual(1, pool.tiny.free_list_len); // still 1, t2 discarded + pool.release(t3); + try testing.expectEqual(1, pool.tiny.free_list_len); // still 1, t3 discarded - _ = try a1.alloc(u8, 16); - _ = try a2.alloc(u8, 32); - _ = try a3.alloc(u8, 48); + // Acquire 3 medium arenas + const m1 = try pool.acquire(.medium, "m1"); + const m2 = try pool.acquire(.medium, "m2"); + const m3 = try pool.acquire(.medium, "m3"); - pool.release(a1); - pool.release(a2); - pool.release(a3); - - try testing.expectEqual(@as(u16, 3), pool.free_list_len); + // Release all 3, but only 2 should be kept (medium_max = 2) + pool.release(m1); + pool.release(m2); + pool.release(m3); + try testing.expectEqual(2, pool.medium.free_list_len); } -test "arena pool - free list respects max limit" { - // Cap the free list at 1 so the second release discards its arena. - var pool = ArenaPool.init(testing.allocator, 1, 1024 * 16); +test "ArenaPool: reset clears memory without releasing" { + var pool = ArenaPool.init(testing.allocator, .{}); defer pool.deinit(); - const a1 = try pool.acquire(.{ .debug = "test1" }); - const a2 = try pool.acquire(.{ .debug = "test2" }); - - pool.release(a1); - try testing.expectEqual(@as(u16, 1), pool.free_list_len); - - // The free list is full; a2's arena should be destroyed, not queued. - pool.release(a2); - try testing.expectEqual(@as(u16, 1), pool.free_list_len); -} - -test "arena pool - reset clears memory without releasing" { - var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16); - defer pool.deinit(); - - const alloc = try pool.acquire(.{ .debug = "test" }); + const alloc = try pool.acquire(.medium, "test"); const buf = try alloc.alloc(u8, 128); @memset(buf, 0xFF); @@ -246,7 +301,7 @@ test "arena pool - reset clears memory without releasing" { pool.reset(alloc, 0); // The free list must stay empty; the allocator was not released. - try testing.expectEqual(@as(u16, 0), pool.free_list_len); + try testing.expectEqual(0, pool.medium.free_list_len); // Allocating again through the same arena must still work. const buf2 = try alloc.alloc(u8, 64); @@ -256,18 +311,60 @@ test "arena pool - reset clears memory without releasing" { pool.release(alloc); } -test "arena pool - deinit with entries in free list" { +test "ArenaPool: deinit with entries in free list" { // Verifies that deinit properly cleans up free-listed arenas (no leaks // detected by the test allocator). - var pool = ArenaPool.init(testing.allocator, 512, 1024 * 16); + var pool = ArenaPool.init(testing.allocator, .{}); - const a1 = try pool.acquire(.{ .debug = "test1" }); - const a2 = try pool.acquire(.{ .debug = "test2" }); + const a1 = try pool.acquire(.tiny, "test1"); + const a2 = try pool.acquire(.medium, "test2"); _ = try a1.alloc(u8, 256); _ = try a2.alloc(u8, 512); pool.release(a1); pool.release(a2); - try testing.expectEqual(@as(u16, 2), pool.free_list_len); + try testing.expectEqual(1, pool.tiny.free_list_len); + try testing.expectEqual(1, pool.medium.free_list_len); pool.deinit(); } + +test "ArenaPool: small bucket" { + var pool = ArenaPool.init(testing.allocator, .{ + .small = .{ .max = 2, .retain = 4 * 1024 }, + }); + defer pool.deinit(); + + const s1 = try pool.acquire(.small, "s1"); + const s2 = try pool.acquire(.small, "s2"); + const s3 = try pool.acquire(.small, "s3"); + + pool.release(s1); + pool.release(s2); + pool.release(s3); + + try testing.expectEqual(2, pool.small.free_list_len); +} + +test "ArenaPool: size-based acquire" { + var pool = ArenaPool.init(testing.allocator, .{}); + defer pool.deinit(); + + // <= 1KB -> tiny + const a = try pool.acquire(500, "fits-tiny"); + // <= 4KB -> small + const b = try pool.acquire(2000, "fits-small"); + // <= 16KB -> medium + const c = try pool.acquire(8000, "fits-medium"); + // > 16KB -> large + const d = try pool.acquire(20000, "fits-large"); + + pool.release(a); + pool.release(b); + pool.release(c); + pool.release(d); + + try testing.expectEqual(1, pool.tiny.free_list_len); + try testing.expectEqual(1, pool.small.free_list_len); + try testing.expectEqual(1, pool.medium.free_list_len); + try testing.expectEqual(1, pool.large.free_list_len); +} diff --git a/src/browser/HttpClient.zig b/src/browser/HttpClient.zig index b90029ac..a2da34d5 100644 --- a/src/browser/HttpClient.zig +++ b/src/browser/HttpClient.zig @@ -374,7 +374,8 @@ fn serveFromCache(req: Request, cached: *const CachedResponse) !void { fn processRequest(self: *Client, req: Request) !void { if (self.network.cache) |*cache| { if (req.method == .GET) { - const arena = try self.network.app.arena_pool.acquire(.{ .debug = "HttpClient.processRequest.cache" }); + // cache is only used to read the meta data + const arena = try self.network.app.arena_pool.acquire(.small, "HttpClient.cache"); defer self.network.app.arena_pool.release(arena); var iter = req.headers.iterator(); diff --git a/src/browser/Page.zig b/src/browser/Page.zig index f12b606b..8ddd29ee 100644 --- a/src/browser/Page.zig +++ b/src/browser/Page.zig @@ -248,7 +248,7 @@ pub fn init(self: *Page, frame_id: u32, session: *Session, parent: ?*Page) !void log.debug(.page, "page.init", .{}); } - const call_arena = try session.getArena(.{ .debug = "call_arena" }); + const call_arena = try session.getArena(.medium, "call_arena"); errdefer session.releaseArena(call_arena); const factory = &session.factory; @@ -429,8 +429,8 @@ pub fn headersForRequest(self: *Page, headers: *HttpClient.Headers) !void { } } -pub fn getArena(self: *Page, comptime opts: Session.GetArenaOpts) !Allocator { - return self._session.getArena(opts); +pub fn getArena(self: *Page, size_or_bucket: anytype, debug: []const u8) !Allocator { + return self._session.getArena(size_or_bucket, debug); } pub fn releaseArena(self: *Page, allocator: Allocator) void { @@ -510,7 +510,7 @@ pub fn navigate(self: *Page, request_url: [:0]const u8, opts: NavigateOpts) !voi log.warn(.js, "invalid blob", .{ .url = request_url }); return error.BlobNotFound; }; - const parse_arena = try self.getArena(.{ .debug = "Page.parseBlob" }); + const parse_arena = try self.getArena(.medium, "Page.parseBlob"); defer self.releaseArena(parse_arena); var parser = Parser.init(parse_arena, self.document.asNode(), self); parser.parse(blob._slice); @@ -619,7 +619,7 @@ pub fn scheduleNavigation(self: *Page, request_url: []const u8, opts: NavigateOp if (self.canScheduleNavigation(std.meta.activeTag(nt)) == false) { return; } - const arena = try self._session.getArena(.{ .debug = "scheduleNavigation" }); + const arena = try self._session.getArena(.small, "scheduleNavigation"); errdefer self._session.releaseArena(arena); return self.scheduleNavigationWithArena(arena, request_url, opts, nt); } @@ -1022,7 +1022,7 @@ fn pageDoneCallback(ctx: *anyopaque) !void { }); }; - const parse_arena = try self.getArena(.{ .debug = "Page.parse" }); + const parse_arena = try self.getArena(.medium, "Page.parse"); defer self.releaseArena(parse_arena); var parser = Parser.init(parse_arena, self.document.asNode(), self); @@ -3568,7 +3568,7 @@ pub fn submitForm(self: *Page, submitter_: ?*Element, form_: ?*Element.Html.Form // I don't think this is technically correct, but FormData handles it ok const form_data = try FormData.init(form, submitter_, self); - const arena = try self._session.getArena(.{ .debug = "submitForm" }); + const arena = try self._session.getArena(.medium, "submitForm"); errdefer self._session.releaseArena(arena); const encoding = form_element.getAttributeSafe(comptime .wrap("enctype")); diff --git a/src/browser/Runner.zig b/src/browser/Runner.zig index 4ee753ea..fd3889e6 100644 --- a/src/browser/Runner.zig +++ b/src/browser/Runner.zig @@ -249,7 +249,7 @@ fn _tick(self: *Runner, comptime is_cdp: bool, opts: TickOpts) !CDPTickResult { } pub fn waitForSelector(self: *Runner, selector: [:0]const u8, timeout_ms: u32) !*Node.Element { - const arena = try self.session.getArena(.{ .debug = "Runner.waitForSelector" }); + const arena = try self.session.getArena(.small, "Runner.waitForSelector"); defer self.session.releaseArena(arena); var timer = try std.time.Timer.start(); diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index 984ecccc..95b7f839 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -188,7 +188,7 @@ pub fn addFromElement(self: *ScriptManager, comptime from_parser: bool, script_e var handover = false; const page = self.page; - const arena = try page.getArena(.{ .debug = "addFromElement" }); + const arena = try page.getArena(.large, "SM.addFromElement"); errdefer if (!handover) { page.releaseArena(arena); }; @@ -369,7 +369,7 @@ pub fn preloadImport(self: *ScriptManager, url: [:0]const u8, referrer: []const errdefer _ = self.imported_modules.remove(url); const page = self.page; - const arena = try page.getArena(.{ .debug = "preloadImport" }); + const arena = try page.getArena(.large, "SM.preloadImport"); errdefer page.releaseArena(arena); const script = try arena.create(Script); @@ -469,7 +469,7 @@ pub fn waitForImport(self: *ScriptManager, url: [:0]const u8) !ModuleSource { pub fn getAsyncImport(self: *ScriptManager, url: [:0]const u8, cb: ImportAsync.Callback, cb_data: *anyopaque, referrer: []const u8) !void { const page = self.page; - const arena = try page.getArena(.{ .debug = "getAsyncImport" }); + const arena = try page.getArena(.large, "SM.getAsyncImport"); errdefer page.releaseArena(arena); const script = try arena.create(Script); diff --git a/src/browser/Session.zig b/src/browser/Session.zig index 8ec3e217..baea1590 100644 --- a/src/browser/Session.zig +++ b/src/browser/Session.zig @@ -110,10 +110,10 @@ pub fn init(self: *Session, browser: *Browser, notification: *Notification) !voi const allocator = browser.app.allocator; const arena_pool = browser.arena_pool; - const arena = try arena_pool.acquire(.{ .debug = "Session" }); + const arena = try arena_pool.acquire(.small, "Session"); errdefer arena_pool.release(arena); - const page_arena = try arena_pool.acquire(.{ .debug = "Session.page_arena" }); + const page_arena = try arena_pool.acquire(.large, "Session.page_arena"); errdefer arena_pool.release(page_arena); self.* = .{ @@ -186,12 +186,8 @@ pub fn removePage(self: *Session) void { } } -pub const GetArenaOpts = struct { - debug: []const u8, -}; - -pub fn getArena(self: *Session, opts: GetArenaOpts) !Allocator { - return self.arena_pool.acquire(.{ .debug = opts.debug }); +pub fn getArena(self: *Session, size_or_bucket: anytype, debug: []const u8) !Allocator { + return self.arena_pool.acquire(size_or_bucket, debug); } pub fn releaseArena(self: *Session, allocator: Allocator) void { diff --git a/src/browser/StyleManager.zig b/src/browser/StyleManager.zig index 161ebca0..404a11ed 100644 --- a/src/browser/StyleManager.zig +++ b/src/browser/StyleManager.zig @@ -66,7 +66,7 @@ dirty: bool = false, pub fn init(page: *Page) !StyleManager { return .{ .page = page, - .arena = try page.getArena(.{ .debug = "StyleManager" }), + .arena = try page.getArena(.medium, "StyleManager"), }; } diff --git a/src/browser/js/Env.zig b/src/browser/js/Env.zig index 2c1ebf38..03eadac3 100644 --- a/src/browser/js/Env.zig +++ b/src/browser/js/Env.zig @@ -261,7 +261,7 @@ pub const ContextParams = struct { }; pub fn createContext(self: *Env, page: *Page, params: ContextParams) !*Context { - const context_arena = try self.app.arena_pool.acquire(.{ .debug = params.debug_name }); + const context_arena = try self.app.arena_pool.acquire(.large, params.debug_name); errdefer self.app.arena_pool.release(context_arena); const isolate = self.isolate; diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index 4d91ed2e..170e5c0c 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -1479,7 +1479,7 @@ fn createFinalizerCallback( ) !*Session.FinalizerCallback { const session = self.ctx.session; - const arena = try session.getArena(.{ .debug = "FinalizerCallback" }); + const arena = try session.getArena(.tiny, "FinalizerCallback"); errdefer session.releaseArena(arena); const fc = try arena.create(Session.FinalizerCallback); diff --git a/src/browser/js/Origin.zig b/src/browser/js/Origin.zig index c6c6bf81..fce37ce0 100644 --- a/src/browser/js/Origin.zig +++ b/src/browser/js/Origin.zig @@ -45,7 +45,7 @@ key: []const u8, security_token: v8.Global, pub fn init(app: *App, isolate: js.Isolate, key: []const u8) !*Origin { - const arena = try app.arena_pool.acquire(.{ .debug = "Origin" }); + const arena = try app.arena_pool.acquire(.tiny, "Origin"); errdefer app.arena_pool.release(arena); var hs: js.HandleScope = undefined; diff --git a/src/browser/js/String.zig b/src/browser/js/String.zig index 2cbe6a17..8d29d838 100644 --- a/src/browser/js/String.zig +++ b/src/browser/js/String.zig @@ -44,11 +44,11 @@ fn _toSlice(self: String, comptime null_terminate: bool, allocator: Allocator) ! const handle = self.handle; const isolate = local.isolate.handle; - const len = v8.v8__String__Utf8Length(handle, isolate); - const buf = try (if (comptime null_terminate) allocator.allocSentinel(u8, @intCast(len), 0) else allocator.alloc(u8, @intCast(len))); + const l = v8.v8__String__Utf8Length(handle, isolate); + const buf = try (if (comptime null_terminate) allocator.allocSentinel(u8, @intCast(l), 0) else allocator.alloc(u8, @intCast(l))); const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8); if (comptime IS_DEBUG) { - std.debug.assert(n == len); + std.debug.assert(n == l); } return buf; @@ -64,32 +64,32 @@ pub fn toSSOWithAlloc(self: String, allocator: Allocator) !SSO { const handle = self.handle; const isolate = self.local.isolate.handle; - const len: usize = @intCast(v8.v8__String__Utf8Length(handle, isolate)); + const l: usize = @intCast(v8.v8__String__Utf8Length(handle, isolate)); - if (len <= 12) { + if (l <= 12) { var content: [12]u8 = undefined; const n = v8.v8__String__WriteUtf8(handle, isolate, &content[0], content.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8); if (comptime IS_DEBUG) { - std.debug.assert(n == len); + std.debug.assert(n == l); } // Weird that we do this _after_, but we have to..I've seen weird issues // in ReleaseMode where v8 won't write to content if it starts off zero // initiated - @memset(content[len..], 0); - return .{ .len = @intCast(len), .payload = .{ .content = content } }; + @memset(content[l..], 0); + return .{ .len = @intCast(l), .payload = .{ .content = content } }; } - const buf = try allocator.alloc(u8, len); + const buf = try allocator.alloc(u8, l); const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8); if (comptime IS_DEBUG) { - std.debug.assert(n == len); + std.debug.assert(n == l); } var prefix: [4]u8 = @splat(0); @memcpy(&prefix, buf[0..4]); return .{ - .len = @intCast(len), + .len = @intCast(l), .payload = .{ .heap = .{ .prefix = prefix, .ptr = buf.ptr, @@ -103,9 +103,13 @@ pub fn format(self: String, writer: *std.Io.Writer) !void { const isolate = local.isolate.handle; var small: [1024]u8 = undefined; - const len = v8.v8__String__Utf8Length(handle, isolate); - var buf = if (len < 1024) &small else local.call_arena.alloc(u8, @intCast(len)) catch return error.WriteFailed; + const l = v8.v8__String__Utf8Length(handle, isolate); + var buf = if (l < 1024) &small else local.call_arena.alloc(u8, @intCast(l)) catch return error.WriteFailed; const n = v8.v8__String__WriteUtf8(handle, isolate, buf.ptr, buf.len, v8.NO_NULL_TERMINATION | v8.REPLACE_INVALID_UTF8); return writer.writeAll(buf[0..n]); } + +pub fn len(self: String) usize { + return @intCast(v8.v8__String__Utf8Length(self.handle, self.local.isolate.handle)); +} diff --git a/src/browser/webapi/Blob.zig b/src/browser/webapi/Blob.zig index bf0c1118..0598d7fc 100644 --- a/src/browser/webapi/Blob.zig +++ b/src/browser/webapi/Blob.zig @@ -77,7 +77,15 @@ pub fn initWithMimeValidation( validate_mime: bool, page: *Page, ) !*Blob { - const arena = try page.getArena(.{ .debug = "Blob" }); + const data_len = blk: { + const parts = maybe_blob_parts orelse break :blk 0; + var size: usize = 0; + for (parts) |p| { + size += p.len; + } + break :blk size; + }; + const arena = try page.getArena(256 + data_len, "Blob"); errdefer page.releaseArena(arena); const options: InitOptions = maybe_options orelse .{}; diff --git a/src/browser/webapi/DOMParser.zig b/src/browser/webapi/DOMParser.zig index 10a94bca..7bd5b600 100644 --- a/src/browser/webapi/DOMParser.zig +++ b/src/browser/webapi/DOMParser.zig @@ -50,7 +50,7 @@ pub fn parseFromString( @"image/svg+xml", }, mime_type) orelse return error.NotSupported; - const arena = try page.getArena(.{ .debug = "DOMParser.parseFromString" }); + const arena = try page.getArena(.medium, "DOMParser.parseFromString"); defer page.releaseArena(arena); return switch (target_mime) { diff --git a/src/browser/webapi/Document.zig b/src/browser/webapi/Document.zig index cf15c49b..5b6c0a4a 100644 --- a/src/browser/webapi/Document.zig +++ b/src/browser/webapi/Document.zig @@ -666,7 +666,7 @@ pub fn write(self: *Document, text: []const []const u8, page: *Page) !void { page._parse_mode = .document_write; defer page._parse_mode = previous_parse_mode; - const arena = try page.getArena(.{ .debug = "Document.write" }); + const arena = try page.getArena(.medium, "Document.write"); defer page.releaseArena(arena); var parser = Parser.init(arena, fragment_node, page); diff --git a/src/browser/webapi/Event.zig b/src/browser/webapi/Event.zig index b573bfc7..50895866 100644 --- a/src/browser/webapi/Event.zig +++ b/src/browser/webapi/Event.zig @@ -90,14 +90,14 @@ pub const Options = struct { }; pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*Event { - const arena = try page.getArena(.{ .debug = "Event" }); + const arena = try page.getArena(.tiny, "Event"); errdefer page.releaseArena(arena); const str = try String.init(arena, typ, .{}); return initWithTrusted(arena, str, opts_, false); } pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*Event { - const arena = try page.getArena(.{ .debug = "Event.trusted" }); + const arena = try page.getArena(.tiny, "Event.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, opts_, true); } diff --git a/src/browser/webapi/File.zig b/src/browser/webapi/File.zig index fb27359a..e4c70662 100644 --- a/src/browser/webapi/File.zig +++ b/src/browser/webapi/File.zig @@ -31,7 +31,7 @@ _proto: *Blob, // TODO: Implement File API. pub fn init(page: *Page) !*File { - const arena = try page.getArena(.{ .debug = "File" }); + const arena = try page.getArena(.tiny, "File"); errdefer page.releaseArena(arena); return page._factory.blob(arena, File{ ._proto = undefined }); } diff --git a/src/browser/webapi/FileReader.zig b/src/browser/webapi/FileReader.zig index 109fdc7b..33f0e209 100644 --- a/src/browser/webapi/FileReader.zig +++ b/src/browser/webapi/FileReader.zig @@ -63,7 +63,7 @@ const Result = union(enum) { }; pub fn init(page: *Page) !*FileReader { - const arena = try page.getArena(.{ .debug = "FileReader" }); + const arena = try page.getArena(.tiny, "FileReader"); errdefer page.releaseArena(arena); return page._factory.eventTargetWithAllocator(arena, FileReader{ diff --git a/src/browser/webapi/IntersectionObserver.zig b/src/browser/webapi/IntersectionObserver.zig index cbc9278f..990c45ee 100644 --- a/src/browser/webapi/IntersectionObserver.zig +++ b/src/browser/webapi/IntersectionObserver.zig @@ -71,7 +71,7 @@ pub const ObserverInit = struct { }; pub fn init(callback: js.Function.Temp, options: ?ObserverInit, page: *Page) !*IntersectionObserver { - const arena = try page.getArena(.{ .debug = "IntersectionObserver" }); + const arena = try page.getArena(.medium, "IntersectionObserver"); errdefer page.releaseArena(arena); const opts = options orelse ObserverInit{}; @@ -266,7 +266,7 @@ fn checkIntersection(self: *IntersectionObserver, target: *Element, page: *Page) (was_intersecting_opt != null and was_intersecting_opt.? != is_now_intersecting); if (should_report) { - const arena = try page.getArena(.{ .debug = "IntersectionObserverEntry" }); + const arena = try page.getArena(.tiny, "IntersectionObserverEntry"); errdefer page.releaseArena(arena); const entry = try arena.create(IntersectionObserverEntry); diff --git a/src/browser/webapi/MutationObserver.zig b/src/browser/webapi/MutationObserver.zig index 5453e797..6a99fcb5 100644 --- a/src/browser/webapi/MutationObserver.zig +++ b/src/browser/webapi/MutationObserver.zig @@ -76,7 +76,7 @@ pub const ObserveOptions = struct { }; pub fn init(callback: js.Function.Temp, page: *Page) !*MutationObserver { - const arena = try page.getArena(.{ .debug = "MutationObserver" }); + const arena = try page.getArena(.medium, "MutationObserver"); errdefer page.releaseArena(arena); const self = try arena.create(MutationObserver); @@ -227,7 +227,7 @@ pub fn notifyAttributeChange( } } - const arena = try page.getArena(.{ .debug = "MutationRecord" }); + const arena = try page.getArena(.tiny, "MutationRecord"); const record = try arena.create(MutationRecord); record.* = .{ ._arena = arena, @@ -271,7 +271,7 @@ pub fn notifyCharacterDataChange( continue; } - const arena = try page.getArena(.{ .debug = "MutationRecord" }); + const arena = try page.getArena(.tiny, "MutationRecord"); const record = try arena.create(MutationRecord); record.* = .{ ._arena = arena, @@ -318,7 +318,7 @@ pub fn notifyChildListChange( continue; } - const arena = try page.getArena(.{ .debug = "MutationRecord" }); + const arena = try page.getArena(.tiny, "MutationRecord"); const record = try arena.create(MutationRecord); record.* = .{ ._arena = arena, diff --git a/src/browser/webapi/Permissions.zig b/src/browser/webapi/Permissions.zig index 8a06b4f4..84ff810f 100644 --- a/src/browser/webapi/Permissions.zig +++ b/src/browser/webapi/Permissions.zig @@ -38,7 +38,7 @@ const QueryDescriptor = struct { }; // We always report 'prompt' (the default safe value — neither granted nor denied). pub fn query(_: *const Permissions, qd: QueryDescriptor, page: *Page) !js.Promise { - const arena = try page.getArena(.{ .debug = "PermissionStatus" }); + const arena = try page.getArena(.tiny, "PermissionStatus"); errdefer page.releaseArena(arena); const status = try arena.create(PermissionStatus); diff --git a/src/browser/webapi/Range.zig b/src/browser/webapi/Range.zig index 720fc5ff..dab3db89 100644 --- a/src/browser/webapi/Range.zig +++ b/src/browser/webapi/Range.zig @@ -33,7 +33,7 @@ const Range = @This(); _proto: *AbstractRange, pub fn init(page: *Page) !*Range { - const arena = try page.getArena(.{ .debug = "Range" }); + const arena = try page.getArena(.medium, "Range"); errdefer page.releaseArena(arena); return page._factory.abstractRange(arena, Range{ ._proto = undefined }, page); } @@ -312,7 +312,7 @@ pub fn intersectsNode(self: *const Range, node: *Node) bool { } pub fn cloneRange(self: *const Range, page: *Page) !*Range { - const arena = try page.getArena(.{ .debug = "Range.clone" }); + const arena = try page.getArena(.medium, "Range.clone"); errdefer page.releaseArena(arena); const clone = try page._factory.abstractRange(arena, Range{ ._proto = undefined }, page); diff --git a/src/browser/webapi/Window.zig b/src/browser/webapi/Window.zig index 418037fd..ef076663 100644 --- a/src/browser/webapi/Window.zig +++ b/src/browser/webapi/Window.zig @@ -407,7 +407,7 @@ pub fn postMessage(self: *Window, message: js.Value.Temp, target_origin: ?[]cons const target_page = self._page; const source_window = target_page.js.getIncumbent().window; - const arena = try target_page.getArena(.{ .debug = "Window.postMessage" }); + const arena = try target_page.getArena(.medium, "Window.postMessage"); errdefer target_page.releaseArena(arena); // Origin should be the source window's origin (where the message came from) @@ -645,7 +645,7 @@ fn scheduleCallback(self: *Window, cb: js.Function.Temp, delay_ms: u32, opts: Sc return error.TooManyTimeout; } - const arena = try page.getArena(.{ .debug = "Window.schedule" }); + const arena = try page.getArena(.tiny, "Window.schedule"); errdefer page.releaseArena(arena); const timer_id = self._timer_id +% 1; diff --git a/src/browser/webapi/animation/Animation.zig b/src/browser/webapi/animation/Animation.zig index 08eb21c2..4bddfd1d 100644 --- a/src/browser/webapi/animation/Animation.zig +++ b/src/browser/webapi/animation/Animation.zig @@ -52,7 +52,7 @@ _playState: PlayState = .idle, // // TODO add support for effect and timeline pub fn init(page: *Page) !*Animation { - const arena = try page.getArena(.{ .debug = "Animation" }); + const arena = try page.getArena(.tiny, "Animation"); errdefer page.releaseArena(arena); const self = try arena.create(Animation); diff --git a/src/browser/webapi/collections/ChildNodes.zig b/src/browser/webapi/collections/ChildNodes.zig index df3e7ee1..410c12b7 100644 --- a/src/browser/webapi/collections/ChildNodes.zig +++ b/src/browser/webapi/collections/ChildNodes.zig @@ -39,7 +39,7 @@ pub const ValueIterator = GenericIterator(Iterator, "1"); pub const EntryIterator = GenericIterator(Iterator, null); pub fn init(node: *Node, page: *Page) !*ChildNodes { - const arena = try page.getArena(.{ .debug = "ChildNodes" }); + const arena = try page.getArena(.small, "ChildNodes"); errdefer page.releaseArena(arena); const self = try arena.create(ChildNodes); diff --git a/src/browser/webapi/css/FontFace.zig b/src/browser/webapi/css/FontFace.zig index 075d9135..9ccb0c4c 100644 --- a/src/browser/webapi/css/FontFace.zig +++ b/src/browser/webapi/css/FontFace.zig @@ -33,7 +33,7 @@ _family: []const u8, pub fn init(family: []const u8, source: []const u8, page: *Page) !*FontFace { _ = source; - const arena = try page.getArena(.{ .debug = "FontFace" }); + const arena = try page.getArena(.tiny, "FontFace"); errdefer page.releaseArena(arena); const self = try arena.create(FontFace); diff --git a/src/browser/webapi/css/FontFaceSet.zig b/src/browser/webapi/css/FontFaceSet.zig index b20017ca..f43dc405 100644 --- a/src/browser/webapi/css/FontFaceSet.zig +++ b/src/browser/webapi/css/FontFaceSet.zig @@ -34,7 +34,7 @@ _proto: *EventTarget, _arena: Allocator, pub fn init(page: *Page) !*FontFaceSet { - const arena = try page.getArena(.{ .debug = "FontFaceSet" }); + const arena = try page.getArena(.tiny, "FontFaceSet"); errdefer page.releaseArena(arena); return page._factory.eventTargetWithAllocator(arena, FontFaceSet{ diff --git a/src/browser/webapi/element/Html.zig b/src/browser/webapi/element/Html.zig index d72b4fa8..7a33d25a 100644 --- a/src/browser/webapi/element/Html.zig +++ b/src/browser/webapi/element/Html.zig @@ -292,7 +292,7 @@ pub fn insertAdjacentHTML( }); const doc_node = doc.asNode(); - const arena = try page.getArena(.{ .debug = "HTML.insertAdjacentHTML" }); + const arena = try page.getArena(.medium, "HTML.insertAdjacentHTML"); defer page.releaseArena(arena); const Parser = @import("../../parser/Parser.zig"); diff --git a/src/browser/webapi/encoding/TextDecoder.zig b/src/browser/webapi/encoding/TextDecoder.zig index c117df09..7da889e4 100644 --- a/src/browser/webapi/encoding/TextDecoder.zig +++ b/src/browser/webapi/encoding/TextDecoder.zig @@ -48,7 +48,7 @@ pub fn init(label_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*TextDecoder { _ = std.meta.stringToEnum(Label, label) orelse return error.RangeError; } - const arena = try page.getArena(.{ .debug = "TextDecoder" }); + const arena = try page.getArena(.large, "TextDecoder"); errdefer page.releaseArena(arena); const opts = opts_ orelse InitOpts{}; diff --git a/src/browser/webapi/event/CloseEvent.zig b/src/browser/webapi/event/CloseEvent.zig index aa9f1d2b..dbe5f21a 100644 --- a/src/browser/webapi/event/CloseEvent.zig +++ b/src/browser/webapi/event/CloseEvent.zig @@ -39,14 +39,14 @@ const CloseEventOptions = struct { const Options = Event.inheritOptions(CloseEvent, CloseEventOptions); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*CloseEvent { - const arena = try page.getArena(.{ .debug = "CloseEvent" }); + const arena = try page.getArena(.tiny, "CloseEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*CloseEvent { - const arena = try page.getArena(.{ .debug = "CloseEvent.trusted" }); + const arena = try page.getArena(.tiny, "CloseEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } diff --git a/src/browser/webapi/event/CompositionEvent.zig b/src/browser/webapi/event/CompositionEvent.zig index 7f3fd1d2..04077994 100644 --- a/src/browser/webapi/event/CompositionEvent.zig +++ b/src/browser/webapi/event/CompositionEvent.zig @@ -35,7 +35,7 @@ const CompositionEventOptions = struct { const Options = Event.inheritOptions(CompositionEvent, CompositionEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CompositionEvent { - const arena = try page.getArena(.{ .debug = "CompositionEvent" }); + const arena = try page.getArena(.tiny, "CompositionEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); diff --git a/src/browser/webapi/event/CustomEvent.zig b/src/browser/webapi/event/CustomEvent.zig index 9013bb4a..51efa36c 100644 --- a/src/browser/webapi/event/CustomEvent.zig +++ b/src/browser/webapi/event/CustomEvent.zig @@ -38,7 +38,7 @@ const CustomEventOptions = struct { const Options = Event.inheritOptions(CustomEvent, CustomEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*CustomEvent { - const arena = try page.getArena(.{ .debug = "CustomEvent" }); + const arena = try page.getArena(.tiny, "CustomEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); diff --git a/src/browser/webapi/event/ErrorEvent.zig b/src/browser/webapi/event/ErrorEvent.zig index aef63a0e..4bb68573 100644 --- a/src/browser/webapi/event/ErrorEvent.zig +++ b/src/browser/webapi/event/ErrorEvent.zig @@ -47,14 +47,14 @@ pub const ErrorEventOptions = struct { const Options = Event.inheritOptions(ErrorEvent, ErrorEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*ErrorEvent { - const arena = try page.getArena(.{ .debug = "ErrorEvent" }); + const arena = try page.getArena(.small, "ErrorEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, opts_, false, page); } pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*ErrorEvent { - const arena = try page.getArena(.{ .debug = "ErrorEvent.trusted" }); + const arena = try page.getArena(.small, "ErrorEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, opts_, true, page); } diff --git a/src/browser/webapi/event/FocusEvent.zig b/src/browser/webapi/event/FocusEvent.zig index 776605db..59e88e36 100644 --- a/src/browser/webapi/event/FocusEvent.zig +++ b/src/browser/webapi/event/FocusEvent.zig @@ -42,13 +42,13 @@ pub const Options = Event.inheritOptions( ); pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*FocusEvent { - const arena = try page.getArena(.{ .debug = "FocusEvent.trusted" }); + const arena = try page.getArena(.tiny, "FocusEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*FocusEvent { - const arena = try page.getArena(.{ .debug = "FocusEvent" }); + const arena = try page.getArena(.tiny, "FocusEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); diff --git a/src/browser/webapi/event/FormDataEvent.zig b/src/browser/webapi/event/FormDataEvent.zig index 93eadfa3..ce45a9d0 100644 --- a/src/browser/webapi/event/FormDataEvent.zig +++ b/src/browser/webapi/event/FormDataEvent.zig @@ -38,14 +38,14 @@ const Options = Event.inheritOptions(FormDataEvent, struct { }); pub fn init(typ: []const u8, maybe_options: Options, page: *Page) !*FormDataEvent { - const arena = try page.getArena(.{ .debug = "FormDataEvent" }); + const arena = try page.getArena(.tiny, "FormDataEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, maybe_options, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*FormDataEvent { - const arena = try page.getArena(.{ .debug = "FormDataEvent.trusted" }); + const arena = try page.getArena(.tiny, "FormDataEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } diff --git a/src/browser/webapi/event/InputEvent.zig b/src/browser/webapi/event/InputEvent.zig index 3b01b900..3c00debd 100644 --- a/src/browser/webapi/event/InputEvent.zig +++ b/src/browser/webapi/event/InputEvent.zig @@ -46,13 +46,13 @@ const Options = Event.inheritOptions( ); pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*InputEvent { - const arena = try page.getArena(.{ .debug = "InputEvent.trusted" }); + const arena = try page.getArena(.tiny, "InputEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*InputEvent { - const arena = try page.getArena(.{ .debug = "InputEvent" }); + const arena = try page.getArena(.tiny, "InputEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); diff --git a/src/browser/webapi/event/KeyboardEvent.zig b/src/browser/webapi/event/KeyboardEvent.zig index ddc7548d..f8056cc3 100644 --- a/src/browser/webapi/event/KeyboardEvent.zig +++ b/src/browser/webapi/event/KeyboardEvent.zig @@ -186,13 +186,13 @@ const Options = Event.inheritOptions( ); pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*KeyboardEvent { - const arena = try page.getArena(.{ .debug = "KeyboardEvent.trusted" }); + const arena = try page.getArena(.tiny, "KeyboardEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*KeyboardEvent { - const arena = try page.getArena(.{ .debug = "KeyboardEvent" }); + const arena = try page.getArena(.tiny, "KeyboardEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); diff --git a/src/browser/webapi/event/MessageEvent.zig b/src/browser/webapi/event/MessageEvent.zig index dfd813d5..27fdfb23 100644 --- a/src/browser/webapi/event/MessageEvent.zig +++ b/src/browser/webapi/event/MessageEvent.zig @@ -50,14 +50,14 @@ pub const Data = union(enum) { const Options = Event.inheritOptions(MessageEvent, MessageEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*MessageEvent { - const arena = try page.getArena(.{ .debug = "MessageEvent" }); + const arena = try page.getArena(.small, "MessageEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, opts_, false, page); } pub fn initTrusted(typ: String, opts_: ?Options, page: *Page) !*MessageEvent { - const arena = try page.getArena(.{ .debug = "MessageEvent.trusted" }); + const arena = try page.getArena(.small, "MessageEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, opts_, true, page); } diff --git a/src/browser/webapi/event/MouseEvent.zig b/src/browser/webapi/event/MouseEvent.zig index 999bd010..ff2b1118 100644 --- a/src/browser/webapi/event/MouseEvent.zig +++ b/src/browser/webapi/event/MouseEvent.zig @@ -82,14 +82,14 @@ pub const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*MouseEvent { - const arena = try page.getArena(.{ .debug = "MouseEvent" }); + const arena = try page.getArena(.tiny, "MouseEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*MouseEvent { - const arena = try page.getArena(.{ .debug = "MouseEvent.trusted" }); + const arena = try page.getArena(.tiny, "MouseEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } diff --git a/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig b/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig index 816fa1c8..d791cb39 100644 --- a/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig +++ b/src/browser/webapi/event/NavigationCurrentEntryChangeEvent.zig @@ -45,14 +45,14 @@ const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent { - const arena = try page.getArena(.{ .debug = "NavigationCurrentEntryChangeEvent" }); + const arena = try page.getArena(.tiny, "NavigationCurrentEntryChangeEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, opts, false, page); } pub fn initTrusted(typ: String, opts: Options, page: *Page) !*NavigationCurrentEntryChangeEvent { - const arena = try page.getArena(.{ .debug = "NavigationCurrentEntryChangeEvent.trusted" }); + const arena = try page.getArena(.tiny, "NavigationCurrentEntryChangeEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, opts, true, page); } diff --git a/src/browser/webapi/event/PageTransitionEvent.zig b/src/browser/webapi/event/PageTransitionEvent.zig index e11be386..335a665a 100644 --- a/src/browser/webapi/event/PageTransitionEvent.zig +++ b/src/browser/webapi/event/PageTransitionEvent.zig @@ -38,14 +38,14 @@ const PageTransitionEventOptions = struct { const Options = Event.inheritOptions(PageTransitionEvent, PageTransitionEventOptions); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PageTransitionEvent { - const arena = try page.getArena(.{ .debug = "PageTransitionEvent" }); + const arena = try page.getArena(.tiny, "PageTransitionEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*PageTransitionEvent { - const arena = try page.getArena(.{ .debug = "PageTransitionEvent.trusted" }); + const arena = try page.getArena(.tiny, "PageTransitionEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } diff --git a/src/browser/webapi/event/PointerEvent.zig b/src/browser/webapi/event/PointerEvent.zig index c5440d45..4eb1e250 100644 --- a/src/browser/webapi/event/PointerEvent.zig +++ b/src/browser/webapi/event/PointerEvent.zig @@ -84,7 +84,7 @@ const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PointerEvent { - const arena = try page.getArena(.{ .debug = "UIEvent" }); + const arena = try page.getArena(.tiny, "PointerEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); diff --git a/src/browser/webapi/event/PopStateEvent.zig b/src/browser/webapi/event/PopStateEvent.zig index cd430cf8..3b0fe4e4 100644 --- a/src/browser/webapi/event/PopStateEvent.zig +++ b/src/browser/webapi/event/PopStateEvent.zig @@ -39,14 +39,14 @@ const PopStateEventOptions = struct { const Options = Event.inheritOptions(PopStateEvent, PopStateEventOptions); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*PopStateEvent { - const arena = try page.getArena(.{ .debug = "PopStateEvent" }); + const arena = try page.getArena(.tiny, "PopStateEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*PopStateEvent { - const arena = try page.getArena(.{ .debug = "PopStateEvent.trusted" }); + const arena = try page.getArena(.tiny, "PopStateEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } diff --git a/src/browser/webapi/event/ProgressEvent.zig b/src/browser/webapi/event/ProgressEvent.zig index 6498da48..895bff09 100644 --- a/src/browser/webapi/event/ProgressEvent.zig +++ b/src/browser/webapi/event/ProgressEvent.zig @@ -39,14 +39,14 @@ const ProgressEventOptions = struct { const Options = Event.inheritOptions(ProgressEvent, ProgressEventOptions); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*ProgressEvent { - const arena = try page.getArena(.{ .debug = "ProgressEvent" }); + const arena = try page.getArena(.tiny, "ProgressEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, _opts, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*ProgressEvent { - const arena = try page.getArena(.{ .debug = "ProgressEvent.trusted" }); + const arena = try page.getArena(.tiny, "ProgressEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } diff --git a/src/browser/webapi/event/PromiseRejectionEvent.zig b/src/browser/webapi/event/PromiseRejectionEvent.zig index cc014b39..44af3904 100644 --- a/src/browser/webapi/event/PromiseRejectionEvent.zig +++ b/src/browser/webapi/event/PromiseRejectionEvent.zig @@ -37,7 +37,7 @@ const PromiseRejectionEventOptions = struct { const Options = Event.inheritOptions(PromiseRejectionEvent, PromiseRejectionEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*PromiseRejectionEvent { - const arena = try page.getArena(.{ .debug = "PromiseRejectionEvent" }); + const arena = try page.getArena(.tiny, "PromiseRejectionEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); diff --git a/src/browser/webapi/event/SubmitEvent.zig b/src/browser/webapi/event/SubmitEvent.zig index f48365dc..3400cbcd 100644 --- a/src/browser/webapi/event/SubmitEvent.zig +++ b/src/browser/webapi/event/SubmitEvent.zig @@ -39,14 +39,14 @@ const SubmitEventOptions = struct { const Options = Event.inheritOptions(SubmitEvent, SubmitEventOptions); pub fn init(typ: []const u8, opts_: ?Options, page: *Page) !*SubmitEvent { - const arena = try page.getArena(.{ .debug = "SubmitEvent" }); + const arena = try page.getArena(.tiny, "SubmitEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); return initWithTrusted(arena, type_string, opts_, false, page); } pub fn initTrusted(typ: String, _opts: ?Options, page: *Page) !*SubmitEvent { - const arena = try page.getArena(.{ .debug = "SubmitEvent.trusted" }); + const arena = try page.getArena(.tiny, "SubmitEvent.trusted"); errdefer page.releaseArena(arena); return initWithTrusted(arena, typ, _opts, true, page); } diff --git a/src/browser/webapi/event/TextEvent.zig b/src/browser/webapi/event/TextEvent.zig index 3ddb2636..dcc5e478 100644 --- a/src/browser/webapi/event/TextEvent.zig +++ b/src/browser/webapi/event/TextEvent.zig @@ -40,7 +40,7 @@ pub const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*TextEvent { - const arena = try page.getArena(.{ .debug = "TextEvent" }); + const arena = try page.getArena(.tiny, "TextEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); diff --git a/src/browser/webapi/event/UIEvent.zig b/src/browser/webapi/event/UIEvent.zig index 6874d6d5..2b456738 100644 --- a/src/browser/webapi/event/UIEvent.zig +++ b/src/browser/webapi/event/UIEvent.zig @@ -51,7 +51,7 @@ pub const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*UIEvent { - const arena = try page.getArena(.{ .debug = "UIEvent" }); + const arena = try page.getArena(.tiny, "UIEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); diff --git a/src/browser/webapi/event/WheelEvent.zig b/src/browser/webapi/event/WheelEvent.zig index 4711ac25..8f79ab8a 100644 --- a/src/browser/webapi/event/WheelEvent.zig +++ b/src/browser/webapi/event/WheelEvent.zig @@ -50,7 +50,7 @@ pub const Options = Event.inheritOptions( ); pub fn init(typ: []const u8, _opts: ?Options, page: *Page) !*WheelEvent { - const arena = try page.getArena(.{ .debug = "WheelEvent" }); + const arena = try page.getArena(.medium, "WheelEvent"); errdefer page.releaseArena(arena); const type_string = try String.init(arena, typ, .{}); diff --git a/src/browser/webapi/net/Response.zig b/src/browser/webapi/net/Response.zig index e4fbd46d..7ed7ba4d 100644 --- a/src/browser/webapi/net/Response.zig +++ b/src/browser/webapi/net/Response.zig @@ -57,7 +57,7 @@ const InitOpts = struct { }; pub fn init(body_: ?[]const u8, opts_: ?InitOpts, page: *Page) !*Response { - const arena = try page.getArena(.{ .debug = "Response" }); + const arena = try page.getArena(.large, "Response"); errdefer page.releaseArena(arena); const opts = opts_ orelse InitOpts{}; @@ -174,7 +174,7 @@ pub fn bytes(self: *const Response, page: *Page) !js.Promise { } pub fn clone(self: *const Response, page: *Page) !*Response { - const arena = try page.getArena(.{ .debug = "Response.clone" }); + const arena = try page.getArena((self._body orelse "").len + self._url.len + 256, "Response.clone"); errdefer page.releaseArena(arena); const body = if (self._body) |b| try arena.dupe(u8, b) else null; diff --git a/src/browser/webapi/net/WebSocket.zig b/src/browser/webapi/net/WebSocket.zig index 1244a61e..20a622c6 100644 --- a/src/browser/webapi/net/WebSocket.zig +++ b/src/browser/webapi/net/WebSocket.zig @@ -105,7 +105,7 @@ pub fn init(url: []const u8, protocols_: ?[]const u8, page: *Page) !*WebSocket { } } - const arena = try page.getArena(.{ .debug = "WebSocket" }); + const arena = try page.getArena(.medium, "WebSocket"); errdefer page.releaseArena(arena); const resolved_url = try URL.resolve(arena, page.base(), url, .{ .always_dupe = true, .encode = true }); @@ -272,12 +272,10 @@ pub fn send(self: *WebSocket, data: SendData) !void { return error.InvalidStateError; } - // Get a dedicated arena for this message - const arena = try self._page._session.getArena(.{ .debug = "WebSocket message" }); - errdefer self._page._session.releaseArena(arena); - switch (data) { .blob => |blob| { + const arena = try self._page._session.getArena(blob._slice.len, "WebSocket.message"); + errdefer self._page._session.releaseArena(arena); try self.queueMessage(.{ .binary = .{ .arena = arena, .data = try arena.dupe(u8, blob._slice), @@ -285,15 +283,21 @@ pub fn send(self: *WebSocket, data: SendData) !void { }, .js_val => |js_val| { if (js_val.isString()) |str| { + const arena = try self._page._session.getArena(str.len(), "WebSocket.message"); + errdefer self._page._session.releaseArena(arena); try self.queueMessage(.{ .text = .{ .arena = arena, .data = try str.toSliceWithAlloc(arena), } }); } else { const binary = try js_val.toZig(BinaryData); + const buffer = binary.asBuffer(); + + const arena = try self._page._session.getArena(buffer.len, "WebSocket.message"); + errdefer self._page._session.releaseArena(arena); try self.queueMessage(.{ .binary = .{ .arena = arena, - .data = try arena.dupe(u8, binary.asBuffer()), + .data = try arena.dupe(u8, buffer), } }); } }, diff --git a/src/browser/webapi/net/XMLHttpRequest.zig b/src/browser/webapi/net/XMLHttpRequest.zig index 62e05a17..1024e1e7 100644 --- a/src/browser/webapi/net/XMLHttpRequest.zig +++ b/src/browser/webapi/net/XMLHttpRequest.zig @@ -89,7 +89,7 @@ const ResponseType = enum { }; pub fn init(page: *Page) !*XMLHttpRequest { - const arena = try page.getArena(.{ .debug = "XMLHttpRequest" }); + const arena = try page.getArena(.large, "XMLHttpRequest"); errdefer page.releaseArena(arena); const self = try page._factory.xhrEventTarget(arena, XMLHttpRequest{ ._page = page, diff --git a/src/browser/webapi/selector/Selector.zig b/src/browser/webapi/selector/Selector.zig index a3d5d894..838cecfd 100644 --- a/src/browser/webapi/selector/Selector.zig +++ b/src/browser/webapi/selector/Selector.zig @@ -45,7 +45,7 @@ pub fn querySelectorAll(root: *Node, input: []const u8, page: *Page) !*List { return error.SyntaxError; } - const arena = try page.getArena(.{ .debug = "querySelectorAll" }); + const arena = try page.getArena(.small, "querySelectorAll"); errdefer page.releaseArena(arena); var nodes: std.AutoArrayHashMapUnmanaged(*Node, void) = .empty; diff --git a/src/cdp/CDP.zig b/src/cdp/CDP.zig index 024954db..222098c8 100644 --- a/src/cdp/CDP.zig +++ b/src/cdp/CDP.zig @@ -487,10 +487,10 @@ pub const BrowserContext = struct { pub fn createIsolatedWorld(self: *BrowserContext, world_name: []const u8, grant_universal_access: bool) !*IsolatedWorld { const browser = &self.cdp.browser; - const arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld" }); + const arena = try browser.arena_pool.acquire(.small, "IsolatedWorld"); errdefer browser.arena_pool.release(arena); - const call_arena = try browser.arena_pool.acquire(.{ .debug = "IsolatedWorld.call_arena" }); + const call_arena = try browser.arena_pool.acquire(.tiny, "IsolatedWorld.call_arena"); errdefer browser.arena_pool.release(call_arena); const world = try arena.create(IsolatedWorld);