lib/choir/src/core/attrs.zig
daab053ee43316e1809a84551d573ddd1e5bf3d2
1 const std = @import("std");
2 const attribute = @import("attribute.zig");
3 const Attribute = attribute.Attribute;
4 const Type = @import("type.zig").Type;
5 const interfaces = @import("interfaces/root.zig");
6
7 const default_hash_load = std.hash_map.default_max_load_percentage;
8
9 fn hashUpdateU64(hasher: *std.hash.Wyhash, value: u64) void {
10 var buf: [8]u8 = undefined;
11 std.mem.writeInt(u64, &buf, value, .little);
12 hasher.update(&buf);
13 }
14
15 pub const DialectAttrKey = struct {
16 attr_id: u32,
17 payload: []const u8,
18 };
19
20 const DialectAttrKeyContext = struct {
21 pub fn hash(_: DialectAttrKeyContext, key: DialectAttrKey) u64 {
22 var hasher = std.hash.Wyhash.init(0);
23 hashUpdateU64(&hasher, key.attr_id);
24 hasher.update(key.payload);
25 return hasher.final();
26 }
27
28 pub fn eql(_: DialectAttrKeyContext, a: DialectAttrKey, b: DialectAttrKey) bool {
29 return a.attr_id == b.attr_id and std.mem.eql(u8, a.payload, b.payload);
30 }
31 };
32
33 pub const IntegerAttrKey = struct {
34 value: i64,
35 width: u8,
36 is_signed: bool,
37 };
38
39 const IntegerAttrKeyContext = struct {
40 pub fn hash(_: IntegerAttrKeyContext, key: IntegerAttrKey) u64 {
41 var hasher = std.hash.Wyhash.init(0);
42 hashUpdateU64(&hasher, @bitCast(key.value));
43 hasher.update(&[_]u8{key.width});
44 hasher.update(&[_]u8{if (key.is_signed) 1 else 0});
45 return hasher.final();
46 }
47
48 pub fn eql(_: IntegerAttrKeyContext, a: IntegerAttrKey, b: IntegerAttrKey) bool {
49 return a.value == b.value and a.width == b.width and a.is_signed == b.is_signed;
50 }
51 };
52
53 pub const FloatAttrKey = struct {
54 value_bits: u64,
55 width: u8,
56 };
57
58 const FloatAttrKeyContext = struct {
59 pub fn hash(_: FloatAttrKeyContext, key: FloatAttrKey) u64 {
60 var hasher = std.hash.Wyhash.init(0);
61 hashUpdateU64(&hasher, key.value_bits);
62 hasher.update(&[_]u8{key.width});
63 return hasher.final();
64 }
65
66 pub fn eql(_: FloatAttrKeyContext, a: FloatAttrKey, b: FloatAttrKey) bool {
67 return a.value_bits == b.value_bits and a.width == b.width;
68 }
69 };
70
71 pub const StringAttrKey = struct {
72 value: []const u8,
73 };
74
75 const StringAttrKeyContext = struct {
76 pub fn hash(_: StringAttrKeyContext, key: StringAttrKey) u64 {
77 var hasher = std.hash.Wyhash.init(0);
78 hasher.update(key.value);
79 return hasher.final();
80 }
81
82 pub fn eql(_: StringAttrKeyContext, a: StringAttrKey, b: StringAttrKey) bool {
83 return std.mem.eql(u8, a.value, b.value);
84 }
85 };
86
87 pub const SymbolRefAttrKey = struct {
88 root_reference: []const u8,
89 nested_references: []const []const u8,
90 };
91
92 const SymbolRefAttrKeyContext = struct {
93 pub fn hash(_: SymbolRefAttrKeyContext, key: SymbolRefAttrKey) u64 {
94 var hasher = std.hash.Wyhash.init(0);
95 hashUpdateU64(&hasher, @intCast(key.root_reference.len));
96 hasher.update(key.root_reference);
97 hashUpdateU64(&hasher, @intCast(key.nested_references.len));
98 for (key.nested_references) |nested| {
99 hashUpdateU64(&hasher, @intCast(nested.len));
100 hasher.update(nested);
101 }
102 return hasher.final();
103 }
104
105 pub fn eql(_: SymbolRefAttrKeyContext, a: SymbolRefAttrKey, b: SymbolRefAttrKey) bool {
106 if (!std.mem.eql(u8, a.root_reference, b.root_reference)) return false;
107 if (a.nested_references.len != b.nested_references.len) return false;
108 for (a.nested_references, b.nested_references) |lhs, rhs| {
109 if (!std.mem.eql(u8, lhs, rhs)) return false;
110 }
111 return true;
112 }
113 };
114
115 pub const StringListAttrKey = struct {
116 values: []const []const u8,
117 };
118
119 const StringListAttrKeyContext = struct {
120 pub fn hash(_: StringListAttrKeyContext, key: StringListAttrKey) u64 {
121 var hasher = std.hash.Wyhash.init(0);
122 for (key.values) |value| {
123 hashUpdateU64(&hasher, @intCast(value.len));
124 hasher.update(value);
125 }
126 return hasher.final();
127 }
128
129 pub fn eql(_: StringListAttrKeyContext, a: StringListAttrKey, b: StringListAttrKey) bool {
130 if (a.values.len != b.values.len) return false;
131 for (a.values, b.values) |lhs, rhs| {
132 if (!std.mem.eql(u8, lhs, rhs)) return false;
133 }
134 return true;
135 }
136 };
137
138 pub const TypeListAttrKey = struct {
139 values: []const Type,
140 };
141
142 const TypeListAttrKeyContext = struct {
143 pub fn hash(_: TypeListAttrKeyContext, key: TypeListAttrKey) u64 {
144 var hasher = std.hash.Wyhash.init(0);
145 for (key.values) |typ| {
146 hashUpdateU64(&hasher, typ.uniqueId());
147 }
148 return hasher.final();
149 }
150
151 pub fn eql(_: TypeListAttrKeyContext, a: TypeListAttrKey, b: TypeListAttrKey) bool {
152 if (a.values.len != b.values.len) return false;
153 for (a.values, b.values) |lhs, rhs| {
154 if (!lhs.eql(rhs)) return false;
155 }
156 return true;
157 }
158 };
159
160 pub const ArrayAttrKey = struct {
161 values: []const Attribute,
162 };
163
164 const ArrayAttrKeyContext = struct {
165 pub fn hash(_: ArrayAttrKeyContext, key: ArrayAttrKey) u64 {
166 var hasher = std.hash.Wyhash.init(0);
167 hashUpdateU64(&hasher, @intCast(key.values.len));
168 for (key.values) |attr| {
169 hashUpdateU64(&hasher, @backingInt(attr.attr_id));
170 }
171 return hasher.final();
172 }
173
174 pub fn eql(_: ArrayAttrKeyContext, a: ArrayAttrKey, b: ArrayAttrKey) bool {
175 if (a.values.len != b.values.len) return false;
176 for (a.values, b.values) |lhs, rhs| {
177 if (!lhs.eql(rhs)) return false;
178 }
179 return true;
180 }
181 };
182
183 pub const AttrStorage = struct {
184 dialect_attrs: DialectAttrMap,
185 integer_attrs: IntegerAttrMap,
186 float_attrs: FloatAttrMap,
187 bool_attrs: BoolAttrMap,
188 string_attrs: StringAttrMap,
189 symbol_ref_attrs: SymbolRefAttrMap,
190 string_list_attrs: StringListAttrMap,
191 type_list_attrs: TypeListAttrMap,
192 array_attrs: ArrayAttrMap,
193
194 const DialectAttrMap: type = std.HashMap(DialectAttrKey, Attribute, DialectAttrKeyContext, default_hash_load);
195 const IntegerAttrMap: type = std.HashMap(IntegerAttrKey, Attribute, IntegerAttrKeyContext, default_hash_load);
196 const FloatAttrMap: type = std.HashMap(FloatAttrKey, Attribute, FloatAttrKeyContext, default_hash_load);
197 const BoolAttrMap: type = std.AutoHashMap(bool, Attribute);
198 const StringAttrMap: type = std.HashMap(StringAttrKey, Attribute, StringAttrKeyContext, default_hash_load);
199 const SymbolRefAttrMap: type = std.HashMap(SymbolRefAttrKey, Attribute, SymbolRefAttrKeyContext, default_hash_load);
200 const StringListAttrMap: type = std.HashMap(StringListAttrKey, Attribute, StringListAttrKeyContext, default_hash_load);
201 const TypeListAttrMap: type = std.HashMap(TypeListAttrKey, Attribute, TypeListAttrKeyContext, default_hash_load);
202 const ArrayAttrMap: type = std.HashMap(ArrayAttrKey, Attribute, ArrayAttrKeyContext, default_hash_load);
203
204 pub fn init(allocator: std.mem.Allocator) AttrStorage {
205 return .{
206 .dialect_attrs = DialectAttrMap.init(allocator),
207 .integer_attrs = IntegerAttrMap.init(allocator),
208 .float_attrs = FloatAttrMap.init(allocator),
209 .bool_attrs = BoolAttrMap.init(allocator),
210 .string_attrs = StringAttrMap.init(allocator),
211 .symbol_ref_attrs = SymbolRefAttrMap.init(allocator),
212 .string_list_attrs = StringListAttrMap.init(allocator),
213 .type_list_attrs = TypeListAttrMap.init(allocator),
214 .array_attrs = ArrayAttrMap.init(allocator),
215 };
216 }
217
218 pub fn deinit(self: *AttrStorage, payload_allocator: std.mem.Allocator) void {
219 var dialect_iter = self.dialect_attrs.valueIterator();
220 while (dialect_iter.next()) |attr| {
221 const impl: *Attribute.DialectAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
222 payload_allocator.free(impl.payload);
223 payload_allocator.destroy(impl);
224 }
225 self.dialect_attrs.deinit();
226
227 var int_iter = self.integer_attrs.valueIterator();
228 while (int_iter.next()) |attr| {
229 const impl: *Attribute.IntegerAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
230 payload_allocator.destroy(impl);
231 }
232 self.integer_attrs.deinit();
233
234 var float_iter = self.float_attrs.valueIterator();
235 while (float_iter.next()) |attr| {
236 const impl: *Attribute.FloatAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
237 payload_allocator.destroy(impl);
238 }
239 self.float_attrs.deinit();
240
241 var bool_iter = self.bool_attrs.valueIterator();
242 while (bool_iter.next()) |attr| {
243 const impl: *Attribute.BoolAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
244 payload_allocator.destroy(impl);
245 }
246 self.bool_attrs.deinit();
247
248 var string_iter = self.string_attrs.valueIterator();
249 while (string_iter.next()) |attr| {
250 const impl: *Attribute.StringAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
251 payload_allocator.free(impl.value);
252 payload_allocator.destroy(impl);
253 }
254 self.string_attrs.deinit();
255
256 var symbol_ref_iter = self.symbol_ref_attrs.valueIterator();
257 while (symbol_ref_iter.next()) |attr| {
258 const impl: *Attribute.SymbolRefAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
259 payload_allocator.free(impl.root_reference);
260 for (impl.nested_references) |nested| {
261 payload_allocator.free(nested);
262 }
263 if (impl.nested_references.len > 0) payload_allocator.free(impl.nested_references);
264 payload_allocator.destroy(impl);
265 }
266 self.symbol_ref_attrs.deinit();
267
268 var string_list_iter = self.string_list_attrs.valueIterator();
269 while (string_list_iter.next()) |attr| {
270 const impl: *Attribute.StringListAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
271 for (impl.values) |value| {
272 payload_allocator.free(value);
273 }
274 if (impl.values.len > 0) payload_allocator.free(impl.values);
275 payload_allocator.destroy(impl);
276 }
277 self.string_list_attrs.deinit();
278
279 var type_list_iter = self.type_list_attrs.valueIterator();
280 while (type_list_iter.next()) |attr| {
281 const impl: *Attribute.TypeListAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
282 if (impl.values.len > 0) payload_allocator.free(impl.values);
283 payload_allocator.destroy(impl);
284 }
285 self.type_list_attrs.deinit();
286
287 var array_iter = self.array_attrs.valueIterator();
288 while (array_iter.next()) |attr| {
289 const impl: *Attribute.ArrayAttr = @ptrCast(@alignCast(@constCast(attr.impl)));
290 if (impl.values.len > 0) payload_allocator.free(impl.values);
291 payload_allocator.destroy(impl);
292 }
293 self.array_attrs.deinit();
294 }
295 };
296
297 pub const AttributeInterner = struct {
298 storage: AttrStorage,
299 registry: interfaces.AttributeRegistry,
300
301 pub fn init(
302 table_allocator: std.mem.Allocator,
303 registry_allocator: std.mem.Allocator,
304 ) AttributeInterner {
305 return .{
306 .storage = AttrStorage.init(table_allocator),
307 .registry = interfaces.AttributeRegistry.init(registry_allocator, attribute.Attribute.first_dynamic_attr_id),
308 };
309 }
310
311 pub fn deinit(self: *AttributeInterner, payload_allocator: std.mem.Allocator) void {
312 self.storage.deinit(payload_allocator);
313 self.registry.deinit();
314 }
315 };