Merge pull request #560 from lightpanda-io/remove_arena_frees

Remove unnecessary cleanup when we know we have an arena
This commit is contained in:
Pierre Tachoire
2025-04-24 09:04:46 +02:00
committed by GitHub
5 changed files with 67 additions and 153 deletions

View File

@@ -17,6 +17,7 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>. // along with this program. If not, see <https://www.gnu.org/licenses/>.
const std = @import("std"); const std = @import("std");
const Allocator = std.mem.Allocator;
const parser = @import("../netsurf.zig"); const parser = @import("../netsurf.zig");
@@ -42,25 +43,11 @@ const Matcher = union(enum) {
pub fn match(self: Matcher, node: *parser.Node) !bool { pub fn match(self: Matcher, node: *parser.Node) !bool {
switch (self) { switch (self) {
inline .matchTrue => return true, .matchTrue => return true,
inline .matchFalse => return false, .matchFalse => return false,
inline .matchByTagName => |case| return case.match(node), .matchByLinks => return MatchByLinks.match(node),
inline .matchByClassName => |case| return case.match(node), .matchByAnchors => return MatchByAnchors.match(node),
inline .matchByName => |case| return case.match(node), inline else => |m| return m.match(node),
inline .matchByLinks => return MatchByLinks.match(node),
inline .matchByAnchors => return MatchByAnchors.match(node),
}
}
pub fn deinit(self: Matcher, alloc: std.mem.Allocator) void {
switch (self) {
inline .matchTrue => return,
inline .matchFalse => return,
inline .matchByTagName => |case| return case.deinit(alloc),
inline .matchByClassName => |case| return case.deinit(alloc),
inline .matchByName => |case| return case.deinit(alloc),
inline .matchByLinks => return,
inline .matchByAnchors => return,
} }
} }
}; };
@@ -71,54 +58,49 @@ pub const MatchByTagName = struct {
tag: []const u8, tag: []const u8,
is_wildcard: bool, is_wildcard: bool,
fn init(alloc: std.mem.Allocator, tag_name: []const u8) !MatchByTagName { fn init(arena: Allocator, tag_name: []const u8) !MatchByTagName {
const tag_name_alloc = try alloc.alloc(u8, tag_name.len); if (std.mem.eql(u8, tag_name, "*")) {
@memcpy(tag_name_alloc, tag_name); return .{ .tag = "*", .is_wildcard = true };
return MatchByTagName{ }
.tag = tag_name_alloc,
.is_wildcard = std.mem.eql(u8, tag_name, "*"), return .{
.tag = try arena.dupe(u8, tag_name),
.is_wildcard = false,
}; };
} }
pub fn match(self: MatchByTagName, node: *parser.Node) !bool { pub fn match(self: MatchByTagName, node: *parser.Node) !bool {
return self.is_wildcard or std.ascii.eqlIgnoreCase(self.tag, try parser.nodeName(node)); return self.is_wildcard or std.ascii.eqlIgnoreCase(self.tag, try parser.nodeName(node));
} }
fn deinit(self: MatchByTagName, alloc: std.mem.Allocator) void {
alloc.free(self.tag);
}
}; };
pub fn HTMLCollectionByTagName( pub fn HTMLCollectionByTagName(
alloc: std.mem.Allocator, arena: Allocator,
root: ?*parser.Node, root: ?*parser.Node,
tag_name: []const u8, tag_name: []const u8,
include_root: bool, include_root: bool,
) !HTMLCollection { ) !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = root, .root = root,
.walker = Walker{ .walkerDepthFirst = .{} }, .walker = .{ .walkerDepthFirst = .{} },
.matcher = Matcher{ .matcher = .{ .matchByTagName = try MatchByTagName.init(arena, tag_name) },
.matchByTagName = try MatchByTagName.init(alloc, tag_name),
},
.include_root = include_root, .include_root = include_root,
}; };
} }
pub const MatchByClassName = struct { pub const MatchByClassName = struct {
classNames: []const u8, class_names: []const u8,
fn init(alloc: std.mem.Allocator, classNames: []const u8) !MatchByClassName { fn init(arena: Allocator, class_names: []const u8) !MatchByClassName {
const class_names_alloc = try alloc.alloc(u8, classNames.len); return .{
@memcpy(class_names_alloc, classNames); .class_names = try arena.dupe(u8, class_names),
return MatchByClassName{
.classNames = class_names_alloc,
}; };
} }
pub fn match(self: MatchByClassName, node: *parser.Node) !bool { pub fn match(self: MatchByClassName, node: *parser.Node) !bool {
var it = std.mem.splitAny(u8, self.classNames, " ");
const e = parser.nodeToElement(node); const e = parser.nodeToElement(node);
var it = std.mem.splitScalar(u8, self.class_names, ' ');
while (it.next()) |c| { while (it.next()) |c| {
if (!try parser.elementHasClass(e, c)) { if (!try parser.elementHasClass(e, c)) {
return false; return false;
@@ -127,24 +109,18 @@ pub const MatchByClassName = struct {
return true; return true;
} }
fn deinit(self: MatchByClassName, alloc: std.mem.Allocator) void {
alloc.free(self.classNames);
}
}; };
pub fn HTMLCollectionByClassName( pub fn HTMLCollectionByClassName(
alloc: std.mem.Allocator, arena: Allocator,
root: ?*parser.Node, root: ?*parser.Node,
classNames: []const u8, classNames: []const u8,
include_root: bool, include_root: bool,
) !HTMLCollection { ) !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = root, .root = root,
.walker = Walker{ .walkerDepthFirst = .{} }, .walker = .{ .walkerDepthFirst = .{} },
.matcher = Matcher{ .matcher = .{ .matchByClassName = try MatchByClassName.init(arena, classNames) },
.matchByClassName = try MatchByClassName.init(alloc, classNames),
},
.include_root = include_root, .include_root = include_root,
}; };
} }
@@ -152,11 +128,9 @@ pub fn HTMLCollectionByClassName(
pub const MatchByName = struct { pub const MatchByName = struct {
name: []const u8, name: []const u8,
fn init(alloc: std.mem.Allocator, name: []const u8) !MatchByName { fn init(arena: Allocator, name: []const u8) !MatchByName {
const names_alloc = try alloc.alloc(u8, name.len); return .{
@memcpy(names_alloc, name); .name = try arena.dupe(u8, name),
return MatchByName{
.name = names_alloc,
}; };
} }
@@ -165,24 +139,18 @@ pub const MatchByName = struct {
const nname = try parser.elementGetAttribute(e, "name") orelse return false; const nname = try parser.elementGetAttribute(e, "name") orelse return false;
return std.mem.eql(u8, self.name, nname); return std.mem.eql(u8, self.name, nname);
} }
fn deinit(self: MatchByName, alloc: std.mem.Allocator) void {
alloc.free(self.name);
}
}; };
pub fn HTMLCollectionByName( pub fn HTMLCollectionByName(
alloc: std.mem.Allocator, arena: Allocator,
root: ?*parser.Node, root: ?*parser.Node,
name: []const u8, name: []const u8,
include_root: bool, include_root: bool,
) !HTMLCollection { ) !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = root, .root = root,
.walker = Walker{ .walkerDepthFirst = .{} }, .walker = .{ .walkerDepthFirst = .{} },
.matcher = Matcher{ .matcher = .{ .matchByName = try MatchByName.init(arena, name) },
.matchByName = try MatchByName.init(alloc, name),
},
.include_root = include_root, .include_root = include_root,
}; };
} }
@@ -193,8 +161,8 @@ pub fn HTMLCollectionAll(
) !HTMLCollection { ) !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = root, .root = root,
.walker = Walker{ .walkerDepthFirst = .{} }, .walker = .{ .walkerDepthFirst = .{} },
.matcher = Matcher{ .matchTrue = .{} }, .matcher = .{ .matchTrue = .{} },
.include_root = include_root, .include_root = include_root,
}; };
} }
@@ -205,8 +173,8 @@ pub fn HTMLCollectionChildren(
) !HTMLCollection { ) !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = root, .root = root,
.walker = Walker{ .walkerChildren = .{} }, .walker = .{ .walkerChildren = .{} },
.matcher = Matcher{ .matchTrue = .{} }, .matcher = .{ .matchTrue = .{} },
.include_root = include_root, .include_root = include_root,
}; };
} }
@@ -214,8 +182,8 @@ pub fn HTMLCollectionChildren(
pub fn HTMLCollectionEmpty() !HTMLCollection { pub fn HTMLCollectionEmpty() !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = null, .root = null,
.walker = Walker{ .walkerNone = .{} }, .walker = .{ .walkerNone = .{} },
.matcher = Matcher{ .matchFalse = .{} }, .matcher = .{ .matchFalse = .{} },
.include_root = false, .include_root = false,
}; };
} }
@@ -240,10 +208,8 @@ pub fn HTMLCollectionByLinks(
) !HTMLCollection { ) !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = root, .root = root,
.walker = Walker{ .walkerDepthFirst = .{} }, .walker = .{ .walkerDepthFirst = .{} },
.matcher = Matcher{ .matcher = .{ .matchByLinks = MatchByLinks{} },
.matchByLinks = MatchByLinks{},
},
.include_root = include_root, .include_root = include_root,
}; };
} }
@@ -267,10 +233,8 @@ pub fn HTMLCollectionByAnchors(
) !HTMLCollection { ) !HTMLCollection {
return HTMLCollection{ return HTMLCollection{
.root = root, .root = root,
.walker = Walker{ .walkerDepthFirst = .{} }, .walker = .{ .walkerDepthFirst = .{} },
.matcher = Matcher{ .matcher = .{ .matchByAnchors = MatchByAnchors{} },
.matchByAnchors = MatchByAnchors{},
},
.include_root = include_root, .include_root = include_root,
}; };
} }
@@ -321,7 +285,7 @@ pub const HTMLCollection = struct {
cur_node: ?*parser.Node = undefined, cur_node: ?*parser.Node = undefined,
// start returns the first node to walk on. // start returns the first node to walk on.
fn start(self: HTMLCollection) !?*parser.Node { fn start(self: *const HTMLCollection) !?*parser.Node {
if (self.root == null) return null; if (self.root == null) return null;
if (self.include_root) { if (self.include_root) {

View File

@@ -45,13 +45,6 @@ pub const MutationObserver = struct {
options: MutationObserverInit, options: MutationObserverInit,
}; };
const deinitFunc = struct {
fn deinit(ctx: ?*anyopaque, allocator: std.mem.Allocator) void {
const o: *Observer = @ptrCast(@alignCast(ctx));
allocator.destroy(o);
}
}.deinit;
const Observers = std.ArrayListUnmanaged(*Observer); const Observers = std.ArrayListUnmanaged(*Observer);
pub const MutationObserverInit = struct { pub const MutationObserverInit = struct {
@@ -104,7 +97,7 @@ pub const MutationObserver = struct {
arena, arena,
"DOMNodeInserted", "DOMNodeInserted",
EventHandler, EventHandler,
.{ .cbk = self.cbk, .ctx = o, .deinitFunc = deinitFunc }, .{ .cbk = self.cbk, .ctx = o },
false, false,
); );
try parser.eventTargetAddEventListener( try parser.eventTargetAddEventListener(
@@ -112,7 +105,7 @@ pub const MutationObserver = struct {
arena, arena,
"DOMNodeRemoved", "DOMNodeRemoved",
EventHandler, EventHandler,
.{ .cbk = self.cbk, .ctx = o, .deinitFunc = deinitFunc }, .{ .cbk = self.cbk, .ctx = o },
false, false,
); );
} }
@@ -122,7 +115,7 @@ pub const MutationObserver = struct {
arena, arena,
"DOMAttrModified", "DOMAttrModified",
EventHandler, EventHandler,
.{ .cbk = self.cbk, .ctx = o, .deinitFunc = deinitFunc }, .{ .cbk = self.cbk, .ctx = o },
false, false,
); );
} }
@@ -132,7 +125,7 @@ pub const MutationObserver = struct {
arena, arena,
"DOMCharacterDataModified", "DOMCharacterDataModified",
EventHandler, EventHandler,
.{ .cbk = self.cbk, .ctx = o, .deinitFunc = deinitFunc }, .{ .cbk = self.cbk, .ctx = o },
false, false,
); );
} }
@@ -142,7 +135,7 @@ pub const MutationObserver = struct {
arena, arena,
"DOMSubtreeModified", "DOMSubtreeModified",
EventHandler, EventHandler,
.{ .cbk = self.cbk, .ctx = o, .deinitFunc = deinitFunc }, .{ .cbk = self.cbk, .ctx = o },
false, false,
); );
} }
@@ -153,15 +146,6 @@ pub const MutationObserver = struct {
// TODO unregister listeners. // TODO unregister listeners.
} }
pub fn deinit(self: *MutationObserver, state: *SessionState) void {
const arena = state.arena;
// TODO unregister listeners.
for (self.observers.items) |o| {
arena.destroy(o);
}
self.observers.deinit(arena);
}
// TODO // TODO
pub fn _takeRecords(_: *const MutationObserver) ?[]const u8 { pub fn _takeRecords(_: *const MutationObserver) ?[]const u8 {
return &[_]u8{}; return &[_]u8{};

View File

@@ -51,7 +51,6 @@ pub const URL = struct {
) !URL { ) !URL {
const arena = state.arena; const arena = state.arena;
const raw = try std.mem.concat(arena, u8, &[_][]const u8{ url, base orelse "" }); const raw = try std.mem.concat(arena, u8, &[_][]const u8{ url, base orelse "" });
errdefer arena.free(raw);
const uri = std.Uri.parse(raw) catch return error.TypeError; const uri = std.Uri.parse(raw) catch return error.TypeError;
return init(arena, uri); return init(arena, uri);

View File

@@ -48,15 +48,7 @@ pub const XMLHttpRequestUpload = struct {
proto: XMLHttpRequestEventTarget = XMLHttpRequestEventTarget{}, proto: XMLHttpRequestEventTarget = XMLHttpRequestEventTarget{},
}; };
pub const XMLHttpRequestBodyInitTag = enum { const XMLHttpRequestBodyInit = union(enum) {
Blob,
BufferSource,
FormData,
URLSearchParams,
String,
};
pub const XMLHttpRequestBodyInit = union(XMLHttpRequestBodyInitTag) {
Blob: []const u8, Blob: []const u8,
BufferSource: []const u8, BufferSource: []const u8,
FormData: []const u8, FormData: []const u8,
@@ -173,11 +165,6 @@ pub const XMLHttpRequest = struct {
}; };
} }
fn deinit(self: *Headers) void {
self.free();
self.list.deinit(self.arena);
}
fn append(self: *Headers, k: []const u8, v: []const u8) !void { fn append(self: *Headers, k: []const u8, v: []const u8) !void {
// duplicate strings // duplicate strings
const kk = try self.arena.dupe(u8, k); const kk = try self.arena.dupe(u8, k);
@@ -185,17 +172,8 @@ pub const XMLHttpRequest = struct {
try self.list.append(self.arena, .{ .name = kk, .value = vv }); try self.list.append(self.arena, .{ .name = kk, .value = vv });
} }
// free all strings allocated. fn reset(self: *Headers) void {
fn free(self: *Headers) void { self.list.clearRetainingCapacity();
for (self.list.items) |h| {
self.arena.free(h.name);
self.arena.free(h.value);
}
}
fn clearAndFree(self: *Headers) void {
self.free();
self.list.clearAndFree(self.arena);
} }
fn has(self: Headers, k: []const u8) bool { fn has(self: Headers, k: []const u8) bool {
@@ -222,9 +200,7 @@ pub const XMLHttpRequest = struct {
fn set(self: *Headers, k: []const u8, v: []const u8) !void { fn set(self: *Headers, k: []const u8, v: []const u8) !void {
for (self.list.items, 0..) |h, i| { for (self.list.items, 0..) |h, i| {
if (std.ascii.eqlIgnoreCase(k, h.name)) { if (std.ascii.eqlIgnoreCase(k, h.name)) {
const hh = self.list.swapRemove(i); _ = self.list.swapRemove(i);
self.arena.free(hh.name);
self.arena.free(hh.value);
} }
} }
self.append(k, v); self.append(k, v);
@@ -247,25 +223,19 @@ pub const XMLHttpRequest = struct {
JSON: JSONValue, JSON: JSONValue,
}; };
const ResponseObjTag = enum { const ResponseObj = union(enum) {
Document,
Failure,
JSON,
};
const ResponseObj = union(ResponseObjTag) {
Document: *parser.Document, Document: *parser.Document,
Failure: void, Failure: void,
JSON: std.json.Parsed(JSONValue), JSON: JSONValue,
fn deinit(self: ResponseObj) void { fn deinit(self: ResponseObj) void {
return switch (self) { switch (self) {
.JSON, .Failure => {},
.Document => |d| { .Document => |d| {
const doc = @as(*parser.DocumentHTML, @ptrCast(d)); const doc = @as(*parser.DocumentHTML, @ptrCast(d));
parser.documentHTMLClose(doc) catch {}; parser.documentHTMLClose(doc) catch {};
}, },
.JSON => |p| p.deinit(), }
.Failure => {},
};
} }
}; };
@@ -291,15 +261,17 @@ pub const XMLHttpRequest = struct {
pub fn reset(self: *XMLHttpRequest) void { pub fn reset(self: *XMLHttpRequest) void {
self.url = null; self.url = null;
if (self.response_obj) |v| v.deinit(); if (self.response_obj) |v| {
v.deinit();
}
self.response_obj = null; self.response_obj = null;
self.response_type = .Empty; self.response_type = .Empty;
self.response_mime = null; self.response_mime = null;
// TODO should we clearRetainingCapacity instead? // TODO should we clearRetainingCapacity instead?
self.headers.clearAndFree(); self.headers.reset();
self.response_headers.clearAndFree(); self.response_headers.reset();
self.response_status = 0; self.response_status = 0;
self.send_flag = false; self.send_flag = false;
@@ -308,13 +280,9 @@ pub const XMLHttpRequest = struct {
} }
pub fn deinit(self: *XMLHttpRequest, alloc: Allocator) void { pub fn deinit(self: *XMLHttpRequest, alloc: Allocator) void {
self.reset(); if (self.response_obj) |v| {
self.headers.deinit(); v.deinit();
self.response_headers.deinit();
if (self.response_mime) |*mime| {
mime.deinit();
} }
self.proto.deinit(alloc); self.proto.deinit(alloc);
} }
@@ -666,7 +634,7 @@ pub const XMLHttpRequest = struct {
return switch (obj) { return switch (obj) {
.Failure => null, .Failure => null,
.Document => |v| .{ .Document = v }, .Document => |v| .{ .Document = v },
.JSON => |v| .{ .JSON = v.value }, .JSON => |v| .{ .JSON = v },
}; };
} }
@@ -707,7 +675,7 @@ pub const XMLHttpRequest = struct {
return switch (obj) { return switch (obj) {
.Failure => null, .Failure => null,
.Document => |v| .{ .Document = v }, .Document => |v| .{ .Document = v },
.JSON => |v| .{ .JSON = v.value }, .JSON => |v| .{ .JSON = v },
}; };
} }
@@ -753,7 +721,7 @@ pub const XMLHttpRequest = struct {
fn setResponseObjJSON(self: *XMLHttpRequest) void { fn setResponseObjJSON(self: *XMLHttpRequest) void {
// TODO should we use parseFromSliceLeaky if we expect the allocator is // TODO should we use parseFromSliceLeaky if we expect the allocator is
// already an arena? // already an arena?
const p = std.json.parseFromSlice( const p = std.json.parseFromSliceLeaky(
JSONValue, JSONValue,
self.arena, self.arena,
self.response_bytes.items, self.response_bytes.items,

View File

@@ -151,7 +151,6 @@ fn navigate(cmd: anytype) !void {
.loaderId = bc.loader_id, .loaderId = bc.loader_id,
}, .{}); }, .{});
std.debug.print("page: {s}\n", .{target_id});
try page.navigate(url, .{ try page.navigate(url, .{
.reason = .address_bar, .reason = .address_bar,
}); });