tiny.ui.style.share
Defined in style.
API (8)
Actions
Public operations.
Types and contracts
Public types and contracts.
Source
Source: lib/ui/src/style/root.zig:4
zig
pub const share = @import("share.zig");Source: lib/ui/src/style/share.zig
zig
const std = @import("std");const css = @import("css");const computed = @import("computed.zig");const asset = @import("../asset/root.zig");pub const Key = extern struct { matched_rules: u32, inline_first: u32, inline_count: u32, parent_style: u32,};pub const KeySlot = struct { key: Key = .{ .matched_rules = 0, .inline_first = 0, .inline_count = 0, .parent_style = 0 }, style: u32 = 0, matched: [4]u64 = @splat(0), matched_count: u8 = 0,};comptime { std.debug.assert(@sizeOf(Key) == 16); std.debug.assert(@alignOf(Key) == 4); std.debug.assert(@offsetOf(Key, "parent_style") == 12);}pub const Pool = struct { records: []computed.Record, slots: []u32, keys: []KeySlot = &.{}, chains: []asset.AssetHandle = &.{}, font_faces: u32 = 0, used: u32 = 0, pub fn reset(self: *Pool) void { self.used = 0; @memset(self.slots, 0); for (self.keys) |*slot| slot.* = .{}; } pub fn truncate(self: *Pool, retained: u32) void { std.debug.assert(retained <= self.used); self.used = retained; @memset(self.slots, 0); for (self.keys) |*slot| slot.* = .{}; const mask = self.slots.len - 1; for (self.records[0..retained], 0..) |*record, index| { const hash = std.hash.Wyhash.hash(0, std.mem.asBytes(&record.computed)); var at: usize = @intCast(hash & @as(u64, @intCast(mask))); while (self.slots[at] != 0) at = (at + 1) & mask; self.slots[at] = @intCast(index + 1); } } fn sameMatches(slot: KeySlot, matches: []const css.match.Match) bool { if (matches.len != slot.matched_count) return false; for (matches, 0..) |item, at| { const identity = (@as(u64, item.rule) << 32) | item.selector; if (slot.matched[at] != identity) return false; } return true; } fn remember(slot: *KeySlot, key: Key, matches: []const css.match.Match, style: u32) void { slot.* = .{ .key = key, .style = style, .matched_count = @intCast(matches.len) }; for (matches, 0..) |item, at| { slot.matched[at] = (@as(u64, item.rule) << 32) | item.selector; } } pub fn lookup(self: *const Pool, key: Key, matches: []const css.match.Match) ?u32 { if (self.keys.len == 0 or matches.len > 4) return null; const key_hash = std.hash.Wyhash.hash(0, std.mem.asBytes(&key)); const mask = self.keys.len - 1; var at: usize = @intCast(key_hash & @as(u64, @intCast(mask))); var probe: usize = 0; while (probe < self.keys.len) : (probe += 1) { const slot = self.keys[at]; if (slot.style == 0) return null; if (std.meta.eql(slot.key, key) and sameMatches(slot, matches)) return slot.style; at = (at + 1) & mask; } return null; } pub fn intern(self: *Pool, key: Key, matches: []const css.match.Match, style: css.cascade.Computed, metrics: computed.Metrics, fonts: ?computed.FontSource) error{DistinctStyleQuota}!u32 { var key_slot: ?*KeySlot = null; if (self.keys.len != 0 and matches.len <= 4) { std.debug.assert(std.math.isPowerOfTwo(self.keys.len)); const key_hash = std.hash.Wyhash.hash(0, std.mem.asBytes(&key)); const key_mask = self.keys.len - 1; var key_at: usize = @intCast(key_hash & @as(u64, @intCast(key_mask))); var key_probe: usize = 0; while (key_probe < self.keys.len) : (key_probe += 1) { const slot = &self.keys[key_at]; if (slot.style == 0) { key_slot = slot; break; } if (std.meta.eql(slot.key, key) and sameMatches(slot.*, matches) and std.mem.eql(u8, std.mem.asBytes(&self.records[slot.style - 1].computed), std.mem.asBytes(&style))) return slot.style; key_at = (key_at + 1) & key_mask; } } std.debug.assert(std.math.isPowerOfTwo(self.slots.len)); const hash = std.hash.Wyhash.hash(0, std.mem.asBytes(&style)); const mask = self.slots.len - 1; var probe: usize = 0; var at: usize = @intCast(hash & @as(u64, @intCast(mask))); while (probe < self.slots.len) : (probe += 1) { const id = self.slots[at]; if (id == 0) { if (self.used == self.records.len) return error.DistinctStyleQuota; const font_environment: ?computed.FontEnvironment = if (fonts) |source| blk: { std.debug.assert(source.registry.font_used <= self.font_faces); const first = @as(usize, self.used) * self.font_faces; break :blk .{ .source = source, .fallback_out = self.chains[first..][0..self.font_faces], .first = @intCast(first), }; } else null; self.used += 1; self.records[self.used - 1] = computed.lower(style, metrics, font_environment); self.slots[at] = self.used; if (key_slot) |slot| remember(slot, key, matches, self.used); return self.used; } if (std.mem.eql(u8, std.mem.asBytes(&self.records[id - 1].computed), std.mem.asBytes(&style))) { if (key_slot) |slot| remember(slot, key, matches, id); return id; } at = (at + 1) & mask; } return error.DistinctStyleQuota; } pub fn get(self: *const Pool, id: u32) *const computed.Record { std.debug.assert(id > 0); std.debug.assert(id <= self.used); return &self.records[id - 1]; }};test "style sharing interns equal independent cascade results" { var records: [3]computed.Record = undefined; var slots: [8]u32 = @splat(0); var pool = Pool{ .records = &records, .slots = &slots }; const initial = css.cascade.Computed.initial(); const left = try pool.intern(.{ .matched_rules = 1, .inline_first = 0, .inline_count = 0, .parent_style = 0 }, &.{}, initial, .{}, null); const right = try pool.intern(.{ .matched_rules = 2, .inline_first = 1, .inline_count = 0, .parent_style = 0 }, &.{}, initial, .{}, null); try std.testing.expectEqual(left, right); var changed = initial; changed.set(.color, css.value.Value.color(0xFF0000FF)); const other = try pool.intern(.{ .matched_rules = 2, .inline_first = 1, .inline_count = 1, .parent_style = 0 }, &.{}, changed, .{}, null); try std.testing.expect(other != left); try std.testing.expectEqual(@as(u32, 2), pool.used);}Audit
| Definitions | 9 |
|---|---|
| Public names | 9 |
| Members | 14 |
| Version | 26.7.0 |
| Revision | daab053ee433 |