micro-optimize pseudoclass parsing, add :modal

This commit is contained in:
Karl Seguin
2025-11-20 22:01:14 +08:00
parent 57aa267f24
commit 302b9f9dd7
3 changed files with 28 additions and 23 deletions

View File

@@ -499,6 +499,7 @@ fn attributeContainsWord(value: []const u8, word: []const u8) bool {
fn matchesPseudoClass(el: *Node.Element, pseudo: Selector.PseudoClass) bool { fn matchesPseudoClass(el: *Node.Element, pseudo: Selector.PseudoClass) bool {
switch (pseudo) { switch (pseudo) {
.modal => return false,
.first_child => return isFirstChild(el), .first_child => return isFirstChild(el),
.last_child => return isLastChild(el), .last_child => return isLastChild(el),
.only_child => return isFirstChild(el) and isLastChild(el), .only_child => return isFirstChild(el) and isLastChild(el),

View File

@@ -398,29 +398,25 @@ fn pseudoClass(self: *Parser, arena: Allocator, page: *Page) !Selector.PseudoCla
return error.UnknownPseudoClass; return error.UnknownPseudoClass;
} }
// Simple pseudo-classes without arguments switch (name.len) {
if (std.mem.eql(u8, name, "first-child")) { 5 => {
return .first_child; if (fastEql(name, "modal")) return .modal;
} },
10 => {
if (std.mem.eql(u8, name, "last-child")) { if (fastEql(name, "only-child")) return .only_child;
return .last_child; if (fastEql(name, "last-child")) return .last_child;
} },
11 => {
if (std.mem.eql(u8, name, "only-child")) { if (fastEql(name, "first-child")) return .first_child;
return .only_child; },
} 12 => {
if (fastEql(name, "only-of-type")) return .only_of_type;
if (std.mem.eql(u8, name, "first-of-type")) { if (fastEql(name, "last-of-type")) return .last_of_type;
return .first_of_type; },
} 13 => {
if (fastEql(name, "first-of-type")) return .first_of_type;
if (std.mem.eql(u8, name, "last-of-type")) { },
return .last_of_type; else => {},
}
if (std.mem.eql(u8, name, "only-of-type")) {
return .only_of_type;
} }
return error.UnknownPseudoClass; return error.UnknownPseudoClass;
@@ -801,6 +797,13 @@ fn asUint(comptime string: anytype) std.meta.Int(
return @bitCast(@as(*const [byteLength]u8, string).*); return @bitCast(@as(*const [byteLength]u8, string).*);
} }
fn fastEql(a: []const u8, comptime b: []const u8) bool {
for (a, b) |a_byte, b_byte| {
if (a_byte != b_byte) return false;
}
return true;
}
const testing = @import("../../../testing.zig"); const testing = @import("../../../testing.zig");
test "Selector: Parser.ID" { test "Selector: Parser.ID" {
{ {

View File

@@ -131,6 +131,7 @@ pub const AttributeMatcher = union(enum) {
}; };
pub const PseudoClass = union(enum) { pub const PseudoClass = union(enum) {
modal,
first_child, first_child,
last_child, last_child,
only_child, only_child,