| author | |
| committer | |
| log | 289eab9177443bdfadfe750afda8f7f32f43be0f |
| tree | ddf557298d623e567aefe9a7ace46b0ad63b9a1f |
| parent | 0ae1157e4553d6f54e0d489daebb006c402e0f63 |
| parent | 3a89f214aa672c5844def1704845ad38ea60bdcd |
| signature |
reimplement std.HashMap16 files changed, 985 insertions(+), 563 deletions(-)
doc/docgen.zig+1-1| ... | ... | @@ -392,7 +392,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { |
| 392 | 392 | .n = header_stack_size, |
| 393 | 393 | }, |
| 394 | 394 | }); |
| 395 | if (try urls.put(urlized, tag_token)) |entry| { | |
| 395 | if (try urls.fetchPut(urlized, tag_token)) |entry| { | |
| 396 | 396 | parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {}; |
| 397 | 397 | parseError(tokenizer, entry.value, "other tag here", .{}) catch {}; |
| 398 | 398 | return error.ParseError; |
doc/langref.html.in+3-11| ... | ... | @@ -5363,11 +5363,11 @@ const std = @import("std"); |
| 5363 | 5363 | const assert = std.debug.assert; |
| 5364 | 5364 | |
| 5365 | 5365 | test "turn HashMap into a set with void" { |
| 5366 | var map = std.HashMap(i32, void, hash_i32, eql_i32).init(std.testing.allocator); | |
| 5366 | var map = std.AutoHashMap(i32, void).init(std.testing.allocator); | |
| 5367 | 5367 | defer map.deinit(); |
| 5368 | 5368 | |
| 5369 | _ = try map.put(1, {}); | |
| 5370 | _ = try map.put(2, {}); | |
| 5369 | try map.put(1, {}); | |
| 5370 | try map.put(2, {}); | |
| 5371 | 5371 | |
| 5372 | 5372 | assert(map.contains(2)); |
| 5373 | 5373 | assert(!map.contains(3)); |
| ... | ... | @@ -5375,14 +5375,6 @@ test "turn HashMap into a set with void" { |
| 5375 | 5375 | _ = map.remove(2); |
| 5376 | 5376 | assert(!map.contains(2)); |
| 5377 | 5377 | } |
| 5378 | ||
| 5379 | fn hash_i32(x: i32) u32 { | |
| 5380 | return @bitCast(u32, x); | |
| 5381 | } | |
| 5382 | ||
| 5383 | fn eql_i32(a: i32, b: i32) bool { | |
| 5384 | return a == b; | |
| 5385 | } | |
| 5386 | 5378 | {#code_end#} |
| 5387 | 5379 | <p>Note that this is different from using a dummy value for the hash map value. |
| 5388 | 5380 | By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and |
lib/std/array_list.zig+16| ... | ... | @@ -210,6 +210,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 210 | 210 | self.capacity = new_len; |
| 211 | 211 | } |
| 212 | 212 | |
| 213 | /// Reduce length to `new_len`. | |
| 214 | /// Invalidates element pointers. | |
| 215 | /// Keeps capacity the same. | |
| 216 | pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { | |
| 217 | assert(new_len <= self.items.len); | |
| 218 | self.items.len = new_len; | |
| 219 | } | |
| 220 | ||
| 213 | 221 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { |
| 214 | 222 | var better_capacity = self.capacity; |
| 215 | 223 | if (better_capacity >= new_capacity) return; |
| ... | ... | @@ -432,6 +440,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 432 | 440 | self.capacity = new_len; |
| 433 | 441 | } |
| 434 | 442 | |
| 443 | /// Reduce length to `new_len`. | |
| 444 | /// Invalidates element pointers. | |
| 445 | /// Keeps capacity the same. | |
| 446 | pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void { | |
| 447 | assert(new_len <= self.items.len); | |
| 448 | self.items.len = new_len; | |
| 449 | } | |
| 450 | ||
| 435 | 451 | pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void { |
| 436 | 452 | var better_capacity = self.capacity; |
| 437 | 453 | if (better_capacity >= new_capacity) return; |
lib/std/buf_map.zig+7-8| ... | ... | @@ -33,10 +33,10 @@ pub const BufMap = struct { |
| 33 | 33 | pub fn setMove(self: *BufMap, key: []u8, value: []u8) !void { |
| 34 | 34 | const get_or_put = try self.hash_map.getOrPut(key); |
| 35 | 35 | if (get_or_put.found_existing) { |
| 36 | self.free(get_or_put.kv.key); | |
| 37 | get_or_put.kv.key = key; | |
| 36 | self.free(get_or_put.entry.key); | |
| 37 | get_or_put.entry.key = key; | |
| 38 | 38 | } |
| 39 | get_or_put.kv.value = value; | |
| 39 | get_or_put.entry.value = value; | |
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 42 | /// `key` and `value` are copied into the BufMap. |
| ... | ... | @@ -45,19 +45,18 @@ pub const BufMap = struct { |
| 45 | 45 | errdefer self.free(value_copy); |
| 46 | 46 | const get_or_put = try self.hash_map.getOrPut(key); |
| 47 | 47 | if (get_or_put.found_existing) { |
| 48 | self.free(get_or_put.kv.value); | |
| 48 | self.free(get_or_put.entry.value); | |
| 49 | 49 | } else { |
| 50 | get_or_put.kv.key = self.copy(key) catch |err| { | |
| 50 | get_or_put.entry.key = self.copy(key) catch |err| { | |
| 51 | 51 | _ = self.hash_map.remove(key); |
| 52 | 52 | return err; |
| 53 | 53 | }; |
| 54 | 54 | } |
| 55 | get_or_put.kv.value = value_copy; | |
| 55 | get_or_put.entry.value = value_copy; | |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | 58 | pub fn get(self: BufMap, key: []const u8) ?[]const u8 { |
| 59 | const entry = self.hash_map.get(key) orelse return null; | |
| 60 | return entry.value; | |
| 59 | return self.hash_map.get(key); | |
| 61 | 60 | } |
| 62 | 61 | |
| 63 | 62 | pub fn delete(self: *BufMap, key: []const u8) void { |
lib/std/buf_set.zig+3-5| ... | ... | @@ -14,14 +14,12 @@ pub const BufSet = struct { |
| 14 | 14 | return self; |
| 15 | 15 | } |
| 16 | 16 | |
| 17 | pub fn deinit(self: *const BufSet) void { | |
| 18 | var it = self.hash_map.iterator(); | |
| 19 | while (true) { | |
| 20 | const entry = it.next() orelse break; | |
| 17 | pub fn deinit(self: *BufSet) void { | |
| 18 | for (self.hash_map.items()) |entry| { | |
| 21 | 19 | self.free(entry.key); |
| 22 | 20 | } |
| 23 | ||
| 24 | 21 | self.hash_map.deinit(); |
| 22 | self.* = undefined; | |
| 25 | 23 | } |
| 26 | 24 | |
| 27 | 25 | pub fn put(self: *BufSet, key: []const u8) !void { |
lib/std/build.zig+6-6| ... | ... | @@ -422,12 +422,12 @@ pub const Builder = struct { |
| 422 | 422 | .type_id = type_id, |
| 423 | 423 | .description = description, |
| 424 | 424 | }; |
| 425 | if ((self.available_options_map.put(name, available_option) catch unreachable) != null) { | |
| 425 | if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { | |
| 426 | 426 | panic("Option '{}' declared twice", .{name}); |
| 427 | 427 | } |
| 428 | 428 | self.available_options_list.append(available_option) catch unreachable; |
| 429 | 429 | |
| 430 | const entry = self.user_input_options.get(name) orelse return null; | |
| 430 | const entry = self.user_input_options.getEntry(name) orelse return null; | |
| 431 | 431 | entry.value.used = true; |
| 432 | 432 | switch (type_id) { |
| 433 | 433 | TypeId.Bool => switch (entry.value.value) { |
| ... | ... | @@ -634,7 +634,7 @@ pub const Builder = struct { |
| 634 | 634 | pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool { |
| 635 | 635 | const gop = try self.user_input_options.getOrPut(name); |
| 636 | 636 | if (!gop.found_existing) { |
| 637 | gop.kv.value = UserInputOption{ | |
| 637 | gop.entry.value = UserInputOption{ | |
| 638 | 638 | .name = name, |
| 639 | 639 | .value = UserValue{ .Scalar = value }, |
| 640 | 640 | .used = false, |
| ... | ... | @@ -643,7 +643,7 @@ pub const Builder = struct { |
| 643 | 643 | } |
| 644 | 644 | |
| 645 | 645 | // option already exists |
| 646 | switch (gop.kv.value.value) { | |
| 646 | switch (gop.entry.value.value) { | |
| 647 | 647 | UserValue.Scalar => |s| { |
| 648 | 648 | // turn it into a list |
| 649 | 649 | var list = ArrayList([]const u8).init(self.allocator); |
| ... | ... | @@ -675,7 +675,7 @@ pub const Builder = struct { |
| 675 | 675 | pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool { |
| 676 | 676 | const gop = try self.user_input_options.getOrPut(name); |
| 677 | 677 | if (!gop.found_existing) { |
| 678 | gop.kv.value = UserInputOption{ | |
| 678 | gop.entry.value = UserInputOption{ | |
| 679 | 679 | .name = name, |
| 680 | 680 | .value = UserValue{ .Flag = {} }, |
| 681 | 681 | .used = false, |
| ... | ... | @@ -684,7 +684,7 @@ pub const Builder = struct { |
| 684 | 684 | } |
| 685 | 685 | |
| 686 | 686 | // option already exists |
| 687 | switch (gop.kv.value.value) { | |
| 687 | switch (gop.entry.value.value) { | |
| 688 | 688 | UserValue.Scalar => |s| { |
| 689 | 689 | warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s }); |
| 690 | 690 | return true; |
lib/std/debug.zig+4-4| ... | ... | @@ -1132,7 +1132,7 @@ pub const DebugInfo = struct { |
| 1132 | 1132 | const seg_end = seg_start + segment_cmd.vmsize; |
| 1133 | 1133 | |
| 1134 | 1134 | if (rebased_address >= seg_start and rebased_address < seg_end) { |
| 1135 | if (self.address_map.getValue(base_address)) |obj_di| { | |
| 1135 | if (self.address_map.get(base_address)) |obj_di| { | |
| 1136 | 1136 | return obj_di; |
| 1137 | 1137 | } |
| 1138 | 1138 | |
| ... | ... | @@ -1204,7 +1204,7 @@ pub const DebugInfo = struct { |
| 1204 | 1204 | const seg_end = seg_start + info.SizeOfImage; |
| 1205 | 1205 | |
| 1206 | 1206 | if (address >= seg_start and address < seg_end) { |
| 1207 | if (self.address_map.getValue(seg_start)) |obj_di| { | |
| 1207 | if (self.address_map.get(seg_start)) |obj_di| { | |
| 1208 | 1208 | return obj_di; |
| 1209 | 1209 | } |
| 1210 | 1210 | |
| ... | ... | @@ -1278,7 +1278,7 @@ pub const DebugInfo = struct { |
| 1278 | 1278 | else => return error.MissingDebugInfo, |
| 1279 | 1279 | } |
| 1280 | 1280 | |
| 1281 | if (self.address_map.getValue(ctx.base_address)) |obj_di| { | |
| 1281 | if (self.address_map.get(ctx.base_address)) |obj_di| { | |
| 1282 | 1282 | return obj_di; |
| 1283 | 1283 | } |
| 1284 | 1284 | |
| ... | ... | @@ -1441,7 +1441,7 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1441 | 1441 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); |
| 1442 | 1442 | |
| 1443 | 1443 | // Check if its debug infos are already in the cache |
| 1444 | var o_file_di = self.ofiles.getValue(o_file_path) orelse | |
| 1444 | var o_file_di = self.ofiles.get(o_file_path) orelse | |
| 1445 | 1445 | (self.loadOFile(o_file_path) catch |err| switch (err) { |
| 1446 | 1446 | error.FileNotFound, |
| 1447 | 1447 | error.MissingDebugInfo, |
lib/std/hash_map.zig+763-310| ... | ... | @@ -9,17 +9,15 @@ const autoHash = std.hash.autoHash; |
| 9 | 9 | const Wyhash = std.hash.Wyhash; |
| 10 | 10 | const Allocator = mem.Allocator; |
| 11 | 11 | const builtin = @import("builtin"); |
| 12 | ||
| 13 | const want_modification_safety = std.debug.runtime_safety; | |
| 14 | const debug_u32 = if (want_modification_safety) u32 else void; | |
| 12 | const hash_map = @This(); | |
| 15 | 13 | |
| 16 | 14 | pub fn AutoHashMap(comptime K: type, comptime V: type) type { |
| 17 | return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K)); | |
| 15 | return HashMap(K, V, getAutoHashFn(K), getAutoEqlFn(K), autoEqlIsCheap(K)); | |
| 18 | 16 | } |
| 19 | 17 | |
| 20 | 18 | /// Builtin hashmap for strings as keys. |
| 21 | 19 | pub fn StringHashMap(comptime V: type) type { |
| 22 | return HashMap([]const u8, V, hashString, eqlString); | |
| 20 | return HashMap([]const u8, V, hashString, eqlString, true); | |
| 23 | 21 | } |
| 24 | 22 | |
| 25 | 23 | pub fn eqlString(a: []const u8, b: []const u8) bool { |
| ... | ... | @@ -30,422 +28,859 @@ pub fn hashString(s: []const u8) u32 { |
| 30 | 28 | return @truncate(u32, std.hash.Wyhash.hash(0, s)); |
| 31 | 29 | } |
| 32 | 30 | |
| 33 | pub fn HashMap(comptime K: type, comptime V: type, comptime hash: fn (key: K) u32, comptime eql: fn (a: K, b: K) bool) type { | |
| 31 | /// Insertion order is preserved. | |
| 32 | /// Deletions perform a "swap removal" on the entries list. | |
| 33 | /// Modifying the hash map while iterating is allowed, however one must understand | |
| 34 | /// the (well defined) behavior when mixing insertions and deletions with iteration. | |
| 35 | /// For a hash map that can be initialized directly that does not store an Allocator | |
| 36 | /// field, see `HashMapUnmanaged`. | |
| 37 | /// When `store_hash` is `false`, this data structure is biased towards cheap `eql` | |
| 38 | /// functions. It does not store each item's hash in the table. Setting `store_hash` | |
| 39 | /// to `true` incurs slightly more memory cost by storing each key's hash in the table | |
| 40 | /// but only has to call `eql` for hash collisions. | |
| 41 | pub fn HashMap( | |
| 42 | comptime K: type, | |
| 43 | comptime V: type, | |
| 44 | comptime hash: fn (key: K) u32, | |
| 45 | comptime eql: fn (a: K, b: K) bool, | |
| 46 | comptime store_hash: bool, | |
| 47 | ) type { | |
| 34 | 48 | return struct { |
| 35 | entries: []Entry, | |
| 36 | size: usize, | |
| 37 | max_distance_from_start_index: usize, | |
| 49 | unmanaged: Unmanaged, | |
| 38 | 50 | allocator: *Allocator, |
| 39 | 51 | |
| 40 | /// This is used to detect bugs where a hashtable is edited while an iterator is running. | |
| 41 | modification_count: debug_u32, | |
| 42 | ||
| 43 | const Self = @This(); | |
| 44 | ||
| 45 | /// A *KV is a mutable pointer into this HashMap's internal storage. | |
| 46 | /// Modifying the key is undefined behavior. | |
| 47 | /// Modifying the value is harmless. | |
| 48 | /// *KV pointers become invalid whenever this HashMap is modified, | |
| 49 | /// and then any access to the *KV is undefined behavior. | |
| 50 | pub const KV = struct { | |
| 51 | key: K, | |
| 52 | value: V, | |
| 53 | }; | |
| 54 | ||
| 55 | const Entry = struct { | |
| 56 | used: bool, | |
| 57 | distance_from_start_index: usize, | |
| 58 | kv: KV, | |
| 59 | }; | |
| 60 | ||
| 61 | pub const GetOrPutResult = struct { | |
| 62 | kv: *KV, | |
| 63 | found_existing: bool, | |
| 64 | }; | |
| 52 | pub const Unmanaged = HashMapUnmanaged(K, V, hash, eql, store_hash); | |
| 53 | pub const Entry = Unmanaged.Entry; | |
| 54 | pub const Hash = Unmanaged.Hash; | |
| 55 | pub const GetOrPutResult = Unmanaged.GetOrPutResult; | |
| 65 | 56 | |
| 57 | /// Deprecated. Iterate using `items`. | |
| 66 | 58 | pub const Iterator = struct { |
| 67 | 59 | hm: *const Self, |
| 68 | // how many items have we returned | |
| 69 | count: usize, | |
| 70 | // iterator through the entry array | |
| 60 | /// Iterator through the entry array. | |
| 71 | 61 | index: usize, |
| 72 | // used to detect concurrent modification | |
| 73 | initial_modification_count: debug_u32, | |
| 74 | 62 | |
| 75 | pub fn next(it: *Iterator) ?*KV { | |
| 76 | if (want_modification_safety) { | |
| 77 | assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification | |
| 78 | } | |
| 79 | if (it.count >= it.hm.size) return null; | |
| 80 | while (it.index < it.hm.entries.len) : (it.index += 1) { | |
| 81 | const entry = &it.hm.entries[it.index]; | |
| 82 | if (entry.used) { | |
| 83 | it.index += 1; | |
| 84 | it.count += 1; | |
| 85 | return &entry.kv; | |
| 86 | } | |
| 87 | } | |
| 88 | unreachable; // no next item | |
| 63 | pub fn next(it: *Iterator) ?*Entry { | |
| 64 | if (it.index >= it.hm.unmanaged.entries.items.len) return null; | |
| 65 | const result = &it.hm.unmanaged.entries.items[it.index]; | |
| 66 | it.index += 1; | |
| 67 | return result; | |
| 89 | 68 | } |
| 90 | 69 | |
| 91 | // Reset the iterator to the initial index | |
| 70 | /// Reset the iterator to the initial index | |
| 92 | 71 | pub fn reset(it: *Iterator) void { |
| 93 | it.count = 0; | |
| 94 | 72 | it.index = 0; |
| 95 | // Resetting the modification count too | |
| 96 | it.initial_modification_count = it.hm.modification_count; | |
| 97 | 73 | } |
| 98 | 74 | }; |
| 99 | 75 | |
| 76 | const Self = @This(); | |
| 77 | const Index = Unmanaged.Index; | |
| 78 | ||
| 100 | 79 | pub fn init(allocator: *Allocator) Self { |
| 101 | return Self{ | |
| 102 | .entries = &[_]Entry{}, | |
| 80 | return .{ | |
| 81 | .unmanaged = .{}, | |
| 103 | 82 | .allocator = allocator, |
| 104 | .size = 0, | |
| 105 | .max_distance_from_start_index = 0, | |
| 106 | .modification_count = if (want_modification_safety) 0 else {}, | |
| 107 | 83 | }; |
| 108 | 84 | } |
| 109 | 85 | |
| 110 | pub fn deinit(hm: Self) void { | |
| 111 | hm.allocator.free(hm.entries); | |
| 86 | pub fn deinit(self: *Self) void { | |
| 87 | self.unmanaged.deinit(self.allocator); | |
| 88 | self.* = undefined; | |
| 112 | 89 | } |
| 113 | 90 | |
| 114 | pub fn clear(hm: *Self) void { | |
| 115 | for (hm.entries) |*entry| { | |
| 116 | entry.used = false; | |
| 117 | } | |
| 118 | hm.size = 0; | |
| 119 | hm.max_distance_from_start_index = 0; | |
| 120 | hm.incrementModificationCount(); | |
| 91 | pub fn clearRetainingCapacity(self: *Self) void { | |
| 92 | return self.unmanaged.clearRetainingCapacity(); | |
| 121 | 93 | } |
| 122 | 94 | |
| 95 | pub fn clearAndFree(self: *Self, allocator: *Allocator) void { | |
| 96 | return self.unmanaged.clearAndFree(self.allocator); | |
| 97 | } | |
| 98 | ||
| 99 | /// Deprecated. Use `items().len`. | |
| 123 | 100 | pub fn count(self: Self) usize { |
| 124 | return self.size; | |
| 101 | return self.items().len; | |
| 102 | } | |
| 103 | ||
| 104 | /// Deprecated. Iterate using `items`. | |
| 105 | pub fn iterator(self: *const Self) Iterator { | |
| 106 | return Iterator{ | |
| 107 | .hm = self, | |
| 108 | .index = 0, | |
| 109 | }; | |
| 125 | 110 | } |
| 126 | 111 | |
| 127 | 112 | /// If key exists this function cannot fail. |
| 128 | 113 | /// If there is an existing item with `key`, then the result |
| 129 | /// kv pointer points to it, and found_existing is true. | |
| 114 | /// `Entry` pointer points to it, and found_existing is true. | |
| 130 | 115 | /// Otherwise, puts a new item with undefined value, and |
| 131 | /// the kv pointer points to it. Caller should then initialize | |
| 132 | /// the data. | |
| 116 | /// the `Entry` pointer points to it. Caller should then initialize | |
| 117 | /// the value (but not the key). | |
| 133 | 118 | pub fn getOrPut(self: *Self, key: K) !GetOrPutResult { |
| 134 | // TODO this implementation can be improved - we should only | |
| 135 | // have to hash once and find the entry once. | |
| 136 | if (self.get(key)) |kv| { | |
| 137 | return GetOrPutResult{ | |
| 138 | .kv = kv, | |
| 139 | .found_existing = true, | |
| 140 | }; | |
| 141 | } | |
| 142 | self.incrementModificationCount(); | |
| 143 | try self.autoCapacity(); | |
| 144 | const put_result = self.internalPut(key); | |
| 145 | assert(put_result.old_kv == null); | |
| 146 | return GetOrPutResult{ | |
| 147 | .kv = &put_result.new_entry.kv, | |
| 148 | .found_existing = false, | |
| 149 | }; | |
| 119 | return self.unmanaged.getOrPut(self.allocator, key); | |
| 150 | 120 | } |
| 151 | 121 | |
| 152 | pub fn getOrPutValue(self: *Self, key: K, value: V) !*KV { | |
| 153 | const res = try self.getOrPut(key); | |
| 154 | if (!res.found_existing) | |
| 155 | res.kv.value = value; | |
| 122 | /// If there is an existing item with `key`, then the result | |
| 123 | /// `Entry` pointer points to it, and found_existing is true. | |
| 124 | /// Otherwise, puts a new item with undefined value, and | |
| 125 | /// the `Entry` pointer points to it. Caller should then initialize | |
| 126 | /// the value (but not the key). | |
| 127 | /// If a new entry needs to be stored, this function asserts there | |
| 128 | /// is enough capacity to store it. | |
| 129 | pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult { | |
| 130 | return self.unmanaged.getOrPutAssumeCapacity(key); | |
| 131 | } | |
| 132 | ||
| 133 | pub fn getOrPutValue(self: *Self, key: K, value: V) !*Entry { | |
| 134 | return self.unmanaged.getOrPutValue(self.allocator, key, value); | |
| 135 | } | |
| 136 | ||
| 137 | /// Increases capacity, guaranteeing that insertions up until the | |
| 138 | /// `expected_count` will not cause an allocation, and therefore cannot fail. | |
| 139 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { | |
| 140 | return self.unmanaged.ensureCapacity(self.allocator, new_capacity); | |
| 141 | } | |
| 142 | ||
| 143 | /// Returns the number of total elements which may be present before it is | |
| 144 | /// no longer guaranteed that no allocations will be performed. | |
| 145 | pub fn capacity(self: *Self) usize { | |
| 146 | return self.unmanaged.capacity(); | |
| 147 | } | |
| 148 | ||
| 149 | /// Clobbers any existing data. To detect if a put would clobber | |
| 150 | /// existing data, see `getOrPut`. | |
| 151 | pub fn put(self: *Self, key: K, value: V) !void { | |
| 152 | return self.unmanaged.put(self.allocator, key, value); | |
| 153 | } | |
| 154 | ||
| 155 | /// Inserts a key-value pair into the hash map, asserting that no previous | |
| 156 | /// entry with the same key is already present | |
| 157 | pub fn putNoClobber(self: *Self, key: K, value: V) !void { | |
| 158 | return self.unmanaged.putNoClobber(self.allocator, key, value); | |
| 159 | } | |
| 160 | ||
| 161 | /// Asserts there is enough capacity to store the new key-value pair. | |
| 162 | /// Clobbers any existing data. To detect if a put would clobber | |
| 163 | /// existing data, see `getOrPutAssumeCapacity`. | |
| 164 | pub fn putAssumeCapacity(self: *Self, key: K, value: V) void { | |
| 165 | return self.unmanaged.putAssumeCapacity(key, value); | |
| 166 | } | |
| 167 | ||
| 168 | /// Asserts there is enough capacity to store the new key-value pair. | |
| 169 | /// Asserts that it does not clobber any existing data. | |
| 170 | /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`. | |
| 171 | pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void { | |
| 172 | return self.unmanaged.putAssumeCapacityNoClobber(key, value); | |
| 173 | } | |
| 174 | ||
| 175 | /// Inserts a new `Entry` into the hash map, returning the previous one, if any. | |
| 176 | pub fn fetchPut(self: *Self, key: K, value: V) !?Entry { | |
| 177 | return self.unmanaged.fetchPut(self.allocator, key, value); | |
| 178 | } | |
| 179 | ||
| 180 | /// Inserts a new `Entry` into the hash map, returning the previous one, if any. | |
| 181 | /// If insertion happuns, asserts there is enough capacity without allocating. | |
| 182 | pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?Entry { | |
| 183 | return self.unmanaged.fetchPutAssumeCapacity(key, value); | |
| 184 | } | |
| 185 | ||
| 186 | pub fn getEntry(self: Self, key: K) ?*Entry { | |
| 187 | return self.unmanaged.getEntry(key); | |
| 188 | } | |
| 189 | ||
| 190 | pub fn get(self: Self, key: K) ?V { | |
| 191 | return self.unmanaged.get(key); | |
| 192 | } | |
| 156 | 193 | |
| 157 | return res.kv; | |
| 194 | pub fn contains(self: Self, key: K) bool { | |
| 195 | return self.unmanaged.contains(key); | |
| 158 | 196 | } |
| 159 | 197 | |
| 160 | fn optimizedCapacity(expected_count: usize) usize { | |
| 161 | // ensure that the hash map will be at most 60% full if | |
| 162 | // expected_count items are put into it | |
| 163 | var optimized_capacity = expected_count * 5 / 3; | |
| 164 | // an overflow here would mean the amount of memory required would not | |
| 165 | // be representable in the address space | |
| 166 | return math.ceilPowerOfTwo(usize, optimized_capacity) catch unreachable; | |
| 198 | /// If there is an `Entry` with a matching key, it is deleted from | |
| 199 | /// the hash map, and then returned from this function. | |
| 200 | pub fn remove(self: *Self, key: K) ?Entry { | |
| 201 | return self.unmanaged.remove(key); | |
| 167 | 202 | } |
| 168 | 203 | |
| 169 | /// Increases capacity so that the hash map will be at most | |
| 170 | /// 60% full when expected_count items are put into it | |
| 171 | pub fn ensureCapacity(self: *Self, expected_count: usize) !void { | |
| 172 | if (expected_count == 0) return; | |
| 173 | const optimized_capacity = optimizedCapacity(expected_count); | |
| 174 | return self.ensureCapacityExact(optimized_capacity); | |
| 204 | /// Asserts there is an `Entry` with matching key, deletes it from the hash map, | |
| 205 | /// and discards it. | |
| 206 | pub fn removeAssertDiscard(self: *Self, key: K) void { | |
| 207 | return self.unmanaged.removeAssertDiscard(key); | |
| 175 | 208 | } |
| 176 | 209 | |
| 177 | /// Sets the capacity to the new capacity if the new | |
| 178 | /// capacity is greater than the current capacity. | |
| 179 | /// New capacity must be a power of two. | |
| 180 | fn ensureCapacityExact(self: *Self, new_capacity: usize) !void { | |
| 181 | // capacity must always be a power of two to allow for modulo | |
| 182 | // optimization in the constrainIndex fn | |
| 183 | assert(math.isPowerOfTwo(new_capacity)); | |
| 210 | pub fn items(self: Self) []Entry { | |
| 211 | return self.unmanaged.items(); | |
| 212 | } | |
| 213 | ||
| 214 | pub fn clone(self: Self) !Self { | |
| 215 | var other = try self.unmanaged.clone(self.allocator); | |
| 216 | return other.promote(self.allocator); | |
| 217 | } | |
| 218 | }; | |
| 219 | } | |
| 220 | ||
| 221 | /// General purpose hash table. | |
| 222 | /// Insertion order is preserved. | |
| 223 | /// Deletions perform a "swap removal" on the entries list. | |
| 224 | /// Modifying the hash map while iterating is allowed, however one must understand | |
| 225 | /// the (well defined) behavior when mixing insertions and deletions with iteration. | |
| 226 | /// This type does not store an Allocator field - the Allocator must be passed in | |
| 227 | /// with each function call that requires it. See `HashMap` for a type that stores | |
| 228 | /// an Allocator field for convenience. | |
| 229 | /// Can be initialized directly using the default field values. | |
| 230 | /// This type is designed to have low overhead for small numbers of entries. When | |
| 231 | /// `store_hash` is `false` and the number of entries in the map is less than 9, | |
| 232 | /// the overhead cost of using `HashMapUnmanaged` rather than `std.ArrayList` is | |
| 233 | /// only a single pointer-sized integer. | |
| 234 | /// When `store_hash` is `false`, this data structure is biased towards cheap `eql` | |
| 235 | /// functions. It does not store each item's hash in the table. Setting `store_hash` | |
| 236 | /// to `true` incurs slightly more memory cost by storing each key's hash in the table | |
| 237 | /// but guarantees only one call to `eql` per insertion/deletion. | |
| 238 | pub fn HashMapUnmanaged( | |
| 239 | comptime K: type, | |
| 240 | comptime V: type, | |
| 241 | comptime hash: fn (key: K) u32, | |
| 242 | comptime eql: fn (a: K, b: K) bool, | |
| 243 | comptime store_hash: bool, | |
| 244 | ) type { | |
| 245 | return struct { | |
| 246 | /// It is permitted to access this field directly. | |
| 247 | entries: std.ArrayListUnmanaged(Entry) = .{}, | |
| 248 | ||
| 249 | /// When entries length is less than `linear_scan_max`, this remains `null`. | |
| 250 | /// Once entries length grows big enough, this field is allocated. There is | |
| 251 | /// an IndexHeader followed by an array of Index(I) structs, where I is defined | |
| 252 | /// by how many total indexes there are. | |
| 253 | index_header: ?*IndexHeader = null, | |
| 254 | ||
| 255 | /// Modifying the key is illegal behavior. | |
| 256 | /// Modifying the value is allowed. | |
| 257 | /// Entry pointers become invalid whenever this HashMap is modified, | |
| 258 | /// unless `ensureCapacity` was previously used. | |
| 259 | pub const Entry = struct { | |
| 260 | /// This field is `void` if `store_hash` is `false`. | |
| 261 | hash: Hash, | |
| 262 | key: K, | |
| 263 | value: V, | |
| 264 | }; | |
| 265 | ||
| 266 | pub const Hash = if (store_hash) u32 else void; | |
| 267 | ||
| 268 | pub const GetOrPutResult = struct { | |
| 269 | entry: *Entry, | |
| 270 | found_existing: bool, | |
| 271 | }; | |
| 272 | ||
| 273 | pub const Managed = HashMap(K, V, hash, eql, store_hash); | |
| 274 | ||
| 275 | const Self = @This(); | |
| 276 | ||
| 277 | const linear_scan_max = 8; | |
| 184 | 278 | |
| 185 | if (new_capacity <= self.entries.len) { | |
| 186 | return; | |
| 279 | pub fn promote(self: Self, allocator: *Allocator) Managed { | |
| 280 | return .{ | |
| 281 | .unmanaged = self, | |
| 282 | .allocator = allocator, | |
| 283 | }; | |
| 284 | } | |
| 285 | ||
| 286 | pub fn deinit(self: *Self, allocator: *Allocator) void { | |
| 287 | self.entries.deinit(allocator); | |
| 288 | if (self.index_header) |header| { | |
| 289 | header.free(allocator); | |
| 187 | 290 | } |
| 291 | self.* = undefined; | |
| 292 | } | |
| 293 | ||
| 294 | pub fn clearRetainingCapacity(self: *Self) void { | |
| 295 | self.entries.items.len = 0; | |
| 296 | if (self.index_header) |header| { | |
| 297 | header.max_distance_from_start_index = 0; | |
| 298 | switch (header.capacityIndexType()) { | |
| 299 | .u8 => mem.set(Index(u8), header.indexes(u8), Index(u8).empty), | |
| 300 | .u16 => mem.set(Index(u16), header.indexes(u16), Index(u16).empty), | |
| 301 | .u32 => mem.set(Index(u32), header.indexes(u32), Index(u32).empty), | |
| 302 | .usize => mem.set(Index(usize), header.indexes(usize), Index(usize).empty), | |
| 303 | } | |
| 304 | } | |
| 305 | } | |
| 306 | ||
| 307 | pub fn clearAndFree(self: *Self, allocator: *Allocator) void { | |
| 308 | self.entries.shrink(allocator, 0); | |
| 309 | if (self.index_header) |header| { | |
| 310 | header.free(allocator); | |
| 311 | self.index_header = null; | |
| 312 | } | |
| 313 | } | |
| 314 | ||
| 315 | /// If key exists this function cannot fail. | |
| 316 | /// If there is an existing item with `key`, then the result | |
| 317 | /// `Entry` pointer points to it, and found_existing is true. | |
| 318 | /// Otherwise, puts a new item with undefined value, and | |
| 319 | /// the `Entry` pointer points to it. Caller should then initialize | |
| 320 | /// the value (but not the key). | |
| 321 | pub fn getOrPut(self: *Self, allocator: *Allocator, key: K) !GetOrPutResult { | |
| 322 | self.ensureCapacity(allocator, self.entries.items.len + 1) catch |err| { | |
| 323 | // "If key exists this function cannot fail." | |
| 324 | return GetOrPutResult{ | |
| 325 | .entry = self.getEntry(key) orelse return err, | |
| 326 | .found_existing = true, | |
| 327 | }; | |
| 328 | }; | |
| 329 | return self.getOrPutAssumeCapacity(key); | |
| 330 | } | |
| 188 | 331 | |
| 189 | const old_entries = self.entries; | |
| 190 | try self.initCapacity(new_capacity); | |
| 191 | self.incrementModificationCount(); | |
| 192 | if (old_entries.len > 0) { | |
| 193 | // dump all of the old elements into the new table | |
| 194 | for (old_entries) |*old_entry| { | |
| 195 | if (old_entry.used) { | |
| 196 | self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value; | |
| 332 | /// If there is an existing item with `key`, then the result | |
| 333 | /// `Entry` pointer points to it, and found_existing is true. | |
| 334 | /// Otherwise, puts a new item with undefined value, and | |
| 335 | /// the `Entry` pointer points to it. Caller should then initialize | |
| 336 | /// the value (but not the key). | |
| 337 | /// If a new entry needs to be stored, this function asserts there | |
| 338 | /// is enough capacity to store it. | |
| 339 | pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult { | |
| 340 | const header = self.index_header orelse { | |
| 341 | // Linear scan. | |
| 342 | const h = if (store_hash) hash(key) else {}; | |
| 343 | for (self.entries.items) |*item| { | |
| 344 | if (item.hash == h and eql(key, item.key)) { | |
| 345 | return GetOrPutResult{ | |
| 346 | .entry = item, | |
| 347 | .found_existing = true, | |
| 348 | }; | |
| 197 | 349 | } |
| 198 | 350 | } |
| 199 | self.allocator.free(old_entries); | |
| 351 | const new_entry = self.entries.addOneAssumeCapacity(); | |
| 352 | new_entry.* = .{ | |
| 353 | .hash = if (store_hash) h else {}, | |
| 354 | .key = key, | |
| 355 | .value = undefined, | |
| 356 | }; | |
| 357 | return GetOrPutResult{ | |
| 358 | .entry = new_entry, | |
| 359 | .found_existing = false, | |
| 360 | }; | |
| 361 | }; | |
| 362 | ||
| 363 | switch (header.capacityIndexType()) { | |
| 364 | .u8 => return self.getOrPutInternal(key, header, u8), | |
| 365 | .u16 => return self.getOrPutInternal(key, header, u16), | |
| 366 | .u32 => return self.getOrPutInternal(key, header, u32), | |
| 367 | .usize => return self.getOrPutInternal(key, header, usize), | |
| 368 | } | |
| 369 | } | |
| 370 | ||
| 371 | pub fn getOrPutValue(self: *Self, allocator: *Allocator, key: K, value: V) !*Entry { | |
| 372 | const res = try self.getOrPut(allocator, key); | |
| 373 | if (!res.found_existing) | |
| 374 | res.entry.value = value; | |
| 375 | ||
| 376 | return res.entry; | |
| 377 | } | |
| 378 | ||
| 379 | /// Increases capacity, guaranteeing that insertions up until the | |
| 380 | /// `expected_count` will not cause an allocation, and therefore cannot fail. | |
| 381 | pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void { | |
| 382 | try self.entries.ensureCapacity(allocator, new_capacity); | |
| 383 | if (new_capacity <= linear_scan_max) return; | |
| 384 | ||
| 385 | // Ensure that the indexes will be at most 60% full if | |
| 386 | // `new_capacity` items are put into it. | |
| 387 | const needed_len = new_capacity * 5 / 3; | |
| 388 | if (self.index_header) |header| { | |
| 389 | if (needed_len > header.indexes_len) { | |
| 390 | // An overflow here would mean the amount of memory required would not | |
| 391 | // be representable in the address space. | |
| 392 | const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable; | |
| 393 | const new_header = try IndexHeader.alloc(allocator, new_indexes_len); | |
| 394 | self.insertAllEntriesIntoNewHeader(new_header); | |
| 395 | header.free(allocator); | |
| 396 | self.index_header = new_header; | |
| 397 | } | |
| 398 | } else { | |
| 399 | // An overflow here would mean the amount of memory required would not | |
| 400 | // be representable in the address space. | |
| 401 | const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable; | |
| 402 | const header = try IndexHeader.alloc(allocator, new_indexes_len); | |
| 403 | self.insertAllEntriesIntoNewHeader(header); | |
| 404 | self.index_header = header; | |
| 200 | 405 | } |
| 201 | 406 | } |
| 202 | 407 | |
| 203 | /// Returns the kv pair that was already there. | |
| 204 | pub fn put(self: *Self, key: K, value: V) !?KV { | |
| 205 | try self.autoCapacity(); | |
| 206 | return putAssumeCapacity(self, key, value); | |
| 408 | /// Returns the number of total elements which may be present before it is | |
| 409 | /// no longer guaranteed that no allocations will be performed. | |
| 410 | pub fn capacity(self: Self) usize { | |
| 411 | const entry_cap = self.entries.capacity; | |
| 412 | const header = self.index_header orelse return math.min(linear_scan_max, entry_cap); | |
| 413 | const indexes_cap = (header.indexes_len + 1) * 3 / 4; | |
| 414 | return math.min(entry_cap, indexes_cap); | |
| 207 | 415 | } |
| 208 | 416 | |
| 209 | /// Calls put() and asserts that no kv pair is clobbered. | |
| 210 | pub fn putNoClobber(self: *Self, key: K, value: V) !void { | |
| 211 | assert((try self.put(key, value)) == null); | |
| 417 | /// Clobbers any existing data. To detect if a put would clobber | |
| 418 | /// existing data, see `getOrPut`. | |
| 419 | pub fn put(self: *Self, allocator: *Allocator, key: K, value: V) !void { | |
| 420 | const result = try self.getOrPut(allocator, key); | |
| 421 | result.entry.value = value; | |
| 212 | 422 | } |
| 213 | 423 | |
| 214 | pub fn putAssumeCapacity(self: *Self, key: K, value: V) ?KV { | |
| 215 | assert(self.count() < self.entries.len); | |
| 216 | self.incrementModificationCount(); | |
| 424 | /// Inserts a key-value pair into the hash map, asserting that no previous | |
| 425 | /// entry with the same key is already present | |
| 426 | pub fn putNoClobber(self: *Self, allocator: *Allocator, key: K, value: V) !void { | |
| 427 | const result = try self.getOrPut(allocator, key); | |
| 428 | assert(!result.found_existing); | |
| 429 | result.entry.value = value; | |
| 430 | } | |
| 217 | 431 | |
| 218 | const put_result = self.internalPut(key); | |
| 219 | put_result.new_entry.kv.value = value; | |
| 220 | return put_result.old_kv; | |
| 432 | /// Asserts there is enough capacity to store the new key-value pair. | |
| 433 | /// Clobbers any existing data. To detect if a put would clobber | |
| 434 | /// existing data, see `getOrPutAssumeCapacity`. | |
| 435 | pub fn putAssumeCapacity(self: *Self, key: K, value: V) void { | |
| 436 | const result = self.getOrPutAssumeCapacity(key); | |
| 437 | result.entry.value = value; | |
| 221 | 438 | } |
| 222 | 439 | |
| 440 | /// Asserts there is enough capacity to store the new key-value pair. | |
| 441 | /// Asserts that it does not clobber any existing data. | |
| 442 | /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`. | |
| 223 | 443 | pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void { |
| 224 | assert(self.putAssumeCapacity(key, value) == null); | |
| 444 | const result = self.getOrPutAssumeCapacity(key); | |
| 445 | assert(!result.found_existing); | |
| 446 | result.entry.value = value; | |
| 225 | 447 | } |
| 226 | 448 | |
| 227 | pub fn get(hm: *const Self, key: K) ?*KV { | |
| 228 | if (hm.entries.len == 0) { | |
| 449 | /// Inserts a new `Entry` into the hash map, returning the previous one, if any. | |
| 450 | pub fn fetchPut(self: *Self, allocator: *Allocator, key: K, value: V) !?Entry { | |
| 451 | const gop = try self.getOrPut(allocator, key); | |
| 452 | var result: ?Entry = null; | |
| 453 | if (gop.found_existing) { | |
| 454 | result = gop.entry.*; | |
| 455 | } | |
| 456 | gop.entry.value = value; | |
| 457 | return result; | |
| 458 | } | |
| 459 | ||
| 460 | /// Inserts a new `Entry` into the hash map, returning the previous one, if any. | |
| 461 | /// If insertion happens, asserts there is enough capacity without allocating. | |
| 462 | pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?Entry { | |
| 463 | const gop = self.getOrPutAssumeCapacity(key); | |
| 464 | var result: ?Entry = null; | |
| 465 | if (gop.found_existing) { | |
| 466 | result = gop.entry.*; | |
| 467 | } | |
| 468 | gop.entry.value = value; | |
| 469 | return result; | |
| 470 | } | |
| 471 | ||
| 472 | pub fn getEntry(self: Self, key: K) ?*Entry { | |
| 473 | const header = self.index_header orelse { | |
| 474 | // Linear scan. | |
| 475 | const h = if (store_hash) hash(key) else {}; | |
| 476 | for (self.entries.items) |*item| { | |
| 477 | if (item.hash == h and eql(key, item.key)) { | |
| 478 | return item; | |
| 479 | } | |
| 480 | } | |
| 229 | 481 | return null; |
| 482 | }; | |
| 483 | ||
| 484 | switch (header.capacityIndexType()) { | |
| 485 | .u8 => return self.getInternal(key, header, u8), | |
| 486 | .u16 => return self.getInternal(key, header, u16), | |
| 487 | .u32 => return self.getInternal(key, header, u32), | |
| 488 | .usize => return self.getInternal(key, header, usize), | |
| 230 | 489 | } |
| 231 | return hm.internalGet(key); | |
| 232 | 490 | } |
| 233 | 491 | |
| 234 | pub fn getValue(hm: *const Self, key: K) ?V { | |
| 235 | return if (hm.get(key)) |kv| kv.value else null; | |
| 492 | pub fn get(self: Self, key: K) ?V { | |
| 493 | return if (self.getEntry(key)) |entry| entry.value else null; | |
| 236 | 494 | } |
| 237 | 495 | |
| 238 | pub fn contains(hm: *const Self, key: K) bool { | |
| 239 | return hm.get(key) != null; | |
| 496 | pub fn contains(self: Self, key: K) bool { | |
| 497 | return self.getEntry(key) != null; | |
| 240 | 498 | } |
| 241 | 499 | |
| 242 | /// Returns any kv pair that was removed. | |
| 243 | pub fn remove(hm: *Self, key: K) ?KV { | |
| 244 | if (hm.entries.len == 0) return null; | |
| 245 | hm.incrementModificationCount(); | |
| 246 | const start_index = hm.keyToIndex(key); | |
| 247 | { | |
| 248 | var roll_over: usize = 0; | |
| 249 | while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { | |
| 250 | const index = hm.constrainIndex(start_index + roll_over); | |
| 251 | var entry = &hm.entries[index]; | |
| 252 | ||
| 253 | if (!entry.used) return null; | |
| 254 | ||
| 255 | if (!eql(entry.kv.key, key)) continue; | |
| 256 | ||
| 257 | const removed_kv = entry.kv; | |
| 258 | while (roll_over < hm.entries.len) : (roll_over += 1) { | |
| 259 | const next_index = hm.constrainIndex(start_index + roll_over + 1); | |
| 260 | const next_entry = &hm.entries[next_index]; | |
| 261 | if (!next_entry.used or next_entry.distance_from_start_index == 0) { | |
| 262 | entry.used = false; | |
| 263 | hm.size -= 1; | |
| 264 | return removed_kv; | |
| 265 | } | |
| 266 | entry.* = next_entry.*; | |
| 267 | entry.distance_from_start_index -= 1; | |
| 268 | entry = next_entry; | |
| 500 | /// If there is an `Entry` with a matching key, it is deleted from | |
| 501 | /// the hash map, and then returned from this function. | |
| 502 | pub fn remove(self: *Self, key: K) ?Entry { | |
| 503 | const header = self.index_header orelse { | |
| 504 | // Linear scan. | |
| 505 | const h = if (store_hash) hash(key) else {}; | |
| 506 | for (self.entries.items) |item, i| { | |
| 507 | if (item.hash == h and eql(key, item.key)) { | |
| 508 | return self.entries.swapRemove(i); | |
| 269 | 509 | } |
| 270 | unreachable; // shifting everything in the table | |
| 271 | 510 | } |
| 511 | return null; | |
| 512 | }; | |
| 513 | switch (header.capacityIndexType()) { | |
| 514 | .u8 => return self.removeInternal(key, header, u8), | |
| 515 | .u16 => return self.removeInternal(key, header, u16), | |
| 516 | .u32 => return self.removeInternal(key, header, u32), | |
| 517 | .usize => return self.removeInternal(key, header, usize), | |
| 272 | 518 | } |
| 273 | return null; | |
| 274 | 519 | } |
| 275 | 520 | |
| 276 | /// Calls remove(), asserts that a kv pair is removed, and discards it. | |
| 277 | pub fn removeAssertDiscard(hm: *Self, key: K) void { | |
| 278 | assert(hm.remove(key) != null); | |
| 521 | /// Asserts there is an `Entry` with matching key, deletes it from the hash map, | |
| 522 | /// and discards it. | |
| 523 | pub fn removeAssertDiscard(self: *Self, key: K) void { | |
| 524 | assert(self.remove(key) != null); | |
| 279 | 525 | } |
| 280 | 526 | |
| 281 | pub fn iterator(hm: *const Self) Iterator { | |
| 282 | return Iterator{ | |
| 283 | .hm = hm, | |
| 284 | .count = 0, | |
| 285 | .index = 0, | |
| 286 | .initial_modification_count = hm.modification_count, | |
| 287 | }; | |
| 527 | pub fn items(self: Self) []Entry { | |
| 528 | return self.entries.items; | |
| 288 | 529 | } |
| 289 | 530 | |
| 290 | pub fn clone(self: Self) !Self { | |
| 291 | var other = Self.init(self.allocator); | |
| 292 | try other.initCapacity(self.entries.len); | |
| 293 | var it = self.iterator(); | |
| 294 | while (it.next()) |entry| { | |
| 295 | try other.putNoClobber(entry.key, entry.value); | |
| 531 | pub fn clone(self: Self, allocator: *Allocator) !Self { | |
| 532 | // TODO this can be made more efficient by directly allocating | |
| 533 | // the memory slices and memcpying the elements. | |
| 534 | var other = Self.init(); | |
| 535 | try other.initCapacity(allocator, self.entries.len); | |
| 536 | for (self.entries.items) |entry| { | |
| 537 | other.putAssumeCapacityNoClobber(entry.key, entry.value); | |
| 296 | 538 | } |
| 297 | 539 | return other; |
| 298 | 540 | } |
| 299 | 541 | |
| 300 | fn autoCapacity(self: *Self) !void { | |
| 301 | if (self.entries.len == 0) { | |
| 302 | return self.ensureCapacityExact(16); | |
| 303 | } | |
| 304 | // if we get too full (60%), double the capacity | |
| 305 | if (self.size * 5 >= self.entries.len * 3) { | |
| 306 | return self.ensureCapacityExact(self.entries.len * 2); | |
| 307 | } | |
| 308 | } | |
| 542 | fn removeInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) ?Entry { | |
| 543 | const indexes = header.indexes(I); | |
| 544 | const h = hash(key); | |
| 545 | const start_index = header.constrainIndex(h); | |
| 546 | var roll_over: usize = 0; | |
| 547 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { | |
| 548 | const index_index = header.constrainIndex(start_index + roll_over); | |
| 549 | var index = &indexes[index_index]; | |
| 550 | if (index.isEmpty()) | |
| 551 | return null; | |
| 552 | ||
| 553 | const entry = &self.entries.items[index.entry_index]; | |
| 554 | ||
| 555 | const hash_match = if (store_hash) h == entry.hash else true; | |
| 556 | if (!hash_match or !eql(key, entry.key)) | |
| 557 | continue; | |
| 558 | ||
| 559 | const removed_entry = self.entries.swapRemove(index.entry_index); | |
| 560 | if (self.entries.items.len > 0 and self.entries.items.len != index.entry_index) { | |
| 561 | // Because of the swap remove, now we need to update the index that was | |
| 562 | // pointing to the last entry and is now pointing to this removed item slot. | |
| 563 | self.updateEntryIndex(header, self.entries.items.len, index.entry_index, I, indexes); | |
| 564 | } | |
| 309 | 565 | |
| 310 | fn initCapacity(hm: *Self, capacity: usize) !void { | |
| 311 | hm.entries = try hm.allocator.alloc(Entry, capacity); | |
| 312 | hm.size = 0; | |
| 313 | hm.max_distance_from_start_index = 0; | |
| 314 | for (hm.entries) |*entry| { | |
| 315 | entry.used = false; | |
| 566 | // Now we have to shift over the following indexes. | |
| 567 | roll_over += 1; | |
| 568 | while (roll_over < header.indexes_len) : (roll_over += 1) { | |
| 569 | const next_index_index = header.constrainIndex(start_index + roll_over); | |
| 570 | const next_index = &indexes[next_index_index]; | |
| 571 | if (next_index.isEmpty() or next_index.distance_from_start_index == 0) { | |
| 572 | index.setEmpty(); | |
| 573 | return removed_entry; | |
| 574 | } | |
| 575 | index.* = next_index.*; | |
| 576 | index.distance_from_start_index -= 1; | |
| 577 | index = next_index; | |
| 578 | } | |
| 579 | unreachable; | |
| 316 | 580 | } |
| 581 | return null; | |
| 317 | 582 | } |
| 318 | 583 | |
| 319 | fn incrementModificationCount(hm: *Self) void { | |
| 320 | if (want_modification_safety) { | |
| 321 | hm.modification_count +%= 1; | |
| 584 | fn updateEntryIndex( | |
| 585 | self: *Self, | |
| 586 | header: *IndexHeader, | |
| 587 | old_entry_index: usize, | |
| 588 | new_entry_index: usize, | |
| 589 | comptime I: type, | |
| 590 | indexes: []Index(I), | |
| 591 | ) void { | |
| 592 | const h = if (store_hash) self.entries.items[new_entry_index].hash else hash(self.entries.items[new_entry_index].key); | |
| 593 | const start_index = header.constrainIndex(h); | |
| 594 | var roll_over: usize = 0; | |
| 595 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { | |
| 596 | const index_index = header.constrainIndex(start_index + roll_over); | |
| 597 | const index = &indexes[index_index]; | |
| 598 | if (index.entry_index == old_entry_index) { | |
| 599 | index.entry_index = @intCast(I, new_entry_index); | |
| 600 | return; | |
| 601 | } | |
| 322 | 602 | } |
| 603 | unreachable; | |
| 323 | 604 | } |
| 324 | 605 | |
| 325 | const InternalPutResult = struct { | |
| 326 | new_entry: *Entry, | |
| 327 | old_kv: ?KV, | |
| 328 | }; | |
| 329 | ||
| 330 | /// Returns a pointer to the new entry. | |
| 331 | /// Asserts that there is enough space for the new item. | |
| 332 | fn internalPut(self: *Self, orig_key: K) InternalPutResult { | |
| 333 | var key = orig_key; | |
| 334 | var value: V = undefined; | |
| 335 | const start_index = self.keyToIndex(key); | |
| 606 | /// Must ensureCapacity before calling this. | |
| 607 | fn getOrPutInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) GetOrPutResult { | |
| 608 | const indexes = header.indexes(I); | |
| 609 | const h = hash(key); | |
| 610 | const start_index = header.constrainIndex(h); | |
| 336 | 611 | var roll_over: usize = 0; |
| 337 | 612 | var distance_from_start_index: usize = 0; |
| 338 | var got_result_entry = false; | |
| 339 | var result = InternalPutResult{ | |
| 340 | .new_entry = undefined, | |
| 341 | .old_kv = null, | |
| 342 | }; | |
| 343 | while (roll_over < self.entries.len) : ({ | |
| 613 | while (roll_over <= header.indexes_len) : ({ | |
| 344 | 614 | roll_over += 1; |
| 345 | 615 | distance_from_start_index += 1; |
| 346 | 616 | }) { |
| 347 | const index = self.constrainIndex(start_index + roll_over); | |
| 348 | const entry = &self.entries[index]; | |
| 349 | ||
| 350 | if (entry.used and !eql(entry.kv.key, key)) { | |
| 351 | if (entry.distance_from_start_index < distance_from_start_index) { | |
| 352 | // robin hood to the rescue | |
| 353 | const tmp = entry.*; | |
| 354 | self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index); | |
| 355 | if (!got_result_entry) { | |
| 356 | got_result_entry = true; | |
| 357 | result.new_entry = entry; | |
| 617 | const index_index = header.constrainIndex(start_index + roll_over); | |
| 618 | const index = indexes[index_index]; | |
| 619 | if (index.isEmpty()) { | |
| 620 | indexes[index_index] = .{ | |
| 621 | .distance_from_start_index = @intCast(I, distance_from_start_index), | |
| 622 | .entry_index = @intCast(I, self.entries.items.len), | |
| 623 | }; | |
| 624 | header.maybeBumpMax(distance_from_start_index); | |
| 625 | const new_entry = self.entries.addOneAssumeCapacity(); | |
| 626 | new_entry.* = .{ | |
| 627 | .hash = if (store_hash) h else {}, | |
| 628 | .key = key, | |
| 629 | .value = undefined, | |
| 630 | }; | |
| 631 | return .{ | |
| 632 | .found_existing = false, | |
| 633 | .entry = new_entry, | |
| 634 | }; | |
| 635 | } | |
| 636 | ||
| 637 | // This pointer survives the following append because we call | |
| 638 | // entries.ensureCapacity before getOrPutInternal. | |
| 639 | const entry = &self.entries.items[index.entry_index]; | |
| 640 | const hash_match = if (store_hash) h == entry.hash else true; | |
| 641 | if (hash_match and eql(key, entry.key)) { | |
| 642 | return .{ | |
| 643 | .found_existing = true, | |
| 644 | .entry = entry, | |
| 645 | }; | |
| 646 | } | |
| 647 | if (index.distance_from_start_index < distance_from_start_index) { | |
| 648 | // In this case, we did not find the item. We will put a new entry. | |
| 649 | // However, we will use this index for the new entry, and move | |
| 650 | // the previous index down the line, to keep the max_distance_from_start_index | |
| 651 | // as small as possible. | |
| 652 | indexes[index_index] = .{ | |
| 653 | .distance_from_start_index = @intCast(I, distance_from_start_index), | |
| 654 | .entry_index = @intCast(I, self.entries.items.len), | |
| 655 | }; | |
| 656 | header.maybeBumpMax(distance_from_start_index); | |
| 657 | const new_entry = self.entries.addOneAssumeCapacity(); | |
| 658 | new_entry.* = .{ | |
| 659 | .hash = if (store_hash) h else {}, | |
| 660 | .key = key, | |
| 661 | .value = undefined, | |
| 662 | }; | |
| 663 | ||
| 664 | distance_from_start_index = index.distance_from_start_index; | |
| 665 | var prev_entry_index = index.entry_index; | |
| 666 | ||
| 667 | // Find somewhere to put the index we replaced by shifting | |
| 668 | // following indexes backwards. | |
| 669 | roll_over += 1; | |
| 670 | distance_from_start_index += 1; | |
| 671 | while (roll_over < header.indexes_len) : ({ | |
| 672 | roll_over += 1; | |
| 673 | distance_from_start_index += 1; | |
| 674 | }) { | |
| 675 | const next_index_index = header.constrainIndex(start_index + roll_over); | |
| 676 | const next_index = indexes[next_index_index]; | |
| 677 | if (next_index.isEmpty()) { | |
| 678 | header.maybeBumpMax(distance_from_start_index); | |
| 679 | indexes[next_index_index] = .{ | |
| 680 | .entry_index = prev_entry_index, | |
| 681 | .distance_from_start_index = @intCast(I, distance_from_start_index), | |
| 682 | }; | |
| 683 | return .{ | |
| 684 | .found_existing = false, | |
| 685 | .entry = new_entry, | |
| 686 | }; | |
| 687 | } | |
| 688 | if (next_index.distance_from_start_index < distance_from_start_index) { | |
| 689 | header.maybeBumpMax(distance_from_start_index); | |
| 690 | indexes[next_index_index] = .{ | |
| 691 | .entry_index = prev_entry_index, | |
| 692 | .distance_from_start_index = @intCast(I, distance_from_start_index), | |
| 693 | }; | |
| 694 | distance_from_start_index = next_index.distance_from_start_index; | |
| 695 | prev_entry_index = next_index.entry_index; | |
| 358 | 696 | } |
| 359 | entry.* = Entry{ | |
| 360 | .used = true, | |
| 361 | .distance_from_start_index = distance_from_start_index, | |
| 362 | .kv = KV{ | |
| 363 | .key = key, | |
| 364 | .value = value, | |
| 365 | }, | |
| 366 | }; | |
| 367 | key = tmp.kv.key; | |
| 368 | value = tmp.kv.value; | |
| 369 | distance_from_start_index = tmp.distance_from_start_index; | |
| 370 | 697 | } |
| 371 | continue; | |
| 698 | unreachable; | |
| 372 | 699 | } |
| 700 | } | |
| 701 | unreachable; | |
| 702 | } | |
| 373 | 703 | |
| 374 | if (entry.used) { | |
| 375 | result.old_kv = entry.kv; | |
| 376 | } else { | |
| 377 | // adding an entry. otherwise overwriting old value with | |
| 378 | // same key | |
| 379 | self.size += 1; | |
| 380 | } | |
| 704 | fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry { | |
| 705 | const indexes = header.indexes(I); | |
| 706 | const h = hash(key); | |
| 707 | const start_index = header.constrainIndex(h); | |
| 708 | var roll_over: usize = 0; | |
| 709 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { | |
| 710 | const index_index = header.constrainIndex(start_index + roll_over); | |
| 711 | const index = indexes[index_index]; | |
| 712 | if (index.isEmpty()) | |
| 713 | return null; | |
| 714 | ||
| 715 | const entry = &self.entries.items[index.entry_index]; | |
| 716 | const hash_match = if (store_hash) h == entry.hash else true; | |
| 717 | if (hash_match and eql(key, entry.key)) | |
| 718 | return entry; | |
| 719 | } | |
| 720 | return null; | |
| 721 | } | |
| 381 | 722 | |
| 382 | self.max_distance_from_start_index = math.max(distance_from_start_index, self.max_distance_from_start_index); | |
| 383 | if (!got_result_entry) { | |
| 384 | result.new_entry = entry; | |
| 385 | } | |
| 386 | entry.* = Entry{ | |
| 387 | .used = true, | |
| 388 | .distance_from_start_index = distance_from_start_index, | |
| 389 | .kv = KV{ | |
| 390 | .key = key, | |
| 391 | .value = value, | |
| 392 | }, | |
| 393 | }; | |
| 394 | return result; | |
| 723 | fn insertAllEntriesIntoNewHeader(self: *Self, header: *IndexHeader) void { | |
| 724 | switch (header.capacityIndexType()) { | |
| 725 | .u8 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u8), | |
| 726 | .u16 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u16), | |
| 727 | .u32 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u32), | |
| 728 | .usize => return self.insertAllEntriesIntoNewHeaderGeneric(header, usize), | |
| 395 | 729 | } |
| 396 | unreachable; // put into a full map | |
| 397 | 730 | } |
| 398 | 731 | |
| 399 | fn internalGet(hm: Self, key: K) ?*KV { | |
| 400 | const start_index = hm.keyToIndex(key); | |
| 401 | { | |
| 732 | fn insertAllEntriesIntoNewHeaderGeneric(self: *Self, header: *IndexHeader, comptime I: type) void { | |
| 733 | const indexes = header.indexes(I); | |
| 734 | entry_loop: for (self.entries.items) |entry, i| { | |
| 735 | const h = if (store_hash) entry.hash else hash(entry.key); | |
| 736 | const start_index = header.constrainIndex(h); | |
| 737 | var entry_index = i; | |
| 402 | 738 | var roll_over: usize = 0; |
| 403 | while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { | |
| 404 | const index = hm.constrainIndex(start_index + roll_over); | |
| 405 | const entry = &hm.entries[index]; | |
| 406 | ||
| 407 | if (!entry.used) return null; | |
| 408 | if (eql(entry.kv.key, key)) return &entry.kv; | |
| 739 | var distance_from_start_index: usize = 0; | |
| 740 | while (roll_over < header.indexes_len) : ({ | |
| 741 | roll_over += 1; | |
| 742 | distance_from_start_index += 1; | |
| 743 | }) { | |
| 744 | const index_index = header.constrainIndex(start_index + roll_over); | |
| 745 | const next_index = indexes[index_index]; | |
| 746 | if (next_index.isEmpty()) { | |
| 747 | header.maybeBumpMax(distance_from_start_index); | |
| 748 | indexes[index_index] = .{ | |
| 749 | .distance_from_start_index = @intCast(I, distance_from_start_index), | |
| 750 | .entry_index = @intCast(I, entry_index), | |
| 751 | }; | |
| 752 | continue :entry_loop; | |
| 753 | } | |
| 754 | if (next_index.distance_from_start_index < distance_from_start_index) { | |
| 755 | header.maybeBumpMax(distance_from_start_index); | |
| 756 | indexes[index_index] = .{ | |
| 757 | .distance_from_start_index = @intCast(I, distance_from_start_index), | |
| 758 | .entry_index = @intCast(I, entry_index), | |
| 759 | }; | |
| 760 | distance_from_start_index = next_index.distance_from_start_index; | |
| 761 | entry_index = next_index.entry_index; | |
| 762 | } | |
| 409 | 763 | } |
| 764 | unreachable; | |
| 410 | 765 | } |
| 411 | return null; | |
| 412 | 766 | } |
| 767 | }; | |
| 768 | } | |
| 769 | ||
| 770 | const CapacityIndexType = enum { u8, u16, u32, usize }; | |
| 771 | ||
| 772 | fn capacityIndexType(indexes_len: usize) CapacityIndexType { | |
| 773 | if (indexes_len < math.maxInt(u8)) | |
| 774 | return .u8; | |
| 775 | if (indexes_len < math.maxInt(u16)) | |
| 776 | return .u16; | |
| 777 | if (indexes_len < math.maxInt(u32)) | |
| 778 | return .u32; | |
| 779 | return .usize; | |
| 780 | } | |
| 413 | 781 | |
| 414 | fn keyToIndex(hm: Self, key: K) usize { | |
| 415 | return hm.constrainIndex(@as(usize, hash(key))); | |
| 782 | fn capacityIndexSize(indexes_len: usize) usize { | |
| 783 | switch (capacityIndexType(indexes_len)) { | |
| 784 | .u8 => return @sizeOf(Index(u8)), | |
| 785 | .u16 => return @sizeOf(Index(u16)), | |
| 786 | .u32 => return @sizeOf(Index(u32)), | |
| 787 | .usize => return @sizeOf(Index(usize)), | |
| 788 | } | |
| 789 | } | |
| 790 | ||
| 791 | fn Index(comptime I: type) type { | |
| 792 | return extern struct { | |
| 793 | entry_index: I, | |
| 794 | distance_from_start_index: I, | |
| 795 | ||
| 796 | const Self = @This(); | |
| 797 | ||
| 798 | const empty = Self{ | |
| 799 | .entry_index = math.maxInt(I), | |
| 800 | .distance_from_start_index = undefined, | |
| 801 | }; | |
| 802 | ||
| 803 | fn isEmpty(idx: Self) bool { | |
| 804 | return idx.entry_index == math.maxInt(I); | |
| 416 | 805 | } |
| 417 | 806 | |
| 418 | fn constrainIndex(hm: Self, i: usize) usize { | |
| 419 | // this is an optimization for modulo of power of two integers; | |
| 420 | // it requires hm.entries.len to always be a power of two | |
| 421 | return i & (hm.entries.len - 1); | |
| 807 | fn setEmpty(idx: *Self) void { | |
| 808 | idx.entry_index = math.maxInt(I); | |
| 422 | 809 | } |
| 423 | 810 | }; |
| 424 | 811 | } |
| 425 | 812 | |
| 813 | /// This struct is trailed by an array of `Index(I)`, where `I` | |
| 814 | /// and the array length are determined by `indexes_len`. | |
| 815 | const IndexHeader = struct { | |
| 816 | max_distance_from_start_index: usize, | |
| 817 | indexes_len: usize, | |
| 818 | ||
| 819 | fn constrainIndex(header: IndexHeader, i: usize) usize { | |
| 820 | // This is an optimization for modulo of power of two integers; | |
| 821 | // it requires `indexes_len` to always be a power of two. | |
| 822 | return i & (header.indexes_len - 1); | |
| 823 | } | |
| 824 | ||
| 825 | fn indexes(header: *IndexHeader, comptime I: type) []Index(I) { | |
| 826 | const start = @ptrCast([*]Index(I), @ptrCast([*]u8, header) + @sizeOf(IndexHeader)); | |
| 827 | return start[0..header.indexes_len]; | |
| 828 | } | |
| 829 | ||
| 830 | fn capacityIndexType(header: IndexHeader) CapacityIndexType { | |
| 831 | return hash_map.capacityIndexType(header.indexes_len); | |
| 832 | } | |
| 833 | ||
| 834 | fn maybeBumpMax(header: *IndexHeader, distance_from_start_index: usize) void { | |
| 835 | if (distance_from_start_index > header.max_distance_from_start_index) { | |
| 836 | header.max_distance_from_start_index = distance_from_start_index; | |
| 837 | } | |
| 838 | } | |
| 839 | ||
| 840 | fn alloc(allocator: *Allocator, len: usize) !*IndexHeader { | |
| 841 | const index_size = hash_map.capacityIndexSize(len); | |
| 842 | const nbytes = @sizeOf(IndexHeader) + index_size * len; | |
| 843 | const bytes = try allocator.allocAdvanced(u8, @alignOf(IndexHeader), nbytes, .exact); | |
| 844 | @memset(bytes.ptr + @sizeOf(IndexHeader), 0xff, bytes.len - @sizeOf(IndexHeader)); | |
| 845 | const result = @ptrCast(*IndexHeader, bytes.ptr); | |
| 846 | result.* = .{ | |
| 847 | .max_distance_from_start_index = 0, | |
| 848 | .indexes_len = len, | |
| 849 | }; | |
| 850 | return result; | |
| 851 | } | |
| 852 | ||
| 853 | fn free(header: *IndexHeader, allocator: *Allocator) void { | |
| 854 | const index_size = hash_map.capacityIndexSize(header.indexes_len); | |
| 855 | const ptr = @ptrCast([*]u8, header); | |
| 856 | const slice = ptr[0 .. @sizeOf(IndexHeader) + header.indexes_len * index_size]; | |
| 857 | allocator.free(slice); | |
| 858 | } | |
| 859 | }; | |
| 860 | ||
| 426 | 861 | test "basic hash map usage" { |
| 427 | 862 | var map = AutoHashMap(i32, i32).init(std.testing.allocator); |
| 428 | 863 | defer map.deinit(); |
| 429 | 864 | |
| 430 | testing.expect((try map.put(1, 11)) == null); | |
| 431 | testing.expect((try map.put(2, 22)) == null); | |
| 432 | testing.expect((try map.put(3, 33)) == null); | |
| 433 | testing.expect((try map.put(4, 44)) == null); | |
| 865 | testing.expect((try map.fetchPut(1, 11)) == null); | |
| 866 | testing.expect((try map.fetchPut(2, 22)) == null); | |
| 867 | testing.expect((try map.fetchPut(3, 33)) == null); | |
| 868 | testing.expect((try map.fetchPut(4, 44)) == null); | |
| 434 | 869 | |
| 435 | 870 | try map.putNoClobber(5, 55); |
| 436 | testing.expect((try map.put(5, 66)).?.value == 55); | |
| 437 | testing.expect((try map.put(5, 55)).?.value == 66); | |
| 871 | testing.expect((try map.fetchPut(5, 66)).?.value == 55); | |
| 872 | testing.expect((try map.fetchPut(5, 55)).?.value == 66); | |
| 438 | 873 | |
| 439 | 874 | const gop1 = try map.getOrPut(5); |
| 440 | 875 | testing.expect(gop1.found_existing == true); |
| 441 | testing.expect(gop1.kv.value == 55); | |
| 442 | gop1.kv.value = 77; | |
| 443 | testing.expect(map.get(5).?.value == 77); | |
| 876 | testing.expect(gop1.entry.value == 55); | |
| 877 | gop1.entry.value = 77; | |
| 878 | testing.expect(map.getEntry(5).?.value == 77); | |
| 444 | 879 | |
| 445 | 880 | const gop2 = try map.getOrPut(99); |
| 446 | 881 | testing.expect(gop2.found_existing == false); |
| 447 | gop2.kv.value = 42; | |
| 448 | testing.expect(map.get(99).?.value == 42); | |
| 882 | gop2.entry.value = 42; | |
| 883 | testing.expect(map.getEntry(99).?.value == 42); | |
| 449 | 884 | |
| 450 | 885 | const gop3 = try map.getOrPutValue(5, 5); |
| 451 | 886 | testing.expect(gop3.value == 77); |
| ... | ... | @@ -454,15 +889,15 @@ test "basic hash map usage" { |
| 454 | 889 | testing.expect(gop4.value == 41); |
| 455 | 890 | |
| 456 | 891 | testing.expect(map.contains(2)); |
| 457 | testing.expect(map.get(2).?.value == 22); | |
| 458 | testing.expect(map.getValue(2).? == 22); | |
| 892 | testing.expect(map.getEntry(2).?.value == 22); | |
| 893 | testing.expect(map.get(2).? == 22); | |
| 459 | 894 | |
| 460 | 895 | const rmv1 = map.remove(2); |
| 461 | 896 | testing.expect(rmv1.?.key == 2); |
| 462 | 897 | testing.expect(rmv1.?.value == 22); |
| 463 | 898 | testing.expect(map.remove(2) == null); |
| 899 | testing.expect(map.getEntry(2) == null); | |
| 464 | 900 | testing.expect(map.get(2) == null); |
| 465 | testing.expect(map.getValue(2) == null); | |
| 466 | 901 | |
| 467 | 902 | map.removeAssertDiscard(3); |
| 468 | 903 | } |
| ... | ... | @@ -498,8 +933,8 @@ test "iterator hash map" { |
| 498 | 933 | it.reset(); |
| 499 | 934 | |
| 500 | 935 | var count: usize = 0; |
| 501 | while (it.next()) |kv| : (count += 1) { | |
| 502 | buffer[@intCast(usize, kv.key)] = kv.value; | |
| 936 | while (it.next()) |entry| : (count += 1) { | |
| 937 | buffer[@intCast(usize, entry.key)] = entry.value; | |
| 503 | 938 | } |
| 504 | 939 | testing.expect(count == 3); |
| 505 | 940 | testing.expect(it.next() == null); |
| ... | ... | @@ -510,8 +945,8 @@ test "iterator hash map" { |
| 510 | 945 | |
| 511 | 946 | it.reset(); |
| 512 | 947 | count = 0; |
| 513 | while (it.next()) |kv| { | |
| 514 | buffer[@intCast(usize, kv.key)] = kv.value; | |
| 948 | while (it.next()) |entry| { | |
| 949 | buffer[@intCast(usize, entry.key)] = entry.value; | |
| 515 | 950 | count += 1; |
| 516 | 951 | if (count >= 2) break; |
| 517 | 952 | } |
| ... | ... | @@ -531,14 +966,14 @@ test "ensure capacity" { |
| 531 | 966 | defer map.deinit(); |
| 532 | 967 | |
| 533 | 968 | try map.ensureCapacity(20); |
| 534 | const initialCapacity = map.entries.len; | |
| 535 | testing.expect(initialCapacity >= 20); | |
| 969 | const initial_capacity = map.capacity(); | |
| 970 | testing.expect(initial_capacity >= 20); | |
| 536 | 971 | var i: i32 = 0; |
| 537 | 972 | while (i < 20) : (i += 1) { |
| 538 | testing.expect(map.putAssumeCapacity(i, i + 10) == null); | |
| 973 | testing.expect(map.fetchPutAssumeCapacity(i, i + 10) == null); | |
| 539 | 974 | } |
| 540 | 975 | // shouldn't resize from putAssumeCapacity |
| 541 | testing.expect(initialCapacity == map.entries.len); | |
| 976 | testing.expect(initial_capacity == map.capacity()); | |
| 542 | 977 | } |
| 543 | 978 | |
| 544 | 979 | pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) { |
| ... | ... | @@ -575,6 +1010,24 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { |
| 575 | 1010 | }.eql; |
| 576 | 1011 | } |
| 577 | 1012 | |
| 1013 | pub fn autoEqlIsCheap(comptime K: type) bool { | |
| 1014 | return switch (@typeInfo(K)) { | |
| 1015 | .Bool, | |
| 1016 | .Int, | |
| 1017 | .Float, | |
| 1018 | .Pointer, | |
| 1019 | .ComptimeFloat, | |
| 1020 | .ComptimeInt, | |
| 1021 | .Enum, | |
| 1022 | .Fn, | |
| 1023 | .ErrorSet, | |
| 1024 | .AnyFrame, | |
| 1025 | .EnumLiteral, | |
| 1026 | => true, | |
| 1027 | else => false, | |
| 1028 | }; | |
| 1029 | } | |
| 1030 | ||
| 578 | 1031 | pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) { |
| 579 | 1032 | return struct { |
| 580 | 1033 | fn hash(key: K) u32 { |
lib/std/http/headers.zig+35-37| ... | ... | @@ -118,13 +118,12 @@ pub const Headers = struct { |
| 118 | 118 | }; |
| 119 | 119 | } |
| 120 | 120 | |
| 121 | pub fn deinit(self: Self) void { | |
| 121 | pub fn deinit(self: *Self) void { | |
| 122 | 122 | { |
| 123 | var it = self.index.iterator(); | |
| 124 | while (it.next()) |kv| { | |
| 125 | var dex = &kv.value; | |
| 123 | for (self.index.items()) |*entry| { | |
| 124 | const dex = &entry.value; | |
| 126 | 125 | dex.deinit(); |
| 127 | self.allocator.free(kv.key); | |
| 126 | self.allocator.free(entry.key); | |
| 128 | 127 | } |
| 129 | 128 | self.index.deinit(); |
| 130 | 129 | } |
| ... | ... | @@ -134,6 +133,7 @@ pub const Headers = struct { |
| 134 | 133 | } |
| 135 | 134 | self.data.deinit(); |
| 136 | 135 | } |
| 136 | self.* = undefined; | |
| 137 | 137 | } |
| 138 | 138 | |
| 139 | 139 | pub fn clone(self: Self, allocator: *Allocator) !Self { |
| ... | ... | @@ -155,10 +155,10 @@ pub const Headers = struct { |
| 155 | 155 | const n = self.data.items.len + 1; |
| 156 | 156 | try self.data.ensureCapacity(n); |
| 157 | 157 | var entry: HeaderEntry = undefined; |
| 158 | if (self.index.get(name)) |kv| { | |
| 158 | if (self.index.getEntry(name)) |kv| { | |
| 159 | 159 | entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index); |
| 160 | 160 | errdefer entry.deinit(); |
| 161 | var dex = &kv.value; | |
| 161 | const dex = &kv.value; | |
| 162 | 162 | try dex.append(n - 1); |
| 163 | 163 | } else { |
| 164 | 164 | const name_dup = try self.allocator.dupe(u8, name); |
| ... | ... | @@ -195,7 +195,7 @@ pub const Headers = struct { |
| 195 | 195 | /// Returns boolean indicating if something was deleted. |
| 196 | 196 | pub fn delete(self: *Self, name: []const u8) bool { |
| 197 | 197 | if (self.index.remove(name)) |kv| { |
| 198 | var dex = &kv.value; | |
| 198 | const dex = &kv.value; | |
| 199 | 199 | // iterate backwards |
| 200 | 200 | var i = dex.items.len; |
| 201 | 201 | while (i > 0) { |
| ... | ... | @@ -207,7 +207,7 @@ pub const Headers = struct { |
| 207 | 207 | } |
| 208 | 208 | dex.deinit(); |
| 209 | 209 | self.allocator.free(kv.key); |
| 210 | self.rebuild_index(); | |
| 210 | self.rebuildIndex(); | |
| 211 | 211 | return true; |
| 212 | 212 | } else { |
| 213 | 213 | return false; |
| ... | ... | @@ -216,45 +216,52 @@ pub const Headers = struct { |
| 216 | 216 | |
| 217 | 217 | /// Removes the element at the specified index. |
| 218 | 218 | /// Moves items down to fill the empty space. |
| 219 | /// TODO this implementation can be replaced by adding | |
| 220 | /// orderedRemove to the new hash table implementation as an | |
| 221 | /// alternative to swapRemove. | |
| 219 | 222 | pub fn orderedRemove(self: *Self, i: usize) void { |
| 220 | 223 | const removed = self.data.orderedRemove(i); |
| 221 | const kv = self.index.get(removed.name).?; | |
| 222 | var dex = &kv.value; | |
| 224 | const kv = self.index.getEntry(removed.name).?; | |
| 225 | const dex = &kv.value; | |
| 223 | 226 | if (dex.items.len == 1) { |
| 224 | 227 | // was last item; delete the index |
| 225 | _ = self.index.remove(kv.key); | |
| 226 | 228 | dex.deinit(); |
| 227 | 229 | removed.deinit(); |
| 228 | self.allocator.free(kv.key); | |
| 230 | const key = kv.key; | |
| 231 | _ = self.index.remove(key); // invalidates `kv` and `dex` | |
| 232 | self.allocator.free(key); | |
| 229 | 233 | } else { |
| 230 | 234 | dex.shrink(dex.items.len - 1); |
| 231 | 235 | removed.deinit(); |
| 232 | 236 | } |
| 233 | 237 | // if it was the last item; no need to rebuild index |
| 234 | 238 | if (i != self.data.items.len) { |
| 235 | self.rebuild_index(); | |
| 239 | self.rebuildIndex(); | |
| 236 | 240 | } |
| 237 | 241 | } |
| 238 | 242 | |
| 239 | 243 | /// Removes the element at the specified index. |
| 240 | 244 | /// The empty slot is filled from the end of the list. |
| 245 | /// TODO this implementation can be replaced by simply using the | |
| 246 | /// new hash table which does swap removal. | |
| 241 | 247 | pub fn swapRemove(self: *Self, i: usize) void { |
| 242 | 248 | const removed = self.data.swapRemove(i); |
| 243 | const kv = self.index.get(removed.name).?; | |
| 244 | var dex = &kv.value; | |
| 249 | const kv = self.index.getEntry(removed.name).?; | |
| 250 | const dex = &kv.value; | |
| 245 | 251 | if (dex.items.len == 1) { |
| 246 | 252 | // was last item; delete the index |
| 247 | _ = self.index.remove(kv.key); | |
| 248 | 253 | dex.deinit(); |
| 249 | 254 | removed.deinit(); |
| 250 | self.allocator.free(kv.key); | |
| 255 | const key = kv.key; | |
| 256 | _ = self.index.remove(key); // invalidates `kv` and `dex` | |
| 257 | self.allocator.free(key); | |
| 251 | 258 | } else { |
| 252 | 259 | dex.shrink(dex.items.len - 1); |
| 253 | 260 | removed.deinit(); |
| 254 | 261 | } |
| 255 | 262 | // if it was the last item; no need to rebuild index |
| 256 | 263 | if (i != self.data.items.len) { |
| 257 | self.rebuild_index(); | |
| 264 | self.rebuildIndex(); | |
| 258 | 265 | } |
| 259 | 266 | } |
| 260 | 267 | |
| ... | ... | @@ -266,11 +273,7 @@ pub const Headers = struct { |
| 266 | 273 | /// Returns a list of indices containing headers with the given name. |
| 267 | 274 | /// The returned list should not be modified by the caller. |
| 268 | 275 | pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList { |
| 269 | if (self.index.get(name)) |kv| { | |
| 270 | return kv.value; | |
| 271 | } else { | |
| 272 | return null; | |
| 273 | } | |
| 276 | return self.index.get(name); | |
| 274 | 277 | } |
| 275 | 278 | |
| 276 | 279 | /// Returns a slice containing each header with the given name. |
| ... | ... | @@ -325,25 +328,20 @@ pub const Headers = struct { |
| 325 | 328 | return buf; |
| 326 | 329 | } |
| 327 | 330 | |
| 328 | fn rebuild_index(self: *Self) void { | |
| 329 | { // clear out the indexes | |
| 330 | var it = self.index.iterator(); | |
| 331 | while (it.next()) |kv| { | |
| 332 | var dex = &kv.value; | |
| 333 | dex.items.len = 0; // keeps capacity available | |
| 334 | } | |
| 331 | fn rebuildIndex(self: *Self) void { | |
| 332 | // clear out the indexes | |
| 333 | for (self.index.items()) |*entry| { | |
| 334 | entry.value.shrinkRetainingCapacity(0); | |
| 335 | 335 | } |
| 336 | { // fill up indexes again; we know capacity is fine from before | |
| 337 | for (self.data.span()) |entry, i| { | |
| 338 | var dex = &self.index.get(entry.name).?.value; | |
| 339 | dex.appendAssumeCapacity(i); | |
| 340 | } | |
| 336 | // fill up indexes again; we know capacity is fine from before | |
| 337 | for (self.data.items) |entry, i| { | |
| 338 | self.index.getEntry(entry.name).?.value.appendAssumeCapacity(i); | |
| 341 | 339 | } |
| 342 | 340 | } |
| 343 | 341 | |
| 344 | 342 | pub fn sort(self: *Self) void { |
| 345 | 343 | std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare); |
| 346 | self.rebuild_index(); | |
| 344 | self.rebuildIndex(); | |
| 347 | 345 | } |
| 348 | 346 | |
| 349 | 347 | pub fn format( |
lib/std/json.zig+28-28| ... | ... | @@ -2149,27 +2149,27 @@ test "json.parser.dynamic" { |
| 2149 | 2149 | |
| 2150 | 2150 | var root = tree.root; |
| 2151 | 2151 | |
| 2152 | var image = root.Object.get("Image").?.value; | |
| 2152 | var image = root.Object.get("Image").?; | |
| 2153 | 2153 | |
| 2154 | const width = image.Object.get("Width").?.value; | |
| 2154 | const width = image.Object.get("Width").?; | |
| 2155 | 2155 | testing.expect(width.Integer == 800); |
| 2156 | 2156 | |
| 2157 | const height = image.Object.get("Height").?.value; | |
| 2157 | const height = image.Object.get("Height").?; | |
| 2158 | 2158 | testing.expect(height.Integer == 600); |
| 2159 | 2159 | |
| 2160 | const title = image.Object.get("Title").?.value; | |
| 2160 | const title = image.Object.get("Title").?; | |
| 2161 | 2161 | testing.expect(mem.eql(u8, title.String, "View from 15th Floor")); |
| 2162 | 2162 | |
| 2163 | const animated = image.Object.get("Animated").?.value; | |
| 2163 | const animated = image.Object.get("Animated").?; | |
| 2164 | 2164 | testing.expect(animated.Bool == false); |
| 2165 | 2165 | |
| 2166 | const array_of_object = image.Object.get("ArrayOfObject").?.value; | |
| 2166 | const array_of_object = image.Object.get("ArrayOfObject").?; | |
| 2167 | 2167 | testing.expect(array_of_object.Array.items.len == 1); |
| 2168 | 2168 | |
| 2169 | const obj0 = array_of_object.Array.items[0].Object.get("n").?.value; | |
| 2169 | const obj0 = array_of_object.Array.items[0].Object.get("n").?; | |
| 2170 | 2170 | testing.expect(mem.eql(u8, obj0.String, "m")); |
| 2171 | 2171 | |
| 2172 | const double = image.Object.get("double").?.value; | |
| 2172 | const double = image.Object.get("double").?; | |
| 2173 | 2173 | testing.expect(double.Float == 1.3412); |
| 2174 | 2174 | } |
| 2175 | 2175 | |
| ... | ... | @@ -2217,12 +2217,12 @@ test "write json then parse it" { |
| 2217 | 2217 | var tree = try parser.parse(fixed_buffer_stream.getWritten()); |
| 2218 | 2218 | defer tree.deinit(); |
| 2219 | 2219 | |
| 2220 | testing.expect(tree.root.Object.get("f").?.value.Bool == false); | |
| 2221 | testing.expect(tree.root.Object.get("t").?.value.Bool == true); | |
| 2222 | testing.expect(tree.root.Object.get("int").?.value.Integer == 1234); | |
| 2223 | testing.expect(tree.root.Object.get("array").?.value.Array.items[0].Null == {}); | |
| 2224 | testing.expect(tree.root.Object.get("array").?.value.Array.items[1].Float == 12.34); | |
| 2225 | testing.expect(mem.eql(u8, tree.root.Object.get("str").?.value.String, "hello")); | |
| 2220 | testing.expect(tree.root.Object.get("f").?.Bool == false); | |
| 2221 | testing.expect(tree.root.Object.get("t").?.Bool == true); | |
| 2222 | testing.expect(tree.root.Object.get("int").?.Integer == 1234); | |
| 2223 | testing.expect(tree.root.Object.get("array").?.Array.items[0].Null == {}); | |
| 2224 | testing.expect(tree.root.Object.get("array").?.Array.items[1].Float == 12.34); | |
| 2225 | testing.expect(mem.eql(u8, tree.root.Object.get("str").?.String, "hello")); | |
| 2226 | 2226 | } |
| 2227 | 2227 | |
| 2228 | 2228 | fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value { |
| ... | ... | @@ -2245,7 +2245,7 @@ test "integer after float has proper type" { |
| 2245 | 2245 | \\ "ints": [1, 2, 3] |
| 2246 | 2246 | \\} |
| 2247 | 2247 | ); |
| 2248 | std.testing.expect(json.Object.getValue("ints").?.Array.items[0] == .Integer); | |
| 2248 | std.testing.expect(json.Object.get("ints").?.Array.items[0] == .Integer); | |
| 2249 | 2249 | } |
| 2250 | 2250 | |
| 2251 | 2251 | test "escaped characters" { |
| ... | ... | @@ -2271,16 +2271,16 @@ test "escaped characters" { |
| 2271 | 2271 | |
| 2272 | 2272 | const obj = (try test_parse(&arena_allocator.allocator, input)).Object; |
| 2273 | 2273 | |
| 2274 | testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\"); | |
| 2275 | testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/"); | |
| 2276 | testing.expectEqualSlices(u8, obj.get("newline").?.value.String, "\n"); | |
| 2277 | testing.expectEqualSlices(u8, obj.get("carriagereturn").?.value.String, "\r"); | |
| 2278 | testing.expectEqualSlices(u8, obj.get("tab").?.value.String, "\t"); | |
| 2279 | testing.expectEqualSlices(u8, obj.get("formfeed").?.value.String, "\x0C"); | |
| 2280 | testing.expectEqualSlices(u8, obj.get("backspace").?.value.String, "\x08"); | |
| 2281 | testing.expectEqualSlices(u8, obj.get("doublequote").?.value.String, "\""); | |
| 2282 | testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ą"); | |
| 2283 | testing.expectEqualSlices(u8, obj.get("surrogatepair").?.value.String, "😂"); | |
| 2274 | testing.expectEqualSlices(u8, obj.get("backslash").?.String, "\\"); | |
| 2275 | testing.expectEqualSlices(u8, obj.get("forwardslash").?.String, "/"); | |
| 2276 | testing.expectEqualSlices(u8, obj.get("newline").?.String, "\n"); | |
| 2277 | testing.expectEqualSlices(u8, obj.get("carriagereturn").?.String, "\r"); | |
| 2278 | testing.expectEqualSlices(u8, obj.get("tab").?.String, "\t"); | |
| 2279 | testing.expectEqualSlices(u8, obj.get("formfeed").?.String, "\x0C"); | |
| 2280 | testing.expectEqualSlices(u8, obj.get("backspace").?.String, "\x08"); | |
| 2281 | testing.expectEqualSlices(u8, obj.get("doublequote").?.String, "\""); | |
| 2282 | testing.expectEqualSlices(u8, obj.get("unicode").?.String, "ą"); | |
| 2283 | testing.expectEqualSlices(u8, obj.get("surrogatepair").?.String, "😂"); | |
| 2284 | 2284 | } |
| 2285 | 2285 | |
| 2286 | 2286 | test "string copy option" { |
| ... | ... | @@ -2306,11 +2306,11 @@ test "string copy option" { |
| 2306 | 2306 | const obj_copy = tree_copy.root.Object; |
| 2307 | 2307 | |
| 2308 | 2308 | for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| { |
| 2309 | testing.expectEqualSlices(u8, obj_nocopy.getValue(field_name).?.String, obj_copy.getValue(field_name).?.String); | |
| 2309 | testing.expectEqualSlices(u8, obj_nocopy.get(field_name).?.String, obj_copy.get(field_name).?.String); | |
| 2310 | 2310 | } |
| 2311 | 2311 | |
| 2312 | const nocopy_addr = &obj_nocopy.getValue("noescape").?.String[0]; | |
| 2313 | const copy_addr = &obj_copy.getValue("noescape").?.String[0]; | |
| 2312 | const nocopy_addr = &obj_nocopy.get("noescape").?.String[0]; | |
| 2313 | const copy_addr = &obj_copy.get("noescape").?.String[0]; | |
| 2314 | 2314 | |
| 2315 | 2315 | var found_nocopy = false; |
| 2316 | 2316 | for (input) |_, index| { |
src-self-hosted/Module.zig+83-114| ... | ... | @@ -75,7 +75,7 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 75 | 75 | |
| 76 | 76 | keep_source_files_loaded: bool, |
| 77 | 77 | |
| 78 | const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql); | |
| 78 | const DeclTable = std.HashMap(Scope.NameHash, *Decl, Scope.name_hash_hash, Scope.name_hash_eql, false); | |
| 79 | 79 | |
| 80 | 80 | const WorkItem = union(enum) { |
| 81 | 81 | /// Write the machine code for a Decl to the output file. |
| ... | ... | @@ -795,49 +795,38 @@ pub fn deinit(self: *Module) void { |
| 795 | 795 | const allocator = self.allocator; |
| 796 | 796 | self.deletion_set.deinit(allocator); |
| 797 | 797 | self.work_queue.deinit(); |
| 798 | { | |
| 799 | var it = self.decl_table.iterator(); | |
| 800 | while (it.next()) |kv| { | |
| 801 | kv.value.destroy(allocator); | |
| 802 | } | |
| 803 | self.decl_table.deinit(); | |
| 798 | ||
| 799 | for (self.decl_table.items()) |entry| { | |
| 800 | entry.value.destroy(allocator); | |
| 804 | 801 | } |
| 805 | { | |
| 806 | var it = self.failed_decls.iterator(); | |
| 807 | while (it.next()) |kv| { | |
| 808 | kv.value.destroy(allocator); | |
| 809 | } | |
| 810 | self.failed_decls.deinit(); | |
| 802 | self.decl_table.deinit(); | |
| 803 | ||
| 804 | for (self.failed_decls.items()) |entry| { | |
| 805 | entry.value.destroy(allocator); | |
| 811 | 806 | } |
| 812 | { | |
| 813 | var it = self.failed_files.iterator(); | |
| 814 | while (it.next()) |kv| { | |
| 815 | kv.value.destroy(allocator); | |
| 816 | } | |
| 817 | self.failed_files.deinit(); | |
| 807 | self.failed_decls.deinit(); | |
| 808 | ||
| 809 | for (self.failed_files.items()) |entry| { | |
| 810 | entry.value.destroy(allocator); | |
| 818 | 811 | } |
| 819 | { | |
| 820 | var it = self.failed_exports.iterator(); | |
| 821 | while (it.next()) |kv| { | |
| 822 | kv.value.destroy(allocator); | |
| 823 | } | |
| 824 | self.failed_exports.deinit(); | |
| 812 | self.failed_files.deinit(); | |
| 813 | ||
| 814 | for (self.failed_exports.items()) |entry| { | |
| 815 | entry.value.destroy(allocator); | |
| 825 | 816 | } |
| 826 | { | |
| 827 | var it = self.decl_exports.iterator(); | |
| 828 | while (it.next()) |kv| { | |
| 829 | const export_list = kv.value; | |
| 830 | allocator.free(export_list); | |
| 831 | } | |
| 832 | self.decl_exports.deinit(); | |
| 817 | self.failed_exports.deinit(); | |
| 818 | ||
| 819 | for (self.decl_exports.items()) |entry| { | |
| 820 | const export_list = entry.value; | |
| 821 | allocator.free(export_list); | |
| 833 | 822 | } |
| 834 | { | |
| 835 | var it = self.export_owners.iterator(); | |
| 836 | while (it.next()) |kv| { | |
| 837 | freeExportList(allocator, kv.value); | |
| 838 | } | |
| 839 | self.export_owners.deinit(); | |
| 823 | self.decl_exports.deinit(); | |
| 824 | ||
| 825 | for (self.export_owners.items()) |entry| { | |
| 826 | freeExportList(allocator, entry.value); | |
| 840 | 827 | } |
| 828 | self.export_owners.deinit(); | |
| 829 | ||
| 841 | 830 | self.symbol_exports.deinit(); |
| 842 | 831 | self.root_scope.destroy(allocator); |
| 843 | 832 | self.* = undefined; |
| ... | ... | @@ -918,9 +907,9 @@ pub fn makeBinFileWritable(self: *Module) !void { |
| 918 | 907 | } |
| 919 | 908 | |
| 920 | 909 | pub fn totalErrorCount(self: *Module) usize { |
| 921 | const total = self.failed_decls.size + | |
| 922 | self.failed_files.size + | |
| 923 | self.failed_exports.size; | |
| 910 | const total = self.failed_decls.items().len + | |
| 911 | self.failed_files.items().len + | |
| 912 | self.failed_exports.items().len; | |
| 924 | 913 | return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total; |
| 925 | 914 | } |
| 926 | 915 | |
| ... | ... | @@ -931,32 +920,23 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 931 | 920 | var errors = std.ArrayList(AllErrors.Message).init(self.allocator); |
| 932 | 921 | defer errors.deinit(); |
| 933 | 922 | |
| 934 | { | |
| 935 | var it = self.failed_files.iterator(); | |
| 936 | while (it.next()) |kv| { | |
| 937 | const scope = kv.key; | |
| 938 | const err_msg = kv.value; | |
| 939 | const source = try scope.getSource(self); | |
| 940 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); | |
| 941 | } | |
| 923 | for (self.failed_files.items()) |entry| { | |
| 924 | const scope = entry.key; | |
| 925 | const err_msg = entry.value; | |
| 926 | const source = try scope.getSource(self); | |
| 927 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); | |
| 942 | 928 | } |
| 943 | { | |
| 944 | var it = self.failed_decls.iterator(); | |
| 945 | while (it.next()) |kv| { | |
| 946 | const decl = kv.key; | |
| 947 | const err_msg = kv.value; | |
| 948 | const source = try decl.scope.getSource(self); | |
| 949 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 950 | } | |
| 929 | for (self.failed_decls.items()) |entry| { | |
| 930 | const decl = entry.key; | |
| 931 | const err_msg = entry.value; | |
| 932 | const source = try decl.scope.getSource(self); | |
| 933 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 951 | 934 | } |
| 952 | { | |
| 953 | var it = self.failed_exports.iterator(); | |
| 954 | while (it.next()) |kv| { | |
| 955 | const decl = kv.key.owner_decl; | |
| 956 | const err_msg = kv.value; | |
| 957 | const source = try decl.scope.getSource(self); | |
| 958 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 959 | } | |
| 935 | for (self.failed_exports.items()) |entry| { | |
| 936 | const decl = entry.key.owner_decl; | |
| 937 | const err_msg = entry.value; | |
| 938 | const source = try decl.scope.getSource(self); | |
| 939 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 960 | 940 | } |
| 961 | 941 | |
| 962 | 942 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { |
| ... | ... | @@ -1016,7 +996,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1016 | 996 | decl.analysis = .dependency_failure; |
| 1017 | 997 | }, |
| 1018 | 998 | else => { |
| 1019 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | |
| 999 | try self.failed_decls.ensureCapacity(self.failed_decls.items().len + 1); | |
| 1020 | 1000 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1021 | 1001 | self.allocator, |
| 1022 | 1002 | decl.src(), |
| ... | ... | @@ -1086,7 +1066,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 1086 | 1066 | error.OutOfMemory => return error.OutOfMemory, |
| 1087 | 1067 | error.AnalysisFail => return error.AnalysisFail, |
| 1088 | 1068 | else => { |
| 1089 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | |
| 1069 | try self.failed_decls.ensureCapacity(self.failed_decls.items().len + 1); | |
| 1090 | 1070 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1091 | 1071 | self.allocator, |
| 1092 | 1072 | decl.src(), |
| ... | ... | @@ -1636,7 +1616,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void |
| 1636 | 1616 | fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 1637 | 1617 | switch (root_scope.status) { |
| 1638 | 1618 | .never_loaded, .unloaded_success => { |
| 1639 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); | |
| 1619 | try self.failed_files.ensureCapacity(self.failed_files.items().len + 1); | |
| 1640 | 1620 | |
| 1641 | 1621 | const source = try root_scope.getSource(self); |
| 1642 | 1622 | |
| ... | ... | @@ -1677,7 +1657,7 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1677 | 1657 | |
| 1678 | 1658 | switch (root_scope.status) { |
| 1679 | 1659 | .never_loaded, .unloaded_success => { |
| 1680 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); | |
| 1660 | try self.failed_files.ensureCapacity(self.failed_files.items().len + 1); | |
| 1681 | 1661 | |
| 1682 | 1662 | const source = try root_scope.getSource(self); |
| 1683 | 1663 | |
| ... | ... | @@ -1745,8 +1725,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1745 | 1725 | const name = tree.tokenSliceLoc(name_loc); |
| 1746 | 1726 | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| 1747 | 1727 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| 1748 | if (self.decl_table.get(name_hash)) |kv| { | |
| 1749 | const decl = kv.value; | |
| 1728 | if (self.decl_table.get(name_hash)) |decl| { | |
| 1750 | 1729 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| 1751 | 1730 | // have been re-ordered. |
| 1752 | 1731 | decl.src_index = decl_i; |
| ... | ... | @@ -1774,14 +1753,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1774 | 1753 | // TODO also look for global variable declarations |
| 1775 | 1754 | // TODO also look for comptime blocks and exported globals |
| 1776 | 1755 | } |
| 1777 | { | |
| 1778 | // Handle explicitly deleted decls from the source code. Not to be confused | |
| 1779 | // with when we delete decls because they are no longer referenced. | |
| 1780 | var it = deleted_decls.iterator(); | |
| 1781 | while (it.next()) |kv| { | |
| 1782 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | |
| 1783 | try self.deleteDecl(kv.key); | |
| 1784 | } | |
| 1756 | // Handle explicitly deleted decls from the source code. Not to be confused | |
| 1757 | // with when we delete decls because they are no longer referenced. | |
| 1758 | for (deleted_decls.items()) |entry| { | |
| 1759 | //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name}); | |
| 1760 | try self.deleteDecl(entry.key); | |
| 1785 | 1761 | } |
| 1786 | 1762 | } |
| 1787 | 1763 | |
| ... | ... | @@ -1800,18 +1776,14 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1800 | 1776 | // we know which ones have been deleted. |
| 1801 | 1777 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); |
| 1802 | 1778 | defer deleted_decls.deinit(); |
| 1803 | try deleted_decls.ensureCapacity(self.decl_table.size); | |
| 1804 | { | |
| 1805 | var it = self.decl_table.iterator(); | |
| 1806 | while (it.next()) |kv| { | |
| 1807 | deleted_decls.putAssumeCapacityNoClobber(kv.value, {}); | |
| 1808 | } | |
| 1779 | try deleted_decls.ensureCapacity(self.decl_table.items().len); | |
| 1780 | for (self.decl_table.items()) |entry| { | |
| 1781 | deleted_decls.putAssumeCapacityNoClobber(entry.value, {}); | |
| 1809 | 1782 | } |
| 1810 | 1783 | |
| 1811 | 1784 | for (src_module.decls) |src_decl, decl_i| { |
| 1812 | 1785 | const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name); |
| 1813 | if (self.decl_table.get(name_hash)) |kv| { | |
| 1814 | const decl = kv.value; | |
| 1786 | if (self.decl_table.get(name_hash)) |decl| { | |
| 1815 | 1787 | deleted_decls.removeAssertDiscard(decl); |
| 1816 | 1788 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); |
| 1817 | 1789 | if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { |
| ... | ... | @@ -1835,14 +1807,11 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1835 | 1807 | for (exports_to_resolve.items) |export_decl| { |
| 1836 | 1808 | _ = try self.resolveZirDecl(&root_scope.base, export_decl); |
| 1837 | 1809 | } |
| 1838 | { | |
| 1839 | // Handle explicitly deleted decls from the source code. Not to be confused | |
| 1840 | // with when we delete decls because they are no longer referenced. | |
| 1841 | var it = deleted_decls.iterator(); | |
| 1842 | while (it.next()) |kv| { | |
| 1843 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | |
| 1844 | try self.deleteDecl(kv.key); | |
| 1845 | } | |
| 1810 | // Handle explicitly deleted decls from the source code. Not to be confused | |
| 1811 | // with when we delete decls because they are no longer referenced. | |
| 1812 | for (deleted_decls.items()) |entry| { | |
| 1813 | //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name}); | |
| 1814 | try self.deleteDecl(entry.key); | |
| 1846 | 1815 | } |
| 1847 | 1816 | } |
| 1848 | 1817 | |
| ... | ... | @@ -1888,7 +1857,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 1888 | 1857 | const kv = self.export_owners.remove(decl) orelse return; |
| 1889 | 1858 | |
| 1890 | 1859 | for (kv.value) |exp| { |
| 1891 | if (self.decl_exports.get(exp.exported_decl)) |decl_exports_kv| { | |
| 1860 | if (self.decl_exports.getEntry(exp.exported_decl)) |decl_exports_kv| { | |
| 1892 | 1861 | // Remove exports with owner_decl matching the regenerating decl. |
| 1893 | 1862 | const list = decl_exports_kv.value; |
| 1894 | 1863 | var i: usize = 0; |
| ... | ... | @@ -1983,7 +1952,7 @@ fn createNewDecl( |
| 1983 | 1952 | name_hash: Scope.NameHash, |
| 1984 | 1953 | contents_hash: std.zig.SrcHash, |
| 1985 | 1954 | ) !*Decl { |
| 1986 | try self.decl_table.ensureCapacity(self.decl_table.size + 1); | |
| 1955 | try self.decl_table.ensureCapacity(self.decl_table.items().len + 1); | |
| 1987 | 1956 | const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash); |
| 1988 | 1957 | errdefer self.allocator.destroy(new_decl); |
| 1989 | 1958 | new_decl.name = try mem.dupeZ(self.allocator, u8, decl_name); |
| ... | ... | @@ -2043,7 +2012,7 @@ fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError! |
| 2043 | 2012 | |
| 2044 | 2013 | fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { |
| 2045 | 2014 | const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name); |
| 2046 | const decl = self.decl_table.getValue(name_hash).?; | |
| 2015 | const decl = self.decl_table.get(name_hash).?; | |
| 2047 | 2016 | decl.src_index = src_index; |
| 2048 | 2017 | try self.ensureDeclAnalyzed(decl); |
| 2049 | 2018 | return decl; |
| ... | ... | @@ -2148,8 +2117,8 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2148 | 2117 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), |
| 2149 | 2118 | } |
| 2150 | 2119 | |
| 2151 | try self.decl_exports.ensureCapacity(self.decl_exports.size + 1); | |
| 2152 | try self.export_owners.ensureCapacity(self.export_owners.size + 1); | |
| 2120 | try self.decl_exports.ensureCapacity(self.decl_exports.items().len + 1); | |
| 2121 | try self.export_owners.ensureCapacity(self.export_owners.items().len + 1); | |
| 2153 | 2122 | |
| 2154 | 2123 | const new_export = try self.allocator.create(Export); |
| 2155 | 2124 | errdefer self.allocator.destroy(new_export); |
| ... | ... | @@ -2168,23 +2137,23 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2168 | 2137 | // Add to export_owners table. |
| 2169 | 2138 | const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable; |
| 2170 | 2139 | if (!eo_gop.found_existing) { |
| 2171 | eo_gop.kv.value = &[0]*Export{}; | |
| 2140 | eo_gop.entry.value = &[0]*Export{}; | |
| 2172 | 2141 | } |
| 2173 | eo_gop.kv.value = try self.allocator.realloc(eo_gop.kv.value, eo_gop.kv.value.len + 1); | |
| 2174 | eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export; | |
| 2175 | errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1); | |
| 2142 | eo_gop.entry.value = try self.allocator.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1); | |
| 2143 | eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export; | |
| 2144 | errdefer eo_gop.entry.value = self.allocator.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1); | |
| 2176 | 2145 | |
| 2177 | 2146 | // Add to exported_decl table. |
| 2178 | 2147 | const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable; |
| 2179 | 2148 | if (!de_gop.found_existing) { |
| 2180 | de_gop.kv.value = &[0]*Export{}; | |
| 2149 | de_gop.entry.value = &[0]*Export{}; | |
| 2181 | 2150 | } |
| 2182 | de_gop.kv.value = try self.allocator.realloc(de_gop.kv.value, de_gop.kv.value.len + 1); | |
| 2183 | de_gop.kv.value[de_gop.kv.value.len - 1] = new_export; | |
| 2184 | errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1); | |
| 2151 | de_gop.entry.value = try self.allocator.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); | |
| 2152 | de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; | |
| 2153 | errdefer de_gop.entry.value = self.allocator.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); | |
| 2185 | 2154 | |
| 2186 | 2155 | if (self.symbol_exports.get(symbol_name)) |_| { |
| 2187 | try self.failed_exports.ensureCapacity(self.failed_exports.size + 1); | |
| 2156 | try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1); | |
| 2188 | 2157 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( |
| 2189 | 2158 | self.allocator, |
| 2190 | 2159 | src, |
| ... | ... | @@ -2197,10 +2166,10 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2197 | 2166 | } |
| 2198 | 2167 | |
| 2199 | 2168 | try self.symbol_exports.putNoClobber(symbol_name, new_export); |
| 2200 | self.bin_file.updateDeclExports(self, exported_decl, de_gop.kv.value) catch |err| switch (err) { | |
| 2169 | self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { | |
| 2201 | 2170 | error.OutOfMemory => return error.OutOfMemory, |
| 2202 | 2171 | else => { |
| 2203 | try self.failed_exports.ensureCapacity(self.failed_exports.size + 1); | |
| 2172 | try self.failed_exports.ensureCapacity(self.failed_exports.items().len + 1); | |
| 2204 | 2173 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( |
| 2205 | 2174 | self.allocator, |
| 2206 | 2175 | src, |
| ... | ... | @@ -2494,7 +2463,7 @@ fn getNextAnonNameIndex(self: *Module) usize { |
| 2494 | 2463 | fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { |
| 2495 | 2464 | const namespace = scope.namespace(); |
| 2496 | 2465 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); |
| 2497 | return self.decl_table.getValue(name_hash); | |
| 2466 | return self.decl_table.get(name_hash); | |
| 2498 | 2467 | } |
| 2499 | 2468 | |
| 2500 | 2469 | fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { |
| ... | ... | @@ -3489,8 +3458,8 @@ fn failNode( |
| 3489 | 3458 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { |
| 3490 | 3459 | { |
| 3491 | 3460 | errdefer err_msg.destroy(self.allocator); |
| 3492 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | |
| 3493 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); | |
| 3461 | try self.failed_decls.ensureCapacity(self.failed_decls.items().len + 1); | |
| 3462 | try self.failed_files.ensureCapacity(self.failed_files.items().len + 1); | |
| 3494 | 3463 | } |
| 3495 | 3464 | switch (scope.tag) { |
| 3496 | 3465 | .decl => { |
src-self-hosted/codegen.zig+2-2| ... | ... | @@ -705,7 +705,7 @@ const Function = struct { |
| 705 | 705 | } |
| 706 | 706 | |
| 707 | 707 | fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue { |
| 708 | if (self.inst_table.getValue(inst)) |mcv| { | |
| 708 | if (self.inst_table.get(inst)) |mcv| { | |
| 709 | 709 | return mcv; |
| 710 | 710 | } |
| 711 | 711 | if (inst.cast(ir.Inst.Constant)) |const_inst| { |
| ... | ... | @@ -713,7 +713,7 @@ const Function = struct { |
| 713 | 713 | try self.inst_table.putNoClobber(inst, mcvalue); |
| 714 | 714 | return mcvalue; |
| 715 | 715 | } else { |
| 716 | return self.inst_table.getValue(inst).?; | |
| 716 | return self.inst_table.get(inst).?; | |
| 717 | 717 | } |
| 718 | 718 | } |
| 719 | 719 |
src-self-hosted/link.zig+3-3| ... | ... | @@ -1071,7 +1071,7 @@ pub const ElfFile = struct { |
| 1071 | 1071 | try self.file.?.pwriteAll(code, file_offset); |
| 1072 | 1072 | |
| 1073 | 1073 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. |
| 1074 | const decl_exports = module.decl_exports.getValue(decl) orelse &[0]*Module.Export{}; | |
| 1074 | const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; | |
| 1075 | 1075 | return self.updateDeclExports(module, decl, decl_exports); |
| 1076 | 1076 | } |
| 1077 | 1077 | |
| ... | ... | @@ -1093,7 +1093,7 @@ pub const ElfFile = struct { |
| 1093 | 1093 | for (exports) |exp| { |
| 1094 | 1094 | if (exp.options.section) |section_name| { |
| 1095 | 1095 | if (!mem.eql(u8, section_name, ".text")) { |
| 1096 | try module.failed_exports.ensureCapacity(module.failed_exports.size + 1); | |
| 1096 | try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1); | |
| 1097 | 1097 | module.failed_exports.putAssumeCapacityNoClobber( |
| 1098 | 1098 | exp, |
| 1099 | 1099 | try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}), |
| ... | ... | @@ -1111,7 +1111,7 @@ pub const ElfFile = struct { |
| 1111 | 1111 | }, |
| 1112 | 1112 | .Weak => elf.STB_WEAK, |
| 1113 | 1113 | .LinkOnce => { |
| 1114 | try module.failed_exports.ensureCapacity(module.failed_exports.size + 1); | |
| 1114 | try module.failed_exports.ensureCapacity(module.failed_exports.items().len + 1); | |
| 1115 | 1115 | module.failed_exports.putAssumeCapacityNoClobber( |
| 1116 | 1116 | exp, |
| 1117 | 1117 | try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), |
src-self-hosted/main.zig+2-2| ... | ... | @@ -720,7 +720,7 @@ fn fmtPathDir( |
| 720 | 720 | defer dir.close(); |
| 721 | 721 | |
| 722 | 722 | const stat = try dir.stat(); |
| 723 | if (try fmt.seen.put(stat.inode, {})) |_| return; | |
| 723 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; | |
| 724 | 724 | |
| 725 | 725 | var dir_it = dir.iterate(); |
| 726 | 726 | while (try dir_it.next()) |entry| { |
| ... | ... | @@ -768,7 +768,7 @@ fn fmtPathFile( |
| 768 | 768 | defer fmt.gpa.free(source_code); |
| 769 | 769 | |
| 770 | 770 | // Add to set after no longer possible to get error.IsDir. |
| 771 | if (try fmt.seen.put(stat.inode, {})) |_| return; | |
| 771 | if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; | |
| 772 | 772 | |
| 773 | 773 | const tree = try std.zig.parse(fmt.gpa, source_code); |
| 774 | 774 | defer tree.deinit(); |
src-self-hosted/translate_c.zig+13-14| ... | ... | @@ -20,7 +20,7 @@ pub const Error = error{OutOfMemory}; |
| 20 | 20 | const TypeError = Error || error{UnsupportedType}; |
| 21 | 21 | const TransError = TypeError || error{UnsupportedTranslation}; |
| 22 | 22 | |
| 23 | const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql); | |
| 23 | const DeclTable = std.HashMap(usize, []const u8, addrHash, addrEql, false); | |
| 24 | 24 | |
| 25 | 25 | fn addrHash(x: usize) u32 { |
| 26 | 26 | switch (@typeInfo(usize).Int.bits) { |
| ... | ... | @@ -776,8 +776,8 @@ fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 { |
| 776 | 776 | } |
| 777 | 777 | |
| 778 | 778 | fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node { |
| 779 | if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |kv| | |
| 780 | return transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice | |
| 779 | if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |name| | |
| 780 | return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice | |
| 781 | 781 | const rp = makeRestorePoint(c); |
| 782 | 782 | |
| 783 | 783 | const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl))); |
| ... | ... | @@ -818,8 +818,8 @@ fn transCreateNodeTypedef(rp: RestorePoint, typedef_decl: *const ZigClangTypedef |
| 818 | 818 | } |
| 819 | 819 | |
| 820 | 820 | fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node { |
| 821 | if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv| | |
| 822 | return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice | |
| 821 | if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |name| | |
| 822 | return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice | |
| 823 | 823 | const record_loc = ZigClangRecordDecl_getLocation(record_decl); |
| 824 | 824 | |
| 825 | 825 | var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl))); |
| ... | ... | @@ -969,7 +969,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* |
| 969 | 969 | |
| 970 | 970 | fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node { |
| 971 | 971 | if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name| |
| 972 | return try transCreateNodeIdentifier(c, name.value); // Avoid processing this decl twice | |
| 972 | return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice | |
| 973 | 973 | const rp = makeRestorePoint(c); |
| 974 | 974 | const enum_loc = ZigClangEnumDecl_getLocation(enum_decl); |
| 975 | 975 | |
| ... | ... | @@ -2130,7 +2130,7 @@ fn transInitListExprRecord( |
| 2130 | 2130 | var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl))); |
| 2131 | 2131 | if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { |
| 2132 | 2132 | const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?; |
| 2133 | raw_name = try mem.dupe(rp.c.arena, u8, name.value); | |
| 2133 | raw_name = try mem.dupe(rp.c.arena, u8, name); | |
| 2134 | 2134 | } |
| 2135 | 2135 | const field_name_tok = try appendIdentifier(rp.c, raw_name); |
| 2136 | 2136 | |
| ... | ... | @@ -2855,7 +2855,7 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberE |
| 2855 | 2855 | const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl); |
| 2856 | 2856 | if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { |
| 2857 | 2857 | const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?; |
| 2858 | break :blk try mem.dupe(rp.c.arena, u8, name.value); | |
| 2858 | break :blk try mem.dupe(rp.c.arena, u8, name); | |
| 2859 | 2859 | } |
| 2860 | 2860 | } |
| 2861 | 2861 | const decl = @ptrCast(*const ZigClangNamedDecl, member_decl); |
| ... | ... | @@ -6040,8 +6040,8 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6040 | 6040 | } else if (node.id == .PrefixOp) { |
| 6041 | 6041 | return node; |
| 6042 | 6042 | } else if (node.cast(ast.Node.Identifier)) |ident| { |
| 6043 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { | |
| 6044 | if (kv.value.cast(ast.Node.VarDecl)) |var_decl| | |
| 6043 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { | |
| 6044 | if (value.cast(ast.Node.VarDecl)) |var_decl| | |
| 6045 | 6045 | return getContainer(c, var_decl.init_node.?); |
| 6046 | 6046 | } |
| 6047 | 6047 | } else if (node.cast(ast.Node.InfixOp)) |infix| { |
| ... | ... | @@ -6064,8 +6064,8 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6064 | 6064 | |
| 6065 | 6065 | fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { |
| 6066 | 6066 | if (ref.cast(ast.Node.Identifier)) |ident| { |
| 6067 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { | |
| 6068 | if (kv.value.cast(ast.Node.VarDecl)) |var_decl| { | |
| 6067 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { | |
| 6068 | if (value.cast(ast.Node.VarDecl)) |var_decl| { | |
| 6069 | 6069 | if (var_decl.type_node) |ty| |
| 6070 | 6070 | return getContainer(c, ty); |
| 6071 | 6071 | } |
| ... | ... | @@ -6104,8 +6104,7 @@ fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { |
| 6104 | 6104 | } |
| 6105 | 6105 | |
| 6106 | 6106 | fn addMacros(c: *Context) !void { |
| 6107 | var macro_it = c.global_scope.macro_table.iterator(); | |
| 6108 | while (macro_it.next()) |kv| { | |
| 6107 | for (c.global_scope.macro_table.items()) |kv| { | |
| 6109 | 6108 | if (getFnProto(c, kv.value)) |proto_node| { |
| 6110 | 6109 | // If a macro aliases a global variable which is a function pointer, we conclude that |
| 6111 | 6110 | // the macro is intended to represent a function that assumes the function pointer |
src-self-hosted/zir.zig+16-18| ... | ... | @@ -758,7 +758,7 @@ pub const Module = struct { |
| 758 | 758 | } |
| 759 | 759 | |
| 760 | 760 | fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void { |
| 761 | if (inst_table.getValue(inst)) |info| { | |
| 761 | if (inst_table.get(inst)) |info| { | |
| 762 | 762 | if (info.index) |i| { |
| 763 | 763 | try stream.print("%{}", .{info.index}); |
| 764 | 764 | } else { |
| ... | ... | @@ -843,7 +843,7 @@ const Parser = struct { |
| 843 | 843 | skipSpace(self); |
| 844 | 844 | const decl = try parseInstruction(self, &body_context, ident); |
| 845 | 845 | const ident_index = body_context.instructions.items.len; |
| 846 | if (try body_context.name_map.put(ident, decl.inst)) |_| { | |
| 846 | if (try body_context.name_map.fetchPut(ident, decl.inst)) |_| { | |
| 847 | 847 | return self.fail("redefinition of identifier '{}'", .{ident}); |
| 848 | 848 | } |
| 849 | 849 | try body_context.instructions.append(decl.inst); |
| ... | ... | @@ -929,7 +929,7 @@ const Parser = struct { |
| 929 | 929 | skipSpace(self); |
| 930 | 930 | const decl = try parseInstruction(self, null, ident); |
| 931 | 931 | const ident_index = self.decls.items.len; |
| 932 | if (try self.global_name_map.put(ident, decl.inst)) |_| { | |
| 932 | if (try self.global_name_map.fetchPut(ident, decl.inst)) |_| { | |
| 933 | 933 | return self.fail("redefinition of identifier '{}'", .{ident}); |
| 934 | 934 | } |
| 935 | 935 | try self.decls.append(self.allocator, decl); |
| ... | ... | @@ -1153,7 +1153,7 @@ const Parser = struct { |
| 1153 | 1153 | else => continue, |
| 1154 | 1154 | }; |
| 1155 | 1155 | const ident = self.source[name_start..self.i]; |
| 1156 | const kv = map.get(ident) orelse { | |
| 1156 | return map.get(ident) orelse { | |
| 1157 | 1157 | const bad_name = self.source[name_start - 1 .. self.i]; |
| 1158 | 1158 | const src = name_start - 1; |
| 1159 | 1159 | if (local_ref) { |
| ... | ... | @@ -1172,7 +1172,6 @@ const Parser = struct { |
| 1172 | 1172 | return &declval.base; |
| 1173 | 1173 | } |
| 1174 | 1174 | }; |
| 1175 | return kv.value; | |
| 1176 | 1175 | } |
| 1177 | 1176 | |
| 1178 | 1177 | fn generateName(self: *Parser) ![]u8 { |
| ... | ... | @@ -1219,13 +1218,12 @@ const EmitZIR = struct { |
| 1219 | 1218 | // by the hash table. |
| 1220 | 1219 | var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator); |
| 1221 | 1220 | defer src_decls.deinit(); |
| 1222 | try src_decls.ensureCapacity(self.old_module.decl_table.size); | |
| 1223 | try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size); | |
| 1224 | try self.names.ensureCapacity(self.old_module.decl_table.size); | |
| 1221 | try src_decls.ensureCapacity(self.old_module.decl_table.items().len); | |
| 1222 | try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len); | |
| 1223 | try self.names.ensureCapacity(self.old_module.decl_table.items().len); | |
| 1225 | 1224 | |
| 1226 | var decl_it = self.old_module.decl_table.iterator(); | |
| 1227 | while (decl_it.next()) |kv| { | |
| 1228 | const decl = kv.value; | |
| 1225 | for (self.old_module.decl_table.items()) |entry| { | |
| 1226 | const decl = entry.value; | |
| 1229 | 1227 | src_decls.appendAssumeCapacity(decl); |
| 1230 | 1228 | self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {}); |
| 1231 | 1229 | } |
| ... | ... | @@ -1248,7 +1246,7 @@ const EmitZIR = struct { |
| 1248 | 1246 | .codegen_failure, |
| 1249 | 1247 | .dependency_failure, |
| 1250 | 1248 | .codegen_failure_retryable, |
| 1251 | => if (self.old_module.failed_decls.getValue(ir_decl)) |err_msg| { | |
| 1249 | => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| { | |
| 1252 | 1250 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1253 | 1251 | fail_inst.* = .{ |
| 1254 | 1252 | .base = .{ |
| ... | ... | @@ -1270,7 +1268,7 @@ const EmitZIR = struct { |
| 1270 | 1268 | continue; |
| 1271 | 1269 | }, |
| 1272 | 1270 | } |
| 1273 | if (self.old_module.export_owners.getValue(ir_decl)) |exports| { | |
| 1271 | if (self.old_module.export_owners.get(ir_decl)) |exports| { | |
| 1274 | 1272 | for (exports) |module_export| { |
| 1275 | 1273 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); |
| 1276 | 1274 | const export_inst = try self.arena.allocator.create(Inst.Export); |
| ... | ... | @@ -1314,7 +1312,7 @@ const EmitZIR = struct { |
| 1314 | 1312 | try new_body.inst_table.putNoClobber(inst, new_inst); |
| 1315 | 1313 | return new_inst; |
| 1316 | 1314 | } else { |
| 1317 | return new_body.inst_table.getValue(inst).?; | |
| 1315 | return new_body.inst_table.get(inst).?; | |
| 1318 | 1316 | } |
| 1319 | 1317 | } |
| 1320 | 1318 | |
| ... | ... | @@ -1424,7 +1422,7 @@ const EmitZIR = struct { |
| 1424 | 1422 | try self.emitBody(body, &inst_table, &instructions); |
| 1425 | 1423 | }, |
| 1426 | 1424 | .sema_failure => { |
| 1427 | const err_msg = self.old_module.failed_decls.getValue(module_fn.owner_decl).?; | |
| 1425 | const err_msg = self.old_module.failed_decls.get(module_fn.owner_decl).?; | |
| 1428 | 1426 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1429 | 1427 | fail_inst.* = .{ |
| 1430 | 1428 | .base = .{ |
| ... | ... | @@ -1841,7 +1839,7 @@ const EmitZIR = struct { |
| 1841 | 1839 | self.next_auto_name += 1; |
| 1842 | 1840 | const gop = try self.names.getOrPut(proposed_name); |
| 1843 | 1841 | if (!gop.found_existing) { |
| 1844 | gop.kv.value = {}; | |
| 1842 | gop.entry.value = {}; | |
| 1845 | 1843 | return proposed_name; |
| 1846 | 1844 | } |
| 1847 | 1845 | } |
| ... | ... | @@ -1861,9 +1859,9 @@ const EmitZIR = struct { |
| 1861 | 1859 | }, |
| 1862 | 1860 | .kw_args = .{}, |
| 1863 | 1861 | }; |
| 1864 | gop.kv.value = try self.emitUnnamedDecl(&primitive_inst.base); | |
| 1862 | gop.entry.value = try self.emitUnnamedDecl(&primitive_inst.base); | |
| 1865 | 1863 | } |
| 1866 | return gop.kv.value; | |
| 1864 | return gop.entry.value; | |
| 1867 | 1865 | } |
| 1868 | 1866 | |
| 1869 | 1867 | fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl { |