| author | |
| committer | |
| log | 3e99495ed8d2a384501338edb4885e709d51bf74 |
| tree | 1cd6338398a9a011e5144fe8d29c4991acf84e13 |
| parent | 0298442100b2d5707099f09d29af554e8c6ac87e |
| parent | 39983d7ff524a3e1e25dbd6904e28a6dd11120e6 |
| signature |
std: make ArrayHashMap eql function accept an additional param8 files changed, 364 insertions(+), 28 deletions(-)
lib/std/array_hash_map.zig+21-17| ... | @@ -37,8 +37,9 @@ pub const StringContext = struct { | ... | @@ -37,8 +37,9 @@ pub const StringContext = struct { |
| 37 | _ = self; | 37 | _ = self; |
| 38 | return hashString(s); | 38 | return hashString(s); |
| 39 | } | 39 | } |
| 40 | pub fn eql(self: @This(), a: []const u8, b: []const u8) bool { | 40 | pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool { |
| 41 | _ = self; | 41 | _ = self; |
| 42 | _ = b_index; | ||
| 42 | return eqlString(a, b); | 43 | return eqlString(a, b); |
| 43 | } | 44 | } |
| 44 | }; | 45 | }; |
| ... | @@ -76,7 +77,7 @@ pub fn ArrayHashMap( | ... | @@ -76,7 +77,7 @@ pub fn ArrayHashMap( |
| 76 | comptime Context: type, | 77 | comptime Context: type, |
| 77 | comptime store_hash: bool, | 78 | comptime store_hash: bool, |
| 78 | ) type { | 79 | ) type { |
| 79 | comptime std.hash_map.verifyContext(Context, K, K, u32); | 80 | comptime std.hash_map.verifyContext(Context, K, K, u32, true); |
| 80 | return struct { | 81 | return struct { |
| 81 | unmanaged: Unmanaged, | 82 | unmanaged: Unmanaged, |
| 82 | allocator: Allocator, | 83 | allocator: Allocator, |
| ... | @@ -462,7 +463,7 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -462,7 +463,7 @@ pub fn ArrayHashMapUnmanaged( |
| 462 | comptime Context: type, | 463 | comptime Context: type, |
| 463 | comptime store_hash: bool, | 464 | comptime store_hash: bool, |
| 464 | ) type { | 465 | ) type { |
| 465 | comptime std.hash_map.verifyContext(Context, K, K, u32); | 466 | comptime std.hash_map.verifyContext(Context, K, K, u32, true); |
| 466 | return struct { | 467 | return struct { |
| 467 | /// It is permitted to access this field directly. | 468 | /// It is permitted to access this field directly. |
| 468 | entries: DataList = .{}, | 469 | entries: DataList = .{}, |
| ... | @@ -700,7 +701,7 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -700,7 +701,7 @@ pub fn ArrayHashMapUnmanaged( |
| 700 | const hashes_array = slice.items(.hash); | 701 | const hashes_array = slice.items(.hash); |
| 701 | const keys_array = slice.items(.key); | 702 | const keys_array = slice.items(.key); |
| 702 | for (keys_array) |*item_key, i| { | 703 | for (keys_array) |*item_key, i| { |
| 703 | if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*)) { | 704 | if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*, i)) { |
| 704 | return GetOrPutResult{ | 705 | return GetOrPutResult{ |
| 705 | .key_ptr = item_key, | 706 | .key_ptr = item_key, |
| 706 | // workaround for #6974 | 707 | // workaround for #6974 |
| ... | @@ -933,7 +934,7 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -933,7 +934,7 @@ pub fn ArrayHashMapUnmanaged( |
| 933 | const hashes_array = slice.items(.hash); | 934 | const hashes_array = slice.items(.hash); |
| 934 | const keys_array = slice.items(.key); | 935 | const keys_array = slice.items(.key); |
| 935 | for (keys_array) |*item_key, i| { | 936 | for (keys_array) |*item_key, i| { |
| 936 | if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*)) { | 937 | if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*, i)) { |
| 937 | return i; | 938 | return i; |
| 938 | } | 939 | } |
| 939 | } | 940 | } |
| ... | @@ -1245,7 +1246,7 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -1245,7 +1246,7 @@ pub fn ArrayHashMapUnmanaged( |
| 1245 | const keys_array = slice.items(.key); | 1246 | const keys_array = slice.items(.key); |
| 1246 | for (keys_array) |*item_key, i| { | 1247 | for (keys_array) |*item_key, i| { |
| 1247 | const hash_match = if (store_hash) hashes_array[i] == key_hash else true; | 1248 | const hash_match = if (store_hash) hashes_array[i] == key_hash else true; |
| 1248 | if (hash_match and key_ctx.eql(key, item_key.*)) { | 1249 | if (hash_match and key_ctx.eql(key, item_key.*, i)) { |
| 1249 | const removed_entry: KV = .{ | 1250 | const removed_entry: KV = .{ |
| 1250 | .key = keys_array[i], | 1251 | .key = keys_array[i], |
| 1251 | .value = slice.items(.value)[i], | 1252 | .value = slice.items(.value)[i], |
| ... | @@ -1286,7 +1287,7 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -1286,7 +1287,7 @@ pub fn ArrayHashMapUnmanaged( |
| 1286 | const keys_array = slice.items(.key); | 1287 | const keys_array = slice.items(.key); |
| 1287 | for (keys_array) |*item_key, i| { | 1288 | for (keys_array) |*item_key, i| { |
| 1288 | const hash_match = if (store_hash) hashes_array[i] == key_hash else true; | 1289 | const hash_match = if (store_hash) hashes_array[i] == key_hash else true; |
| 1289 | if (hash_match and key_ctx.eql(key, item_key.*)) { | 1290 | if (hash_match and key_ctx.eql(key, item_key.*, i)) { |
| 1290 | switch (removal_type) { | 1291 | switch (removal_type) { |
| 1291 | .swap => self.entries.swapRemove(i), | 1292 | .swap => self.entries.swapRemove(i), |
| 1292 | .ordered => self.entries.orderedRemove(i), | 1293 | .ordered => self.entries.orderedRemove(i), |
| ... | @@ -1483,8 +1484,9 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -1483,8 +1484,9 @@ pub fn ArrayHashMapUnmanaged( |
| 1483 | 1484 | ||
| 1484 | // This pointer survives the following append because we call | 1485 | // This pointer survives the following append because we call |
| 1485 | // entries.ensureTotalCapacity before getOrPutInternal. | 1486 | // entries.ensureTotalCapacity before getOrPutInternal. |
| 1486 | const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true; | 1487 | const i = slot_data.entry_index; |
| 1487 | if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index])) { | 1488 | const hash_match = if (store_hash) h == hashes_array[i] else true; |
| 1489 | if (hash_match and checkedEql(ctx, key, keys_array[i], i)) { | ||
| 1488 | return .{ | 1490 | return .{ |
| 1489 | .found_existing = true, | 1491 | .found_existing = true, |
| 1490 | .key_ptr = &keys_array[slot_data.entry_index], | 1492 | .key_ptr = &keys_array[slot_data.entry_index], |
| ... | @@ -1571,8 +1573,9 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -1571,8 +1573,9 @@ pub fn ArrayHashMapUnmanaged( |
| 1571 | if (slot_data.isEmpty() or slot_data.distance_from_start_index < distance_from_start_index) | 1573 | if (slot_data.isEmpty() or slot_data.distance_from_start_index < distance_from_start_index) |
| 1572 | return null; | 1574 | return null; |
| 1573 | 1575 | ||
| 1574 | const hash_match = if (store_hash) h == hashes_array[slot_data.entry_index] else true; | 1576 | const i = slot_data.entry_index; |
| 1575 | if (hash_match and checkedEql(ctx, key, keys_array[slot_data.entry_index])) | 1577 | const hash_match = if (store_hash) h == hashes_array[i] else true; |
| 1578 | if (hash_match and checkedEql(ctx, key, keys_array[i], i)) | ||
| 1576 | return slot; | 1579 | return slot; |
| 1577 | } | 1580 | } |
| 1578 | unreachable; | 1581 | unreachable; |
| ... | @@ -1624,7 +1627,7 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -1624,7 +1627,7 @@ pub fn ArrayHashMapUnmanaged( |
| 1624 | } | 1627 | } |
| 1625 | 1628 | ||
| 1626 | inline fn checkedHash(ctx: anytype, key: anytype) u32 { | 1629 | inline fn checkedHash(ctx: anytype, key: anytype) u32 { |
| 1627 | comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(key), K, u32); | 1630 | comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(key), K, u32, true); |
| 1628 | // If you get a compile error on the next line, it means that | 1631 | // If you get a compile error on the next line, it means that |
| 1629 | const hash = ctx.hash(key); // your generic hash function doesn't accept your key | 1632 | const hash = ctx.hash(key); // your generic hash function doesn't accept your key |
| 1630 | if (@TypeOf(hash) != u32) { | 1633 | if (@TypeOf(hash) != u32) { |
| ... | @@ -1633,10 +1636,10 @@ pub fn ArrayHashMapUnmanaged( | ... | @@ -1633,10 +1636,10 @@ pub fn ArrayHashMapUnmanaged( |
| 1633 | } | 1636 | } |
| 1634 | return hash; | 1637 | return hash; |
| 1635 | } | 1638 | } |
| 1636 | inline fn checkedEql(ctx: anytype, a: anytype, b: K) bool { | 1639 | inline fn checkedEql(ctx: anytype, a: anytype, b: K, b_index: usize) bool { |
| 1637 | comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(a), K, u32); | 1640 | comptime std.hash_map.verifyContext(@TypeOf(ctx), @TypeOf(a), K, u32, true); |
| 1638 | // If you get a compile error on the next line, it means that | 1641 | // If you get a compile error on the next line, it means that |
| 1639 | const eql = ctx.eql(a, b); // your generic eql function doesn't accept (self, adapt key, K) | 1642 | const eql = ctx.eql(a, b, b_index); // your generic eql function doesn't accept (self, adapt key, K, index) |
| 1640 | if (@TypeOf(eql) != bool) { | 1643 | if (@TypeOf(eql) != bool) { |
| 1641 | @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic eql function that returns the wrong type!\n" ++ | 1644 | @compileError("Context " ++ @typeName(@TypeOf(ctx)) ++ " has a generic eql function that returns the wrong type!\n" ++ |
| 1642 | @typeName(bool) ++ " was expected, but found " ++ @typeName(@TypeOf(eql))); | 1645 | @typeName(bool) ++ " was expected, but found " ++ @typeName(@TypeOf(eql))); |
| ... | @@ -2255,9 +2258,10 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K) | ... | @@ -2255,9 +2258,10 @@ pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K) |
| 2255 | }.hash; | 2258 | }.hash; |
| 2256 | } | 2259 | } |
| 2257 | 2260 | ||
| 2258 | pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K) bool) { | 2261 | pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K, usize) bool) { |
| 2259 | return struct { | 2262 | return struct { |
| 2260 | fn eql(ctx: Context, a: K, b: K) bool { | 2263 | fn eql(ctx: Context, a: K, b: K, b_index: usize) bool { |
| 2264 | _ = b_index; | ||
| 2261 | _ = ctx; | 2265 | _ = ctx; |
| 2262 | return meta.eql(a, b); | 2266 | return meta.eql(a, b); |
| 2263 | } | 2267 | } |
lib/std/builtin.zig+3| ... | @@ -203,12 +203,14 @@ pub const TypeInfo = union(enum) { | ... | @@ -203,12 +203,14 @@ pub const TypeInfo = union(enum) { |
| 203 | /// therefore must be kept in sync with the compiler implementation. | 203 | /// therefore must be kept in sync with the compiler implementation. |
| 204 | pub const Int = struct { | 204 | pub const Int = struct { |
| 205 | signedness: Signedness, | 205 | signedness: Signedness, |
| 206 | /// TODO make this u16 instead of comptime_int | ||
| 206 | bits: comptime_int, | 207 | bits: comptime_int, |
| 207 | }; | 208 | }; |
| 208 | 209 | ||
| 209 | /// This data structure is used by the Zig language code generation and | 210 | /// This data structure is used by the Zig language code generation and |
| 210 | /// therefore must be kept in sync with the compiler implementation. | 211 | /// therefore must be kept in sync with the compiler implementation. |
| 211 | pub const Float = struct { | 212 | pub const Float = struct { |
| 213 | /// TODO make this u16 instead of comptime_int | ||
| 212 | bits: comptime_int, | 214 | bits: comptime_int, |
| 213 | }; | 215 | }; |
| 214 | 216 | ||
| ... | @@ -218,6 +220,7 @@ pub const TypeInfo = union(enum) { | ... | @@ -218,6 +220,7 @@ pub const TypeInfo = union(enum) { |
| 218 | size: Size, | 220 | size: Size, |
| 219 | is_const: bool, | 221 | is_const: bool, |
| 220 | is_volatile: bool, | 222 | is_volatile: bool, |
| 223 | /// TODO make this u16 instead of comptime_int | ||
| 221 | alignment: comptime_int, | 224 | alignment: comptime_int, |
| 222 | address_space: AddressSpace, | 225 | address_space: AddressSpace, |
| 223 | child: type, | 226 | child: type, |
lib/std/hash/auto_hash.zig+2-1| ... | @@ -81,7 +81,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { | ... | @@ -81,7 +81,6 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { |
| 81 | .NoReturn, | 81 | .NoReturn, |
| 82 | .Opaque, | 82 | .Opaque, |
| 83 | .Undefined, | 83 | .Undefined, |
| 84 | .Void, | ||
| 85 | .Null, | 84 | .Null, |
| 86 | .ComptimeFloat, | 85 | .ComptimeFloat, |
| 87 | .ComptimeInt, | 86 | .ComptimeInt, |
| ... | @@ -91,6 +90,8 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { | ... | @@ -91,6 +90,8 @@ pub fn hash(hasher: anytype, key: anytype, comptime strat: HashStrategy) void { |
| 91 | .Float, | 90 | .Float, |
| 92 | => @compileError("unable to hash type " ++ @typeName(Key)), | 91 | => @compileError("unable to hash type " ++ @typeName(Key)), |
| 93 | 92 | ||
| 93 | .Void => return, | ||
| 94 | |||
| 94 | // Help the optimizer see that hashing an int is easy by inlining! | 95 | // Help the optimizer see that hashing an int is easy by inlining! |
| 95 | // TODO Check if the situation is better after #561 is resolved. | 96 | // TODO Check if the situation is better after #561 is resolved. |
| 96 | .Int => { | 97 | .Int => { |
lib/std/hash_map.zig+16-7| ... | @@ -131,7 +131,13 @@ pub const default_max_load_percentage = 80; | ... | @@ -131,7 +131,13 @@ pub const default_max_load_percentage = 80; |
| 131 | /// If you are passing a context to a *Adapted function, PseudoKey is the type | 131 | /// If you are passing a context to a *Adapted function, PseudoKey is the type |
| 132 | /// of the key parameter. Otherwise, when creating a HashMap or HashMapUnmanaged | 132 | /// of the key parameter. Otherwise, when creating a HashMap or HashMapUnmanaged |
| 133 | /// type, PseudoKey = Key = K. | 133 | /// type, PseudoKey = Key = K. |
| 134 | pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, comptime Key: type, comptime Hash: type) void { | 134 | pub fn verifyContext( |
| 135 | comptime RawContext: type, | ||
| 136 | comptime PseudoKey: type, | ||
| 137 | comptime Key: type, | ||
| 138 | comptime Hash: type, | ||
| 139 | comptime is_array: bool, | ||
| 140 | ) void { | ||
| 135 | comptime { | 141 | comptime { |
| 136 | var allow_const_ptr = false; | 142 | var allow_const_ptr = false; |
| 137 | var allow_mutable_ptr = false; | 143 | var allow_mutable_ptr = false; |
| ... | @@ -166,7 +172,9 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti | ... | @@ -166,7 +172,9 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti |
| 166 | const prefix = "\n "; | 172 | const prefix = "\n "; |
| 167 | const deep_prefix = prefix ++ " "; | 173 | const deep_prefix = prefix ++ " "; |
| 168 | const hash_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ") " ++ @typeName(Hash); | 174 | const hash_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ") " ++ @typeName(Hash); |
| 169 | const eql_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ", " ++ @typeName(Key) ++ ") bool"; | 175 | const index_param = if (is_array) ", b_index: usize" else ""; |
| 176 | const eql_signature = "fn (self, " ++ @typeName(PseudoKey) ++ ", " ++ | ||
| 177 | @typeName(Key) ++ index_param ++ ") bool"; | ||
| 170 | const err_invalid_hash_signature = prefix ++ @typeName(Context) ++ ".hash must be " ++ hash_signature ++ | 178 | const err_invalid_hash_signature = prefix ++ @typeName(Context) ++ ".hash must be " ++ hash_signature ++ |
| 171 | deep_prefix ++ "but is actually " ++ @typeName(@TypeOf(Context.hash)); | 179 | deep_prefix ++ "but is actually " ++ @typeName(@TypeOf(Context.hash)); |
| 172 | const err_invalid_eql_signature = prefix ++ @typeName(Context) ++ ".eql must be " ++ eql_signature ++ | 180 | const err_invalid_eql_signature = prefix ++ @typeName(Context) ++ ".eql must be " ++ eql_signature ++ |
| ... | @@ -255,7 +263,8 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti | ... | @@ -255,7 +263,8 @@ pub fn verifyContext(comptime RawContext: type, comptime PseudoKey: type, compti |
| 255 | const info = @typeInfo(@TypeOf(eql)); | 263 | const info = @typeInfo(@TypeOf(eql)); |
| 256 | if (info == .Fn) { | 264 | if (info == .Fn) { |
| 257 | const func = info.Fn; | 265 | const func = info.Fn; |
| 258 | if (func.args.len != 3) { | 266 | const args_len = if (is_array) 4 else 3; |
| 267 | if (func.args.len != args_len) { | ||
| 259 | errors = errors ++ lazy.err_invalid_eql_signature; | 268 | errors = errors ++ lazy.err_invalid_eql_signature; |
| 260 | } else { | 269 | } else { |
| 261 | var emitted_signature = false; | 270 | var emitted_signature = false; |
| ... | @@ -360,7 +369,7 @@ pub fn HashMap( | ... | @@ -360,7 +369,7 @@ pub fn HashMap( |
| 360 | comptime Context: type, | 369 | comptime Context: type, |
| 361 | comptime max_load_percentage: u64, | 370 | comptime max_load_percentage: u64, |
| 362 | ) type { | 371 | ) type { |
| 363 | comptime verifyContext(Context, K, K, u64); | 372 | comptime verifyContext(Context, K, K, u64, false); |
| 364 | return struct { | 373 | return struct { |
| 365 | unmanaged: Unmanaged, | 374 | unmanaged: Unmanaged, |
| 366 | allocator: Allocator, | 375 | allocator: Allocator, |
| ... | @@ -683,7 +692,7 @@ pub fn HashMapUnmanaged( | ... | @@ -683,7 +692,7 @@ pub fn HashMapUnmanaged( |
| 683 | ) type { | 692 | ) type { |
| 684 | if (max_load_percentage <= 0 or max_load_percentage >= 100) | 693 | if (max_load_percentage <= 0 or max_load_percentage >= 100) |
| 685 | @compileError("max_load_percentage must be between 0 and 100."); | 694 | @compileError("max_load_percentage must be between 0 and 100."); |
| 686 | comptime verifyContext(Context, K, K, u64); | 695 | comptime verifyContext(Context, K, K, u64, false); |
| 687 | 696 | ||
| 688 | return struct { | 697 | return struct { |
| 689 | const Self = @This(); | 698 | const Self = @This(); |
| ... | @@ -1108,7 +1117,7 @@ pub fn HashMapUnmanaged( | ... | @@ -1108,7 +1117,7 @@ pub fn HashMapUnmanaged( |
| 1108 | /// from this function. To encourage that, this function is | 1117 | /// from this function. To encourage that, this function is |
| 1109 | /// marked as inline. | 1118 | /// marked as inline. |
| 1110 | inline fn getIndex(self: Self, key: anytype, ctx: anytype) ?usize { | 1119 | inline fn getIndex(self: Self, key: anytype, ctx: anytype) ?usize { |
| 1111 | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash); | 1120 | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false); |
| 1112 | 1121 | ||
| 1113 | if (self.size == 0) { | 1122 | if (self.size == 0) { |
| 1114 | return null; | 1123 | return null; |
| ... | @@ -1291,7 +1300,7 @@ pub fn HashMapUnmanaged( | ... | @@ -1291,7 +1300,7 @@ pub fn HashMapUnmanaged( |
| 1291 | return result; | 1300 | return result; |
| 1292 | } | 1301 | } |
| 1293 | pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { | 1302 | pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult { |
| 1294 | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash); | 1303 | comptime verifyContext(@TypeOf(ctx), @TypeOf(key), K, Hash, false); |
| 1295 | 1304 | ||
| 1296 | // If you get a compile error on this line, it means that your generic hash | 1305 | // If you get a compile error on this line, it means that your generic hash |
| 1297 | // function is invalid for these parameters. | 1306 | // function is invalid for these parameters. |
src/InternArena.zig created+316| ... | @@ -0,0 +1,316 @@ | ||
| 1 | map: std.AutoArrayHashMapUnmanaged(void, void) = .{}, | ||
| 2 | items: std.MultiArrayList(Item) = .{}, | ||
| 3 | extra: std.ArrayListUnmanaged(u32) = .{}, | ||
| 4 | |||
| 5 | const InternArena = @This(); | ||
| 6 | const std = @import("std"); | ||
| 7 | const Allocator = std.mem.Allocator; | ||
| 8 | const assert = std.debug.assert; | ||
| 9 | |||
| 10 | const KeyAdapter = struct { | ||
| 11 | intern_arena: *const InternArena, | ||
| 12 | |||
| 13 | pub fn eql(ctx: @This(), a: Key, b_void: void, b_map_index: usize) bool { | ||
| 14 | _ = b_void; | ||
| 15 | return ctx.intern_arena.indexToKey(@intToEnum(Index, b_map_index)).eql(a); | ||
| 16 | } | ||
| 17 | |||
| 18 | pub fn hash(ctx: @This(), a: Key) u32 { | ||
| 19 | _ = ctx; | ||
| 20 | return a.hash(); | ||
| 21 | } | ||
| 22 | }; | ||
| 23 | |||
| 24 | pub const Key = union(enum) { | ||
| 25 | int_type: struct { | ||
| 26 | signedness: std.builtin.Signedness, | ||
| 27 | bits: u16, | ||
| 28 | }, | ||
| 29 | ptr_type: struct { | ||
| 30 | elem_type: Index, | ||
| 31 | sentinel: Index, | ||
| 32 | alignment: u16, | ||
| 33 | size: std.builtin.TypeInfo.Pointer.Size, | ||
| 34 | is_const: bool, | ||
| 35 | is_volatile: bool, | ||
| 36 | is_allowzero: bool, | ||
| 37 | address_space: std.builtin.AddressSpace, | ||
| 38 | }, | ||
| 39 | array_type: struct { | ||
| 40 | len: u64, | ||
| 41 | child: Index, | ||
| 42 | sentinel: Index, | ||
| 43 | }, | ||
| 44 | vector_type: struct { | ||
| 45 | len: u32, | ||
| 46 | child: Index, | ||
| 47 | }, | ||
| 48 | optional_type: struct { | ||
| 49 | payload_type: Index, | ||
| 50 | }, | ||
| 51 | error_union_type: struct { | ||
| 52 | error_set_type: Index, | ||
| 53 | payload_type: Index, | ||
| 54 | }, | ||
| 55 | simple: Simple, | ||
| 56 | |||
| 57 | pub fn hash(key: Key) u32 { | ||
| 58 | var hasher = std.hash.Wyhash.init(0); | ||
| 59 | switch (key) { | ||
| 60 | .int_type => |int_type| { | ||
| 61 | std.hash.autoHash(&hasher, int_type); | ||
| 62 | }, | ||
| 63 | .array_type => |array_type| { | ||
| 64 | std.hash.autoHash(&hasher, array_type); | ||
| 65 | }, | ||
| 66 | else => @panic("TODO"), | ||
| 67 | } | ||
| 68 | return @truncate(u32, hasher.final()); | ||
| 69 | } | ||
| 70 | |||
| 71 | pub fn eql(a: Key, b: Key) bool { | ||
| 72 | const KeyTag = std.meta.Tag(Key); | ||
| 73 | const a_tag: KeyTag = a; | ||
| 74 | const b_tag: KeyTag = b; | ||
| 75 | if (a_tag != b_tag) return false; | ||
| 76 | switch (a) { | ||
| 77 | .int_type => |a_info| { | ||
| 78 | const b_info = b.int_type; | ||
| 79 | return std.meta.eql(a_info, b_info); | ||
| 80 | }, | ||
| 81 | .array_type => |a_info| { | ||
| 82 | const b_info = b.array_type; | ||
| 83 | return std.meta.eql(a_info, b_info); | ||
| 84 | }, | ||
| 85 | else => @panic("TODO"), | ||
| 86 | } | ||
| 87 | } | ||
| 88 | }; | ||
| 89 | |||
| 90 | pub const Item = struct { | ||
| 91 | tag: Tag, | ||
| 92 | /// The doc comments on the respective Tag explain how to interpret this. | ||
| 93 | data: u32, | ||
| 94 | }; | ||
| 95 | |||
| 96 | /// Represents an index into `map`. It represents the canonical index | ||
| 97 | /// of a `Value` within this `InternArena`. The values are typed. | ||
| 98 | /// Two values which have the same type can be equality compared simply | ||
| 99 | /// by checking if their indexes are equal, provided they are both in | ||
| 100 | /// the same `InternArena`. | ||
| 101 | pub const Index = enum(u32) { | ||
| 102 | none = std.math.maxInt(u32), | ||
| 103 | _, | ||
| 104 | }; | ||
| 105 | |||
| 106 | pub const Tag = enum(u8) { | ||
| 107 | /// An integer type. | ||
| 108 | /// data is number of bits | ||
| 109 | type_int_signed, | ||
| 110 | /// An integer type. | ||
| 111 | /// data is number of bits | ||
| 112 | type_int_unsigned, | ||
| 113 | /// An array type. | ||
| 114 | /// data is payload to Array. | ||
| 115 | type_array, | ||
| 116 | /// A type or value that can be represented with only an enum tag. | ||
| 117 | /// data is Simple enum value | ||
| 118 | simple, | ||
| 119 | /// An unsigned integer value that can be represented by u32. | ||
| 120 | /// data is integer value | ||
| 121 | int_u32, | ||
| 122 | /// An unsigned integer value that can be represented by i32. | ||
| 123 | /// data is integer value bitcasted to u32. | ||
| 124 | int_i32, | ||
| 125 | /// A positive integer value that does not fit in 32 bits. | ||
| 126 | /// data is a extra index to BigInt. | ||
| 127 | int_big_positive, | ||
| 128 | /// A negative integer value that does not fit in 32 bits. | ||
| 129 | /// data is a extra index to BigInt. | ||
| 130 | int_big_negative, | ||
| 131 | /// A float value that can be represented by f32. | ||
| 132 | /// data is float value bitcasted to u32. | ||
| 133 | float_f32, | ||
| 134 | /// A float value that can be represented by f64. | ||
| 135 | /// data is payload index to Float64. | ||
| 136 | float_f64, | ||
| 137 | /// A float value that can be represented by f128. | ||
| 138 | /// data is payload index to Float128. | ||
| 139 | float_f128, | ||
| 140 | }; | ||
| 141 | |||
| 142 | pub const Simple = enum(u32) { | ||
| 143 | f16, | ||
| 144 | f32, | ||
| 145 | f64, | ||
| 146 | f80, | ||
| 147 | f128, | ||
| 148 | usize, | ||
| 149 | isize, | ||
| 150 | c_short, | ||
| 151 | c_ushort, | ||
| 152 | c_int, | ||
| 153 | c_uint, | ||
| 154 | c_long, | ||
| 155 | c_ulong, | ||
| 156 | c_longlong, | ||
| 157 | c_ulonglong, | ||
| 158 | c_longdouble, | ||
| 159 | anyopaque, | ||
| 160 | bool, | ||
| 161 | void, | ||
| 162 | type, | ||
| 163 | anyerror, | ||
| 164 | comptime_int, | ||
| 165 | comptime_float, | ||
| 166 | noreturn, | ||
| 167 | @"anyframe", | ||
| 168 | null_type, | ||
| 169 | undefined_type, | ||
| 170 | enum_literal_type, | ||
| 171 | @"undefined", | ||
| 172 | void_value, | ||
| 173 | @"null", | ||
| 174 | bool_true, | ||
| 175 | bool_false, | ||
| 176 | }; | ||
| 177 | |||
| 178 | pub const Array = struct { | ||
| 179 | len: u32, | ||
| 180 | child: Index, | ||
| 181 | }; | ||
| 182 | |||
| 183 | pub fn deinit(ia: *InternArena, gpa: Allocator) void { | ||
| 184 | ia.map.deinit(gpa); | ||
| 185 | ia.items.deinit(gpa); | ||
| 186 | ia.extra.deinit(gpa); | ||
| 187 | } | ||
| 188 | |||
| 189 | pub fn indexToKey(ia: InternArena, index: Index) Key { | ||
| 190 | const item = ia.items.get(@enumToInt(index)); | ||
| 191 | const data = item.data; | ||
| 192 | return switch (item.tag) { | ||
| 193 | .type_int_signed => .{ | ||
| 194 | .int_type = .{ | ||
| 195 | .signedness = .signed, | ||
| 196 | .bits = @intCast(u16, data), | ||
| 197 | }, | ||
| 198 | }, | ||
| 199 | .type_int_unsigned => .{ | ||
| 200 | .int_type = .{ | ||
| 201 | .signedness = .unsigned, | ||
| 202 | .bits = @intCast(u16, data), | ||
| 203 | }, | ||
| 204 | }, | ||
| 205 | .type_array => { | ||
| 206 | const array_info = ia.extraData(Array, data); | ||
| 207 | return .{ .array_type = .{ | ||
| 208 | .len = array_info.len, | ||
| 209 | .child = array_info.child, | ||
| 210 | .sentinel = .none, | ||
| 211 | } }; | ||
| 212 | }, | ||
| 213 | .simple => .{ .simple = @intToEnum(Simple, data) }, | ||
| 214 | |||
| 215 | else => @panic("TODO"), | ||
| 216 | }; | ||
| 217 | } | ||
| 218 | |||
| 219 | pub fn get(ia: *InternArena, gpa: Allocator, key: Key) Allocator.Error!Index { | ||
| 220 | const adapter: KeyAdapter = .{ .intern_arena = ia }; | ||
| 221 | const gop = try ia.map.getOrPutAdapted(gpa, key, adapter); | ||
| 222 | if (gop.found_existing) { | ||
| 223 | return @intToEnum(Index, gop.index); | ||
| 224 | } | ||
| 225 | switch (key) { | ||
| 226 | .int_type => |int_type| { | ||
| 227 | const tag: Tag = switch (int_type.signedness) { | ||
| 228 | .signed => .type_int_signed, | ||
| 229 | .unsigned => .type_int_unsigned, | ||
| 230 | }; | ||
| 231 | try ia.items.append(gpa, .{ | ||
| 232 | .tag = tag, | ||
| 233 | .data = int_type.bits, | ||
| 234 | }); | ||
| 235 | }, | ||
| 236 | .array_type => |array_type| { | ||
| 237 | const len = @intCast(u32, array_type.len); // TODO have a big_array encoding | ||
| 238 | assert(array_type.sentinel == .none); // TODO have a sentinel_array encoding | ||
| 239 | try ia.items.append(gpa, .{ | ||
| 240 | .tag = .type_array, | ||
| 241 | .data = try ia.addExtra(gpa, Array{ | ||
| 242 | .len = len, | ||
| 243 | .child = array_type.child, | ||
| 244 | }), | ||
| 245 | }); | ||
| 246 | }, | ||
| 247 | else => @panic("TODO"), | ||
| 248 | } | ||
| 249 | return @intToEnum(Index, ia.items.len - 1); | ||
| 250 | } | ||
| 251 | |||
| 252 | fn addExtra(ia: *InternArena, gpa: Allocator, extra: anytype) Allocator.Error!u32 { | ||
| 253 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 254 | try ia.extra.ensureUnusedCapacity(gpa, fields.len); | ||
| 255 | return ia.addExtraAssumeCapacity(extra); | ||
| 256 | } | ||
| 257 | |||
| 258 | fn addExtraAssumeCapacity(ia: *InternArena, extra: anytype) u32 { | ||
| 259 | const fields = std.meta.fields(@TypeOf(extra)); | ||
| 260 | const result = @intCast(u32, ia.extra.items.len); | ||
| 261 | inline for (fields) |field| { | ||
| 262 | ia.extra.appendAssumeCapacity(switch (field.field_type) { | ||
| 263 | u32 => @field(extra, field.name), | ||
| 264 | Index => @enumToInt(@field(extra, field.name)), | ||
| 265 | i32 => @bitCast(u32, @field(extra, field.name)), | ||
| 266 | else => @compileError("bad field type"), | ||
| 267 | }); | ||
| 268 | } | ||
| 269 | return result; | ||
| 270 | } | ||
| 271 | |||
| 272 | fn extraData(ia: InternArena, comptime T: type, index: usize) T { | ||
| 273 | const fields = std.meta.fields(T); | ||
| 274 | var i: usize = index; | ||
| 275 | var result: T = undefined; | ||
| 276 | inline for (fields) |field| { | ||
| 277 | @field(result, field.name) = switch (field.field_type) { | ||
| 278 | u32 => ia.extra.items[i], | ||
| 279 | Index => @intToEnum(Index, ia.extra.items[i]), | ||
| 280 | i32 => @bitCast(i32, ia.extra.items[i]), | ||
| 281 | else => @compileError("bad field type"), | ||
| 282 | }; | ||
| 283 | i += 1; | ||
| 284 | } | ||
| 285 | return result; | ||
| 286 | } | ||
| 287 | |||
| 288 | test "basic usage" { | ||
| 289 | const gpa = std.testing.allocator; | ||
| 290 | |||
| 291 | var ia: InternArena = .{}; | ||
| 292 | defer ia.deinit(gpa); | ||
| 293 | |||
| 294 | const i32_type = try ia.get(gpa, .{ .int_type = .{ | ||
| 295 | .signedness = .signed, | ||
| 296 | .bits = 32, | ||
| 297 | } }); | ||
| 298 | const array_i32 = try ia.get(gpa, .{ .array_type = .{ | ||
| 299 | .len = 10, | ||
| 300 | .child = i32_type, | ||
| 301 | .sentinel = .none, | ||
| 302 | } }); | ||
| 303 | |||
| 304 | const another_i32_type = try ia.get(gpa, .{ .int_type = .{ | ||
| 305 | .signedness = .signed, | ||
| 306 | .bits = 32, | ||
| 307 | } }); | ||
| 308 | try std.testing.expect(another_i32_type == i32_type); | ||
| 309 | |||
| 310 | const another_array_i32 = try ia.get(gpa, .{ .array_type = .{ | ||
| 311 | .len = 10, | ||
| 312 | .child = i32_type, | ||
| 313 | .sentinel = .none, | ||
| 314 | } }); | ||
| 315 | try std.testing.expect(another_array_i32 == array_i32); | ||
| 316 | } | ||
src/codegen/spirv/type.zig+2-1| ... | @@ -157,8 +157,9 @@ pub const Type = extern union { | ... | @@ -157,8 +157,9 @@ pub const Type = extern union { |
| 157 | _ = self; | 157 | _ = self; |
| 158 | return @truncate(u32, t.hashShallow()); | 158 | return @truncate(u32, t.hashShallow()); |
| 159 | } | 159 | } |
| 160 | pub fn eql(self: @This(), a: Type, b: Type) bool { | 160 | pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool { |
| 161 | _ = self; | 161 | _ = self; |
| 162 | _ = b_index; | ||
| 162 | return a.eqlShallow(b); | 163 | return a.eqlShallow(b); |
| 163 | } | 164 | } |
| 164 | }; | 165 | }; |
src/type.zig+2-1| ... | @@ -798,8 +798,9 @@ pub const Type = extern union { | ... | @@ -798,8 +798,9 @@ pub const Type = extern union { |
| 798 | _ = self; | 798 | _ = self; |
| 799 | return @truncate(u32, t.hash()); | 799 | return @truncate(u32, t.hash()); |
| 800 | } | 800 | } |
| 801 | pub fn eql(self: @This(), a: Type, b: Type) bool { | 801 | pub fn eql(self: @This(), a: Type, b: Type, b_index: usize) bool { |
| 802 | _ = self; | 802 | _ = self; |
| 803 | _ = b_index; | ||
| 803 | return a.eql(b); | 804 | return a.eql(b); |
| 804 | } | 805 | } |
| 805 | }; | 806 | }; |
src/value.zig+2-1| ... | @@ -1765,7 +1765,8 @@ pub const Value = extern union { | ... | @@ -1765,7 +1765,8 @@ pub const Value = extern union { |
| 1765 | const other_context: HashContext = .{ .ty = self.ty }; | 1765 | const other_context: HashContext = .{ .ty = self.ty }; |
| 1766 | return @truncate(u32, other_context.hash(val)); | 1766 | return @truncate(u32, other_context.hash(val)); |
| 1767 | } | 1767 | } |
| 1768 | pub fn eql(self: @This(), a: Value, b: Value) bool { | 1768 | pub fn eql(self: @This(), a: Value, b: Value, b_index: usize) bool { |
| 1769 | _ = b_index; | ||
| 1769 | return a.eql(b, self.ty); | 1770 | return a.eql(b, self.ty); |
| 1770 | } | 1771 | } |
| 1771 | }; | 1772 | }; |