From 1443f38e5f05e68ecc931bd7498beb03e45a172a Mon Sep 17 00:00:00 2001 From: Karl Seguin Date: Thu, 28 Aug 2025 19:42:26 +0800 Subject: [PATCH] Zig 0.15.1 Depends on https://github.com/lightpanda-io/zig-v8-fork/pull/89 --- .github/actions/install/action.yml | 2 +- .github/workflows/zig-fmt.yml | 2 +- Dockerfile | 2 +- README.md | 2 +- build.zig | 2 +- build.zig.zon | 6 +- src/browser/Scheduler.zig | 10 +- src/browser/ScriptManager.zig | 58 +++++----- src/browser/dom/character_data.zig | 2 +- src/browser/dom/document.zig | 10 +- src/browser/dom/document_fragment.zig | 2 +- src/browser/dom/element.zig | 22 ++-- src/browser/dom/mutation_observer.zig | 2 +- src/browser/dom/node.zig | 2 +- src/browser/dom/shadow_root.zig | 6 +- src/browser/dump.zig | 18 ++-- src/browser/html/AbortController.zig | 2 +- src/browser/html/document.zig | 6 +- src/browser/html/elements.zig | 20 ++-- src/browser/html/select.zig | 16 +-- src/browser/html/window.zig | 2 +- src/browser/mimalloc.zig | 12 +-- src/browser/netsurf.zig | 52 ++++----- src/browser/page.zig | 32 +++--- src/browser/storage/cookie.zig | 19 ++-- src/browser/url/url.zig | 39 ++++--- src/browser/xhr/form_data.zig | 6 +- src/browser/xhr/xhr.zig | 14 +-- src/browser/xmlserializer/xmlserializer.zig | 10 +- src/cdp/Node.zig | 12 +-- src/cdp/cdp.zig | 26 ++--- src/cdp/domains/dom.zig | 2 +- src/cdp/domains/fetch.zig | 4 +- src/cdp/domains/network.zig | 114 +++++++++++--------- src/cdp/domains/storage.zig | 2 +- src/cdp/domains/target.zig | 12 +-- src/cdp/testing.zig | 8 +- src/datetime.zig | 66 ++++++------ src/http/Client.zig | 64 ++++++----- src/http/Http.zig | 6 +- src/id.zig | 2 +- src/log.zig | 49 +++++---- src/main.zig | 12 ++- src/main_wpt.zig | 44 ++++---- src/notification.zig | 65 ++++++----- src/runtime/js.zig | 41 +++---- src/server.zig | 16 ++- src/telemetry/telemetry.zig | 2 +- src/test_runner.zig | 70 ++++++------ src/testing.zig | 6 +- src/url.zig | 32 +----- 51 files changed, 508 insertions(+), 525 deletions(-) diff --git a/.github/actions/install/action.yml b/.github/actions/install/action.yml index 7e9bba86..7b0e5a1c 100644 --- a/.github/actions/install/action.yml +++ b/.github/actions/install/action.yml @@ -5,7 +5,7 @@ inputs: zig: description: 'Zig version to install' required: false - default: '0.14.1' + default: '0.15.1' arch: description: 'CPU arch used to select the v8 lib' required: false diff --git a/.github/workflows/zig-fmt.yml b/.github/workflows/zig-fmt.yml index 696e0c40..2a1fdd52 100644 --- a/.github/workflows/zig-fmt.yml +++ b/.github/workflows/zig-fmt.yml @@ -1,7 +1,7 @@ name: zig-fmt env: - ZIG_VERSION: 0.14.1 + ZIG_VERSION: 0.15.1 on: pull_request: diff --git a/Dockerfile b/Dockerfile index 00ca1104..d4d9dd5a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM debian:stable ARG MINISIG=0.12 -ARG ZIG=0.14.1 +ARG ZIG=0.15.1 ARG ZIG_MINISIG=RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U ARG V8=13.6.233.8 ARG ZIG_V8=v0.1.28 diff --git a/README.md b/README.md index 492ebb80..ff5a248b 100644 --- a/README.md +++ b/README.md @@ -165,7 +165,7 @@ You can also follow the progress of our Javascript support in our dedicated [zig ### Prerequisites -Lightpanda is written with [Zig](https://ziglang.org/) `0.14.1`. You have to +Lightpanda is written with [Zig](https://ziglang.org/) `0.15.1`. You have to install it with the right version in order to build the project. Lightpanda also depends on diff --git a/build.zig b/build.zig index f7972134..8b497afe 100644 --- a/build.zig +++ b/build.zig @@ -23,7 +23,7 @@ const Build = std.Build; /// Do not rename this constant. It is scanned by some scripts to determine /// which zig version to install. -const recommended_zig_version = "0.14.1"; +const recommended_zig_version = "0.15.1"; pub fn build(b: *Build) !void { switch (comptime builtin.zig_version.order(std.SemanticVersion.parse(recommended_zig_version) catch unreachable)) { diff --git a/build.zig.zon b/build.zig.zon index 05165cab..dd64b6e2 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -5,9 +5,9 @@ .fingerprint = 0xda130f3af836cea0, .dependencies = .{ .v8 = .{ - .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/cf412d5b3d9d608582571d821e0d552337ef690d.tar.gz", - .hash = "v8-0.0.0-xddH69zDAwA4fp1dBo_jEDjS5bhXycPwRlZHp6_X890t", + .url = "https://github.com/lightpanda-io/zig-v8-fork/archive/e62726800663d0397d8766f7185040d8b8b69402.tar.gz", + .hash = "v8-0.0.0-xddH69zDAwD5i0hGhAsv3SPeihlj5fXGpJyO15KqBWOn", }, - //.v8 = .{ .path = "../zig-v8-fork" }, + //.v8 = .{ .path = "../zig-v8-fork" } }, } diff --git a/src/browser/Scheduler.zig b/src/browser/Scheduler.zig index 2c2d90ed..d79ca14c 100644 --- a/src/browser/Scheduler.zig +++ b/src/browser/Scheduler.zig @@ -129,18 +129,18 @@ test "Scheduler" { try testing.expectDelta(3, try s.runHighPriority(), 1); try testing.expectEqual(0, task.calls.items.len); - std.time.sleep(std.time.ns_per_ms * 5); + std.Thread.sleep(std.time.ns_per_ms * 5); try testing.expectEqual(null, s.runHighPriority()); try testing.expectEqualSlices(u32, &.{1}, task.calls.items); try s.add(&task, TestTask.run2, 3, .{}); try s.add(&task, TestTask.run1, 2, .{}); - std.time.sleep(std.time.ns_per_ms * 5); + std.Thread.sleep(std.time.ns_per_ms * 5); try testing.expectDelta(null, try s.runHighPriority(), 1); try testing.expectEqualSlices(u32, &.{ 1, 1, 2 }, task.calls.items); - std.time.sleep(std.time.ns_per_ms * 5); + std.Thread.sleep(std.time.ns_per_ms * 5); // wont' run secondary try testing.expectEqual(null, try s.runHighPriority()); try testing.expectEqualSlices(u32, &.{ 1, 1, 2 }, task.calls.items); @@ -155,13 +155,13 @@ const TestTask = struct { calls: std.ArrayListUnmanaged(u32) = .{}, fn run1(ctx: *anyopaque) ?u32 { - var self: *TestTask = @alignCast(@ptrCast(ctx)); + var self: *TestTask = @ptrCast(@alignCast(ctx)); self.calls.append(self.allocator, 1) catch unreachable; return null; } fn run2(ctx: *anyopaque) ?u32 { - var self: *TestTask = @alignCast(@ptrCast(ctx)); + var self: *TestTask = @ptrCast(@alignCast(ctx)); self.calls.append(self.allocator, 2) catch unreachable; return 2; } diff --git a/src/browser/ScriptManager.zig b/src/browser/ScriptManager.zig index 737b88f0..facd86a6 100644 --- a/src/browser/ScriptManager.zig +++ b/src/browser/ScriptManager.zig @@ -62,7 +62,7 @@ allocator: Allocator, buffer_pool: BufferPool, script_pool: std.heap.MemoryPool(PendingScript), -const OrderList = std.DoublyLinkedList(*PendingScript); +const OrderList = std.DoublyLinkedList; pub fn init(browser: *Browser, page: *Page) ScriptManager { // page isn't fully initialized, we can setup our reference, but that's it. @@ -96,7 +96,7 @@ pub fn reset(self: *ScriptManager) void { fn clearList(_: *const ScriptManager, list: *OrderList) void { while (list.first) |node| { - const pending_script = node.data; + const pending_script: *PendingScript = @fieldParentPtr("node", node); // this removes it from the list pending_script.deinit(); } @@ -179,7 +179,7 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void { .script = script, .complete = false, .manager = self, - .node = .{ .data = pending_script }, + .node = .{}, }; if (source == .@"inline") { @@ -193,7 +193,6 @@ pub fn addFromElement(self: *ScriptManager, element: *parser.Element) !void { log.debug(.http, "script queue", .{ .url = remote_url.? }); } - pending_script.node = .{ .data = pending_script }; self.getList(&pending_script.script).append(&pending_script.node); errdefer pending_script.deinit(); @@ -323,7 +322,7 @@ fn evaluate(self: *ScriptManager) void { defer self.is_evaluating = false; while (self.scripts.first) |n| { - var pending_script = n.data; + var pending_script: *PendingScript = @fieldParentPtr("node", n); if (pending_script.complete == false) { return; } @@ -343,7 +342,7 @@ fn evaluate(self: *ScriptManager) void { } while (self.deferreds.first) |n| { - var pending_script = n.data; + var pending_script: *PendingScript = @fieldParentPtr("node", n); if (pending_script.complete == false) { return; } @@ -395,7 +394,7 @@ fn getList(self: *ScriptManager, script: *const Script) *OrderList { } fn startCallback(transfer: *Http.Transfer) !void { - const script: *PendingScript = @alignCast(@ptrCast(transfer.ctx)); + const script: *PendingScript = @ptrCast(@alignCast(transfer.ctx)); script.startCallback(transfer) catch |err| { log.err(.http, "SM.startCallback", .{ .err = err, .transfer = transfer }); return err; @@ -403,7 +402,7 @@ fn startCallback(transfer: *Http.Transfer) !void { } fn headerCallback(transfer: *Http.Transfer) !void { - const script: *PendingScript = @alignCast(@ptrCast(transfer.ctx)); + const script: *PendingScript = @ptrCast(@alignCast(transfer.ctx)); script.headerCallback(transfer) catch |err| { log.err(.http, "SM.headerCallback", .{ .err = err, @@ -415,7 +414,7 @@ fn headerCallback(transfer: *Http.Transfer) !void { } fn dataCallback(transfer: *Http.Transfer, data: []const u8) !void { - const script: *PendingScript = @alignCast(@ptrCast(transfer.ctx)); + const script: *PendingScript = @ptrCast(@alignCast(transfer.ctx)); script.dataCallback(transfer, data) catch |err| { log.err(.http, "SM.dataCallback", .{ .err = err, .transfer = transfer, .len = data.len }); return err; @@ -423,12 +422,12 @@ fn dataCallback(transfer: *Http.Transfer, data: []const u8) !void { } fn doneCallback(ctx: *anyopaque) !void { - const script: *PendingScript = @alignCast(@ptrCast(ctx)); + const script: *PendingScript = @ptrCast(@alignCast(ctx)); script.doneCallback(); } fn errorCallback(ctx: *anyopaque, err: anyerror) void { - const script: *PendingScript = @alignCast(@ptrCast(ctx)); + const script: *PendingScript = @ptrCast(@alignCast(ctx)); script.errorCallback(err); } @@ -682,9 +681,14 @@ const BufferPool = struct { available: List = .{}, allocator: Allocator, max_concurrent_transfers: u8, - node_pool: std.heap.MemoryPool(List.Node), + mem_pool: std.heap.MemoryPool(Container), - const List = std.DoublyLinkedList(std.ArrayListUnmanaged(u8)); + const List = std.DoublyLinkedList; + + const Container = struct { + node: List.Node, + buf: std.ArrayListUnmanaged(u8), + }; fn init(allocator: Allocator, max_concurrent_transfers: u8) BufferPool { return .{ @@ -692,7 +696,7 @@ const BufferPool = struct { .count = 0, .allocator = allocator, .max_concurrent_transfers = max_concurrent_transfers, - .node_pool = std.heap.MemoryPool(List.Node).init(allocator), + .mem_pool = std.heap.MemoryPool(Container).init(allocator), }; } @@ -701,21 +705,23 @@ const BufferPool = struct { var node = self.available.first; while (node) |n| { - n.data.deinit(allocator); + const container: *Container = @fieldParentPtr("node", n); + container.buf.deinit(allocator); node = n.next; } - self.node_pool.deinit(); + self.mem_pool.deinit(); } - fn get(self: *BufferPool) ArrayListUnmanaged(u8) { + fn get(self: *BufferPool) std.ArrayListUnmanaged(u8) { const node = self.available.popFirst() orelse { // return a new buffer return .{}; }; self.count -= 1; - defer self.node_pool.destroy(node); - return node.data; + const container: *Container = @fieldParentPtr("node", node); + defer self.mem_pool.destroy(container); + return container.buf; } fn release(self: *BufferPool, buffer: ArrayListUnmanaged(u8)) void { @@ -727,16 +733,16 @@ const BufferPool = struct { return; } - const node = self.node_pool.create() catch |err| { + const container = self.mem_pool.create() catch |err| { b.deinit(self.allocator); log.err(.http, "SM BufferPool release", .{ .err = err }); return; }; b.clearRetainingCapacity(); - node.* = .{ .data = b }; + container.* = .{ .buf = b, .node = .{} }; self.count += 1; - self.available.append(node); + self.available.append(&container.node); } }; @@ -769,7 +775,7 @@ const Blocking = struct { return error.InvalidStatusCode; } - var self: *Blocking = @alignCast(@ptrCast(transfer.ctx)); + var self: *Blocking = @ptrCast(@alignCast(transfer.ctx)); self.buffer = self.buffer_pool.get(); } @@ -780,7 +786,7 @@ const Blocking = struct { // .blocking = true, // }); - var self: *Blocking = @alignCast(@ptrCast(transfer.ctx)); + var self: *Blocking = @ptrCast(@alignCast(transfer.ctx)); self.buffer.appendSlice(self.allocator, data) catch |err| { log.err(.http, "SM.dataCallback", .{ .err = err, @@ -793,7 +799,7 @@ const Blocking = struct { } fn doneCallback(ctx: *anyopaque) !void { - var self: *Blocking = @alignCast(@ptrCast(ctx)); + var self: *Blocking = @ptrCast(@alignCast(ctx)); self.state = .{ .done = .{ .buffer = self.buffer, .buffer_pool = self.buffer_pool, @@ -801,7 +807,7 @@ const Blocking = struct { } fn errorCallback(ctx: *anyopaque, err: anyerror) void { - var self: *Blocking = @alignCast(@ptrCast(ctx)); + var self: *Blocking = @ptrCast(@alignCast(ctx)); self.state = .{ .err = err }; self.buffer_pool.release(self.buffer); } diff --git a/src/browser/dom/character_data.zig b/src/browser/dom/character_data.zig index 774572a3..b230d777 100644 --- a/src/browser/dom/character_data.zig +++ b/src/browser/dom/character_data.zig @@ -102,7 +102,7 @@ pub const CharacterData = struct { // netsurf's CharacterData (text, comment) doesn't implement the // dom_node_get_attributes and thus will crash if we try to call nodeIsEqualNode. pub fn _isEqualNode(self: *parser.CharacterData, other_node: *parser.Node) !bool { - if (try parser.nodeType(@alignCast(@ptrCast(self))) != try parser.nodeType(other_node)) { + if (try parser.nodeType(@ptrCast(@alignCast(self))) != try parser.nodeType(other_node)) { return false; } diff --git a/src/browser/dom/document.zig b/src/browser/dom/document.zig index 4facba99..ecca014a 100644 --- a/src/browser/dom/document.zig +++ b/src/browser/dom/document.zig @@ -258,14 +258,14 @@ pub const Document = struct { } pub fn getActiveElement(self: *parser.Document, page: *Page) !?*parser.Element { - if (page.getNodeState(@alignCast(@ptrCast(self)))) |state| { + if (page.getNodeState(@ptrCast(@alignCast(self)))) |state| { if (state.active_element) |ae| { return ae; } } if (try parser.documentHTMLBody(page.window.document)) |body| { - return @alignCast(@ptrCast(body)); + return @ptrCast(@alignCast(body)); } return try parser.documentGetDocumentElement(self); @@ -281,7 +281,7 @@ pub const Document = struct { // we could look for the "disabled" attribute, but that's only meaningful // on certain types, and libdom's vtable doesn't seem to expose this. pub fn setFocus(self: *parser.Document, e: *parser.ElementHTML, page: *Page) !void { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); state.active_element = @ptrCast(e); } @@ -295,7 +295,7 @@ pub const Document = struct { } pub fn get_adoptedStyleSheets(self: *parser.Document, page: *Page) !Env.JsObject { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); if (state.adopted_style_sheets) |obj| { return obj; } @@ -306,7 +306,7 @@ pub const Document = struct { } pub fn set_adoptedStyleSheets(self: *parser.Document, sheets: Env.JsObject, page: *Page) !void { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); state.adopted_style_sheets = try sheets.persist(); } }; diff --git a/src/browser/dom/document_fragment.zig b/src/browser/dom/document_fragment.zig index d447c687..2eb3709b 100644 --- a/src/browser/dom/document_fragment.zig +++ b/src/browser/dom/document_fragment.zig @@ -85,7 +85,7 @@ pub const DocumentFragment = struct { } pub fn _getElementById(self: *parser.DocumentFragment, id: []const u8) !?ElementUnion { - const e = try parser.nodeGetElementById(@alignCast(@ptrCast(self)), id) orelse return null; + const e = try parser.nodeGetElementById(@ptrCast(@alignCast(self)), id) orelse return null; return try Element.toInterface(e); } }; diff --git a/src/browser/dom/element.zig b/src/browser/dom/element.zig index 16554567..bc30e5e5 100644 --- a/src/browser/dom/element.zig +++ b/src/browser/dom/element.zig @@ -137,15 +137,15 @@ pub const Element = struct { } pub fn get_innerHTML(self: *parser.Element, page: *Page) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; - try dump.writeChildren(parser.elementToNode(self), .{}, buf.writer(page.call_arena)); - return buf.items; + var aw = std.Io.Writer.Allocating.init(page.call_arena); + try dump.writeChildren(parser.elementToNode(self), .{}, &aw.writer); + return aw.written(); } pub fn get_outerHTML(self: *parser.Element, page: *Page) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; - try dump.writeNode(parser.elementToNode(self), .{}, buf.writer(page.call_arena)); - return buf.items; + var aw = std.Io.Writer.Allocating.init(page.call_arena); + try dump.writeNode(parser.elementToNode(self), .{}, &aw.writer); + return aw.written(); } pub fn set_innerHTML(self: *parser.Element, str: []const u8, page: *Page) !void { @@ -184,7 +184,7 @@ pub const Element = struct { // always index 0, because nodeAppendChild moves the node out of // the nodeList and into the new tree const child = try parser.nodeListItem(children, 0) orelse continue; - _ = try parser.nodeAppendChild(@alignCast(@ptrCast(clean)), child); + _ = try parser.nodeAppendChild(@ptrCast(@alignCast(clean)), child); } const state = try page.getOrCreateNodeState(node); @@ -537,14 +537,14 @@ pub const Element = struct { }; pub fn _attachShadow(self: *parser.Element, opts: AttachShadowOpts, page: *Page) !*ShadowRoot { const mode = std.meta.stringToEnum(ShadowRoot.Mode, opts.mode) orelse return error.InvalidArgument; - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); if (state.shadow_root) |sr| { if (mode != sr.mode) { // this is the behavior per the spec return error.NotSupportedError; } - try Node.removeChildren(@alignCast(@ptrCast(sr.proto))); + try Node.removeChildren(@ptrCast(@alignCast(sr.proto))); return sr; } @@ -558,13 +558,13 @@ pub const Element = struct { .proto = fragment, }; state.shadow_root = sr; - parser.documentFragmentSetHost(sr.proto, @alignCast(@ptrCast(self))); + parser.documentFragmentSetHost(sr.proto, @ptrCast(@alignCast(self))); return sr; } pub fn get_shadowRoot(self: *parser.Element, page: *Page) ?*ShadowRoot { - const state = page.getNodeState(@alignCast(@ptrCast(self))) orelse return null; + const state = page.getNodeState(@ptrCast(@alignCast(self))) orelse return null; const sr = state.shadow_root orelse return null; if (sr.mode == .closed) { return null; diff --git a/src/browser/dom/mutation_observer.zig b/src/browser/dom/mutation_observer.zig index 92ed9e18..230eee90 100644 --- a/src/browser/dom/mutation_observer.zig +++ b/src/browser/dom/mutation_observer.zig @@ -111,7 +111,7 @@ pub const MutationObserver = struct { } fn callback(ctx: *anyopaque) ?u32 { - const self: *MutationObserver = @alignCast(@ptrCast(ctx)); + const self: *MutationObserver = @ptrCast(@alignCast(ctx)); if (self.connected == false) { self.scheduled = true; return null; diff --git a/src/browser/dom/node.zig b/src/browser/dom/node.zig index 48e3a1a2..aa3933d0 100644 --- a/src/browser/dom/node.zig +++ b/src/browser/dom/node.zig @@ -601,7 +601,7 @@ pub const Node = struct { fn toNode(self: NodeOrText, doc: *parser.Document) !*parser.Node { return switch (self) { .node => |n| n, - .text => |txt| @alignCast(@ptrCast(try parser.documentCreateTextNode(doc, txt))), + .text => |txt| @ptrCast(@alignCast(try parser.documentCreateTextNode(doc, txt))), }; } diff --git a/src/browser/dom/shadow_root.zig b/src/browser/dom/shadow_root.zig index 0295256a..76353b73 100644 --- a/src/browser/dom/shadow_root.zig +++ b/src/browser/dom/shadow_root.zig @@ -60,9 +60,9 @@ pub const ShadowRoot = struct { } pub fn get_innerHTML(self: *ShadowRoot, page: *Page) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; - try dump.writeChildren(parser.documentFragmentToNode(self.proto), .{}, buf.writer(page.call_arena)); - return buf.items; + var aw = std.Io.Writer.Allocating.init(page.call_arena); + try dump.writeChildren(parser.documentFragmentToNode(self.proto), .{}, &aw.writer); + return aw.written(); } pub fn set_innerHTML(self: *ShadowRoot, str_: ?[]const u8) !void { diff --git a/src/browser/dump.zig b/src/browser/dump.zig index da890b98..ed630ed5 100644 --- a/src/browser/dump.zig +++ b/src/browser/dump.zig @@ -30,14 +30,14 @@ pub const Opts = struct { }; // writer must be a std.io.Writer -pub fn writeHTML(doc: *parser.Document, opts: Opts, writer: anytype) !void { +pub fn writeHTML(doc: *parser.Document, opts: Opts, writer: *std.Io.Writer) !void { try writer.writeAll("\n"); try writeChildren(parser.documentToNode(doc), opts, writer); try writer.writeAll("\n"); } // Spec: https://www.w3.org/TR/xml/#sec-prolog-dtd -pub fn writeDocType(doc_type: *parser.DocumentType, writer: anytype) !void { +pub fn writeDocType(doc_type: *parser.DocumentType, writer: *std.Io.Writer) !void { try writer.writeAll(""); } -pub fn writeNode(node: *parser.Node, opts: Opts, writer: anytype) anyerror!void { +pub fn writeNode(node: *parser.Node, opts: Opts, writer: *std.Io.Writer) anyerror!void { switch (try parser.nodeType(node)) { .element => { // open the tag @@ -95,7 +95,7 @@ pub fn writeNode(node: *parser.Node, opts: Opts, writer: anytype) anyerror!void if (opts.page) |page| { if (page.getNodeState(node)) |state| { if (state.shadow_root) |sr| { - try writeChildren(@alignCast(@ptrCast(sr.proto)), opts, writer); + try writeChildren(@ptrCast(@alignCast(sr.proto)), opts, writer); } } } @@ -150,7 +150,7 @@ pub fn writeNode(node: *parser.Node, opts: Opts, writer: anytype) anyerror!void } // writer must be a std.io.Writer -pub fn writeChildren(root: *parser.Node, opts: Opts, writer: anytype) !void { +pub fn writeChildren(root: *parser.Node, opts: Opts, writer: *std.Io.Writer) !void { const walker = Walker{}; var next: ?*parser.Node = null; while (true) { @@ -271,13 +271,13 @@ fn testWriteHTML(comptime expected_body: []const u8, src: []const u8) !void { } fn testWriteFullHTML(comptime expected: []const u8, src: []const u8) !void { - var buf = std.ArrayListUnmanaged(u8){}; - defer buf.deinit(testing.allocator); + var aw = std.Io.Writer.Allocating.init(testing.allocator); + defer aw.deinit(); const doc_html = try parser.documentHTMLParseFromStr(src); defer parser.documentHTMLClose(doc_html) catch {}; const doc = parser.documentHTMLToDocument(doc_html); - try writeHTML(doc, .{}, buf.writer(testing.allocator)); - try testing.expectEqualStrings(expected, buf.items); + try writeHTML(doc, .{}, &aw.writer); + try testing.expectEqualStrings(expected, aw.written()); } diff --git a/src/browser/html/AbortController.zig b/src/browser/html/AbortController.zig index c3a78caa..aa1dbaea 100644 --- a/src/browser/html/AbortController.zig +++ b/src/browser/html/AbortController.zig @@ -129,7 +129,7 @@ const TimeoutCallback = struct { signal: AbortSignal, fn run(ctx: *anyopaque) ?u32 { - const self: *TimeoutCallback = @alignCast(@ptrCast(ctx)); + const self: *TimeoutCallback = @ptrCast(@alignCast(ctx)); self.signal.abort("TimeoutError") catch |err| { log.warn(.app, "abort signal timeout", .{ .err = err }); }; diff --git a/src/browser/html/document.zig b/src/browser/html/document.zig index ee9eb846..2920d09c 100644 --- a/src/browser/html/document.zig +++ b/src/browser/html/document.zig @@ -209,7 +209,7 @@ pub const HTMLDocument = struct { } pub fn get_readyState(self: *parser.DocumentHTML, page: *Page) ![]const u8 { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); return @tagName(state.ready_state); } @@ -292,7 +292,7 @@ pub const HTMLDocument = struct { } pub fn documentIsLoaded(self: *parser.DocumentHTML, page: *Page) !void { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); state.ready_state = .interactive; log.debug(.script_event, "dispatch event", .{ @@ -309,7 +309,7 @@ pub const HTMLDocument = struct { } pub fn documentIsComplete(self: *parser.DocumentHTML, page: *Page) !void { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); state.ready_state = .complete; } }; diff --git a/src/browser/html/elements.zig b/src/browser/html/elements.zig index 3a735dd7..c9494578 100644 --- a/src/browser/html/elements.zig +++ b/src/browser/html/elements.zig @@ -145,7 +145,7 @@ pub const HTMLElement = struct { try Node.removeChildren(n); // attach the text node. - _ = try parser.nodeAppendChild(n, @as(*parser.Node, @alignCast(@ptrCast(t)))); + _ = try parser.nodeAppendChild(n, @as(*parser.Node, @ptrCast(@alignCast(t)))); } pub fn _click(e: *parser.ElementHTML) !void { @@ -264,7 +264,7 @@ pub const HTMLAnchorElement = struct { // But // document.createElement('a').host // should not fail, it should return an empty string - if (try parser.elementGetAttribute(@alignCast(@ptrCast(self)), "href")) |href| { + if (try parser.elementGetAttribute(@ptrCast(@alignCast(self)), "href")) |href| { return URL.constructor(.{ .string = href }, null, page); // TODO inject base url } return .empty; @@ -869,7 +869,7 @@ pub const HTMLScriptElement = struct { v, ); - if (try Node.get_isConnected(@alignCast(@ptrCast(self)))) { + if (try Node.get_isConnected(@ptrCast(@alignCast(self)))) { // There are sites which do set the src AFTER appending the script // tag to the document: // const s = document.createElement('script'); @@ -877,7 +877,7 @@ pub const HTMLScriptElement = struct { // s.src = '...'; // This should load the script. // addFromElement protects against double execution. - try page.script_manager.addFromElement(@alignCast(@ptrCast(self))); + try page.script_manager.addFromElement(@ptrCast(@alignCast(self))); } } @@ -976,22 +976,22 @@ pub const HTMLScriptElement = struct { } pub fn get_onload(self: *parser.Script, page: *Page) !?Env.Function { - const state = page.getNodeState(@alignCast(@ptrCast(self))) orelse return null; + const state = page.getNodeState(@ptrCast(@alignCast(self))) orelse return null; return state.onload; } pub fn set_onload(self: *parser.Script, function: ?Env.Function, page: *Page) !void { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); state.onload = function; } pub fn get_onerror(self: *parser.Script, page: *Page) !?Env.Function { - const state = page.getNodeState(@alignCast(@ptrCast(self))) orelse return null; + const state = page.getNodeState(@ptrCast(@alignCast(self))) orelse return null; return state.onerror; } pub fn set_onerror(self: *parser.Script, function: ?Env.Function, page: *Page) !void { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); state.onerror = function; } }; @@ -1014,7 +1014,7 @@ pub const HTMLStyleElement = struct { pub const subtype = .node; pub fn get_sheet(self: *parser.Style, page: *Page) !*StyleSheet { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); if (state.style_sheet) |ss| { return ss; } @@ -1068,7 +1068,7 @@ pub const HTMLTemplateElement = struct { pub const subtype = .node; pub fn get_content(self: *parser.Template, page: *Page) !*parser.DocumentFragment { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(self))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(self))); if (state.template_content) |tc| { return tc; } diff --git a/src/browser/html/select.zig b/src/browser/html/select.zig index b31183c1..aaf90caa 100644 --- a/src/browser/html/select.zig +++ b/src/browser/html/select.zig @@ -64,7 +64,7 @@ pub const HTMLSelectElement = struct { } pub fn get_selectedIndex(select: *parser.Select, page: *Page) !i32 { - const state = try page.getOrCreateNodeState(@alignCast(@ptrCast(select))); + const state = try page.getOrCreateNodeState(@ptrCast(@alignCast(select))); const selected_index = try parser.selectGetSelectedIndex(select); // See the explicit_index_set field documentation @@ -83,7 +83,7 @@ pub const HTMLSelectElement = struct { // Libdom's dom_html_select_select_set_selected_index will crash if index // is out of range, and it doesn't properly unset options pub fn set_selectedIndex(select: *parser.Select, index: i32, page: *Page) !void { - var state = try page.getOrCreateNodeState(@alignCast(@ptrCast(select))); + var state = try page.getOrCreateNodeState(@ptrCast(@alignCast(select))); state.explicit_index_set = true; const options = try parser.selectGetOptions(select); @@ -101,7 +101,7 @@ pub const HTMLSelectElement = struct { pub fn get_options(select: *parser.Select) HTMLOptionsCollection { return .{ .select = select, - .proto = collection.HTMLCollectionChildren(@alignCast(@ptrCast(select)), .{ + .proto = collection.HTMLCollectionChildren(@ptrCast(@alignCast(select)), .{ .mutable = true, .include_root = false, }), @@ -176,24 +176,24 @@ pub const HTMLOptionsCollection = struct { }; const insert_before: *parser.Node = switch (before) { - .option => |o| @alignCast(@ptrCast(o)), + .option => |o| @ptrCast(@alignCast(o)), .index => |i| (try self.proto.item(i)) orelse return self.appendOption(option), }; return Node.before(insert_before, &.{ - .{ .node = @alignCast(@ptrCast(option)) }, + .{ .node = @ptrCast(@alignCast(option)) }, }); } pub fn _remove(self: *HTMLOptionsCollection, index: u32) !void { const Node = @import("../dom/node.zig").Node; const option = (try self.proto.item(index)) orelse return; - _ = try Node._removeChild(@alignCast(@ptrCast(self.select)), option); + _ = try Node._removeChild(@ptrCast(@alignCast(self.select)), option); } fn appendOption(self: *HTMLOptionsCollection, option: *parser.Option) !void { const Node = @import("../dom/node.zig").Node; - return Node.append(@alignCast(@ptrCast(self.select)), &.{ - .{ .node = @alignCast(@ptrCast(option)) }, + return Node.append(@ptrCast(@alignCast(self.select)), &.{ + .{ .node = @ptrCast(@alignCast(option)) }, }); } }; diff --git a/src/browser/html/window.zig b/src/browser/html/window.zig index dec77578..496b17ac 100644 --- a/src/browser/html/window.zig +++ b/src/browser/html/window.zig @@ -403,7 +403,7 @@ const TimerCallback = struct { args: []Env.JsObject = &.{}, fn run(ctx: *anyopaque) ?u32 { - const self: *TimerCallback = @alignCast(@ptrCast(ctx)); + const self: *TimerCallback = @ptrCast(@alignCast(ctx)); if (self.repeat != null) { if (self.window.timers.contains(self.timer_id) == false) { // it was called diff --git a/src/browser/mimalloc.zig b/src/browser/mimalloc.zig index 0cb15156..df770904 100644 --- a/src/browser/mimalloc.zig +++ b/src/browser/mimalloc.zig @@ -44,32 +44,32 @@ pub fn destroy() void { heap = null; } -pub export fn m_alloc(size: usize) callconv(.C) ?*anyopaque { +pub export fn m_alloc(size: usize) callconv(.c) ?*anyopaque { if (heap == null) return null; return c.mi_heap_malloc(heap.?, size); } -pub export fn re_alloc(ptr: ?*anyopaque, size: usize) callconv(.C) ?*anyopaque { +pub export fn re_alloc(ptr: ?*anyopaque, size: usize) callconv(.c) ?*anyopaque { if (heap == null) return null; return c.mi_heap_realloc(heap.?, ptr, size); } -pub export fn c_alloc(nmemb: usize, size: usize) callconv(.C) ?*anyopaque { +pub export fn c_alloc(nmemb: usize, size: usize) callconv(.c) ?*anyopaque { if (heap == null) return null; return c.mi_heap_calloc(heap.?, nmemb, size); } -pub export fn str_dup(s: [*c]const u8) callconv(.C) [*c]u8 { +pub export fn str_dup(s: [*c]const u8) callconv(.c) [*c]u8 { if (heap == null) return null; return c.mi_heap_strdup(heap.?, s); } -pub export fn strn_dup(s: [*c]const u8, size: usize) callconv(.C) [*c]u8 { +pub export fn strn_dup(s: [*c]const u8, size: usize) callconv(.c) [*c]u8 { if (heap == null) return null; return c.mi_heap_strndup(heap.?, s, size); } // NOOP, use destroy to clear all the memory allocated at once. -pub export fn f_ree(_: ?*anyopaque) callconv(.C) void { +pub export fn f_ree(_: ?*anyopaque) callconv(.c) void { return; } diff --git a/src/browser/netsurf.zig b/src/browser/netsurf.zig index 2cfff7b2..6a9428af 100644 --- a/src/browser/netsurf.zig +++ b/src/browser/netsurf.zig @@ -575,7 +575,7 @@ pub fn mutationEventRelatedNode(evt: *MutationEvent) !?*Node { const err = c._dom_mutation_event_get_related_node(evt, &n); try DOMErr(err); if (n == null) return null; - return @as(*Node, @alignCast(@ptrCast(n))); + return @as(*Node, @ptrCast(@alignCast(n))); } // EventListener @@ -590,7 +590,7 @@ fn eventListenerGetData(lst: *EventListener) ?*anyopaque { pub const EventTarget = c.dom_event_target; pub fn eventTargetToNode(et: *EventTarget) *Node { - return @as(*Node, @alignCast(@ptrCast(et))); + return @as(*Node, @ptrCast(@alignCast(et))); } fn eventTargetVtable(et: *EventTarget) c.dom_event_target_vtable { @@ -631,7 +631,7 @@ pub const EventNode = struct { fn idFromListener(lst: *EventListener) ?usize { const ctx = eventListenerGetData(lst) orelse return null; - const node: *EventNode = @alignCast(@ptrCast(ctx)); + const node: *EventNode = @ptrCast(@alignCast(ctx)); return node.id; } }; @@ -643,11 +643,11 @@ pub fn eventTargetAddEventListener( capture: bool, ) !*EventListener { const event_handler = struct { - fn handle(event_: ?*Event, ptr_: ?*anyopaque) callconv(.C) void { + fn handle(event_: ?*Event, ptr_: ?*anyopaque) callconv(.c) void { const ptr = ptr_ orelse return; const event = event_ orelse return; - const node_: *EventNode = @alignCast(@ptrCast(ptr)); + const node_: *EventNode = @ptrCast(@alignCast(ptr)); node_.func(node_, event); } }.handle; @@ -829,12 +829,12 @@ pub const EventTargetTBase = extern struct { eti: c.dom_event_target_internal = c.dom_event_target_internal{ .listeners = null }, internal_target_type: InternalType, - pub fn add_event_listener(et: [*c]c.dom_event_target, t: [*c]c.dom_string, l: ?*c.struct_dom_event_listener, capture: bool) callconv(.C) c.dom_exception { + pub fn add_event_listener(et: [*c]c.dom_event_target, t: [*c]c.dom_string, l: ?*c.struct_dom_event_listener, capture: bool) callconv(.c) c.dom_exception { const self = @as(*Self, @ptrCast(et)); return c._dom_event_target_add_event_listener(&self.eti, t, l, capture); } - pub fn dispatch_event(et: [*c]c.dom_event_target, evt: ?*c.struct_dom_event, res: [*c]bool) callconv(.C) c.dom_exception { + pub fn dispatch_event(et: [*c]c.dom_event_target, evt: ?*c.struct_dom_event, res: [*c]bool) callconv(.c) c.dom_exception { const self = @as(*Self, @ptrCast(et)); // Set the event target to the target dispatched. const e = c._dom_event_set_target(evt, et); @@ -844,7 +844,7 @@ pub const EventTargetTBase = extern struct { return c._dom_event_target_dispatch(et, &self.eti, evt, c.DOM_AT_TARGET, res); } - pub fn remove_event_listener(et: [*c]c.dom_event_target, t: [*c]c.dom_string, l: ?*c.struct_dom_event_listener, capture: bool) callconv(.C) c.dom_exception { + pub fn remove_event_listener(et: [*c]c.dom_event_target, t: [*c]c.dom_string, l: ?*c.struct_dom_event_listener, capture: bool) callconv(.c) c.dom_exception { const self = @as(*Self, @ptrCast(et)); return c._dom_event_target_remove_event_listener(&self.eti, t, l, capture); } @@ -856,12 +856,12 @@ pub const EventTargetTBase = extern struct { cur: [*c]c.struct_listener_entry, next: [*c][*c]c.struct_listener_entry, l: [*c]?*c.struct_dom_event_listener, - ) callconv(.C) c.dom_exception { + ) callconv(.c) c.dom_exception { const self = @as(*Self, @ptrCast(et)); return c._dom_event_target_iter_event_listener(self.eti, t, capture, cur, next, l); } - pub fn internal_type(et: [*c]c.dom_event_target, internal_type_: [*c]u32) callconv(.C) c.dom_exception { + pub fn internal_type(et: [*c]c.dom_event_target, internal_type_: [*c]u32) callconv(.c) c.dom_exception { const self = @as(*Self, @ptrCast(et)); internal_type_.* = @intFromEnum(self.internal_target_type); return c.DOM_NO_ERR; @@ -1016,7 +1016,7 @@ pub fn nodeListItem(nodeList: *NodeList, index: u32) !?*Node { const err = c._dom_nodelist_item(nodeList, index, &n); try DOMErr(err); if (n == null) return null; - return @as(*Node, @alignCast(@ptrCast(n))); + return @as(*Node, @ptrCast(@alignCast(n))); } // NodeExternal is the libdom public representation of a Node. @@ -1452,7 +1452,7 @@ fn characterDataVtable(data: *CharacterData) c.dom_characterdata_vtable { } pub inline fn characterDataToNode(cdata: *CharacterData) *Node { - return @as(*Node, @alignCast(@ptrCast(cdata))); + return @as(*Node, @ptrCast(@alignCast(cdata))); } pub fn characterDataData(cdata: *CharacterData) ![]const u8 { @@ -1537,7 +1537,7 @@ pub const ProcessingInstruction = c.dom_processing_instruction; // processingInstructionToNode is an helper to convert an ProcessingInstruction to a node. pub inline fn processingInstructionToNode(pi: *ProcessingInstruction) *Node { - return @as(*Node, @alignCast(@ptrCast(pi))); + return @as(*Node, @ptrCast(@alignCast(pi))); } pub fn processInstructionCopy(pi: *ProcessingInstruction) !*ProcessingInstruction { @@ -1592,7 +1592,7 @@ pub fn attributeGetOwnerElement(a: *Attribute) !?*Element { // attributeToNode is an helper to convert an attribute to a node. pub inline fn attributeToNode(a: *Attribute) *Node { - return @as(*Node, @alignCast(@ptrCast(a))); + return @as(*Node, @ptrCast(@alignCast(a))); } // Element @@ -1754,7 +1754,7 @@ pub fn elementHasClass(elem: *Element, class: []const u8) !bool { // elementToNode is an helper to convert an element to a node. pub inline fn elementToNode(e: *Element) *Node { - return @as(*Node, @alignCast(@ptrCast(e))); + return @as(*Node, @ptrCast(@alignCast(e))); } // TokenList @@ -1823,14 +1823,14 @@ fn elementHTMLVtable(elem_html: *ElementHTML) c.dom_html_element_vtable { // scriptToElt is an helper to convert an script to an element. pub inline fn scriptToElt(s: *Script) *Element { - return @as(*Element, @alignCast(@ptrCast(s))); + return @as(*Element, @ptrCast(@alignCast(s))); } // HTMLAnchorElement // anchorToNode is an helper to convert an anchor to a node. pub inline fn anchorToNode(a: *Anchor) *Node { - return @as(*Node, @alignCast(@ptrCast(a))); + return @as(*Node, @ptrCast(@alignCast(a))); } pub fn anchorGetTarget(a: *Anchor) ![]const u8 { @@ -1990,7 +1990,7 @@ pub const OptionCollection = c.dom_html_options_collection; pub const DocumentFragment = c.dom_document_fragment; pub inline fn documentFragmentToNode(doc: *DocumentFragment) *Node { - return @as(*Node, @alignCast(@ptrCast(doc))); + return @as(*Node, @ptrCast(@alignCast(doc))); } pub fn documentFragmentGetHost(frag: *DocumentFragment) ?*Node { @@ -2097,7 +2097,7 @@ pub inline fn domImplementationCreateHTMLDocument(title: ?[]const u8) !*Document if (title) |t| { const htitle = try documentCreateElement(doc, "title"); const txt = try documentCreateTextNode(doc, t); - _ = try nodeAppendChild(elementToNode(htitle), @as(*Node, @alignCast(@ptrCast(txt)))); + _ = try nodeAppendChild(elementToNode(htitle), @as(*Node, @ptrCast(@alignCast(txt)))); _ = try nodeAppendChild(elementToNode(head), elementToNode(htitle)); } @@ -2115,7 +2115,7 @@ fn documentVtable(doc: *Document) c.dom_document_vtable { } pub inline fn documentToNode(doc: *Document) *Node { - return @as(*Node, @alignCast(@ptrCast(doc))); + return @as(*Node, @ptrCast(@alignCast(doc))); } pub inline fn documentGetElementById(doc: *Document, id: []const u8) !?*Element { @@ -2284,7 +2284,7 @@ pub inline fn documentImportNode(doc: *Document, node: *Node, deep: bool) !*Node const nodeext = toNodeExternal(Node, node); const err = documentVtable(doc).dom_document_import_node.?(doc, nodeext, deep, &res); try DOMErr(err); - return @as(*Node, @alignCast(@ptrCast(res))); + return @as(*Node, @ptrCast(@alignCast(res))); } pub inline fn documentAdoptNode(doc: *Document, node: *Node) !*Node { @@ -2292,7 +2292,7 @@ pub inline fn documentAdoptNode(doc: *Document, node: *Node) !*Node { const nodeext = toNodeExternal(Node, node); const err = documentVtable(doc).dom_document_adopt_node.?(doc, nodeext, &res); try DOMErr(err); - return @as(*Node, @alignCast(@ptrCast(res))); + return @as(*Node, @ptrCast(@alignCast(res))); } pub inline fn documentCreateAttribute(doc: *Document, name: []const u8) !*Attribute { @@ -2327,7 +2327,7 @@ pub const DocumentHTML = c.dom_html_document; // documentHTMLToNode is an helper to convert a documentHTML to an node. pub inline fn documentHTMLToNode(doc: *DocumentHTML) *Node { - return @as(*Node, @alignCast(@ptrCast(doc))); + return @as(*Node, @ptrCast(@alignCast(doc))); } fn documentHTMLVtable(doc_html: *DocumentHTML) c.dom_html_document_vtable { @@ -2489,7 +2489,7 @@ pub inline fn documentHTMLBody(doc_html: *DocumentHTML) !?*Body { } pub inline fn bodyToElement(body: *Body) *Element { - return @as(*Element, @alignCast(@ptrCast(body))); + return @as(*Element, @ptrCast(@alignCast(body))); } pub inline fn documentHTMLSetBody(doc_html: *DocumentHTML, elt: ?*ElementHTML) !void { @@ -2520,7 +2520,7 @@ pub inline fn documentHTMLSetTitle(doc: *DocumentHTML, v: []const u8) !void { pub fn documentHTMLSetCurrentScript(doc: *DocumentHTML, script: ?*Script) !void { var s: ?*ElementHTML = null; - if (script != null) s = @alignCast(@ptrCast(script.?)); + if (script != null) s = @ptrCast(@alignCast(script.?)); const err = documentHTMLVtable(doc).set_current_script.?(doc, s); try DOMErr(err); } @@ -2999,7 +2999,7 @@ pub fn inputSetType(input: *Input, type_: []const u8) !void { } } const new_type = if (found) type_ else "text"; - try elementSetAttribute(@alignCast(@ptrCast(input)), "type", new_type); + try elementSetAttribute(@ptrCast(@alignCast(input)), "type", new_type); } pub fn inputGetValue(input: *Input) ![]const u8 { diff --git a/src/browser/page.zig b/src/browser/page.zig index 6bf5239f..b1bff355 100644 --- a/src/browser/page.zig +++ b/src/browser/page.zig @@ -173,13 +173,13 @@ pub const Page = struct { } fn runMicrotasks(ctx: *anyopaque) ?u32 { - const self: *Page = @alignCast(@ptrCast(ctx)); + const self: *Page = @ptrCast(@alignCast(ctx)); self.session.browser.runMicrotasks(); return 5; } fn runMessageLoop(ctx: *anyopaque) ?u32 { - const self: *Page = @alignCast(@ptrCast(ctx)); + const self: *Page = @ptrCast(@alignCast(ctx)); self.session.browser.runMessageLoop(); return 100; } @@ -192,7 +192,7 @@ pub const Page = struct { }; // dump writes the page content into the given file. - pub fn dump(self: *const Page, opts: DumpOpts, out: std.fs.File) !void { + pub fn dump(self: *const Page, opts: DumpOpts, out: *std.Io.Writer) !void { switch (self.mode) { .pre => return error.PageNotLoaded, .raw => |buf| { @@ -347,7 +347,7 @@ pub const Page = struct { // overflow. const _ms: u64 = @intCast(ms); - std.time.sleep(std.time.ns_per_ms * _ms); + std.Thread.sleep(std.time.ns_per_ms * _ms); break :SW; } @@ -469,9 +469,9 @@ pub const Page = struct { } pub fn origin(self: *const Page, arena: Allocator) ![]const u8 { - var arr: std.ArrayListUnmanaged(u8) = .{}; - try self.url.origin(arr.writer(arena)); - return arr.items; + var aw = std.Io.Writer.Allocating.init(arena); + try self.url.origin(&aw.writer); + return aw.written(); } const RequestCookieOpts = struct { @@ -610,7 +610,7 @@ pub const Page = struct { } fn pageHeaderDoneCallback(transfer: *Http.Transfer) !void { - var self: *Page = @alignCast(@ptrCast(transfer.ctx)); + var self: *Page = @ptrCast(@alignCast(transfer.ctx)); // would be different than self.url in the case of a redirect const header = &transfer.response_header.?; @@ -625,7 +625,7 @@ pub const Page = struct { } fn pageDataCallback(transfer: *Http.Transfer, data: []const u8) !void { - var self: *Page = @alignCast(@ptrCast(transfer.ctx)); + var self: *Page = @ptrCast(@alignCast(transfer.ctx)); if (self.mode == .pre) { // we lazily do this, because we might need the first chunk of data @@ -686,7 +686,7 @@ pub const Page = struct { fn pageDoneCallback(ctx: *anyopaque) !void { log.debug(.http, "navigate done", .{}); - var self: *Page = @alignCast(@ptrCast(ctx)); + var self: *Page = @ptrCast(@alignCast(ctx)); self.clearTransferArena(); switch (self.mode) { @@ -772,7 +772,7 @@ pub const Page = struct { fn pageErrorCallback(ctx: *anyopaque, err: anyerror) void { log.err(.http, "navigate failed", .{ .err = err }); - var self: *Page = @alignCast(@ptrCast(ctx)); + var self: *Page = @ptrCast(@alignCast(ctx)); self.clearTransferArena(); switch (self.mode) { @@ -1015,7 +1015,7 @@ pub const Page = struct { pub fn getNodeState(_: *const Page, node: *parser.Node) ?*State { if (parser.nodeGetEmbedderData(node)) |state| { - return @alignCast(@ptrCast(state)); + return @ptrCast(@alignCast(state)); } return null; } @@ -1026,13 +1026,13 @@ pub const Page = struct { const transfer_arena = self.session.transfer_arena; var form_data = try FormData.fromForm(form, submitter, self); - const encoding = try parser.elementGetAttribute(@alignCast(@ptrCast(form)), "enctype"); + const encoding = try parser.elementGetAttribute(@ptrCast(@alignCast(form)), "enctype"); var buf: std.ArrayListUnmanaged(u8) = .empty; try form_data.write(encoding, buf.writer(transfer_arena)); - const method = try parser.elementGetAttribute(@alignCast(@ptrCast(form)), "method") orelse ""; - var action = try parser.elementGetAttribute(@alignCast(@ptrCast(form)), "action") orelse self.url.raw; + const method = try parser.elementGetAttribute(@ptrCast(@alignCast(form)), "method") orelse ""; + var action = try parser.elementGetAttribute(@ptrCast(@alignCast(form)), "action") orelse self.url.raw; var opts = NavigateOpts{ .reason = .form, @@ -1113,7 +1113,7 @@ fn timestamp() u32 { // after the document is loaded, it's ok to execute any async and defer scripts // immediately. pub export fn scriptAddedCallback(ctx: ?*anyopaque, element: ?*parser.Element) callconv(.c) void { - const self: *Page = @alignCast(@ptrCast(ctx.?)); + const self: *Page = @ptrCast(@alignCast(ctx.?)); if (self.delayed_navigation) { // if we're planning on navigating to another page, don't run this script diff --git a/src/browser/storage/cookie.zig b/src/browser/storage/cookie.zig index de18dd4e..cd95fec6 100644 --- a/src/browser/storage/cookie.zig +++ b/src/browser/storage/cookie.zig @@ -358,14 +358,10 @@ pub const Cookie = struct { if (domain.len > 0) { const no_leading_dot = if (domain[0] == '.') domain[1..] else domain; - var list: std.ArrayListUnmanaged(u8) = .empty; - try list.ensureTotalCapacity(arena, no_leading_dot.len + 1); // Expect no precents needed - list.appendAssumeCapacity('.'); - try std.Uri.Component.percentEncode(list.writer( - arena, - ), no_leading_dot, isHostChar); - var owned_domain: []u8 = list.items; // @memory retains memory used before growing - _ = toLower(owned_domain); + var aw = try std.Io.Writer.Allocating.initCapacity(arena, no_leading_dot.len + 1); + try aw.writer.writeByte('.'); + try std.Uri.Component.percentEncode(&aw.writer, no_leading_dot, isHostChar); + const owned_domain = toLower(aw.written()); if (std.mem.indexOfScalarPos(u8, owned_domain, 1, '.') == null and std.mem.eql(u8, "localhost", owned_domain[1..]) == false) { // can't set a cookie for a TLD @@ -387,10 +383,9 @@ pub const Cookie = struct { pub fn percentEncode(arena: Allocator, component: std.Uri.Component, comptime isValidChar: fn (u8) bool) ![]u8 { switch (component) { .raw => |str| { - var list: std.ArrayListUnmanaged(u8) = .empty; - try list.ensureTotalCapacity(arena, str.len); // Expect no precents needed - try std.Uri.Component.percentEncode(list.writer(arena), str, isValidChar); - return list.items; // @memory retains memory used before growing + var aw = try std.Io.Writer.Allocating.initCapacity(arena, str.len); + try std.Uri.Component.percentEncode(&aw.writer, str, isValidChar); + return aw.written(); // @memory retains memory used before growing }, .percent_encoded => |str| { return try arena.dupe(u8, str); diff --git a/src/browser/url/url.zig b/src/browser/url/url.zig index 18cda3a2..625e1452 100644 --- a/src/browser/url/url.zig +++ b/src/browser/url/url.zig @@ -114,16 +114,16 @@ pub const URL = struct { } pub fn get_origin(self: *URL, page: *Page) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; - try self.uri.writeToStream(.{ + var aw = std.Io.Writer.Allocating.init(page.arena); + try self.uri.writeToStream(&aw.writer, .{ .scheme = true, .authentication = false, .authority = true, .path = false, .query = false, .fragment = false, - }, buf.writer(page.arena)); - return buf.items; + }); + return aw.written(); } // get_href returns the URL by writing all its components. @@ -137,28 +137,28 @@ pub const URL = struct { // format the url with all its components. pub fn toString(self: *const URL, arena: Allocator) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; - try self.uri.writeToStream(.{ + var aw = std.Io.Writer.Allocating.init(arena); + try self.uri.writeToStream(&aw.writer, .{ .scheme = true, .authentication = true, .authority = true, .path = uriComponentNullStr(self.uri.path).len > 0, - }, buf.writer(arena)); + }); if (self.search_params.get_size() > 0) { - try buf.append(arena, '?'); - try self.search_params.write(buf.writer(arena)); + try aw.writer.writeByte('?'); + try self.search_params.write(&aw.writer); } { const fragment = uriComponentNullStr(self.uri.fragment); if (fragment.len > 0) { - try buf.append(arena, '#'); - try buf.appendSlice(arena, fragment); + try aw.writer.writeByte('#'); + try aw.writer.writeAll(fragment); } } - return buf.items; + return aw.written(); } pub fn get_protocol(self: *URL, page: *Page) ![]const u8 { @@ -174,17 +174,16 @@ pub const URL = struct { } pub fn get_host(self: *URL, page: *Page) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; - - try self.uri.writeToStream(.{ + var aw = std.Io.Writer.Allocating.init(page.arena); + try self.uri.writeToStream(&aw.writer, .{ .scheme = false, .authentication = false, .authority = true, .path = false, .query = false, .fragment = false, - }, buf.writer(page.arena)); - return buf.items; + }); + return aw.written(); } pub fn get_hostname(self: *URL) []const u8 { @@ -195,9 +194,9 @@ pub const URL = struct { const arena = page.arena; if (self.uri.port == null) return try arena.dupe(u8, ""); - var buf: std.ArrayListUnmanaged(u8) = .empty; - try std.fmt.formatInt(self.uri.port.?, 10, .lower, .{}, buf.writer(arena)); - return buf.items; + var aw = std.Io.Writer.Allocating.init(arena); + try aw.writer.printInt(self.uri.port.?, 10, .lower, .{}); + return aw.written(); } pub fn get_pathname(self: *URL) []const u8 { diff --git a/src/browser/xhr/form_data.zig b/src/browser/xhr/form_data.zig index 6dc1a69f..eefde975 100644 --- a/src/browser/xhr/form_data.zig +++ b/src/browser/xhr/form_data.zig @@ -123,7 +123,7 @@ fn collectForm(form: *parser.Form, submitter_: ?*parser.ElementHTML, page: *Page // probably want to implement (like disabled fieldsets), so we might want // to stick with our own walker even if fix libdom to properly support // dynamically added elements. - const node_list = try @import("../dom/css.zig").querySelectorAll(arena, @alignCast(@ptrCast(form)), "input,select,button,textarea"); + const node_list = try @import("../dom/css.zig").querySelectorAll(arena, @ptrCast(@alignCast(form)), "input,select,button,textarea"); const nodes = node_list.nodes.items; var entries: kv.List = .{}; @@ -220,7 +220,7 @@ fn collectSelectValues(arena: Allocator, select: *parser.Select, name: []const u if (is_multiple == false) { const option = try parser.optionCollectionItem(options, @intCast(selected_index)); - if (try parser.elementGetAttribute(@alignCast(@ptrCast(option)), "disabled") != null) { + if (try parser.elementGetAttribute(@ptrCast(@alignCast(option)), "disabled") != null) { return; } const value = try parser.optionGetValue(option); @@ -232,7 +232,7 @@ fn collectSelectValues(arena: Allocator, select: *parser.Select, name: []const u // we can go directly to the first one for (@intCast(selected_index)..len) |i| { const option = try parser.optionCollectionItem(options, @intCast(i)); - if (try parser.elementGetAttribute(@alignCast(@ptrCast(option)), "disabled") != null) { + if (try parser.elementGetAttribute(@ptrCast(@alignCast(option)), "disabled") != null) { continue; } diff --git a/src/browser/xhr/xhr.zig b/src/browser/xhr/xhr.zig index 51b27454..76ca98e9 100644 --- a/src/browser/xhr/xhr.zig +++ b/src/browser/xhr/xhr.zig @@ -352,7 +352,7 @@ pub const XMLHttpRequest = struct { return self.headers.append( self.arena, - try std.fmt.allocPrintZ(self.arena, "{s}: {s}", .{ name, value }), + try std.fmt.allocPrintSentinel(self.arena, "{s}: {s}", .{ name, value }, 0), ); } @@ -393,19 +393,19 @@ pub const XMLHttpRequest = struct { } fn httpStartCallback(transfer: *Http.Transfer) !void { - const self: *XMLHttpRequest = @alignCast(@ptrCast(transfer.ctx)); + const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); log.debug(.http, "request start", .{ .method = self.method, .url = self.url, .source = "xhr" }); self.transfer = transfer; } fn httpHeaderCallback(transfer: *Http.Transfer, header: Http.Header) !void { - const self: *XMLHttpRequest = @alignCast(@ptrCast(transfer.ctx)); + const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); const joined = try std.fmt.allocPrint(self.arena, "{s}: {s}", .{ header.name, header.value }); try self.response_headers.append(self.arena, joined); } fn httpHeaderDoneCallback(transfer: *Http.Transfer) !void { - const self: *XMLHttpRequest = @alignCast(@ptrCast(transfer.ctx)); + const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); const header = &transfer.response_header.?; @@ -441,7 +441,7 @@ pub const XMLHttpRequest = struct { } fn httpDataCallback(transfer: *Http.Transfer, data: []const u8) !void { - const self: *XMLHttpRequest = @alignCast(@ptrCast(transfer.ctx)); + const self: *XMLHttpRequest = @ptrCast(@alignCast(transfer.ctx)); try self.response_bytes.appendSlice(self.arena, data); const now = std.time.milliTimestamp(); @@ -459,7 +459,7 @@ pub const XMLHttpRequest = struct { } fn httpDoneCallback(ctx: *anyopaque) !void { - const self: *XMLHttpRequest = @alignCast(@ptrCast(ctx)); + const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx)); log.info(.http, "request complete", .{ .source = "xhr", @@ -484,7 +484,7 @@ pub const XMLHttpRequest = struct { } fn httpErrorCallback(ctx: *anyopaque, err: anyerror) void { - const self: *XMLHttpRequest = @alignCast(@ptrCast(ctx)); + const self: *XMLHttpRequest = @ptrCast(@alignCast(ctx)); // http client will close it after an error, it isn't safe to keep around self.transfer = null; self.onErr(err); diff --git a/src/browser/xmlserializer/xmlserializer.zig b/src/browser/xmlserializer/xmlserializer.zig index 6df52bf3..b268d21d 100644 --- a/src/browser/xmlserializer/xmlserializer.zig +++ b/src/browser/xmlserializer/xmlserializer.zig @@ -34,13 +34,13 @@ pub const XMLSerializer = struct { } pub fn _serializeToString(_: *const XMLSerializer, root: *parser.Node, page: *Page) ![]const u8 { - var buf: std.ArrayListUnmanaged(u8) = .empty; + var aw = std.Io.Writer.Allocating.init(page.call_arena); switch (try parser.nodeType(root)) { - .document => try dump.writeHTML(@as(*parser.Document, @ptrCast(root)), .{}, buf.writer(page.call_arena)), - .document_type => try dump.writeDocType(@as(*parser.DocumentType, @ptrCast(root)), buf.writer(page.call_arena)), - else => try dump.writeNode(root, .{}, buf.writer(page.call_arena)), + .document => try dump.writeHTML(@as(*parser.Document, @ptrCast(root)), .{}, &aw.writer), + .document_type => try dump.writeDocType(@as(*parser.DocumentType, @ptrCast(root)), &aw.writer), + else => try dump.writeNode(root, .{}, &aw.writer), } - return buf.items; + return aw.written(); } }; diff --git a/src/cdp/Node.zig b/src/cdp/Node.zig index 709b4a0a..6d4f5f8d 100644 --- a/src/cdp/Node.zig +++ b/src/cdp/Node.zig @@ -211,11 +211,11 @@ pub const Writer = struct { exclude_root: bool = false, }; - pub fn jsonStringify(self: *const Writer, w: anytype) !void { + pub fn jsonStringify(self: *const Writer, w: anytype) error{WriteFailed}!void { if (self.exclude_root) { _ = self.writeChildren(self.root, 1, w) catch |err| { log.err(.cdp, "node writeChildren", .{ .err = err }); - return error.OutOfMemory; + return error.WriteFailed; }; } else { self.toJSON(self.root, 0, w) catch |err| { @@ -223,7 +223,7 @@ pub const Writer = struct { // @TypeOf(w).Error. In other words, our code can't return its own // error, we can only return a writer error. Kinda sucks. log.err(.cdp, "node toJSON stringify", .{ .err = err }); - return error.OutOfMemory; + return error.WriteFailed; }; } } @@ -425,7 +425,7 @@ test "cdp Node: Writer" { { const node = try registry.register(doc.asNode()); - const json = try std.json.stringifyAlloc(testing.allocator, Writer{ + const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, .depth = 0, .exclude_root = false, @@ -465,7 +465,7 @@ test "cdp Node: Writer" { { const node = registry.lookup_by_id.get(1).?; - const json = try std.json.stringifyAlloc(testing.allocator, Writer{ + const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, .depth = 1, .exclude_root = false, @@ -520,7 +520,7 @@ test "cdp Node: Writer" { { const node = registry.lookup_by_id.get(1).?; - const json = try std.json.stringifyAlloc(testing.allocator, Writer{ + const json = try std.json.Stringify.valueAlloc(testing.allocator, Writer{ .root = node, .depth = -1, .exclude_root = true, diff --git a/src/cdp/cdp.zig b/src/cdp/cdp.zig index b8b8bfc6..a9d932f0 100644 --- a/src/cdp/cdp.zig +++ b/src/cdp/cdp.zig @@ -487,58 +487,58 @@ pub fn BrowserContext(comptime CDP_T: type) type { } pub fn onPageRemove(ctx: *anyopaque, _: Notification.PageRemove) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); try @import("domains/page.zig").pageRemove(self); } pub fn onPageCreated(ctx: *anyopaque, page: *Page) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); return @import("domains/page.zig").pageCreated(self, page); } pub fn onPageNavigate(ctx: *anyopaque, msg: *const Notification.PageNavigate) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); defer self.resetNotificationArena(); return @import("domains/page.zig").pageNavigate(self.notification_arena, self, msg); } pub fn onPageNavigated(ctx: *anyopaque, msg: *const Notification.PageNavigated) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); return @import("domains/page.zig").pageNavigated(self, msg); } pub fn onHttpRequestStart(ctx: *anyopaque, msg: *const Notification.RequestStart) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); defer self.resetNotificationArena(); try @import("domains/network.zig").httpRequestStart(self.notification_arena, self, msg); } pub fn onHttpRequestIntercept(ctx: *anyopaque, msg: *const Notification.RequestIntercept) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); defer self.resetNotificationArena(); try @import("domains/fetch.zig").requestIntercept(self.notification_arena, self, msg); } pub fn onHttpRequestFail(ctx: *anyopaque, msg: *const Notification.RequestFail) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); defer self.resetNotificationArena(); return @import("domains/network.zig").httpRequestFail(self.notification_arena, self, msg); } pub fn onHttpResponseHeadersDone(ctx: *anyopaque, msg: *const Notification.ResponseHeaderDone) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); defer self.resetNotificationArena(); return @import("domains/network.zig").httpResponseHeaderDone(self.notification_arena, self, msg); } pub fn onHttpRequestDone(ctx: *anyopaque, msg: *const Notification.RequestDone) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); defer self.resetNotificationArena(); return @import("domains/network.zig").httpRequestDone(self.notification_arena, self, msg); } pub fn onHttpResponseData(ctx: *anyopaque, msg: *const Notification.ResponseData) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); const arena = self.arena; const id = msg.transfer.id; @@ -550,7 +550,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { } pub fn onHttpRequestAuthRequired(ctx: *anyopaque, data: *const Notification.RequestAuthRequired) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); defer self.resetNotificationArena(); try @import("domains/fetch.zig").requestAuthRequired(self.notification_arena, self, data); } @@ -566,7 +566,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { } pub fn onInspectorResponse(ctx: *anyopaque, _: u32, msg: []const u8) void { - sendInspectorMessage(@alignCast(@ptrCast(ctx)), msg) catch |err| { + sendInspectorMessage(@ptrCast(@alignCast(ctx)), msg) catch |err| { log.err(.cdp, "send inspector response", .{ .err = err }); }; } @@ -583,7 +583,7 @@ pub fn BrowserContext(comptime CDP_T: type) type { log.debug(.cdp, "inspector event", .{ .method = method }); } - sendInspectorMessage(@alignCast(@ptrCast(ctx)), msg) catch |err| { + sendInspectorMessage(@ptrCast(@alignCast(ctx)), msg) catch |err| { log.err(.cdp, "send inspector event", .{ .err = err }); }; } diff --git a/src/cdp/domains/dom.zig b/src/cdp/domains/dom.zig index 294f78a7..64f24fdf 100644 --- a/src/cdp/domains/dom.zig +++ b/src/cdp/domains/dom.zig @@ -372,7 +372,7 @@ fn getNode(arena: Allocator, browser_context: anytype, node_id: ?Node.Id, backen if (object_id) |object_id_| { // Retrieve the object from which ever context it is in. const parser_node = try browser_context.inspector.getNodePtr(arena, object_id_); - return try browser_context.node_registry.register(@alignCast(@ptrCast(parser_node))); + return try browser_context.node_registry.register(@ptrCast(@alignCast(parser_node))); } return error.MissingParams; } diff --git a/src/cdp/domains/fetch.zig b/src/cdp/domains/fetch.zig index ca4258bd..9ff26102 100644 --- a/src/cdp/domains/fetch.zig +++ b/src/cdp/domains/fetch.zig @@ -315,10 +315,10 @@ fn continueWithAuth(cmd: anytype) !void { // restart the request with the provided credentials. const arena = transfer.arena.allocator(); transfer.updateCredentials( - try std.fmt.allocPrintZ(arena, "{s}:{s}", .{ + try std.fmt.allocPrintSentinel(arena, "{s}:{s}", .{ params.authChallengeResponse.username, params.authChallengeResponse.password, - }), + }, 0), ); transfer.reset(); diff --git a/src/cdp/domains/network.zig b/src/cdp/domains/network.zig index dd983137..e713b103 100644 --- a/src/cdp/domains/network.zig +++ b/src/cdp/domains/network.zig @@ -81,7 +81,7 @@ fn setExtraHTTPHeaders(cmd: anytype) !void { try extra_headers.ensureTotalCapacity(arena, params.headers.map.count()); var it = params.headers.map.iterator(); while (it.next()) |header| { - const header_string = try std.fmt.allocPrintZ(arena, "{s}: {s}", .{ header.key_ptr.*, header.value_ptr.* }); + const header_string = try std.fmt.allocPrintSentinel(arena, "{s}: {s}", .{ header.key_ptr.*, header.value_ptr.* }, 0); extra_headers.appendAssumeCapacity(header_string); } @@ -296,58 +296,61 @@ pub const TransferAsRequestWriter = struct { }; } - pub fn jsonStringify(self: *const TransferAsRequestWriter, writer: anytype) !void { - const stream = writer.stream; + pub fn jsonStringify(self: *const TransferAsRequestWriter, jws: anytype) !void { + self._jsonStringify(jws) catch return error.WriteFailed; + } + fn _jsonStringify(self: *const TransferAsRequestWriter, jws: anytype) !void { + const writer = jws.writer; const transfer = self.transfer; - try writer.beginObject(); + try jws.beginObject(); { - try writer.objectField("url"); - try writer.beginWriteRaw(); - try stream.writeByte('\"'); - try transfer.uri.writeToStream(.{ + try jws.objectField("url"); + try jws.beginWriteRaw(); + try writer.writeByte('\"'); + try transfer.uri.writeToStream(writer, .{ .scheme = true, .authentication = true, .authority = true, .path = true, .query = true, - }, stream); - try stream.writeByte('\"'); - writer.endWriteRaw(); + }); + try writer.writeByte('\"'); + jws.endWriteRaw(); } { if (transfer.uri.fragment) |frag| { - try writer.objectField("urlFragment"); - try writer.beginWriteRaw(); - try stream.writeAll("\"#"); - try stream.writeAll(frag.percent_encoded); - try stream.writeByte('\"'); - writer.endWriteRaw(); + try jws.objectField("urlFragment"); + try jws.beginWriteRaw(); + try writer.writeAll("\"#"); + try writer.writeAll(frag.percent_encoded); + try writer.writeByte('\"'); + jws.endWriteRaw(); } } { - try writer.objectField("method"); - try writer.write(@tagName(transfer.req.method)); + try jws.objectField("method"); + try jws.write(@tagName(transfer.req.method)); } { - try writer.objectField("hasPostData"); - try writer.write(transfer.req.body != null); + try jws.objectField("hasPostData"); + try jws.write(transfer.req.body != null); } { - try writer.objectField("headers"); - try writer.beginObject(); + try jws.objectField("headers"); + try jws.beginObject(); var it = transfer.req.headers.iterator(); while (it.next()) |hdr| { - try writer.objectField(hdr.name); - try writer.write(hdr.value); + try jws.objectField(hdr.name); + try jws.write(hdr.value); } - try writer.endObject(); + try jws.endObject(); } - try writer.endObject(); + try jws.endObject(); } }; @@ -362,35 +365,39 @@ const TransferAsResponseWriter = struct { }; } - pub fn jsonStringify(self: *const TransferAsResponseWriter, writer: anytype) !void { - const stream = writer.stream; + pub fn jsonStringify(self: *const TransferAsResponseWriter, jws: anytype) !void { + self._jsonStringify(jws) catch return error.WriteFailed; + } + + fn _jsonStringify(self: *const TransferAsResponseWriter, jws: anytype) !void { + const writer = jws.writer; const transfer = self.transfer; - try writer.beginObject(); + try jws.beginObject(); { - try writer.objectField("url"); - try writer.beginWriteRaw(); - try stream.writeByte('\"'); - try transfer.uri.writeToStream(.{ + try jws.objectField("url"); + try jws.beginWriteRaw(); + try writer.writeByte('\"'); + try transfer.uri.writeToStream(writer, .{ .scheme = true, .authentication = true, .authority = true, .path = true, .query = true, - }, stream); - try stream.writeByte('\"'); - writer.endWriteRaw(); + }); + try writer.writeByte('\"'); + jws.endWriteRaw(); } if (transfer.response_header) |*rh| { // it should not be possible for this to be false, but I'm not // feeling brave today. const status = rh.status; - try writer.objectField("status"); - try writer.write(status); + try jws.objectField("status"); + try jws.write(status); - try writer.objectField("statusText"); - try writer.write(@as(std.http.Status, @enumFromInt(status)).phrase() orelse "Unknown"); + try jws.objectField("statusText"); + try jws.write(@as(std.http.Status, @enumFromInt(status)).phrase() orelse "Unknown"); } { @@ -410,10 +417,10 @@ const TransferAsResponseWriter = struct { } } - try writer.objectField("headers"); - try writer.write(std.json.ArrayHashMap([]const u8){ .map = map }); + try jws.objectField("headers"); + try jws.write(std.json.ArrayHashMap([]const u8){ .map = map }); } - try writer.endObject(); + try jws.endObject(); } }; @@ -426,20 +433,23 @@ const DocumentUrlWriter = struct { }; } - pub fn jsonStringify(self: *const DocumentUrlWriter, writer: anytype) !void { - const stream = writer.stream; + pub fn jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void { + self._jsonStringify(jws) catch return error.WriteFailed; + } + fn _jsonStringify(self: *const DocumentUrlWriter, jws: anytype) !void { + const writer = jws.writer; - try writer.beginWriteRaw(); - try stream.writeByte('\"'); - try self.uri.writeToStream(.{ + try jws.beginWriteRaw(); + try writer.writeByte('\"'); + try self.uri.writeToStream(writer, .{ .scheme = true, .authentication = true, .authority = true, .path = true, .query = true, - }, stream); - try stream.writeByte('\"'); - writer.endWriteRaw(); + }); + try writer.writeByte('\"'); + jws.endWriteRaw(); } }; diff --git a/src/cdp/domains/storage.zig b/src/cdp/domains/storage.zig index 53e8320c..bb60f6a2 100644 --- a/src/cdp/domains/storage.zig +++ b/src/cdp/domains/storage.zig @@ -170,7 +170,7 @@ pub const CookieWriter = struct { self.writeCookies(w) catch |err| { // The only error our jsonStringify method can return is @TypeOf(w).Error. log.err(.cdp, "json stringify", .{ .err = err }); - return error.OutOfMemory; + return error.WriteFailed; }; } diff --git a/src/cdp/domains/target.zig b/src/cdp/domains/target.zig index fdf01be4..87fe4324 100644 --- a/src/cdp/domains/target.zig +++ b/src/cdp/domains/target.zig @@ -304,19 +304,17 @@ fn sendMessageToTarget(cmd: anytype) !void { } const Capture = struct { - allocator: std.mem.Allocator, - buf: std.ArrayListUnmanaged(u8), + aw: std.Io.Writer.Allocating, pub fn sendJSON(self: *@This(), message: anytype) !void { - return std.json.stringify(message, .{ + return std.json.Stringify.value(message, .{ .emit_null_optional_fields = false, - }, self.buf.writer(self.allocator)); + }, &self.aw.writer); } }; var capture = Capture{ - .buf = .{}, - .allocator = cmd.arena, + .aw = .init(cmd.arena), }; cmd.cdp.dispatch(cmd.arena, &capture, params.message) catch |err| { @@ -325,7 +323,7 @@ fn sendMessageToTarget(cmd: anytype) !void { }; try cmd.sendEvent("Target.receivedMessageFromTarget", .{ - .message = capture.buf.items, + .message = capture.aw.written(), .sessionId = params.sessionId, }, .{}); } diff --git a/src/cdp/testing.zig b/src/cdp/testing.zig index c6076c4e..1ca0ab89 100644 --- a/src/cdp/testing.zig +++ b/src/cdp/testing.zig @@ -50,10 +50,10 @@ const Client = struct { }; } - pub fn sendJSON(self: *Client, message: anytype, opts: json.StringifyOptions) !void { + pub fn sendJSON(self: *Client, message: anytype, opts: json.Stringify.Options) !void { var opts_copy = opts; opts_copy.whitespace = .indent_2; - const serialized = try json.stringifyAlloc(self.allocator, message, opts_copy); + const serialized = try json.Stringify.valueAlloc(self.allocator, message, opts_copy); try self.serialized.append(self.allocator, serialized); const value = try json.parseFromSliceLeaky(json.Value, self.allocator, serialized, .{}); @@ -131,7 +131,7 @@ const TestContext = struct { pub fn processMessage(self: *TestContext, msg: anytype) !void { var json_message: []const u8 = undefined; if (@typeInfo(@TypeOf(msg)) != .pointer) { - json_message = try std.json.stringifyAlloc(self.arena.allocator(), msg, .{}); + json_message = try std.json.Stringify.valueAlloc(self.arena.allocator(), msg, .{}); } else { // assume this is a string we want to send as-is, if it isn't, we'll // get a compile error, so no big deal. @@ -189,7 +189,7 @@ const TestContext = struct { index: ?usize = null, }; pub fn expectSent(self: *TestContext, expected: anytype, opts: SentOpts) !void { - const serialized = try json.stringifyAlloc(self.arena.allocator(), expected, .{ + const serialized = try json.Stringify.valueAlloc(self.arena.allocator(), expected, .{ .whitespace = .indent_2, .emit_null_optional_fields = false, }); diff --git a/src/datetime.zig b/src/datetime.zig index d54ff330..3bfe4043 100644 --- a/src/datetime.zig +++ b/src/datetime.zig @@ -67,10 +67,10 @@ pub const Date = struct { return std.math.order(a.day, b.day); } - pub fn format(self: Date, comptime _: []const u8, _: std.fmt.FormatOptions, out: anytype) !void { + pub fn format(self: Date, writer: *std.Io.Writer) !void { var buf: [11]u8 = undefined; const n = writeDate(&buf, self); - try out.writeAll(buf[0..n]); + try writer.writeAll(buf[0..n]); } pub fn jsonStringify(self: Date, out: anytype) !void { @@ -167,10 +167,10 @@ pub const Time = struct { return std.math.order(a.micros, b.micros); } - pub fn format(self: Time, comptime _: []const u8, _: std.fmt.FormatOptions, out: anytype) !void { + pub fn format(self: Time, writer: *std.Io.Writer) !void { var buf: [15]u8 = undefined; const n = writeTime(&buf, self); - try out.writeAll(buf[0..n]); + try writer.writeAll(buf[0..n]); } pub fn jsonStringify(self: Time, out: anytype) !void { @@ -464,10 +464,10 @@ pub const DateTime = struct { return std.math.order(a.micros, b.micros); } - pub fn format(self: DateTime, comptime _: []const u8, _: std.fmt.FormatOptions, out: anytype) !void { + pub fn format(self: DateTime, writer: *std.Io.Writer) !void { var buf: [28]u8 = undefined; const n = self.bufWrite(&buf); - try out.writeAll(buf[0..n]); + try writer.writeAll(buf[0..n]); } pub fn jsonStringify(self: DateTime, out: anytype) !void { @@ -510,11 +510,11 @@ fn writeDate(into: []u8, date: Date) u8 { // the padding (we need to do it ourselfs) const year = date.year; if (year < 0) { - _ = std.fmt.formatIntBuf(into[1..], @as(u16, @intCast(year * -1)), 10, .lower, .{ .width = 4, .fill = '0' }); + _ = std.fmt.printInt(into[1..], @as(u16, @intCast(year * -1)), 10, .lower, .{ .width = 4, .fill = '0' }); into[0] = '-'; buf = into[5..]; } else { - _ = std.fmt.formatIntBuf(into, @as(u16, @intCast(year)), 10, .lower, .{ .width = 4, .fill = '0' }); + _ = std.fmt.printInt(into, @as(u16, @intCast(year)), 10, .lower, .{ .width = 4, .fill = '0' }); buf = into[4..]; } @@ -541,12 +541,12 @@ fn writeTime(into: []u8, time: Time) u8 { if (@rem(micros, 1000) == 0) { into[8] = '.'; - _ = std.fmt.formatIntBuf(into[9..12], micros / 1000, 10, .lower, .{ .width = 3, .fill = '0' }); + _ = std.fmt.printInt(into[9..12], micros / 1000, 10, .lower, .{ .width = 3, .fill = '0' }); return 12; } into[8] = '.'; - _ = std.fmt.formatIntBuf(into[9..15], micros, 10, .lower, .{ .width = 6, .fill = '0' }); + _ = std.fmt.printInt(into[9..15], micros, 10, .lower, .{ .width = 6, .fill = '0' }); return 15; } @@ -730,7 +730,7 @@ test "Date: json" { { // date, positive year const date = Date{ .year = 2023, .month = 9, .day = 22 }; - const out = try std.json.stringifyAlloc(testing.allocator, date, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, date, .{}); defer testing.allocator.free(out); try testing.expectString("\"2023-09-22\"", out); } @@ -738,7 +738,7 @@ test "Date: json" { { // date, negative year const date = Date{ .year = -4, .month = 12, .day = 3 }; - const out = try std.json.stringifyAlloc(testing.allocator, date, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, date, .{}); defer testing.allocator.free(out); try testing.expectString("\"-0004-12-03\"", out); } @@ -754,13 +754,13 @@ test "Date: json" { test "Date: format" { { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Date{ .year = 2023, .month = 5, .day = 22 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Date{ .year = 2023, .month = 5, .day = 22 }}); try testing.expectString("2023-05-22", out); } { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Date{ .year = -102, .month = 12, .day = 9 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Date{ .year = -102, .month = 12, .day = 9 }}); try testing.expectString("-0102-12-09", out); } } @@ -913,7 +913,7 @@ test "Time: json" { { // time no fraction const time = Time{ .hour = 23, .min = 59, .sec = 2, .micros = 0 }; - const out = try std.json.stringifyAlloc(testing.allocator, time, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, time, .{}); defer testing.allocator.free(out); try testing.expectString("\"23:59:02\"", out); } @@ -921,7 +921,7 @@ test "Time: json" { { // time, milliseconds only const time = Time{ .hour = 7, .min = 9, .sec = 32, .micros = 202000 }; - const out = try std.json.stringifyAlloc(testing.allocator, time, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, time, .{}); defer testing.allocator.free(out); try testing.expectString("\"07:09:32.202\"", out); } @@ -929,7 +929,7 @@ test "Time: json" { { // time, micros const time = Time{ .hour = 1, .min = 2, .sec = 3, .micros = 123456 }; - const out = try std.json.stringifyAlloc(testing.allocator, time, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, time, .{}); defer testing.allocator.free(out); try testing.expectString("\"01:02:03.123456\"", out); } @@ -945,37 +945,37 @@ test "Time: json" { test "Time: format" { { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Time{ .hour = 23, .min = 59, .sec = 59, .micros = 0 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Time{ .hour = 23, .min = 59, .sec = 59, .micros = 0 }}); try testing.expectString("23:59:59", out); } { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 12 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 12 }}); try testing.expectString("08:09:10.000012", out); } { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 123 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 123 }}); try testing.expectString("08:09:10.000123", out); } { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 1234 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 1234 }}); try testing.expectString("08:09:10.001234", out); } { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 12345 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 12345 }}); try testing.expectString("08:09:10.012345", out); } { var buf: [20]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 123456 }}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{Time{ .hour = 8, .min = 9, .sec = 10, .micros = 123456 }}); try testing.expectString("08:09:10.123456", out); } } @@ -1625,7 +1625,7 @@ test "DateTime: json" { { // DateTime, time no fraction const dt = try DateTime.parse("2023-09-22T23:59:02Z", .rfc3339); - const out = try std.json.stringifyAlloc(testing.allocator, dt, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, dt, .{}); defer testing.allocator.free(out); try testing.expectString("\"2023-09-22T23:59:02Z\"", out); } @@ -1633,7 +1633,7 @@ test "DateTime: json" { { // time, milliseconds only const dt = try DateTime.parse("2023-09-22T07:09:32.202Z", .rfc3339); - const out = try std.json.stringifyAlloc(testing.allocator, dt, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, dt, .{}); defer testing.allocator.free(out); try testing.expectString("\"2023-09-22T07:09:32.202Z\"", out); } @@ -1641,7 +1641,7 @@ test "DateTime: json" { { // time, micros const dt = try DateTime.parse("-0004-12-03T01:02:03.123456Z", .rfc3339); - const out = try std.json.stringifyAlloc(testing.allocator, dt, .{}); + const out = try std.json.Stringify.valueAlloc(testing.allocator, dt, .{}); defer testing.allocator.free(out); try testing.expectString("\"-0004-12-03T01:02:03.123456Z\"", out); } @@ -1657,37 +1657,37 @@ test "DateTime: json" { test "DateTime: format" { { var buf: [30]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{try DateTime.initUTC(2023, 5, 22, 23, 59, 59, 0)}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{try DateTime.initUTC(2023, 5, 22, 23, 59, 59, 0)}); try testing.expectString("2023-05-22T23:59:59Z", out); } { var buf: [30]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{try DateTime.initUTC(2023, 5, 22, 8, 9, 10, 12)}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{try DateTime.initUTC(2023, 5, 22, 8, 9, 10, 12)}); try testing.expectString("2023-05-22T08:09:10.000012Z", out); } { var buf: [30]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{try DateTime.initUTC(2023, 5, 22, 8, 9, 10, 123)}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{try DateTime.initUTC(2023, 5, 22, 8, 9, 10, 123)}); try testing.expectString("2023-05-22T08:09:10.000123Z", out); } { var buf: [30]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{try DateTime.initUTC(2023, 5, 22, 8, 9, 10, 1234)}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{try DateTime.initUTC(2023, 5, 22, 8, 9, 10, 1234)}); try testing.expectString("2023-05-22T08:09:10.001234Z", out); } { var buf: [30]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{try DateTime.initUTC(-102, 12, 9, 8, 9, 10, 12345)}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{try DateTime.initUTC(-102, 12, 9, 8, 9, 10, 12345)}); try testing.expectString("-0102-12-09T08:09:10.012345Z", out); } { var buf: [30]u8 = undefined; - const out = try std.fmt.bufPrint(&buf, "{s}", .{try DateTime.initUTC(-102, 12, 9, 8, 9, 10, 123456)}); + const out = try std.fmt.bufPrint(&buf, "{f}", .{try DateTime.initUTC(-102, 12, 9, 8, 9, 10, 123456)}); try testing.expectString("-0102-12-09T08:09:10.123456Z", out); } } @@ -2075,7 +2075,7 @@ test "DateTime: sub" { fn expectDateTime(expected: []const u8, dt: DateTime) !void { var buf: [30]u8 = undefined; - const actual = try std.fmt.bufPrint(&buf, "{s}", .{dt}); + const actual = try std.fmt.bufPrint(&buf, "{f}", .{dt}); try testing.expectString(expected, actual); } diff --git a/src/http/Client.zig b/src/http/Client.zig index 138e20e1..02e45207 100644 --- a/src/http/Client.zig +++ b/src/http/Client.zig @@ -69,9 +69,6 @@ next_request_id: u64 = 0, // When handles has no more available easys, requests get queued. queue: TransferQueue, -// Memory pool for Queue nodes. -queue_node_pool: std.heap.MemoryPool(TransferQueue.Node), - // The main app allocator allocator: Allocator, @@ -90,15 +87,12 @@ notification: ?*Notification = null, // restoring, this originally-configured value is what it goes to. http_proxy: ?[:0]const u8 = null, -const TransferQueue = std.DoublyLinkedList(*Transfer); +const TransferQueue = std.DoublyLinkedList; pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Client { var transfer_pool = std.heap.MemoryPool(Transfer).init(allocator); errdefer transfer_pool.deinit(); - var queue_node_pool = std.heap.MemoryPool(TransferQueue.Node).init(allocator); - errdefer queue_node_pool.deinit(); - const client = try allocator.create(Client); errdefer allocator.destroy(client); @@ -122,7 +116,6 @@ pub fn init(allocator: Allocator, ca_blob: ?c.curl_blob, opts: Http.Opts) !*Clie .allocator = allocator, .http_proxy = opts.http_proxy, .transfer_pool = transfer_pool, - .queue_node_pool = queue_node_pool, }; return client; @@ -136,13 +129,13 @@ pub fn deinit(self: *Client) void { _ = c.curl_multi_cleanup(self.multi); self.transfer_pool.deinit(); - self.queue_node_pool.deinit(); self.allocator.destroy(self); } pub fn abort(self: *Client) void { while (self.handles.in_use.first) |node| { - var transfer = Transfer.fromEasy(node.data.conn.easy) catch |err| { + const handle: *Handle = @fieldParentPtr("node", node); + var transfer = Transfer.fromEasy(handle.conn.easy) catch |err| { log.err(.http, "get private info", .{ .err = err, .source = "abort" }); continue; }; @@ -152,15 +145,16 @@ pub fn abort(self: *Client) void { var n = self.queue.first; while (n) |node| { + const transfer: *Transfer = @fieldParentPtr("_node", node); + self.transfer_pool.destroy(transfer); n = node.next; - self.queue_node_pool.destroy(node); } self.queue = .{}; // Maybe a bit of overkill // We can remove some (all?) of these once we're confident its right. std.debug.assert(self.handles.in_use.first == null); - std.debug.assert(self.handles.available.len == self.handles.handles.len); + std.debug.assert(self.handles.available.len() == self.handles.handles.len); if (builtin.mode == .Debug) { var running: c_int = undefined; std.debug.assert(c.curl_multi_perform(self.multi, &running) == c.CURLE_OK); @@ -178,12 +172,11 @@ pub fn tick(self: *Client, opts: TickOpts) !bool { break; } const queue_node = self.queue.popFirst() orelse break; - const req = queue_node.data; - self.queue_node_pool.destroy(queue_node); + const transfer: *Transfer = @fieldParentPtr("_node", queue_node); // we know this exists, because we checked isEmpty() above const handle = self.handles.getFreeHandle().?; - try self.makeRequest(handle, req); + try self.makeRequest(handle, transfer); } return self.perform(opts.timeout_ms, opts.poll_socket); } @@ -213,9 +206,7 @@ pub fn process(self: *Client, transfer: *Transfer) !void { return self.makeRequest(handle, transfer); } - const node = try self.queue_node_pool.create(); - node.data = transfer; - self.queue.append(node); + self.queue.append(&transfer._node); } // See ScriptManager.blockingGet @@ -442,7 +433,7 @@ fn endTransfer(self: *Client, transfer: *Transfer) void { log.fatal(.http, "Failed to remove handle", .{ .err = err }); }; - self.handles.release(handle); + self.handles.release(self, handle); transfer._handle = null; self.active -= 1; } @@ -458,7 +449,7 @@ const Handles = struct { in_use: HandleList, available: HandleList, - const HandleList = std.DoublyLinkedList(*Handle); + const HandleList = std.DoublyLinkedList; // pointer to opts is not stable, don't hold a reference to it! fn init(allocator: Allocator, client: *Client, ca_blob: ?c.curl_blob, opts: *const Http.Opts) !Handles { @@ -470,8 +461,7 @@ const Handles = struct { var available: HandleList = .{}; for (0..count) |i| { handles[i] = try Handle.init(client, ca_blob, opts); - handles[i].node = .{ .data = &handles[i] }; - available.append(&handles[i].node.?); + available.append(&handles[i].node); } return .{ @@ -497,16 +487,19 @@ const Handles = struct { node.prev = null; node.next = null; self.in_use.append(node); - return node.data; + return @as(*Handle, @fieldParentPtr("node", node)); } return null; } - fn release(self: *Handles, handle: *Handle) void { - // client.blocking is a handle without a node, it doesn't exist in - // either the in_use or available lists. - const node = &(handle.node orelse return); + fn release(self: *Handles, client: *Client, handle: *Handle) void { + if (handle == &client.blocking) { + // the handle we've reserved for blocking request doesn't participate + // int he in_use/available pools + return; + } + var node = &handle.node; self.in_use.remove(node); node.prev = null; node.next = null; @@ -518,7 +511,7 @@ const Handles = struct { const Handle = struct { client: *Client, conn: Http.Connection, - node: ?Handles.HandleList.Node, + node: Handles.HandleList.Node, // pointer to opts is not stable, don't hold a reference to it! fn init(client: *Client, ca_blob: ?c.curl_blob, opts: *const Http.Opts) !Handle { @@ -534,8 +527,8 @@ const Handle = struct { try errorCheck(c.curl_easy_setopt(easy, c.CURLOPT_WRITEFUNCTION, Transfer.dataCallback)); return .{ + .node = .{}, .conn = conn, - .node = null, .client = client, }; } @@ -664,6 +657,9 @@ pub const Transfer = struct { // incremented by reset func. _tries: u8 = 0, + // for when a Transfer is queued in the client.queue + _node: std.DoublyLinkedList.Node = .{}, + pub fn reset(self: *Transfer) void { self._redirecting = false; self._auth_challenge = null; @@ -678,7 +674,7 @@ pub const Transfer = struct { fn deinit(self: *Transfer) void { self.req.headers.deinit(); if (self._handle) |handle| { - self.client.handles.release(handle); + self.client.handles.release(self.client, handle); } self.arena.deinit(); self.client.transfer_pool.destroy(self); @@ -711,7 +707,7 @@ pub const Transfer = struct { } } - pub fn format(self: *const Transfer, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + pub fn format(self: *Transfer, writer: *std.Io.Writer) !void { const req = self.req; return writer.print("{s} {s}", .{ @tagName(req.method), req.url }); } @@ -850,7 +846,7 @@ pub const Transfer = struct { // libcurl should only ever emit 1 header at a time std.debug.assert(header_count == 1); - const easy: *c.CURL = @alignCast(@ptrCast(data)); + const easy: *c.CURL = @ptrCast(@alignCast(data)); var transfer = fromEasy(easy) catch |err| { log.err(.http, "get private info", .{ .err = err, .source = "header callback" }); return 0; @@ -948,7 +944,7 @@ pub const Transfer = struct { // libcurl should only ever emit 1 chunk at a time std.debug.assert(chunk_count == 1); - const easy: *c.CURL = @alignCast(@ptrCast(data)); + const easy: *c.CURL = @ptrCast(@alignCast(data)); var transfer = fromEasy(easy) catch |err| { log.err(.http, "get private info", .{ .err = err, .source = "body callback" }); return c.CURL_WRITEFUNC_ERROR; @@ -999,7 +995,7 @@ pub const Transfer = struct { pub fn fromEasy(easy: *c.CURL) !*Transfer { var private: *anyopaque = undefined; try errorCheck(c.curl_easy_getinfo(easy, c.CURLINFO_PRIVATE, &private)); - return @alignCast(@ptrCast(private)); + return @ptrCast(@alignCast(private)); } pub fn fulfill(transfer: *Transfer, status: u16, headers: []const Http.Header, body: ?[]const u8) !void { diff --git a/src/http/Http.zig b/src/http/Http.zig index c961dad7..a82087bd 100644 --- a/src/http/Http.zig +++ b/src/http/Http.zig @@ -58,11 +58,7 @@ pub fn init(allocator: Allocator, opts: Opts) !Http { var adjusted_opts = opts; if (opts.proxy_bearer_token) |bt| { - adjusted_opts.proxy_bearer_token = try std.fmt.allocPrintZ( - arena.allocator(), - "Proxy-Authorization: Bearer {s}", - .{bt}, - ); + adjusted_opts.proxy_bearer_token = try std.fmt.allocPrintSentinel(arena.allocator(), "Proxy-Authorization: Bearer {s}", .{bt}, 0); } var ca_blob: ?c.curl_blob = null; diff --git a/src/id.zig b/src/id.zig index d2944cdf..98594c0b 100644 --- a/src/id.zig +++ b/src/id.zig @@ -45,7 +45,7 @@ pub fn Incrementing(comptime T: type, comptime prefix: []const u8) type { const n = counter +% 1; defer self.counter = n; - const size = std.fmt.formatIntBuf(self.buffer[NUMERIC_START..], n, 10, .lower, .{}); + const size = std.fmt.printInt(self.buffer[NUMERIC_START..], n, 10, .lower, .{}); return self.buffer[0 .. NUMERIC_START + size]; } diff --git a/src/log.zig b/src/log.zig index f8e9b0fb..2e8f1910 100644 --- a/src/log.zig +++ b/src/log.zig @@ -121,12 +121,16 @@ pub fn log(comptime scope: Scope, level: Level, comptime msg: []const u8, data: std.debug.lockStdErr(); defer std.debug.unlockStdErr(); - logTo(scope, level, msg, data, std.io.getStdErr().writer()) catch |log_err| { + var buf: [4096]u8 = undefined; + var stderr = std.fs.File.stderr(); + var writer = stderr.writer(&buf); + + logTo(scope, level, msg, data, &writer.interface) catch |log_err| { std.debug.print("$time={d} $level=fatal $scope={s} $msg=\"log err\" err={s} log_msg=\"{s}\"", .{ timestamp(), @errorName(log_err), @tagName(scope), msg }); }; } -fn logTo(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, out: anytype) !void { +fn logTo(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, out: *std.Io.Writer) !void { comptime { if (msg.len > 30) { @compileError("log msg cannot be more than 30 characters: '" ++ msg ++ "'"); @@ -139,12 +143,11 @@ fn logTo(comptime scope: Scope, level: Level, comptime msg: []const u8, data: an } } - var bw = std.io.bufferedWriter(out); switch (opts.format) { - .logfmt => try logLogfmt(scope, level, msg, data, bw.writer()), - .pretty => try logPretty(scope, level, msg, data, bw.writer()), + .logfmt => try logLogfmt(scope, level, msg, data, out), + .pretty => try logPretty(scope, level, msg, data, out), } - bw.flush() catch return; + out.flush() catch return; } fn logLogfmt(comptime scope: Scope, level: Level, comptime msg: []const u8, data: anytype, writer: anytype) !void { @@ -223,6 +226,10 @@ fn logPrettyPrefix(comptime scope: Scope, level: Level, comptime msg: []const u8 pub fn writeValue(comptime format: Format, value: anytype, writer: anytype) !void { const T = @TypeOf(value); + if (std.meta.hasMethod(T, "format")) { + return writer.print("{f}", .{value}); + } + switch (@typeInfo(T)) { .optional => { if (value) |v| { @@ -248,7 +255,7 @@ pub fn writeValue(comptime format: Format, value: anytype, writer: anytype) !voi .array => |arr| if (arr.child == u8) { return writeString(format, value, writer); }, - else => return writer.print("{}", .{value}), + else => return writer.print("{f}", .{value}), }, else => {}, }, @@ -341,16 +348,16 @@ fn elapsed() struct { time: f64, unit: []const u8 } { const testing = @import("testing.zig"); test "log: data" { - var buf: std.ArrayListUnmanaged(u8) = .{}; - defer buf.deinit(testing.allocator); + var aw = std.Io.Writer.Allocating.init(testing.allocator); + defer aw.deinit(); { - try logTo(.browser, .err, "nope", .{}, buf.writer(testing.allocator)); - try testing.expectEqual("$time=1739795092929 $scope=browser $level=error $msg=nope\n", buf.items); + try logTo(.browser, .err, "nope", .{}, &aw.writer); + try testing.expectEqual("$time=1739795092929 $scope=browser $level=error $msg=nope\n", aw.written()); } { - buf.clearRetainingCapacity(); + aw.clearRetainingCapacity(); const string = try testing.allocator.dupe(u8, "spice_must_flow"); defer testing.allocator.free(string); @@ -367,28 +374,28 @@ test "log: data" { .slice = string, .err = error.Nope, .level = Level.warn, - }, buf.writer(testing.allocator)); + }, &aw.writer); try testing.expectEqual("$time=1739795092929 $scope=http $level=warn $msg=\"a msg\" " ++ "cint=5 cfloat=3.43 int=-49 float=0.0003232 bt=true bf=false " ++ "nn=33 n=null lit=over9000! slice=spice_must_flow " ++ - "err=Nope level=warn\n", buf.items); + "err=Nope level=warn\n", aw.written()); } } test "log: string escape" { - var buf: std.ArrayListUnmanaged(u8) = .{}; - defer buf.deinit(testing.allocator); + var aw = std.Io.Writer.Allocating.init(testing.allocator); + defer aw.deinit(); const prefix = "$time=1739795092929 $scope=app $level=error $msg=test "; { - try logTo(.app, .err, "test", .{ .string = "hello world" }, buf.writer(testing.allocator)); - try testing.expectEqual(prefix ++ "string=\"hello world\"\n", buf.items); + try logTo(.app, .err, "test", .{ .string = "hello world" }, &aw.writer); + try testing.expectEqual(prefix ++ "string=\"hello world\"\n", aw.written()); } { - buf.clearRetainingCapacity(); - try logTo(.app, .err, "test", .{ .string = "\n \thi \" \" " }, buf.writer(testing.allocator)); - try testing.expectEqual(prefix ++ "string=\"\\n \thi \\\" \\\" \"\n", buf.items); + aw.clearRetainingCapacity(); + try logTo(.app, .err, "test", .{ .string = "\n \thi \" \" " }, &aw.writer); + try testing.expectEqual(prefix ++ "string=\"\\n \thi \\\" \\\" \"\n", aw.written()); } } diff --git a/src/main.zig b/src/main.zig index f4f0a247..fb02932f 100644 --- a/src/main.zig +++ b/src/main.zig @@ -170,11 +170,14 @@ fn run(alloc: Allocator) !void { // dump if (opts.dump) { + var stdout = std.fs.File.stdout(); + var writer = stdout.writer(&.{}); try page.dump(.{ .page = page, .with_base = opts.withbase, .exclude_scripts = opts.noscript, - }, std.io.getStdOut()); + }, &writer.interface); + try writer.interface.flush(); } }, else => unreachable, @@ -756,11 +759,14 @@ fn serveHTTP(wg: *std.Thread.WaitGroup) !void { wg.finish(); - var read_buffer: [1024]u8 = undefined; + var buf: [1024]u8 = undefined; while (true) { var conn = try listener.accept(); defer conn.stream.close(); - var http_server = std.http.Server.init(conn, &read_buffer); + var conn_reader = conn.stream.reader(&buf); + var conn_writer = conn.stream.writer(&buf); + + var http_server = std.http.Server.init(conn_reader.interface(), &conn_writer.interface); var request = http_server.receiveHead() catch |err| switch (err) { error.HttpConnectionClosing => continue, diff --git a/src/main_wpt.zig b/src/main_wpt.zig index d3741f80..f70aaed3 100644 --- a/src/main_wpt.zig +++ b/src/main_wpt.zig @@ -192,7 +192,7 @@ const Writer = struct { fail_count: usize = 0, case_pass_count: usize = 0, case_fail_count: usize = 0, - out: std.fs.File.Writer, + writer: std.fs.File.Writer, cases: std.ArrayListUnmanaged(Case) = .{}, const Format = enum { @@ -202,28 +202,31 @@ const Writer = struct { }; fn init(arena: Allocator, format: Format) !Writer { - const out = std.io.getStdOut().writer(); + const out = std.fs.File.stdout(); + var writer = out.writer(&.{}); + if (format == .json) { - try out.writeByte('['); + try writer.interface.writeByte('['); } return .{ - .out = out, .arena = arena, .format = format, + .writer = writer, }; } fn finalize(self: *Writer) !void { + var writer = &self.writer.interface; if (self.format == .json) { // When we write a test output, we add a trailing comma to act as // a separator for the next test. We need to add this dummy entry // to make it valid json. // Better option could be to change the formatter to work on JSONL: // https://github.com/lightpanda-io/perf-fmt/blob/main/wpt/wpt.go - try self.out.writeAll("{\"name\":\"empty\",\"pass\": true, \"cases\": []}]"); + try writer.writeAll("{\"name\":\"empty\",\"pass\": true, \"cases\": []}]"); } else { - try self.out.print("\n==Summary==\nTests: {d}/{d}\nCases: {d}/{d}\n", .{ + try writer.print("\n==Summary==\nTests: {d}/{d}\nCases: {d}/{d}\n", .{ self.pass_count, self.pass_count + self.fail_count, self.case_pass_count, @@ -233,18 +236,19 @@ const Writer = struct { } fn process(self: *Writer, test_file: []const u8, result_: ?[]const u8, err_: ?[]const u8) !void { + var writer = &self.writer.interface; if (err_) |err| { self.fail_count += 1; switch (self.format) { - .text => return self.out.print("Fail\t{s}\n\t{s}\n", .{ test_file, err }), - .summary => return self.out.print("Fail 0/0\t{s}\n", .{test_file}), + .text => return writer.print("Fail\t{s}\n\t{s}\n", .{ test_file, err }), + .summary => return writer.print("Fail 0/0\t{s}\n", .{test_file}), .json => { - try std.json.stringify(Test{ + try std.json.Stringify.value(Test{ .pass = false, .name = test_file, .cases = &.{}, - }, .{ .whitespace = .indent_2 }, self.out); - return self.out.writeByte(','); + }, .{ .whitespace = .indent_2 }, writer); + return writer.writeByte(','); }, } // just make sure we didn't fall through by mistake @@ -316,24 +320,24 @@ const Writer = struct { self.case_fail_count += case_fail_count; switch (self.format) { - .summary => try self.out.print("{s} {d}/{d}\t{s}\n", .{ statusText(pass), case_pass_count, case_pass_count + case_fail_count, test_file }), + .summary => try writer.print("{s} {d}/{d}\t{s}\n", .{ statusText(pass), case_pass_count, case_pass_count + case_fail_count, test_file }), .text => { - try self.out.print("{s}\t{s}\n", .{ statusText(pass), test_file }); + try writer.print("{s}\t{s}\n", .{ statusText(pass), test_file }); for (cases.items) |c| { - try self.out.print("\t{s}\t{s}\n", .{ statusText(c.pass), c.name }); + try writer.print("\t{s}\t{s}\n", .{ statusText(c.pass), c.name }); if (c.message) |msg| { - try self.out.print("\t\t{s}\n", .{msg}); + try writer.print("\t\t{s}\n", .{msg}); } } }, .json => { - try std.json.stringify(Test{ + try std.json.Stringify.value(Test{ .pass = pass, .name = test_file, .cases = cases.items, - }, .{ .whitespace = .indent_2 }, self.out); + }, .{ .whitespace = .indent_2 }, writer); // separator, see `finalize` for the hack we use to terminate this - try self.out.writeByte(','); + try writer.writeByte(','); }, } } @@ -362,14 +366,14 @@ fn parseArgs(arena: Allocator) !Command { var args = try std.process.argsWithAllocator(arena); // get the exec name. - const execname = args.next().?; + const exec_name = args.next().?; var format = Writer.Format.text; var filters: std.ArrayListUnmanaged([]const u8) = .{}; while (args.next()) |arg| { if (std.mem.eql(u8, "-h", arg) or std.mem.eql(u8, "--help", arg)) { - try std.io.getStdErr().writer().print(usage, .{execname}); + std.debug.print(usage, .{exec_name}); std.posix.exit(0); } diff --git a/src/notification.zig b/src/notification.zig index 392a57e7..76c5d970 100644 --- a/src/notification.zig +++ b/src/notification.zig @@ -8,8 +8,7 @@ const Transfer = @import("http/Client.zig").Transfer; const Allocator = std.mem.Allocator; -const List = std.DoublyLinkedList(Listener); -const Node = List.Node; +const List = std.DoublyLinkedList; // Allows code to register for and emit events. // Keeps two lists @@ -48,12 +47,12 @@ pub const Notification = struct { event_listeners: EventListeners, // list of listeners for a specified receiver - // @intFromPtr(listener) -> [@intFromPtr(listener1), @intFromPtr(listener2, ...] + // @intFromPtr(receiver) -> [listener1, listener2, ...] // Used when `unregisterAll` is called. - listeners: std.AutoHashMapUnmanaged(usize, std.ArrayListUnmanaged(*Node)), + listeners: std.AutoHashMapUnmanaged(usize, std.ArrayListUnmanaged(*Listener)), allocator: Allocator, - node_pool: std.heap.MemoryPool(Node), + mem_pool: std.heap.MemoryPool(Listener), const EventListeners = struct { page_remove: List = .{}, @@ -143,7 +142,7 @@ pub const Notification = struct { .listeners = .{}, .event_listeners = .{}, .allocator = allocator, - .node_pool = std.heap.MemoryPool(Node).init(allocator), + .mem_pool = std.heap.MemoryPool(Listener).init(allocator), }; if (parent) |pn| { @@ -161,21 +160,22 @@ pub const Notification = struct { listener.deinit(allocator); } self.listeners.deinit(allocator); - self.node_pool.deinit(); + self.mem_pool.deinit(); allocator.destroy(self); } pub fn register(self: *Notification, comptime event: EventType, receiver: anytype, func: EventFunc(event)) !void { var list = &@field(self.event_listeners, @tagName(event)); - var node = try self.node_pool.create(); - errdefer self.node_pool.destroy(node); + var listener = try self.mem_pool.create(); + errdefer self.mem_pool.destroy(listener); - node.data = .{ + listener.* = .{ + .node = .{}, .list = list, - .func = @ptrCast(func), .receiver = receiver, .event = event, + .func = @ptrCast(func), .struct_name = @typeName(@typeInfo(@TypeOf(receiver)).pointer.child), }; @@ -184,44 +184,40 @@ pub const Notification = struct { if (gop.found_existing == false) { gop.value_ptr.* = .{}; } - try gop.value_ptr.append(allocator, node); + try gop.value_ptr.append(allocator, listener); // we don't add this until we've successfully added the entry to // self.listeners - list.append(node); + list.append(&listener.node); } pub fn unregister(self: *Notification, comptime event: EventType, receiver: anytype) void { - var nodes = self.listeners.getPtr(@intFromPtr(receiver)) orelse return; - - const node_pool = &self.node_pool; + var listeners = self.listeners.getPtr(@intFromPtr(receiver)) orelse return; var i: usize = 0; - while (i < nodes.items.len) { - const node = nodes.items[i]; - if (node.data.event != event) { + while (i < listeners.items.len) { + const listener = listeners.items[i]; + if (listener.event != event) { i += 1; continue; } - node.data.list.remove(node); - node_pool.destroy(node); - _ = nodes.swapRemove(i); + listener.list.remove(&listener.node); + self.mem_pool.destroy(listener); + _ = listeners.swapRemove(i); } - if (nodes.items.len == 0) { - nodes.deinit(self.allocator); + if (listeners.items.len == 0) { + listeners.deinit(self.allocator); const removed = self.listeners.remove(@intFromPtr(receiver)); std.debug.assert(removed == true); } } pub fn unregisterAll(self: *Notification, receiver: *anyopaque) void { - const node_pool = &self.node_pool; - var kv = self.listeners.fetchRemove(@intFromPtr(receiver)) orelse return; - for (kv.value.items) |node| { - node.data.list.remove(node); - node_pool.destroy(node); + for (kv.value.items) |listener| { + listener.list.remove(&listener.node); + self.mem_pool.destroy(listener); } kv.value.deinit(self.allocator); } @@ -231,8 +227,8 @@ pub const Notification = struct { var node = list.first; while (node) |n| { - const listener = n.data; - const func: EventFunc(event) = @alignCast(@ptrCast(listener.func)); + const listener: *Listener = @fieldParentPtr("node", n); + const func: EventFunc(event) = @ptrCast(@alignCast(listener.func)); func(listener.receiver, data) catch |err| { log.err(.app, "dispatch error", .{ .err = err, @@ -275,6 +271,9 @@ const Listener = struct { event: Notification.EventType, + // intrusive linked list node + node: List.Node, + // The event list this listener belongs to. // We need this in order to be able to remove the node from the list list: *List, @@ -366,12 +365,12 @@ const TestClient = struct { page_navigated: u32 = 0, fn pageNavigate(ptr: *anyopaque, data: *const Notification.PageNavigate) !void { - const self: *TestClient = @alignCast(@ptrCast(ptr)); + const self: *TestClient = @ptrCast(@alignCast(ptr)); self.page_navigate += data.timestamp; } fn pageNavigated(ptr: *anyopaque, data: *const Notification.PageNavigated) !void { - const self: *TestClient = @alignCast(@ptrCast(ptr)); + const self: *TestClient = @ptrCast(@alignCast(ptr)); self.page_navigated += data.timestamp; } }; diff --git a/src/runtime/js.zig b/src/runtime/js.zig index da7e459d..134fa777 100644 --- a/src/runtime/js.zig +++ b/src/runtime/js.zig @@ -202,7 +202,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { errdefer isolate.exit(); isolate.setHostInitializeImportMetaObjectCallback(struct { - fn callback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_meta: ?*v8.C_Value) callconv(.C) void { + fn callback(c_context: ?*v8.C_Context, c_module: ?*v8.C_Module, c_meta: ?*v8.C_Value) callconv(.c) void { const v8_context = v8.Context{ .handle = c_context.? }; const js_context: *JsContext = @ptrFromInt(v8_context.getEmbedderData(1).castTo(v8.BigInt).getUint64()); js_context.initializeImportMeta(v8.Module{ .handle = c_module.? }, v8.Object{ .handle = c_meta.? }) catch |err| { @@ -382,7 +382,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { }; // If necessary, turn a void context into something we can safely ptrCast - const safe_module_loader: *anyopaque = if (ModuleLoader == ErrorModuleLoader) @constCast(@ptrCast(&{})) else module_loader; + const safe_module_loader: *anyopaque = if (ModuleLoader == ErrorModuleLoader) @ptrCast(@constCast(&{})) else module_loader; const env = self.env; const isolate = env.isolate; @@ -1008,7 +1008,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { if (ptr.sentinel() == null) { if (force_u8 or js_value.isUint8Array() or js_value.isUint8ClampedArray()) { if (byte_len == 0) return &[_]u8{}; - const arr_ptr = @as([*]u8, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]u8, @ptrCast(@alignCast(data))); return arr_ptr[0..byte_len]; } } @@ -1016,49 +1016,49 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { i8 => { if (js_value.isInt8Array()) { if (byte_len == 0) return &[_]i8{}; - const arr_ptr = @as([*]i8, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]i8, @ptrCast(@alignCast(data))); return arr_ptr[0..byte_len]; } }, u16 => { if (js_value.isUint16Array()) { if (byte_len == 0) return &[_]u16{}; - const arr_ptr = @as([*]u16, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]u16, @ptrCast(@alignCast(data))); return arr_ptr[0 .. byte_len / 2]; } }, i16 => { if (js_value.isInt16Array()) { if (byte_len == 0) return &[_]i16{}; - const arr_ptr = @as([*]i16, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]i16, @ptrCast(@alignCast(data))); return arr_ptr[0 .. byte_len / 2]; } }, u32 => { if (js_value.isUint32Array()) { if (byte_len == 0) return &[_]u32{}; - const arr_ptr = @as([*]u32, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]u32, @ptrCast(@alignCast(data))); return arr_ptr[0 .. byte_len / 4]; } }, i32 => { if (js_value.isInt32Array()) { if (byte_len == 0) return &[_]i32{}; - const arr_ptr = @as([*]i32, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]i32, @ptrCast(@alignCast(data))); return arr_ptr[0 .. byte_len / 4]; } }, u64 => { if (js_value.isBigUint64Array()) { if (byte_len == 0) return &[_]u64{}; - const arr_ptr = @as([*]u64, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]u64, @ptrCast(@alignCast(data))); return arr_ptr[0 .. byte_len / 8]; } }, i64 => { if (js_value.isBigInt64Array()) { if (byte_len == 0) return &[_]i64{}; - const arr_ptr = @as([*]i64, @alignCast(@ptrCast(data))); + const arr_ptr = @as([*]i64, @ptrCast(@alignCast(data))); return arr_ptr[0 .. byte_len / 8]; } }, @@ -1418,7 +1418,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { c_specifier: ?*const v8.C_String, import_attributes: ?*const v8.C_FixedArray, c_referrer: ?*const v8.C_Module, - ) callconv(.C) ?*const v8.C_Module { + ) callconv(.c) ?*const v8.C_Module { _ = import_attributes; const v8_context = v8.Context{ .handle = c_context.? }; @@ -1516,12 +1516,12 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { } const op = js_obj.getInternalField(0).castTo(v8.External).get(); - const toa: *TaggedAnyOpaque = @alignCast(@ptrCast(op)); + const toa: *TaggedAnyOpaque = @ptrCast(@alignCast(op)); const expected_type_index = @field(TYPE_LOOKUP, type_name); var type_index = toa.index; if (type_index == expected_type_index) { - return @alignCast(@ptrCast(toa.ptr)); + return @ptrCast(@alignCast(toa.ptr)); } const meta_lookup = self.meta_lookup; @@ -1882,8 +1882,9 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { _: u29 = 0, }; pub fn setIndex(self: JsObject, index: u32, value: anytype, opts: SetOpts) !void { + @setEvalBranchQuota(10000); const key = switch (index) { - inline 0...50 => |i| std.fmt.comptimePrint("{d}", .{i}), + inline 0...20 => |i| std.fmt.comptimePrint("{d}", .{i}), else => try std.fmt.allocPrint(self.js_context.context_arena, "{d}", .{index}), }; return self.set(key, value, opts); @@ -2157,7 +2158,7 @@ pub fn Env(comptime State: type, comptime WebApis: type) type { }; // If necessary, turn a void context into something we can safely ptrCast - const safe_context: *anyopaque = if (ContextT == void) @constCast(@ptrCast(&{})) else ctx; + const safe_context: *anyopaque = if (ContextT == void) @ptrCast(@constCast(&{})) else ctx; const channel = v8.InspectorChannel.init(safe_context, InspectorContainer.onInspectorResponse, InspectorContainer.onInspectorEvent, isolate); @@ -2981,7 +2982,7 @@ fn Caller(comptime JsContext: type, comptime State: type) type { // Therefore, we keep a call_depth, and only reset the call_arena // when a top-level (call_depth == 0) function ends. if (call_depth == 0) { - const arena: *ArenaAllocator = @alignCast(@ptrCast(js_context.call_arena.ptr)); + const arena: *ArenaAllocator = @ptrCast(@alignCast(js_context.call_arena.ptr)); _ = arena.reset(.{ .retain_with_limit = CALL_ARENA_RETAIN }); } @@ -3539,7 +3540,7 @@ fn simpleZigValueToJs(isolate: v8.Isolate, value: anytype, comptime fail: bool) } else { const buffer_len = len * bits / 8; const backing_store = v8.BackingStore.init(isolate, buffer_len); - const data: [*]u8 = @alignCast(@ptrCast(backing_store.getData())); + const data: [*]u8 = @ptrCast(@alignCast(backing_store.getData())); @memcpy(data[0..buffer_len], @as([]const u8, @ptrCast(values))[0..buffer_len]); array_buffer = v8.ArrayBuffer.initWithBackingStore(isolate, &backing_store.toSharedPtr()); } @@ -3944,7 +3945,7 @@ fn serializeFunctionArgs(arena: Allocator, isolate: v8.Isolate, context: v8.Cont pub export fn v8_inspector__Client__IMPL__valueSubtype( _: *v8.c.InspectorClientImpl, c_value: *const v8.C_Value, -) callconv(.C) [*c]const u8 { +) callconv(.c) [*c]const u8 { const external_entry = getTaggedAnyOpaque(.{ .handle = c_value }) orelse return null; return if (external_entry.subtype) |st| @tagName(st) else null; } @@ -3957,7 +3958,7 @@ pub export fn v8_inspector__Client__IMPL__descriptionForValueSubtype( _: *v8.c.InspectorClientImpl, v8_context: *const v8.C_Context, c_value: *const v8.C_Value, -) callconv(.C) [*c]const u8 { +) callconv(.c) [*c]const u8 { _ = v8_context; // We _must_ include a non-null description in order for the subtype value @@ -3976,7 +3977,7 @@ fn getTaggedAnyOpaque(value: v8.Value) ?*TaggedAnyOpaque { } const external_data = obj.getInternalField(0).castTo(v8.External).get().?; - return @alignCast(@ptrCast(external_data)); + return @ptrCast(@alignCast(external_data)); } test { diff --git a/src/server.zig b/src/server.zig index 617be02b..56a3b854 100644 --- a/src/server.zig +++ b/src/server.zig @@ -83,7 +83,7 @@ pub const Server = struct { while (true) { const socket = posix.accept(listener, null, null, posix.SOCK.NONBLOCK) catch |err| { log.err(.app, "CDP accept", .{ .err = err }); - std.time.sleep(std.time.ns_per_s); + std.Thread.sleep(std.time.ns_per_s); continue; }; @@ -456,17 +456,15 @@ pub const Client = struct { // writev, so we need to get creative. We'll JSON serialize to a // buffer, where the first 10 bytes are reserved. We can then backfill // the header and send the slice. - pub fn sendJSON(self: *Client, message: anytype, opts: std.json.StringifyOptions) !void { + pub fn sendJSON(self: *Client, message: anytype, opts: std.json.Stringify.Options) !void { const allocator = self.send_arena.allocator(); - var buf: std.ArrayListUnmanaged(u8) = .{}; - try buf.ensureTotalCapacity(allocator, 512); + var aw = try std.Io.Writer.Allocating.initCapacity(allocator, 512); // reserve space for the maximum possible header - buf.appendSliceAssumeCapacity(&.{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); - - try std.json.stringify(message, opts, buf.writer(allocator)); - const framed = fillWebsocketHeader(buf); + try aw.writer.writeAll(&.{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }); + try std.json.Stringify.value(message, opts, &aw.writer); + const framed = fillWebsocketHeader(aw.toArrayList()); return self.send(framed); } @@ -844,7 +842,7 @@ fn buildJSONVersionResponse( allocator: Allocator, address: net.Address, ) ![]const u8 { - const body_format = "{{\"webSocketDebuggerUrl\": \"ws://{}/\"}}"; + const body_format = "{{\"webSocketDebuggerUrl\": \"ws://{f}/\"}}"; const body_len = std.fmt.count(body_format, .{address}); // We send a Connection: Close (and actually close the connection) diff --git a/src/telemetry/telemetry.zig b/src/telemetry/telemetry.zig index d9e8c304..16fe8256 100644 --- a/src/telemetry/telemetry.zig +++ b/src/telemetry/telemetry.zig @@ -79,7 +79,7 @@ fn TelemetryT(comptime P: type) type { } fn onPageNavigate(ctx: *anyopaque, data: *const Notification.PageNavigate) !void { - const self: *Self = @alignCast(@ptrCast(ctx)); + const self: *Self = @ptrCast(@alignCast(ctx)); self.record(.{ .navigate = .{ .proxy = false, .tls = std.ascii.startsWithIgnoreCase(data.url, "https://"), diff --git a/src/test_runner.zig b/src/test_runner.zig index c7e2ad0d..52bb144d 100644 --- a/src/test_runner.zig +++ b/src/test_runner.zig @@ -67,13 +67,12 @@ pub fn main() !void { var skip: usize = 0; var leak: usize = 0; - const printer = Printer.init(); - printer.fmt("\r\x1b[0K", .{}); // beginning of line and clear to end of line + Printer.fmt("\r\x1b[0K", .{}); // beginning of line and clear to end of line for (builtin.test_functions) |t| { if (isSetup(t)) { t.func() catch |err| { - printer.status(.fail, "\nsetup \"{s}\" failed: {}\n", .{ t.name, err }); + Printer.status(.fail, "\nsetup \"{s}\" failed: {}\n", .{ t.name, err }); return err; }; } @@ -115,7 +114,7 @@ pub fn main() !void { if (std.testing.allocator_instance.deinit() == .leak) { leak += 1; - printer.status(.fail, "\n{s}\n\"{s}\" - Memory Leak\n{s}\n", .{ BORDER, friendly_name, BORDER }); + Printer.status(.fail, "\n{s}\n\"{s}\" - Memory Leak\n{s}\n", .{ BORDER, friendly_name, BORDER }); } if (result) |_| { @@ -130,7 +129,7 @@ pub fn main() !void { else => { status = .fail; fail += 1; - printer.status(.fail, "\n{s}\n\"{s}\" - {s}\n{s}\n", .{ BORDER, friendly_name, @errorName(err), BORDER }); + Printer.status(.fail, "\n{s}\n\"{s}\" - {s}\n{s}\n", .{ BORDER, friendly_name, @errorName(err), BORDER }); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); } @@ -143,9 +142,9 @@ pub fn main() !void { if (is_unnamed_test == false) { if (env.verbose) { const ms = @as(f64, @floatFromInt(ns_taken)) / 1_000_000.0; - printer.status(status, "{s} ({d:.2}ms)\n", .{ friendly_name, ms }); + Printer.status(status, "{s} ({d:.2}ms)\n", .{ friendly_name, ms }); } else { - printer.status(status, ".", .{}); + Printer.status(status, ".", .{}); } } } @@ -153,7 +152,7 @@ pub fn main() !void { for (builtin.test_functions) |t| { if (isTeardown(t)) { t.func() catch |err| { - printer.status(.fail, "\nteardown \"{s}\" failed: {}\n", .{ t.name, err }); + Printer.status(.fail, "\nteardown \"{s}\" failed: {}\n", .{ t.name, err }); return err; }; } @@ -161,21 +160,23 @@ pub fn main() !void { const total_tests = pass + fail; const status = if (fail == 0) Status.pass else Status.fail; - printer.status(status, "\n{d} of {d} test{s} passed\n", .{ pass, total_tests, if (total_tests != 1) "s" else "" }); + Printer.status(status, "\n{d} of {d} test{s} passed\n", .{ pass, total_tests, if (total_tests != 1) "s" else "" }); if (skip > 0) { - printer.status(.skip, "{d} test{s} skipped\n", .{ skip, if (skip != 1) "s" else "" }); + Printer.status(.skip, "{d} test{s} skipped\n", .{ skip, if (skip != 1) "s" else "" }); } if (leak > 0) { - printer.status(.fail, "{d} test{s} leaked\n", .{ leak, if (leak != 1) "s" else "" }); + Printer.status(.fail, "{d} test{s} leaked\n", .{ leak, if (leak != 1) "s" else "" }); } - printer.fmt("\n", .{}); - try slowest.display(printer); - printer.fmt("\n", .{}); + Printer.fmt("\n", .{}); + try slowest.display(); + Printer.fmt("\n", .{}); // TODO: at the very least, `browser` should return real stats if (json_stats) { + var stdout = std.fs.File.stdout(); + var writer = stdout.writer(&.{}); const stats = tracking_allocator.stats(); - try std.json.stringify(&.{ + try std.json.Stringify.value(&.{ .{ .name = "browser", .bench = .{ .duration = js_runner_duration, .alloc_nb = stats.allocation_count, @@ -200,36 +201,25 @@ pub fn main() !void { .realloc_nb = 0, .alloc_size = 0, } }, - }, .{ .whitespace = .indent_2 }, std.io.getStdOut().writer()); + }, .{ .whitespace = .indent_2 }, &writer.interface); } std.posix.exit(if (fail == 0) 0 else 1); } const Printer = struct { - out: std.fs.File.Writer, - - fn init() Printer { - return .{ - .out = std.io.getStdErr().writer(), - }; + fn fmt(comptime format: []const u8, args: anytype) void { + std.debug.print(format, args); } - fn fmt(self: Printer, comptime format: []const u8, args: anytype) void { - std.fmt.format(self.out, format, args) catch unreachable; - } - - fn status(self: Printer, s: Status, comptime format: []const u8, args: anytype) void { - const color = switch (s) { - .pass => "\x1b[32m", - .fail => "\x1b[31m", - .skip => "\x1b[33m", - else => "", - }; - const out = self.out; - out.writeAll(color) catch @panic("writeAll failed?!"); - std.fmt.format(out, format, args) catch @panic("std.fmt.format failed?!"); - self.fmt("\x1b[0m", .{}); + fn status(s: Status, comptime format: []const u8, args: anytype) void { + switch (s) { + .pass => std.debug.print("\x1b[32m", .{}), + .fail => std.debug.print("\x1b[31m", .{}), + .skip => std.debug.print("\x1b[33m", .{}), + else => {}, + } + std.debug.print(format ++ "\x1b[0m", args); } }; @@ -302,13 +292,13 @@ const SlowTracker = struct { return ns; } - fn display(self: *SlowTracker, printer: Printer) !void { + fn display(self: *SlowTracker) !void { var slowest = self.slowest; const count = slowest.count(); - printer.fmt("Slowest {d} test{s}: \n", .{ count, if (count != 1) "s" else "" }); + Printer.fmt("Slowest {d} test{s}: \n", .{ count, if (count != 1) "s" else "" }); while (slowest.removeMinOrNull()) |info| { const ms = @as(f64, @floatFromInt(info.ns)) / 1_000_000.0; - printer.fmt(" {d:.2}ms\t{s}\n", .{ ms, info.name }); + Printer.fmt(" {d:.2}ms\t{s}\n", .{ ms, info.name }); } } diff --git a/src/testing.zig b/src/testing.zig index 7287c5cd..3cd201f5 100644 --- a/src/testing.zig +++ b/src/testing.zig @@ -254,8 +254,8 @@ pub fn expectJson(a: anytype, b: anytype) !void { const b_value = try convertToJson(aa, b); errdefer { - const a_json = std.json.stringifyAlloc(aa, a_value, .{ .whitespace = .indent_2 }) catch unreachable; - const b_json = std.json.stringifyAlloc(aa, b_value, .{ .whitespace = .indent_2 }) catch unreachable; + const a_json = std.json.Stringify.valueAlloc(aa, a_value, .{ .whitespace = .indent_2 }) catch unreachable; + const b_json = std.json.Stringify.valueAlloc(aa, b_value, .{ .whitespace = .indent_2 }) catch unreachable; std.debug.print("== Expected ==\n{s}\n\n== Actual ==\n{s}", .{ a_json, b_json }); } @@ -282,7 +282,7 @@ fn convertToJson(arena: Allocator, value: anytype) !std.json.Value { if (T == []u8 or T == []const u8 or comptime isStringArray(T)) { str = value; } else { - str = try std.json.stringifyAlloc(arena, value, .{}); + str = try std.json.Stringify.valueAlloc(arena, value, .{}); } return std.json.parseFromSliceLeaky(std.json.Value, arena, str, .{}); } diff --git a/src/url.zig b/src/url.zig index 66e7d213..a072431b 100644 --- a/src/url.zig +++ b/src/url.zig @@ -67,17 +67,11 @@ pub const URL = struct { return self.uri.scheme; } - pub fn origin(self: *const URL, writer: anytype) !void { - return self.uri.writeToStream(.{ .scheme = true, .authority = true }, writer); + pub fn origin(self: *const URL, writer: *std.Io.Writer) !void { + return self.uri.writeToStream(writer, .{ .scheme = true, .authority = true }); } - pub fn resolve(self: *const URL, arena: Allocator, url: []const u8) !URL { - var buf = try arena.alloc(u8, 4096); - const new_uri = try self.uri.resolve_inplace(url, &buf); - return fromURI(arena, &new_uri); - } - - pub fn format(self: *const URL, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void { + pub fn format(self: *const URL, writer: *std.Io.Writer) !void { return writer.writeAll(self.raw); } @@ -111,7 +105,7 @@ pub const URL = struct { const protocol = base[0..index]; if (comptime opts.null_terminated) { - return std.fmt.allocPrintZ(allocator, "{s}:{s}", .{ protocol, path }); + return std.fmt.allocPrintSentinel(allocator, "{s}:{s}", .{ protocol, path }, 0); } return std.fmt.allocPrint(allocator, "{s}:{s}", .{ protocol, path }); } @@ -125,7 +119,7 @@ pub const URL = struct { if (path[0] == '/') { const pos = std.mem.indexOfScalarPos(u8, base, protocol_end, '/') orelse base.len; if (comptime opts.null_terminated) { - return std.fmt.allocPrintZ(allocator, "{s}{s}", .{ base[0..pos], path }); + return std.fmt.allocPrintSentinel(allocator, "{s}{s}", .{ base[0..pos], path }, 0); } return std.fmt.allocPrint(allocator, "{s}{s}", .{ base[0..pos], path }); } @@ -262,22 +256,6 @@ test "URL: isComleteHTTPUrl" { try testing.expectEqual(false, isComleteHTTPUrl("//lightpanda.io/about")); } -test "URL: resolve size" { - const base = "https://www.lightpande.io"; - const url = try URL.parse(base, null); - - var url_string: [511]u8 = undefined; // Currently this is the largest url we support, it is however recommmended to at least support 2000 characters - @memset(&url_string, 'a'); - - var buf: [8192]u8 = undefined; // This is approximately the required size to support the current largest supported URL - var fba = std.heap.FixedBufferAllocator.init(&buf); - const out_url = try url.resolve(fba.allocator(), &url_string); - - try std.testing.expectEqualStrings(out_url.raw[0..25], base); - try std.testing.expectEqual(out_url.raw[25], '/'); - try std.testing.expectEqualStrings(out_url.raw[26..], &url_string); -} - test "URL: stitch" { defer testing.reset();