tiny.css.atom
Defined in tiny.css.
Interned identifier atoms.
API (12)
Actions
Public operations.
Table.intern: Internstextand returns its id.Table.lookup: Returns the id oftext, ornonewhen the table has never seen it.Table.name: The text behindid.Table.reset: Empties the table without releasing its placement storage.fold: ASCII foldstextintobuffer, borrowingtextwhen it is already folded.hash: The FNV-1a digest the table probes with.slotCount: The power of two slot count that keeps the table under a half load.
Types and contracts
Public types and contracts.
Record: One interned identifier and the hash that placed it.Table: The open addressed interning table.
Values and defaults
Public values and defaults.
max_atoms: The largest number of distinct atoms one stylesheet may intern.max_text_bytes: The largest identifier this engine interns, in bytes.none: The absent atom.
Source
Source: lib/css/src/atom.zig
zig
//! Interned identifier atoms.//!//! Selector compounds hold `u32` atom ids rather than byte slices so that//! matching an id, a class, a type name, or a role is an integer compare. Id//! zero means absent, so a compound field left unset matches any element.//!//! One table serves every identifier position. Ids and classes intern with//! their authored case. Type names and role names intern ASCII folded, which//! is what lets a consumer resolve its own closed enum once per stylesheet//! through `StyleSheet.token` instead of comparing strings per element.//!//! Atom text borrows the caller's source bytes whenever the identifier carries//! no escape and no case fold. The `arena` holds only the decoded or folded//! copies, so the usual stylesheet leaves it empty.//!//! Capacity is bounded at `max_atoms` so that an atom id narrows to the `u16`//! that `Compound.kind` and `Compound.role` declare.const std = @import("std");/// The absent atom. No interned identifier ever takes this id.pub const none: u32 = 0;/// The largest number of distinct atoms one stylesheet may intern.pub const max_atoms: u32 = 65_535;/// The largest identifier this engine interns, in bytes.pub const max_text_bytes: usize = 255;/// One interned identifier and the hash that placed it.pub const Record = struct { text: []const u8, hash: u32,};/// The open addressed interning table. `records`, `slots`, and `arena` are/// placement owned, so the table allocates nothing.pub const Table = struct { records: []Record, slots: []u32, arena: []u8, count: u32 = 0, arena_used: u32 = 0, /// Empties the table without releasing its placement storage. pub fn reset(self: *Table) void { @memset(self.slots, 0); self.count = 0; self.arena_used = 0; } /// Returns the id of `text`, or `none` when the table has never seen it. pub fn lookup(self: *const Table, text: []const u8) u32 { if (self.slots.len != 0) std.debug.assert(std.math.isPowerOfTwo(self.slots.len)); if (text.len == 0 or text.len > max_text_bytes or self.slots.len == 0) return none; const digest = hash(text); const mask = self.slots.len - 1; var probe: usize = 0; var index = @as(usize, digest) & mask; while (probe < self.slots.len) : (probe += 1) { const slot = self.slots[index]; if (slot == none) return none; const record = self.records[slot - 1]; if (record.hash == digest and std.mem.eql(u8, record.text, text)) return slot; index = (index + 1) & mask; } return none; } /// Interns `text` and returns its id. `copy` moves the bytes into the /// arena, which a caller passing a folded or unescaped stack buffer must /// request. Admission is asserted, never grown. pub fn intern(self: *Table, text: []const u8, copy: bool) u32 { std.debug.assert(text.len > 0); std.debug.assert(text.len <= max_text_bytes); std.debug.assert(self.slots.len > 0); const existing = self.lookup(text); if (existing != none) return existing; std.debug.assert(self.count < self.records.len); std.debug.assert(self.count < max_atoms); const stored = if (copy) self.store(text) else text; const digest = hash(stored); self.records[self.count] = .{ .text = stored, .hash = digest }; self.count += 1; self.place(digest, self.count); return self.count; } /// The text behind `id`. Asserts that the id was interned by this table. pub fn name(self: *const Table, id: u32) []const u8 { std.debug.assert(id != none); std.debug.assert(id <= self.count); return self.records[id - 1].text; } fn store(self: *Table, source: []const u8) []const u8 { const start = self.arena_used; std.debug.assert(start + source.len <= self.arena.len); @memcpy(self.arena[start..][0..source.len], source); self.arena_used = start + @as(u32, @intCast(source.len)); return self.arena[start..][0..source.len]; } fn place(self: *Table, digest: u32, id: u32) void { const mask = self.slots.len - 1; var probe: usize = 0; var index = @as(usize, digest) & mask; while (probe < self.slots.len) : (probe += 1) { if (self.slots[index] == none) { self.slots[index] = id; return; } index = (index + 1) & mask; } unreachable; }};/// The FNV-1a digest the table probes with.pub fn hash(text: []const u8) u32 { var digest: u32 = 2_166_136_261; for (text) |byte| { digest ^= byte; digest *%= 16_777_619; } return digest;}/// The power of two slot count that keeps the table under a half load.pub fn slotCount(atoms: usize) usize { if (atoms == 0) return 0; const wanted = std.math.mul(usize, atoms, 2) catch return 0; return std.math.ceilPowerOfTwo(usize, wanted) catch 0;}/// ASCII folds `text` into `buffer`, borrowing `text` when it is already/// folded. Returns null when the identifier exceeds `max_text_bytes`.pub fn fold(text: []const u8, buffer: []u8) ?[]const u8 { if (text.len == 0 or text.len > max_text_bytes) return null; std.debug.assert(buffer.len >= max_text_bytes); var upper = false; for (text) |byte| { if (byte >= 'A' and byte <= 'Z') { upper = true; break; } } if (!upper) return text; for (text, 0..) |byte, index| buffer[index] = std.ascii.toLower(byte); return buffer[0..text.len];}const TestTable = struct { records: [16]Record = undefined, slots: [32]u32 = @splat(0), arena: [256]u8 = undefined, fn table(self: *TestTable) Table { return .{ .records = &self.records, .slots = &self.slots, .arena = &self.arena }; }};test "an interned identifier keeps one id across repeated interning" { var storage = TestTable{}; var table = storage.table(); const first = table.intern("primary", false); const second = table.intern("primary", false); try std.testing.expectEqual(first, second); try std.testing.expectEqual(@as(u32, 1), first); try std.testing.expectEqual(@as(u32, 1), table.count); try std.testing.expectEqualStrings("primary", table.name(first));}test "a distinct identifier takes the next id and lookup finds both" { var storage = TestTable{}; var table = storage.table(); const first = table.intern("save", false); const second = table.intern("cancel", false); try std.testing.expectEqual(@as(u32, 2), second); try std.testing.expectEqual(first, table.lookup("save")); try std.testing.expectEqual(second, table.lookup("cancel")); try std.testing.expectEqual(none, table.lookup("missing"));}test "a copied identifier lives in the arena and leaves the source" { var storage = TestTable{}; var table = storage.table(); var buffer: [max_text_bytes]u8 = undefined; const folded = fold("Button", &buffer).?; const id = table.intern(folded, true); try std.testing.expectEqualStrings("button", table.name(id)); try std.testing.expectEqual(@as(u32, 6), table.arena_used); try std.testing.expectEqual(id, table.lookup("button"));}test "folding borrows an already folded identifier" { var buffer: [max_text_bytes]u8 = undefined; const borrowed = fold("button", &buffer).?; try std.testing.expectEqual(@as(usize, 6), borrowed.len); try std.testing.expect(borrowed.ptr != &buffer); try std.testing.expect(fold("", &buffer) == null);}test "the slot count stays a power of two above twice the atom count" { try std.testing.expectEqual(@as(usize, 0), slotCount(0)); try std.testing.expectEqual(@as(usize, 2), slotCount(1)); try std.testing.expectEqual(@as(usize, 8), slotCount(4)); try std.testing.expectEqual(@as(usize, 16), slotCount(5));}test "a table reset clears every slot and keeps its storage" { var storage = TestTable{}; var table = storage.table(); _ = table.intern("alpha", false); table.reset(); try std.testing.expectEqual(@as(u32, 0), table.count); try std.testing.expectEqual(none, table.lookup("alpha")); try std.testing.expectEqual(@as(u32, 1), table.intern("beta", false));}Source: lib/css/src/root.zig:35
zig
pub const atom = @import("atom.zig");Audit
| Definitions | 13 |
|---|---|
| Public names | 13 |
| Members | 7 |
| Version | 26.7.0 |
| Revision | daab053ee433 |