| 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,7 +392,7 @@ fn genToc(allocator: *mem.Allocator, tokenizer: *Tokenizer) !Toc { |
| 392 | .n = header_stack_size, | 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 | parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {}; | 396 | parseError(tokenizer, tag_token, "duplicate header url: #{}", .{urlized}) catch {}; |
| 397 | parseError(tokenizer, entry.value, "other tag here", .{}) catch {}; | 397 | parseError(tokenizer, entry.value, "other tag here", .{}) catch {}; |
| 398 | return error.ParseError; | 398 | return error.ParseError; |
doc/langref.html.in+3-11| ... | @@ -5363,11 +5363,11 @@ const std = @import("std"); | ... | @@ -5363,11 +5363,11 @@ const std = @import("std"); |
| 5363 | const assert = std.debug.assert; | 5363 | const assert = std.debug.assert; |
| 5364 | 5364 | ||
| 5365 | test "turn HashMap into a set with void" { | 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 | defer map.deinit(); | 5367 | defer map.deinit(); |
| 5368 | 5368 | ||
| 5369 | _ = try map.put(1, {}); | 5369 | try map.put(1, {}); |
| 5370 | _ = try map.put(2, {}); | 5370 | try map.put(2, {}); |
| 5371 | 5371 | ||
| 5372 | assert(map.contains(2)); | 5372 | assert(map.contains(2)); |
| 5373 | assert(!map.contains(3)); | 5373 | assert(!map.contains(3)); |
| ... | @@ -5375,14 +5375,6 @@ test "turn HashMap into a set with void" { | ... | @@ -5375,14 +5375,6 @@ test "turn HashMap into a set with void" { |
| 5375 | _ = map.remove(2); | 5375 | _ = map.remove(2); |
| 5376 | assert(!map.contains(2)); | 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 | {#code_end#} | 5378 | {#code_end#} |
| 5387 | <p>Note that this is different from using a dummy value for the hash map value. | 5379 | <p>Note that this is different from using a dummy value for the hash map value. |
| 5388 | By using {#syntax#}void{#endsyntax#} as the type of the value, the hash map entry type has no value field, and | 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,6 +210,14 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { |
| 210 | self.capacity = new_len; | 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 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { | 221 | pub fn ensureCapacity(self: *Self, new_capacity: usize) !void { |
| 214 | var better_capacity = self.capacity; | 222 | var better_capacity = self.capacity; |
| 215 | if (better_capacity >= new_capacity) return; | 223 | if (better_capacity >= new_capacity) return; |
| ... | @@ -432,6 +440,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ | ... | @@ -432,6 +440,14 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 432 | self.capacity = new_len; | 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 | pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void { | 451 | pub fn ensureCapacity(self: *Self, allocator: *Allocator, new_capacity: usize) !void { |
| 436 | var better_capacity = self.capacity; | 452 | var better_capacity = self.capacity; |
| 437 | if (better_capacity >= new_capacity) return; | 453 | if (better_capacity >= new_capacity) return; |
lib/std/buf_map.zig+7-8| ... | @@ -33,10 +33,10 @@ pub const BufMap = struct { | ... | @@ -33,10 +33,10 @@ pub const BufMap = struct { |
| 33 | pub fn setMove(self: *BufMap, key: []u8, value: []u8) !void { | 33 | pub fn setMove(self: *BufMap, key: []u8, value: []u8) !void { |
| 34 | const get_or_put = try self.hash_map.getOrPut(key); | 34 | const get_or_put = try self.hash_map.getOrPut(key); |
| 35 | if (get_or_put.found_existing) { | 35 | if (get_or_put.found_existing) { |
| 36 | self.free(get_or_put.kv.key); | 36 | self.free(get_or_put.entry.key); |
| 37 | get_or_put.kv.key = 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 | /// `key` and `value` are copied into the BufMap. | 42 | /// `key` and `value` are copied into the BufMap. |
| ... | @@ -45,19 +45,18 @@ pub const BufMap = struct { | ... | @@ -45,19 +45,18 @@ pub const BufMap = struct { |
| 45 | errdefer self.free(value_copy); | 45 | errdefer self.free(value_copy); |
| 46 | const get_or_put = try self.hash_map.getOrPut(key); | 46 | const get_or_put = try self.hash_map.getOrPut(key); |
| 47 | if (get_or_put.found_existing) { | 47 | if (get_or_put.found_existing) { |
| 48 | self.free(get_or_put.kv.value); | 48 | self.free(get_or_put.entry.value); |
| 49 | } else { | 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 | _ = self.hash_map.remove(key); | 51 | _ = self.hash_map.remove(key); |
| 52 | return err; | 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 | pub fn get(self: BufMap, key: []const u8) ?[]const u8 { | 58 | pub fn get(self: BufMap, key: []const u8) ?[]const u8 { |
| 59 | const entry = self.hash_map.get(key) orelse return null; | 59 | return self.hash_map.get(key); |
| 60 | return entry.value; | ||
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | pub fn delete(self: *BufMap, key: []const u8) void { | 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 +14,12 @@ pub const BufSet = struct { |
| 14 | return self; | 14 | return self; |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | pub fn deinit(self: *const BufSet) void { | 17 | pub fn deinit(self: *BufSet) void { |
| 18 | var it = self.hash_map.iterator(); | 18 | for (self.hash_map.items()) |entry| { |
| 19 | while (true) { | ||
| 20 | const entry = it.next() orelse break; | ||
| 21 | self.free(entry.key); | 19 | self.free(entry.key); |
| 22 | } | 20 | } |
| 23 | |||
| 24 | self.hash_map.deinit(); | 21 | self.hash_map.deinit(); |
| 22 | self.* = undefined; | ||
| 25 | } | 23 | } |
| 26 | 24 | ||
| 27 | pub fn put(self: *BufSet, key: []const u8) !void { | 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,12 +422,12 @@ pub const Builder = struct { |
| 422 | .type_id = type_id, | 422 | .type_id = type_id, |
| 423 | .description = description, | 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 | panic("Option '{}' declared twice", .{name}); | 426 | panic("Option '{}' declared twice", .{name}); |
| 427 | } | 427 | } |
| 428 | self.available_options_list.append(available_option) catch unreachable; | 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 | entry.value.used = true; | 431 | entry.value.used = true; |
| 432 | switch (type_id) { | 432 | switch (type_id) { |
| 433 | TypeId.Bool => switch (entry.value.value) { | 433 | TypeId.Bool => switch (entry.value.value) { |
| ... | @@ -634,7 +634,7 @@ pub const Builder = struct { | ... | @@ -634,7 +634,7 @@ pub const Builder = struct { |
| 634 | pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool { | 634 | pub fn addUserInputOption(self: *Builder, name: []const u8, value: []const u8) !bool { |
| 635 | const gop = try self.user_input_options.getOrPut(name); | 635 | const gop = try self.user_input_options.getOrPut(name); |
| 636 | if (!gop.found_existing) { | 636 | if (!gop.found_existing) { |
| 637 | gop.kv.value = UserInputOption{ | 637 | gop.entry.value = UserInputOption{ |
| 638 | .name = name, | 638 | .name = name, |
| 639 | .value = UserValue{ .Scalar = value }, | 639 | .value = UserValue{ .Scalar = value }, |
| 640 | .used = false, | 640 | .used = false, |
| ... | @@ -643,7 +643,7 @@ pub const Builder = struct { | ... | @@ -643,7 +643,7 @@ pub const Builder = struct { |
| 643 | } | 643 | } |
| 644 | 644 | ||
| 645 | // option already exists | 645 | // option already exists |
| 646 | switch (gop.kv.value.value) { | 646 | switch (gop.entry.value.value) { |
| 647 | UserValue.Scalar => |s| { | 647 | UserValue.Scalar => |s| { |
| 648 | // turn it into a list | 648 | // turn it into a list |
| 649 | var list = ArrayList([]const u8).init(self.allocator); | 649 | var list = ArrayList([]const u8).init(self.allocator); |
| ... | @@ -675,7 +675,7 @@ pub const Builder = struct { | ... | @@ -675,7 +675,7 @@ pub const Builder = struct { |
| 675 | pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool { | 675 | pub fn addUserInputFlag(self: *Builder, name: []const u8) !bool { |
| 676 | const gop = try self.user_input_options.getOrPut(name); | 676 | const gop = try self.user_input_options.getOrPut(name); |
| 677 | if (!gop.found_existing) { | 677 | if (!gop.found_existing) { |
| 678 | gop.kv.value = UserInputOption{ | 678 | gop.entry.value = UserInputOption{ |
| 679 | .name = name, | 679 | .name = name, |
| 680 | .value = UserValue{ .Flag = {} }, | 680 | .value = UserValue{ .Flag = {} }, |
| 681 | .used = false, | 681 | .used = false, |
| ... | @@ -684,7 +684,7 @@ pub const Builder = struct { | ... | @@ -684,7 +684,7 @@ pub const Builder = struct { |
| 684 | } | 684 | } |
| 685 | 685 | ||
| 686 | // option already exists | 686 | // option already exists |
| 687 | switch (gop.kv.value.value) { | 687 | switch (gop.entry.value.value) { |
| 688 | UserValue.Scalar => |s| { | 688 | UserValue.Scalar => |s| { |
| 689 | warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s }); | 689 | warn("Flag '-D{}' conflicts with option '-D{}={}'.\n", .{ name, name, s }); |
| 690 | return true; | 690 | return true; |
lib/std/debug.zig+4-4| ... | @@ -1132,7 +1132,7 @@ pub const DebugInfo = struct { | ... | @@ -1132,7 +1132,7 @@ pub const DebugInfo = struct { |
| 1132 | const seg_end = seg_start + segment_cmd.vmsize; | 1132 | const seg_end = seg_start + segment_cmd.vmsize; |
| 1133 | 1133 | ||
| 1134 | if (rebased_address >= seg_start and rebased_address < seg_end) { | 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 | return obj_di; | 1136 | return obj_di; |
| 1137 | } | 1137 | } |
| 1138 | 1138 | ||
| ... | @@ -1204,7 +1204,7 @@ pub const DebugInfo = struct { | ... | @@ -1204,7 +1204,7 @@ pub const DebugInfo = struct { |
| 1204 | const seg_end = seg_start + info.SizeOfImage; | 1204 | const seg_end = seg_start + info.SizeOfImage; |
| 1205 | 1205 | ||
| 1206 | if (address >= seg_start and address < seg_end) { | 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 | return obj_di; | 1208 | return obj_di; |
| 1209 | } | 1209 | } |
| 1210 | 1210 | ||
| ... | @@ -1278,7 +1278,7 @@ pub const DebugInfo = struct { | ... | @@ -1278,7 +1278,7 @@ pub const DebugInfo = struct { |
| 1278 | else => return error.MissingDebugInfo, | 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 | return obj_di; | 1282 | return obj_di; |
| 1283 | } | 1283 | } |
| 1284 | 1284 | ||
| ... | @@ -1441,7 +1441,7 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { | ... | @@ -1441,7 +1441,7 @@ pub const ModuleDebugInfo = switch (builtin.os.tag) { |
| 1441 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); | 1441 | const o_file_path = mem.spanZ(self.strings[symbol.ofile.?.n_strx..]); |
| 1442 | 1442 | ||
| 1443 | // Check if its debug infos are already in the cache | 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 | (self.loadOFile(o_file_path) catch |err| switch (err) { | 1445 | (self.loadOFile(o_file_path) catch |err| switch (err) { |
| 1446 | error.FileNotFound, | 1446 | error.FileNotFound, |
| 1447 | error.MissingDebugInfo, | 1447 | error.MissingDebugInfo, |
lib/std/hash_map.zig+763-310| ... | @@ -9,17 +9,15 @@ const autoHash = std.hash.autoHash; | ... | @@ -9,17 +9,15 @@ const autoHash = std.hash.autoHash; |
| 9 | const Wyhash = std.hash.Wyhash; | 9 | const Wyhash = std.hash.Wyhash; |
| 10 | const Allocator = mem.Allocator; | 10 | const Allocator = mem.Allocator; |
| 11 | const builtin = @import("builtin"); | 11 | const builtin = @import("builtin"); |
| 12 | 12 | const hash_map = @This(); | |
| 13 | const want_modification_safety = std.debug.runtime_safety; | ||
| 14 | const debug_u32 = if (want_modification_safety) u32 else void; | ||
| 15 | 13 | ||
| 16 | pub fn AutoHashMap(comptime K: type, comptime V: type) type { | 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 | /// Builtin hashmap for strings as keys. | 18 | /// Builtin hashmap for strings as keys. |
| 21 | pub fn StringHashMap(comptime V: type) type { | 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 | pub fn eqlString(a: []const u8, b: []const u8) bool { | 23 | pub fn eqlString(a: []const u8, b: []const u8) bool { |
| ... | @@ -30,422 +28,859 @@ pub fn hashString(s: []const u8) u32 { | ... | @@ -30,422 +28,859 @@ pub fn hashString(s: []const u8) u32 { |
| 30 | return @truncate(u32, std.hash.Wyhash.hash(0, s)); | 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 | return struct { | 48 | return struct { |
| 35 | entries: []Entry, | 49 | unmanaged: Unmanaged, |
| 36 | size: usize, | ||
| 37 | max_distance_from_start_index: usize, | ||
| 38 | allocator: *Allocator, | 50 | allocator: *Allocator, |
| 39 | 51 | ||
| 40 | /// This is used to detect bugs where a hashtable is edited while an iterator is running. | 52 | pub const Unmanaged = HashMapUnmanaged(K, V, hash, eql, store_hash); |
| 41 | modification_count: debug_u32, | 53 | pub const Entry = Unmanaged.Entry; |
| 42 | 54 | pub const Hash = Unmanaged.Hash; | |
| 43 | const Self = @This(); | 55 | pub const GetOrPutResult = Unmanaged.GetOrPutResult; |
| 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 | }; | ||
| 65 | 56 | ||
| 57 | /// Deprecated. Iterate using `items`. | ||
| 66 | pub const Iterator = struct { | 58 | pub const Iterator = struct { |
| 67 | hm: *const Self, | 59 | hm: *const Self, |
| 68 | // how many items have we returned | 60 | /// Iterator through the entry array. |
| 69 | count: usize, | ||
| 70 | // iterator through the entry array | ||
| 71 | index: usize, | 61 | index: usize, |
| 72 | // used to detect concurrent modification | ||
| 73 | initial_modification_count: debug_u32, | ||
| 74 | 62 | ||
| 75 | pub fn next(it: *Iterator) ?*KV { | 63 | pub fn next(it: *Iterator) ?*Entry { |
| 76 | if (want_modification_safety) { | 64 | if (it.index >= it.hm.unmanaged.entries.items.len) return null; |
| 77 | assert(it.initial_modification_count == it.hm.modification_count); // concurrent modification | 65 | const result = &it.hm.unmanaged.entries.items[it.index]; |
| 78 | } | 66 | it.index += 1; |
| 79 | if (it.count >= it.hm.size) return null; | 67 | return result; |
| 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 | ||
| 89 | } | 68 | } |
| 90 | 69 | ||
| 91 | // Reset the iterator to the initial index | 70 | /// Reset the iterator to the initial index |
| 92 | pub fn reset(it: *Iterator) void { | 71 | pub fn reset(it: *Iterator) void { |
| 93 | it.count = 0; | ||
| 94 | it.index = 0; | 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 | pub fn init(allocator: *Allocator) Self { | 79 | pub fn init(allocator: *Allocator) Self { |
| 101 | return Self{ | 80 | return .{ |
| 102 | .entries = &[_]Entry{}, | 81 | .unmanaged = .{}, |
| 103 | .allocator = allocator, | 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 { | 86 | pub fn deinit(self: *Self) void { |
| 111 | hm.allocator.free(hm.entries); | 87 | self.unmanaged.deinit(self.allocator); |
| 88 | self.* = undefined; | ||
| 112 | } | 89 | } |
| 113 | 90 | ||
| 114 | pub fn clear(hm: *Self) void { | 91 | pub fn clearRetainingCapacity(self: *Self) void { |
| 115 | for (hm.entries) |*entry| { | 92 | return self.unmanaged.clearRetainingCapacity(); |
| 116 | entry.used = false; | ||
| 117 | } | ||
| 118 | hm.size = 0; | ||
| 119 | hm.max_distance_from_start_index = 0; | ||
| 120 | hm.incrementModificationCount(); | ||
| 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 | pub fn count(self: Self) usize { | 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 | /// If key exists this function cannot fail. | 112 | /// If key exists this function cannot fail. |
| 128 | /// If there is an existing item with `key`, then the result | 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 | /// Otherwise, puts a new item with undefined value, and | 115 | /// Otherwise, puts a new item with undefined value, and |
| 131 | /// the kv pointer points to it. Caller should then initialize | 116 | /// the `Entry` pointer points to it. Caller should then initialize |
| 132 | /// the data. | 117 | /// the value (but not the key). |
| 133 | pub fn getOrPut(self: *Self, key: K) !GetOrPutResult { | 118 | pub fn getOrPut(self: *Self, key: K) !GetOrPutResult { |
| 134 | // TODO this implementation can be improved - we should only | 119 | return self.unmanaged.getOrPut(self.allocator, key); |
| 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 | }; | ||
| 150 | } | 120 | } |
| 151 | 121 | ||
| 152 | pub fn getOrPutValue(self: *Self, key: K, value: V) !*KV { | 122 | /// If there is an existing item with `key`, then the result |
| 153 | const res = try self.getOrPut(key); | 123 | /// `Entry` pointer points to it, and found_existing is true. |
| 154 | if (!res.found_existing) | 124 | /// Otherwise, puts a new item with undefined value, and |
| 155 | res.kv.value = value; | 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 { | 198 | /// If there is an `Entry` with a matching key, it is deleted from |
| 161 | // ensure that the hash map will be at most 60% full if | 199 | /// the hash map, and then returned from this function. |
| 162 | // expected_count items are put into it | 200 | pub fn remove(self: *Self, key: K) ?Entry { |
| 163 | var optimized_capacity = expected_count * 5 / 3; | 201 | return self.unmanaged.remove(key); |
| 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; | ||
| 167 | } | 202 | } |
| 168 | 203 | ||
| 169 | /// Increases capacity so that the hash map will be at most | 204 | /// Asserts there is an `Entry` with matching key, deletes it from the hash map, |
| 170 | /// 60% full when expected_count items are put into it | 205 | /// and discards it. |
| 171 | pub fn ensureCapacity(self: *Self, expected_count: usize) !void { | 206 | pub fn removeAssertDiscard(self: *Self, key: K) void { |
| 172 | if (expected_count == 0) return; | 207 | return self.unmanaged.removeAssertDiscard(key); |
| 173 | const optimized_capacity = optimizedCapacity(expected_count); | ||
| 174 | return self.ensureCapacityExact(optimized_capacity); | ||
| 175 | } | 208 | } |
| 176 | 209 | ||
| 177 | /// Sets the capacity to the new capacity if the new | 210 | pub fn items(self: Self) []Entry { |
| 178 | /// capacity is greater than the current capacity. | 211 | return self.unmanaged.items(); |
| 179 | /// New capacity must be a power of two. | 212 | } |
| 180 | fn ensureCapacityExact(self: *Self, new_capacity: usize) !void { | 213 | |
| 181 | // capacity must always be a power of two to allow for modulo | 214 | pub fn clone(self: Self) !Self { |
| 182 | // optimization in the constrainIndex fn | 215 | var other = try self.unmanaged.clone(self.allocator); |
| 183 | assert(math.isPowerOfTwo(new_capacity)); | 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) { | 279 | pub fn promote(self: Self, allocator: *Allocator) Managed { |
| 186 | return; | 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; | 332 | /// If there is an existing item with `key`, then the result |
| 190 | try self.initCapacity(new_capacity); | 333 | /// `Entry` pointer points to it, and found_existing is true. |
| 191 | self.incrementModificationCount(); | 334 | /// Otherwise, puts a new item with undefined value, and |
| 192 | if (old_entries.len > 0) { | 335 | /// the `Entry` pointer points to it. Caller should then initialize |
| 193 | // dump all of the old elements into the new table | 336 | /// the value (but not the key). |
| 194 | for (old_entries) |*old_entry| { | 337 | /// If a new entry needs to be stored, this function asserts there |
| 195 | if (old_entry.used) { | 338 | /// is enough capacity to store it. |
| 196 | self.internalPut(old_entry.kv.key).new_entry.kv.value = old_entry.kv.value; | 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. | 408 | /// Returns the number of total elements which may be present before it is |
| 204 | pub fn put(self: *Self, key: K, value: V) !?KV { | 409 | /// no longer guaranteed that no allocations will be performed. |
| 205 | try self.autoCapacity(); | 410 | pub fn capacity(self: Self) usize { |
| 206 | return putAssumeCapacity(self, key, value); | 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. | 417 | /// Clobbers any existing data. To detect if a put would clobber |
| 210 | pub fn putNoClobber(self: *Self, key: K, value: V) !void { | 418 | /// existing data, see `getOrPut`. |
| 211 | assert((try self.put(key, value)) == null); | 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 { | 424 | /// Inserts a key-value pair into the hash map, asserting that no previous |
| 215 | assert(self.count() < self.entries.len); | 425 | /// entry with the same key is already present |
| 216 | self.incrementModificationCount(); | 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); | 432 | /// Asserts there is enough capacity to store the new key-value pair. |
| 219 | put_result.new_entry.kv.value = value; | 433 | /// Clobbers any existing data. To detect if a put would clobber |
| 220 | return put_result.old_kv; | 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 | pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void { | 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 { | 449 | /// Inserts a new `Entry` into the hash map, returning the previous one, if any. |
| 228 | if (hm.entries.len == 0) { | 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 | return null; | 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 { | 492 | pub fn get(self: Self, key: K) ?V { |
| 235 | return if (hm.get(key)) |kv| kv.value else null; | 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 { | 496 | pub fn contains(self: Self, key: K) bool { |
| 239 | return hm.get(key) != null; | 497 | return self.getEntry(key) != null; |
| 240 | } | 498 | } |
| 241 | 499 | ||
| 242 | /// Returns any kv pair that was removed. | 500 | /// If there is an `Entry` with a matching key, it is deleted from |
| 243 | pub fn remove(hm: *Self, key: K) ?KV { | 501 | /// the hash map, and then returned from this function. |
| 244 | if (hm.entries.len == 0) return null; | 502 | pub fn remove(self: *Self, key: K) ?Entry { |
| 245 | hm.incrementModificationCount(); | 503 | const header = self.index_header orelse { |
| 246 | const start_index = hm.keyToIndex(key); | 504 | // Linear scan. |
| 247 | { | 505 | const h = if (store_hash) hash(key) else {}; |
| 248 | var roll_over: usize = 0; | 506 | for (self.entries.items) |item, i| { |
| 249 | while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { | 507 | if (item.hash == h and eql(key, item.key)) { |
| 250 | const index = hm.constrainIndex(start_index + roll_over); | 508 | return self.entries.swapRemove(i); |
| 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; | ||
| 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. | 521 | /// Asserts there is an `Entry` with matching key, deletes it from the hash map, |
| 277 | pub fn removeAssertDiscard(hm: *Self, key: K) void { | 522 | /// and discards it. |
| 278 | assert(hm.remove(key) != null); | 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 { | 527 | pub fn items(self: Self) []Entry { |
| 282 | return Iterator{ | 528 | return self.entries.items; |
| 283 | .hm = hm, | ||
| 284 | .count = 0, | ||
| 285 | .index = 0, | ||
| 286 | .initial_modification_count = hm.modification_count, | ||
| 287 | }; | ||
| 288 | } | 529 | } |
| 289 | 530 | ||
| 290 | pub fn clone(self: Self) !Self { | 531 | pub fn clone(self: Self, allocator: *Allocator) !Self { |
| 291 | var other = Self.init(self.allocator); | 532 | // TODO this can be made more efficient by directly allocating |
| 292 | try other.initCapacity(self.entries.len); | 533 | // the memory slices and memcpying the elements. |
| 293 | var it = self.iterator(); | 534 | var other = Self.init(); |
| 294 | while (it.next()) |entry| { | 535 | try other.initCapacity(allocator, self.entries.len); |
| 295 | try other.putNoClobber(entry.key, entry.value); | 536 | for (self.entries.items) |entry| { |
| 537 | other.putAssumeCapacityNoClobber(entry.key, entry.value); | ||
| 296 | } | 538 | } |
| 297 | return other; | 539 | return other; |
| 298 | } | 540 | } |
| 299 | 541 | ||
| 300 | fn autoCapacity(self: *Self) !void { | 542 | fn removeInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) ?Entry { |
| 301 | if (self.entries.len == 0) { | 543 | const indexes = header.indexes(I); |
| 302 | return self.ensureCapacityExact(16); | 544 | const h = hash(key); |
| 303 | } | 545 | const start_index = header.constrainIndex(h); |
| 304 | // if we get too full (60%), double the capacity | 546 | var roll_over: usize = 0; |
| 305 | if (self.size * 5 >= self.entries.len * 3) { | 547 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { |
| 306 | return self.ensureCapacityExact(self.entries.len * 2); | 548 | const index_index = header.constrainIndex(start_index + roll_over); |
| 307 | } | 549 | var index = &indexes[index_index]; |
| 308 | } | 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 { | 566 | // Now we have to shift over the following indexes. |
| 311 | hm.entries = try hm.allocator.alloc(Entry, capacity); | 567 | roll_over += 1; |
| 312 | hm.size = 0; | 568 | while (roll_over < header.indexes_len) : (roll_over += 1) { |
| 313 | hm.max_distance_from_start_index = 0; | 569 | const next_index_index = header.constrainIndex(start_index + roll_over); |
| 314 | for (hm.entries) |*entry| { | 570 | const next_index = &indexes[next_index_index]; |
| 315 | entry.used = false; | 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 { | 584 | fn updateEntryIndex( |
| 320 | if (want_modification_safety) { | 585 | self: *Self, |
| 321 | hm.modification_count +%= 1; | 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 { | 606 | /// Must ensureCapacity before calling this. |
| 326 | new_entry: *Entry, | 607 | fn getOrPutInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) GetOrPutResult { |
| 327 | old_kv: ?KV, | 608 | const indexes = header.indexes(I); |
| 328 | }; | 609 | const h = hash(key); |
| 329 | 610 | const start_index = header.constrainIndex(h); | |
| 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); | ||
| 336 | var roll_over: usize = 0; | 611 | var roll_over: usize = 0; |
| 337 | var distance_from_start_index: usize = 0; | 612 | var distance_from_start_index: usize = 0; |
| 338 | var got_result_entry = false; | 613 | while (roll_over <= header.indexes_len) : ({ |
| 339 | var result = InternalPutResult{ | ||
| 340 | .new_entry = undefined, | ||
| 341 | .old_kv = null, | ||
| 342 | }; | ||
| 343 | while (roll_over < self.entries.len) : ({ | ||
| 344 | roll_over += 1; | 614 | roll_over += 1; |
| 345 | distance_from_start_index += 1; | 615 | distance_from_start_index += 1; |
| 346 | }) { | 616 | }) { |
| 347 | const index = self.constrainIndex(start_index + roll_over); | 617 | const index_index = header.constrainIndex(start_index + roll_over); |
| 348 | const entry = &self.entries[index]; | 618 | const index = indexes[index_index]; |
| 349 | 619 | if (index.isEmpty()) { | |
| 350 | if (entry.used and !eql(entry.kv.key, key)) { | 620 | indexes[index_index] = .{ |
| 351 | if (entry.distance_from_start_index < distance_from_start_index) { | 621 | .distance_from_start_index = @intCast(I, distance_from_start_index), |
| 352 | // robin hood to the rescue | 622 | .entry_index = @intCast(I, self.entries.items.len), |
| 353 | const tmp = entry.*; | 623 | }; |
| 354 | self.max_distance_from_start_index = math.max(self.max_distance_from_start_index, distance_from_start_index); | 624 | header.maybeBumpMax(distance_from_start_index); |
| 355 | if (!got_result_entry) { | 625 | const new_entry = self.entries.addOneAssumeCapacity(); |
| 356 | got_result_entry = true; | 626 | new_entry.* = .{ |
| 357 | result.new_entry = 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) { | 704 | fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry { |
| 375 | result.old_kv = entry.kv; | 705 | const indexes = header.indexes(I); |
| 376 | } else { | 706 | const h = hash(key); |
| 377 | // adding an entry. otherwise overwriting old value with | 707 | const start_index = header.constrainIndex(h); |
| 378 | // same key | 708 | var roll_over: usize = 0; |
| 379 | self.size += 1; | 709 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { |
| 380 | } | 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); | 723 | fn insertAllEntriesIntoNewHeader(self: *Self, header: *IndexHeader) void { |
| 383 | if (!got_result_entry) { | 724 | switch (header.capacityIndexType()) { |
| 384 | result.new_entry = entry; | 725 | .u8 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u8), |
| 385 | } | 726 | .u16 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u16), |
| 386 | entry.* = Entry{ | 727 | .u32 => return self.insertAllEntriesIntoNewHeaderGeneric(header, u32), |
| 387 | .used = true, | 728 | .usize => return self.insertAllEntriesIntoNewHeaderGeneric(header, usize), |
| 388 | .distance_from_start_index = distance_from_start_index, | ||
| 389 | .kv = KV{ | ||
| 390 | .key = key, | ||
| 391 | .value = value, | ||
| 392 | }, | ||
| 393 | }; | ||
| 394 | return result; | ||
| 395 | } | 729 | } |
| 396 | unreachable; // put into a full map | ||
| 397 | } | 730 | } |
| 398 | 731 | ||
| 399 | fn internalGet(hm: Self, key: K) ?*KV { | 732 | fn insertAllEntriesIntoNewHeaderGeneric(self: *Self, header: *IndexHeader, comptime I: type) void { |
| 400 | const start_index = hm.keyToIndex(key); | 733 | const indexes = header.indexes(I); |
| 401 | { | 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 | var roll_over: usize = 0; | 738 | var roll_over: usize = 0; |
| 403 | while (roll_over <= hm.max_distance_from_start_index) : (roll_over += 1) { | 739 | var distance_from_start_index: usize = 0; |
| 404 | const index = hm.constrainIndex(start_index + roll_over); | 740 | while (roll_over < header.indexes_len) : ({ |
| 405 | const entry = &hm.entries[index]; | 741 | roll_over += 1; |
| 406 | 742 | distance_from_start_index += 1; | |
| 407 | if (!entry.used) return null; | 743 | }) { |
| 408 | if (eql(entry.kv.key, key)) return &entry.kv; | 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 { | 782 | fn capacityIndexSize(indexes_len: usize) usize { |
| 415 | return hm.constrainIndex(@as(usize, hash(key))); | 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 { | 807 | fn setEmpty(idx: *Self) void { |
| 419 | // this is an optimization for modulo of power of two integers; | 808 | idx.entry_index = math.maxInt(I); |
| 420 | // it requires hm.entries.len to always be a power of two | ||
| 421 | return i & (hm.entries.len - 1); | ||
| 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 | test "basic hash map usage" { | 861 | test "basic hash map usage" { |
| 427 | var map = AutoHashMap(i32, i32).init(std.testing.allocator); | 862 | var map = AutoHashMap(i32, i32).init(std.testing.allocator); |
| 428 | defer map.deinit(); | 863 | defer map.deinit(); |
| 429 | 864 | ||
| 430 | testing.expect((try map.put(1, 11)) == null); | 865 | testing.expect((try map.fetchPut(1, 11)) == null); |
| 431 | testing.expect((try map.put(2, 22)) == null); | 866 | testing.expect((try map.fetchPut(2, 22)) == null); |
| 432 | testing.expect((try map.put(3, 33)) == null); | 867 | testing.expect((try map.fetchPut(3, 33)) == null); |
| 433 | testing.expect((try map.put(4, 44)) == null); | 868 | testing.expect((try map.fetchPut(4, 44)) == null); |
| 434 | 869 | ||
| 435 | try map.putNoClobber(5, 55); | 870 | try map.putNoClobber(5, 55); |
| 436 | testing.expect((try map.put(5, 66)).?.value == 55); | 871 | testing.expect((try map.fetchPut(5, 66)).?.value == 55); |
| 437 | testing.expect((try map.put(5, 55)).?.value == 66); | 872 | testing.expect((try map.fetchPut(5, 55)).?.value == 66); |
| 438 | 873 | ||
| 439 | const gop1 = try map.getOrPut(5); | 874 | const gop1 = try map.getOrPut(5); |
| 440 | testing.expect(gop1.found_existing == true); | 875 | testing.expect(gop1.found_existing == true); |
| 441 | testing.expect(gop1.kv.value == 55); | 876 | testing.expect(gop1.entry.value == 55); |
| 442 | gop1.kv.value = 77; | 877 | gop1.entry.value = 77; |
| 443 | testing.expect(map.get(5).?.value == 77); | 878 | testing.expect(map.getEntry(5).?.value == 77); |
| 444 | 879 | ||
| 445 | const gop2 = try map.getOrPut(99); | 880 | const gop2 = try map.getOrPut(99); |
| 446 | testing.expect(gop2.found_existing == false); | 881 | testing.expect(gop2.found_existing == false); |
| 447 | gop2.kv.value = 42; | 882 | gop2.entry.value = 42; |
| 448 | testing.expect(map.get(99).?.value == 42); | 883 | testing.expect(map.getEntry(99).?.value == 42); |
| 449 | 884 | ||
| 450 | const gop3 = try map.getOrPutValue(5, 5); | 885 | const gop3 = try map.getOrPutValue(5, 5); |
| 451 | testing.expect(gop3.value == 77); | 886 | testing.expect(gop3.value == 77); |
| ... | @@ -454,15 +889,15 @@ test "basic hash map usage" { | ... | @@ -454,15 +889,15 @@ test "basic hash map usage" { |
| 454 | testing.expect(gop4.value == 41); | 889 | testing.expect(gop4.value == 41); |
| 455 | 890 | ||
| 456 | testing.expect(map.contains(2)); | 891 | testing.expect(map.contains(2)); |
| 457 | testing.expect(map.get(2).?.value == 22); | 892 | testing.expect(map.getEntry(2).?.value == 22); |
| 458 | testing.expect(map.getValue(2).? == 22); | 893 | testing.expect(map.get(2).? == 22); |
| 459 | 894 | ||
| 460 | const rmv1 = map.remove(2); | 895 | const rmv1 = map.remove(2); |
| 461 | testing.expect(rmv1.?.key == 2); | 896 | testing.expect(rmv1.?.key == 2); |
| 462 | testing.expect(rmv1.?.value == 22); | 897 | testing.expect(rmv1.?.value == 22); |
| 463 | testing.expect(map.remove(2) == null); | 898 | testing.expect(map.remove(2) == null); |
| 899 | testing.expect(map.getEntry(2) == null); | ||
| 464 | testing.expect(map.get(2) == null); | 900 | testing.expect(map.get(2) == null); |
| 465 | testing.expect(map.getValue(2) == null); | ||
| 466 | 901 | ||
| 467 | map.removeAssertDiscard(3); | 902 | map.removeAssertDiscard(3); |
| 468 | } | 903 | } |
| ... | @@ -498,8 +933,8 @@ test "iterator hash map" { | ... | @@ -498,8 +933,8 @@ test "iterator hash map" { |
| 498 | it.reset(); | 933 | it.reset(); |
| 499 | 934 | ||
| 500 | var count: usize = 0; | 935 | var count: usize = 0; |
| 501 | while (it.next()) |kv| : (count += 1) { | 936 | while (it.next()) |entry| : (count += 1) { |
| 502 | buffer[@intCast(usize, kv.key)] = kv.value; | 937 | buffer[@intCast(usize, entry.key)] = entry.value; |
| 503 | } | 938 | } |
| 504 | testing.expect(count == 3); | 939 | testing.expect(count == 3); |
| 505 | testing.expect(it.next() == null); | 940 | testing.expect(it.next() == null); |
| ... | @@ -510,8 +945,8 @@ test "iterator hash map" { | ... | @@ -510,8 +945,8 @@ test "iterator hash map" { |
| 510 | 945 | ||
| 511 | it.reset(); | 946 | it.reset(); |
| 512 | count = 0; | 947 | count = 0; |
| 513 | while (it.next()) |kv| { | 948 | while (it.next()) |entry| { |
| 514 | buffer[@intCast(usize, kv.key)] = kv.value; | 949 | buffer[@intCast(usize, entry.key)] = entry.value; |
| 515 | count += 1; | 950 | count += 1; |
| 516 | if (count >= 2) break; | 951 | if (count >= 2) break; |
| 517 | } | 952 | } |
| ... | @@ -531,14 +966,14 @@ test "ensure capacity" { | ... | @@ -531,14 +966,14 @@ test "ensure capacity" { |
| 531 | defer map.deinit(); | 966 | defer map.deinit(); |
| 532 | 967 | ||
| 533 | try map.ensureCapacity(20); | 968 | try map.ensureCapacity(20); |
| 534 | const initialCapacity = map.entries.len; | 969 | const initial_capacity = map.capacity(); |
| 535 | testing.expect(initialCapacity >= 20); | 970 | testing.expect(initial_capacity >= 20); |
| 536 | var i: i32 = 0; | 971 | var i: i32 = 0; |
| 537 | while (i < 20) : (i += 1) { | 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 | // shouldn't resize from putAssumeCapacity | 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 | pub fn getHashPtrAddrFn(comptime K: type) (fn (K) u32) { | 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,6 +1010,24 @@ pub fn getAutoEqlFn(comptime K: type) (fn (K, K) bool) { |
| 575 | }.eql; | 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 | pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) { | 1031 | pub fn getAutoHashStratFn(comptime K: type, comptime strategy: std.hash.Strategy) (fn (K) u32) { |
| 579 | return struct { | 1032 | return struct { |
| 580 | fn hash(key: K) u32 { | 1033 | fn hash(key: K) u32 { |
lib/std/http/headers.zig+35-37| ... | @@ -118,13 +118,12 @@ pub const Headers = struct { | ... | @@ -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(); | 123 | for (self.index.items()) |*entry| { |
| 124 | while (it.next()) |kv| { | 124 | const dex = &entry.value; |
| 125 | var dex = &kv.value; | ||
| 126 | dex.deinit(); | 125 | dex.deinit(); |
| 127 | self.allocator.free(kv.key); | 126 | self.allocator.free(entry.key); |
| 128 | } | 127 | } |
| 129 | self.index.deinit(); | 128 | self.index.deinit(); |
| 130 | } | 129 | } |
| ... | @@ -134,6 +133,7 @@ pub const Headers = struct { | ... | @@ -134,6 +133,7 @@ pub const Headers = struct { |
| 134 | } | 133 | } |
| 135 | self.data.deinit(); | 134 | self.data.deinit(); |
| 136 | } | 135 | } |
| 136 | self.* = undefined; | ||
| 137 | } | 137 | } |
| 138 | 138 | ||
| 139 | pub fn clone(self: Self, allocator: *Allocator) !Self { | 139 | pub fn clone(self: Self, allocator: *Allocator) !Self { |
| ... | @@ -155,10 +155,10 @@ pub const Headers = struct { | ... | @@ -155,10 +155,10 @@ pub const Headers = struct { |
| 155 | const n = self.data.items.len + 1; | 155 | const n = self.data.items.len + 1; |
| 156 | try self.data.ensureCapacity(n); | 156 | try self.data.ensureCapacity(n); |
| 157 | var entry: HeaderEntry = undefined; | 157 | var entry: HeaderEntry = undefined; |
| 158 | if (self.index.get(name)) |kv| { | 158 | if (self.index.getEntry(name)) |kv| { |
| 159 | entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index); | 159 | entry = try HeaderEntry.init(self.allocator, kv.key, value, never_index); |
| 160 | errdefer entry.deinit(); | 160 | errdefer entry.deinit(); |
| 161 | var dex = &kv.value; | 161 | const dex = &kv.value; |
| 162 | try dex.append(n - 1); | 162 | try dex.append(n - 1); |
| 163 | } else { | 163 | } else { |
| 164 | const name_dup = try self.allocator.dupe(u8, name); | 164 | const name_dup = try self.allocator.dupe(u8, name); |
| ... | @@ -195,7 +195,7 @@ pub const Headers = struct { | ... | @@ -195,7 +195,7 @@ pub const Headers = struct { |
| 195 | /// Returns boolean indicating if something was deleted. | 195 | /// Returns boolean indicating if something was deleted. |
| 196 | pub fn delete(self: *Self, name: []const u8) bool { | 196 | pub fn delete(self: *Self, name: []const u8) bool { |
| 197 | if (self.index.remove(name)) |kv| { | 197 | if (self.index.remove(name)) |kv| { |
| 198 | var dex = &kv.value; | 198 | const dex = &kv.value; |
| 199 | // iterate backwards | 199 | // iterate backwards |
| 200 | var i = dex.items.len; | 200 | var i = dex.items.len; |
| 201 | while (i > 0) { | 201 | while (i > 0) { |
| ... | @@ -207,7 +207,7 @@ pub const Headers = struct { | ... | @@ -207,7 +207,7 @@ pub const Headers = struct { |
| 207 | } | 207 | } |
| 208 | dex.deinit(); | 208 | dex.deinit(); |
| 209 | self.allocator.free(kv.key); | 209 | self.allocator.free(kv.key); |
| 210 | self.rebuild_index(); | 210 | self.rebuildIndex(); |
| 211 | return true; | 211 | return true; |
| 212 | } else { | 212 | } else { |
| 213 | return false; | 213 | return false; |
| ... | @@ -216,45 +216,52 @@ pub const Headers = struct { | ... | @@ -216,45 +216,52 @@ pub const Headers = struct { |
| 216 | 216 | ||
| 217 | /// Removes the element at the specified index. | 217 | /// Removes the element at the specified index. |
| 218 | /// Moves items down to fill the empty space. | 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 | pub fn orderedRemove(self: *Self, i: usize) void { | 222 | pub fn orderedRemove(self: *Self, i: usize) void { |
| 220 | const removed = self.data.orderedRemove(i); | 223 | const removed = self.data.orderedRemove(i); |
| 221 | const kv = self.index.get(removed.name).?; | 224 | const kv = self.index.getEntry(removed.name).?; |
| 222 | var dex = &kv.value; | 225 | const dex = &kv.value; |
| 223 | if (dex.items.len == 1) { | 226 | if (dex.items.len == 1) { |
| 224 | // was last item; delete the index | 227 | // was last item; delete the index |
| 225 | _ = self.index.remove(kv.key); | ||
| 226 | dex.deinit(); | 228 | dex.deinit(); |
| 227 | removed.deinit(); | 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 | } else { | 233 | } else { |
| 230 | dex.shrink(dex.items.len - 1); | 234 | dex.shrink(dex.items.len - 1); |
| 231 | removed.deinit(); | 235 | removed.deinit(); |
| 232 | } | 236 | } |
| 233 | // if it was the last item; no need to rebuild index | 237 | // if it was the last item; no need to rebuild index |
| 234 | if (i != self.data.items.len) { | 238 | if (i != self.data.items.len) { |
| 235 | self.rebuild_index(); | 239 | self.rebuildIndex(); |
| 236 | } | 240 | } |
| 237 | } | 241 | } |
| 238 | 242 | ||
| 239 | /// Removes the element at the specified index. | 243 | /// Removes the element at the specified index. |
| 240 | /// The empty slot is filled from the end of the list. | 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 | pub fn swapRemove(self: *Self, i: usize) void { | 247 | pub fn swapRemove(self: *Self, i: usize) void { |
| 242 | const removed = self.data.swapRemove(i); | 248 | const removed = self.data.swapRemove(i); |
| 243 | const kv = self.index.get(removed.name).?; | 249 | const kv = self.index.getEntry(removed.name).?; |
| 244 | var dex = &kv.value; | 250 | const dex = &kv.value; |
| 245 | if (dex.items.len == 1) { | 251 | if (dex.items.len == 1) { |
| 246 | // was last item; delete the index | 252 | // was last item; delete the index |
| 247 | _ = self.index.remove(kv.key); | ||
| 248 | dex.deinit(); | 253 | dex.deinit(); |
| 249 | removed.deinit(); | 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 | } else { | 258 | } else { |
| 252 | dex.shrink(dex.items.len - 1); | 259 | dex.shrink(dex.items.len - 1); |
| 253 | removed.deinit(); | 260 | removed.deinit(); |
| 254 | } | 261 | } |
| 255 | // if it was the last item; no need to rebuild index | 262 | // if it was the last item; no need to rebuild index |
| 256 | if (i != self.data.items.len) { | 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,11 +273,7 @@ pub const Headers = struct { |
| 266 | /// Returns a list of indices containing headers with the given name. | 273 | /// Returns a list of indices containing headers with the given name. |
| 267 | /// The returned list should not be modified by the caller. | 274 | /// The returned list should not be modified by the caller. |
| 268 | pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList { | 275 | pub fn getIndices(self: Self, name: []const u8) ?HeaderIndexList { |
| 269 | if (self.index.get(name)) |kv| { | 276 | return self.index.get(name); |
| 270 | return kv.value; | ||
| 271 | } else { | ||
| 272 | return null; | ||
| 273 | } | ||
| 274 | } | 277 | } |
| 275 | 278 | ||
| 276 | /// Returns a slice containing each header with the given name. | 279 | /// Returns a slice containing each header with the given name. |
| ... | @@ -325,25 +328,20 @@ pub const Headers = struct { | ... | @@ -325,25 +328,20 @@ pub const Headers = struct { |
| 325 | return buf; | 328 | return buf; |
| 326 | } | 329 | } |
| 327 | 330 | ||
| 328 | fn rebuild_index(self: *Self) void { | 331 | fn rebuildIndex(self: *Self) void { |
| 329 | { // clear out the indexes | 332 | // clear out the indexes |
| 330 | var it = self.index.iterator(); | 333 | for (self.index.items()) |*entry| { |
| 331 | while (it.next()) |kv| { | 334 | entry.value.shrinkRetainingCapacity(0); |
| 332 | var dex = &kv.value; | ||
| 333 | dex.items.len = 0; // keeps capacity available | ||
| 334 | } | ||
| 335 | } | 335 | } |
| 336 | { // fill up indexes again; we know capacity is fine from before | 336 | // fill up indexes again; we know capacity is fine from before |
| 337 | for (self.data.span()) |entry, i| { | 337 | for (self.data.items) |entry, i| { |
| 338 | var dex = &self.index.get(entry.name).?.value; | 338 | self.index.getEntry(entry.name).?.value.appendAssumeCapacity(i); |
| 339 | dex.appendAssumeCapacity(i); | ||
| 340 | } | ||
| 341 | } | 339 | } |
| 342 | } | 340 | } |
| 343 | 341 | ||
| 344 | pub fn sort(self: *Self) void { | 342 | pub fn sort(self: *Self) void { |
| 345 | std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare); | 343 | std.sort.sort(HeaderEntry, self.data.items, {}, HeaderEntry.compare); |
| 346 | self.rebuild_index(); | 344 | self.rebuildIndex(); |
| 347 | } | 345 | } |
| 348 | 346 | ||
| 349 | pub fn format( | 347 | pub fn format( |
lib/std/json.zig+28-28| ... | @@ -2149,27 +2149,27 @@ test "json.parser.dynamic" { | ... | @@ -2149,27 +2149,27 @@ test "json.parser.dynamic" { |
| 2149 | 2149 | ||
| 2150 | var root = tree.root; | 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 | testing.expect(width.Integer == 800); | 2155 | testing.expect(width.Integer == 800); |
| 2156 | 2156 | ||
| 2157 | const height = image.Object.get("Height").?.value; | 2157 | const height = image.Object.get("Height").?; |
| 2158 | testing.expect(height.Integer == 600); | 2158 | testing.expect(height.Integer == 600); |
| 2159 | 2159 | ||
| 2160 | const title = image.Object.get("Title").?.value; | 2160 | const title = image.Object.get("Title").?; |
| 2161 | testing.expect(mem.eql(u8, title.String, "View from 15th Floor")); | 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 | testing.expect(animated.Bool == false); | 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 | testing.expect(array_of_object.Array.items.len == 1); | 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 | testing.expect(mem.eql(u8, obj0.String, "m")); | 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 | testing.expect(double.Float == 1.3412); | 2173 | testing.expect(double.Float == 1.3412); |
| 2174 | } | 2174 | } |
| 2175 | 2175 | ||
| ... | @@ -2217,12 +2217,12 @@ test "write json then parse it" { | ... | @@ -2217,12 +2217,12 @@ test "write json then parse it" { |
| 2217 | var tree = try parser.parse(fixed_buffer_stream.getWritten()); | 2217 | var tree = try parser.parse(fixed_buffer_stream.getWritten()); |
| 2218 | defer tree.deinit(); | 2218 | defer tree.deinit(); |
| 2219 | 2219 | ||
| 2220 | testing.expect(tree.root.Object.get("f").?.value.Bool == false); | 2220 | testing.expect(tree.root.Object.get("f").?.Bool == false); |
| 2221 | testing.expect(tree.root.Object.get("t").?.value.Bool == true); | 2221 | testing.expect(tree.root.Object.get("t").?.Bool == true); |
| 2222 | testing.expect(tree.root.Object.get("int").?.value.Integer == 1234); | 2222 | testing.expect(tree.root.Object.get("int").?.Integer == 1234); |
| 2223 | testing.expect(tree.root.Object.get("array").?.value.Array.items[0].Null == {}); | 2223 | testing.expect(tree.root.Object.get("array").?.Array.items[0].Null == {}); |
| 2224 | testing.expect(tree.root.Object.get("array").?.value.Array.items[1].Float == 12.34); | 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").?.value.String, "hello")); | 2225 | testing.expect(mem.eql(u8, tree.root.Object.get("str").?.String, "hello")); |
| 2226 | } | 2226 | } |
| 2227 | 2227 | ||
| 2228 | fn test_parse(arena_allocator: *std.mem.Allocator, json_str: []const u8) !Value { | 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,7 +2245,7 @@ test "integer after float has proper type" { |
| 2245 | \\ "ints": [1, 2, 3] | 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 | test "escaped characters" { | 2251 | test "escaped characters" { |
| ... | @@ -2271,16 +2271,16 @@ test "escaped characters" { | ... | @@ -2271,16 +2271,16 @@ test "escaped characters" { |
| 2271 | 2271 | ||
| 2272 | const obj = (try test_parse(&arena_allocator.allocator, input)).Object; | 2272 | const obj = (try test_parse(&arena_allocator.allocator, input)).Object; |
| 2273 | 2273 | ||
| 2274 | testing.expectEqualSlices(u8, obj.get("backslash").?.value.String, "\\"); | 2274 | testing.expectEqualSlices(u8, obj.get("backslash").?.String, "\\"); |
| 2275 | testing.expectEqualSlices(u8, obj.get("forwardslash").?.value.String, "/"); | 2275 | testing.expectEqualSlices(u8, obj.get("forwardslash").?.String, "/"); |
| 2276 | testing.expectEqualSlices(u8, obj.get("newline").?.value.String, "\n"); | 2276 | testing.expectEqualSlices(u8, obj.get("newline").?.String, "\n"); |
| 2277 | testing.expectEqualSlices(u8, obj.get("carriagereturn").?.value.String, "\r"); | 2277 | testing.expectEqualSlices(u8, obj.get("carriagereturn").?.String, "\r"); |
| 2278 | testing.expectEqualSlices(u8, obj.get("tab").?.value.String, "\t"); | 2278 | testing.expectEqualSlices(u8, obj.get("tab").?.String, "\t"); |
| 2279 | testing.expectEqualSlices(u8, obj.get("formfeed").?.value.String, "\x0C"); | 2279 | testing.expectEqualSlices(u8, obj.get("formfeed").?.String, "\x0C"); |
| 2280 | testing.expectEqualSlices(u8, obj.get("backspace").?.value.String, "\x08"); | 2280 | testing.expectEqualSlices(u8, obj.get("backspace").?.String, "\x08"); |
| 2281 | testing.expectEqualSlices(u8, obj.get("doublequote").?.value.String, "\""); | 2281 | testing.expectEqualSlices(u8, obj.get("doublequote").?.String, "\""); |
| 2282 | testing.expectEqualSlices(u8, obj.get("unicode").?.value.String, "ą"); | 2282 | testing.expectEqualSlices(u8, obj.get("unicode").?.String, "ą"); |
| 2283 | testing.expectEqualSlices(u8, obj.get("surrogatepair").?.value.String, "😂"); | 2283 | testing.expectEqualSlices(u8, obj.get("surrogatepair").?.String, "😂"); |
| 2284 | } | 2284 | } |
| 2285 | 2285 | ||
| 2286 | test "string copy option" { | 2286 | test "string copy option" { |
| ... | @@ -2306,11 +2306,11 @@ test "string copy option" { | ... | @@ -2306,11 +2306,11 @@ test "string copy option" { |
| 2306 | const obj_copy = tree_copy.root.Object; | 2306 | const obj_copy = tree_copy.root.Object; |
| 2307 | 2307 | ||
| 2308 | for ([_][]const u8{ "noescape", "simple", "unicode", "surrogatepair" }) |field_name| { | 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]; | 2312 | const nocopy_addr = &obj_nocopy.get("noescape").?.String[0]; |
| 2313 | const copy_addr = &obj_copy.getValue("noescape").?.String[0]; | 2313 | const copy_addr = &obj_copy.get("noescape").?.String[0]; |
| 2314 | 2314 | ||
| 2315 | var found_nocopy = false; | 2315 | var found_nocopy = false; |
| 2316 | for (input) |_, index| { | 2316 | for (input) |_, index| { |
src-self-hosted/Module.zig+83-114| ... | @@ -75,7 +75,7 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, | ... | @@ -75,7 +75,7 @@ deletion_set: std.ArrayListUnmanaged(*Decl) = .{}, |
| 75 | 75 | ||
| 76 | keep_source_files_loaded: bool, | 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 | const WorkItem = union(enum) { | 80 | const WorkItem = union(enum) { |
| 81 | /// Write the machine code for a Decl to the output file. | 81 | /// Write the machine code for a Decl to the output file. |
| ... | @@ -795,49 +795,38 @@ pub fn deinit(self: *Module) void { | ... | @@ -795,49 +795,38 @@ pub fn deinit(self: *Module) void { |
| 795 | const allocator = self.allocator; | 795 | const allocator = self.allocator; |
| 796 | self.deletion_set.deinit(allocator); | 796 | self.deletion_set.deinit(allocator); |
| 797 | self.work_queue.deinit(); | 797 | self.work_queue.deinit(); |
| 798 | { | 798 | |
| 799 | var it = self.decl_table.iterator(); | 799 | for (self.decl_table.items()) |entry| { |
| 800 | while (it.next()) |kv| { | 800 | entry.value.destroy(allocator); |
| 801 | kv.value.destroy(allocator); | ||
| 802 | } | ||
| 803 | self.decl_table.deinit(); | ||
| 804 | } | 801 | } |
| 805 | { | 802 | self.decl_table.deinit(); |
| 806 | var it = self.failed_decls.iterator(); | 803 | |
| 807 | while (it.next()) |kv| { | 804 | for (self.failed_decls.items()) |entry| { |
| 808 | kv.value.destroy(allocator); | 805 | entry.value.destroy(allocator); |
| 809 | } | ||
| 810 | self.failed_decls.deinit(); | ||
| 811 | } | 806 | } |
| 812 | { | 807 | self.failed_decls.deinit(); |
| 813 | var it = self.failed_files.iterator(); | 808 | |
| 814 | while (it.next()) |kv| { | 809 | for (self.failed_files.items()) |entry| { |
| 815 | kv.value.destroy(allocator); | 810 | entry.value.destroy(allocator); |
| 816 | } | ||
| 817 | self.failed_files.deinit(); | ||
| 818 | } | 811 | } |
| 819 | { | 812 | self.failed_files.deinit(); |
| 820 | var it = self.failed_exports.iterator(); | 813 | |
| 821 | while (it.next()) |kv| { | 814 | for (self.failed_exports.items()) |entry| { |
| 822 | kv.value.destroy(allocator); | 815 | entry.value.destroy(allocator); |
| 823 | } | ||
| 824 | self.failed_exports.deinit(); | ||
| 825 | } | 816 | } |
| 826 | { | 817 | self.failed_exports.deinit(); |
| 827 | var it = self.decl_exports.iterator(); | 818 | |
| 828 | while (it.next()) |kv| { | 819 | for (self.decl_exports.items()) |entry| { |
| 829 | const export_list = kv.value; | 820 | const export_list = entry.value; |
| 830 | allocator.free(export_list); | 821 | allocator.free(export_list); |
| 831 | } | ||
| 832 | self.decl_exports.deinit(); | ||
| 833 | } | 822 | } |
| 834 | { | 823 | self.decl_exports.deinit(); |
| 835 | var it = self.export_owners.iterator(); | 824 | |
| 836 | while (it.next()) |kv| { | 825 | for (self.export_owners.items()) |entry| { |
| 837 | freeExportList(allocator, kv.value); | 826 | freeExportList(allocator, entry.value); |
| 838 | } | ||
| 839 | self.export_owners.deinit(); | ||
| 840 | } | 827 | } |
| 828 | self.export_owners.deinit(); | ||
| 829 | |||
| 841 | self.symbol_exports.deinit(); | 830 | self.symbol_exports.deinit(); |
| 842 | self.root_scope.destroy(allocator); | 831 | self.root_scope.destroy(allocator); |
| 843 | self.* = undefined; | 832 | self.* = undefined; |
| ... | @@ -918,9 +907,9 @@ pub fn makeBinFileWritable(self: *Module) !void { | ... | @@ -918,9 +907,9 @@ pub fn makeBinFileWritable(self: *Module) !void { |
| 918 | } | 907 | } |
| 919 | 908 | ||
| 920 | pub fn totalErrorCount(self: *Module) usize { | 909 | pub fn totalErrorCount(self: *Module) usize { |
| 921 | const total = self.failed_decls.size + | 910 | const total = self.failed_decls.items().len + |
| 922 | self.failed_files.size + | 911 | self.failed_files.items().len + |
| 923 | self.failed_exports.size; | 912 | self.failed_exports.items().len; |
| 924 | return if (total == 0) @boolToInt(self.link_error_flags.no_entry_point_found) else total; | 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,32 +920,23 @@ pub fn getAllErrorsAlloc(self: *Module) !AllErrors { |
| 931 | var errors = std.ArrayList(AllErrors.Message).init(self.allocator); | 920 | var errors = std.ArrayList(AllErrors.Message).init(self.allocator); |
| 932 | defer errors.deinit(); | 921 | defer errors.deinit(); |
| 933 | 922 | ||
| 934 | { | 923 | for (self.failed_files.items()) |entry| { |
| 935 | var it = self.failed_files.iterator(); | 924 | const scope = entry.key; |
| 936 | while (it.next()) |kv| { | 925 | const err_msg = entry.value; |
| 937 | const scope = kv.key; | 926 | const source = try scope.getSource(self); |
| 938 | const err_msg = kv.value; | 927 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); |
| 939 | const source = try scope.getSource(self); | ||
| 940 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); | ||
| 941 | } | ||
| 942 | } | 928 | } |
| 943 | { | 929 | for (self.failed_decls.items()) |entry| { |
| 944 | var it = self.failed_decls.iterator(); | 930 | const decl = entry.key; |
| 945 | while (it.next()) |kv| { | 931 | const err_msg = entry.value; |
| 946 | const decl = kv.key; | 932 | const source = try decl.scope.getSource(self); |
| 947 | const err_msg = kv.value; | 933 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); |
| 948 | const source = try decl.scope.getSource(self); | ||
| 949 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | ||
| 950 | } | ||
| 951 | } | 934 | } |
| 952 | { | 935 | for (self.failed_exports.items()) |entry| { |
| 953 | var it = self.failed_exports.iterator(); | 936 | const decl = entry.key.owner_decl; |
| 954 | while (it.next()) |kv| { | 937 | const err_msg = entry.value; |
| 955 | const decl = kv.key.owner_decl; | 938 | const source = try decl.scope.getSource(self); |
| 956 | const err_msg = kv.value; | 939 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); |
| 957 | const source = try decl.scope.getSource(self); | ||
| 958 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | ||
| 959 | } | ||
| 960 | } | 940 | } |
| 961 | 941 | ||
| 962 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { | 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,7 +996,7 @@ pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { |
| 1016 | decl.analysis = .dependency_failure; | 996 | decl.analysis = .dependency_failure; |
| 1017 | }, | 997 | }, |
| 1018 | else => { | 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 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 1000 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1021 | self.allocator, | 1001 | self.allocator, |
| 1022 | decl.src(), | 1002 | decl.src(), |
| ... | @@ -1086,7 +1066,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { | ... | @@ -1086,7 +1066,7 @@ fn ensureDeclAnalyzed(self: *Module, decl: *Decl) InnerError!void { |
| 1086 | error.OutOfMemory => return error.OutOfMemory, | 1066 | error.OutOfMemory => return error.OutOfMemory, |
| 1087 | error.AnalysisFail => return error.AnalysisFail, | 1067 | error.AnalysisFail => return error.AnalysisFail, |
| 1088 | else => { | 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 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | 1070 | self.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( |
| 1091 | self.allocator, | 1071 | self.allocator, |
| 1092 | decl.src(), | 1072 | decl.src(), |
| ... | @@ -1636,7 +1616,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void | ... | @@ -1636,7 +1616,7 @@ fn declareDeclDependency(self: *Module, depender: *Decl, dependee: *Decl) !void |
| 1636 | fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { | 1616 | fn getSrcModule(self: *Module, root_scope: *Scope.ZIRModule) !*zir.Module { |
| 1637 | switch (root_scope.status) { | 1617 | switch (root_scope.status) { |
| 1638 | .never_loaded, .unloaded_success => { | 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 | const source = try root_scope.getSource(self); | 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,7 +1657,7 @@ fn getAstTree(self: *Module, root_scope: *Scope.File) !*ast.Tree { |
| 1677 | 1657 | ||
| 1678 | switch (root_scope.status) { | 1658 | switch (root_scope.status) { |
| 1679 | .never_loaded, .unloaded_success => { | 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 | const source = try root_scope.getSource(self); | 1662 | const source = try root_scope.getSource(self); |
| 1683 | 1663 | ||
| ... | @@ -1745,8 +1725,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { | ... | @@ -1745,8 +1725,7 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1745 | const name = tree.tokenSliceLoc(name_loc); | 1725 | const name = tree.tokenSliceLoc(name_loc); |
| 1746 | const name_hash = root_scope.fullyQualifiedNameHash(name); | 1726 | const name_hash = root_scope.fullyQualifiedNameHash(name); |
| 1747 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); | 1727 | const contents_hash = std.zig.hashSrc(tree.getNodeSource(src_decl)); |
| 1748 | if (self.decl_table.get(name_hash)) |kv| { | 1728 | if (self.decl_table.get(name_hash)) |decl| { |
| 1749 | const decl = kv.value; | ||
| 1750 | // Update the AST Node index of the decl, even if its contents are unchanged, it may | 1729 | // Update the AST Node index of the decl, even if its contents are unchanged, it may |
| 1751 | // have been re-ordered. | 1730 | // have been re-ordered. |
| 1752 | decl.src_index = decl_i; | 1731 | decl.src_index = decl_i; |
| ... | @@ -1774,14 +1753,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { | ... | @@ -1774,14 +1753,11 @@ fn analyzeRootSrcFile(self: *Module, root_scope: *Scope.File) !void { |
| 1774 | // TODO also look for global variable declarations | 1753 | // TODO also look for global variable declarations |
| 1775 | // TODO also look for comptime blocks and exported globals | 1754 | // TODO also look for comptime blocks and exported globals |
| 1776 | } | 1755 | } |
| 1777 | { | 1756 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 1778 | // Handle explicitly deleted decls from the source code. Not to be confused | 1757 | // with when we delete decls because they are no longer referenced. |
| 1779 | // with when we delete decls because they are no longer referenced. | 1758 | for (deleted_decls.items()) |entry| { |
| 1780 | var it = deleted_decls.iterator(); | 1759 | //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name}); |
| 1781 | while (it.next()) |kv| { | 1760 | try self.deleteDecl(entry.key); |
| 1782 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | ||
| 1783 | try self.deleteDecl(kv.key); | ||
| 1784 | } | ||
| 1785 | } | 1761 | } |
| 1786 | } | 1762 | } |
| 1787 | 1763 | ||
| ... | @@ -1800,18 +1776,14 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { | ... | @@ -1800,18 +1776,14 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1800 | // we know which ones have been deleted. | 1776 | // we know which ones have been deleted. |
| 1801 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); | 1777 | var deleted_decls = std.AutoHashMap(*Decl, void).init(self.allocator); |
| 1802 | defer deleted_decls.deinit(); | 1778 | defer deleted_decls.deinit(); |
| 1803 | try deleted_decls.ensureCapacity(self.decl_table.size); | 1779 | try deleted_decls.ensureCapacity(self.decl_table.items().len); |
| 1804 | { | 1780 | for (self.decl_table.items()) |entry| { |
| 1805 | var it = self.decl_table.iterator(); | 1781 | deleted_decls.putAssumeCapacityNoClobber(entry.value, {}); |
| 1806 | while (it.next()) |kv| { | ||
| 1807 | deleted_decls.putAssumeCapacityNoClobber(kv.value, {}); | ||
| 1808 | } | ||
| 1809 | } | 1782 | } |
| 1810 | 1783 | ||
| 1811 | for (src_module.decls) |src_decl, decl_i| { | 1784 | for (src_module.decls) |src_decl, decl_i| { |
| 1812 | const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name); | 1785 | const name_hash = root_scope.fullyQualifiedNameHash(src_decl.name); |
| 1813 | if (self.decl_table.get(name_hash)) |kv| { | 1786 | if (self.decl_table.get(name_hash)) |decl| { |
| 1814 | const decl = kv.value; | ||
| 1815 | deleted_decls.removeAssertDiscard(decl); | 1787 | deleted_decls.removeAssertDiscard(decl); |
| 1816 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); | 1788 | //std.debug.warn("'{}' contents: '{}'\n", .{ src_decl.name, src_decl.contents }); |
| 1817 | if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { | 1789 | if (!srcHashEql(src_decl.contents_hash, decl.contents_hash)) { |
| ... | @@ -1835,14 +1807,11 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { | ... | @@ -1835,14 +1807,11 @@ fn analyzeRootZIRModule(self: *Module, root_scope: *Scope.ZIRModule) !void { |
| 1835 | for (exports_to_resolve.items) |export_decl| { | 1807 | for (exports_to_resolve.items) |export_decl| { |
| 1836 | _ = try self.resolveZirDecl(&root_scope.base, export_decl); | 1808 | _ = try self.resolveZirDecl(&root_scope.base, export_decl); |
| 1837 | } | 1809 | } |
| 1838 | { | 1810 | // Handle explicitly deleted decls from the source code. Not to be confused |
| 1839 | // Handle explicitly deleted decls from the source code. Not to be confused | 1811 | // with when we delete decls because they are no longer referenced. |
| 1840 | // with when we delete decls because they are no longer referenced. | 1812 | for (deleted_decls.items()) |entry| { |
| 1841 | var it = deleted_decls.iterator(); | 1813 | //std.debug.warn("noticed '{}' deleted from source\n", .{entry.key.name}); |
| 1842 | while (it.next()) |kv| { | 1814 | try self.deleteDecl(entry.key); |
| 1843 | //std.debug.warn("noticed '{}' deleted from source\n", .{kv.key.name}); | ||
| 1844 | try self.deleteDecl(kv.key); | ||
| 1845 | } | ||
| 1846 | } | 1815 | } |
| 1847 | } | 1816 | } |
| 1848 | 1817 | ||
| ... | @@ -1888,7 +1857,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { | ... | @@ -1888,7 +1857,7 @@ fn deleteDeclExports(self: *Module, decl: *Decl) void { |
| 1888 | const kv = self.export_owners.remove(decl) orelse return; | 1857 | const kv = self.export_owners.remove(decl) orelse return; |
| 1889 | 1858 | ||
| 1890 | for (kv.value) |exp| { | 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 | // Remove exports with owner_decl matching the regenerating decl. | 1861 | // Remove exports with owner_decl matching the regenerating decl. |
| 1893 | const list = decl_exports_kv.value; | 1862 | const list = decl_exports_kv.value; |
| 1894 | var i: usize = 0; | 1863 | var i: usize = 0; |
| ... | @@ -1983,7 +1952,7 @@ fn createNewDecl( | ... | @@ -1983,7 +1952,7 @@ fn createNewDecl( |
| 1983 | name_hash: Scope.NameHash, | 1952 | name_hash: Scope.NameHash, |
| 1984 | contents_hash: std.zig.SrcHash, | 1953 | contents_hash: std.zig.SrcHash, |
| 1985 | ) !*Decl { | 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 | const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash); | 1956 | const new_decl = try self.allocateNewDecl(scope, src_index, contents_hash); |
| 1988 | errdefer self.allocator.destroy(new_decl); | 1957 | errdefer self.allocator.destroy(new_decl); |
| 1989 | new_decl.name = try mem.dupeZ(self.allocator, u8, decl_name); | 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,7 +2012,7 @@ fn resolveZirDecl(self: *Module, scope: *Scope, src_decl: *zir.Decl) InnerError! |
| 2043 | 2012 | ||
| 2044 | fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { | 2013 | fn resolveZirDeclHavingIndex(self: *Module, scope: *Scope, src_decl: *zir.Decl, src_index: usize) InnerError!*Decl { |
| 2045 | const name_hash = scope.namespace().fullyQualifiedNameHash(src_decl.name); | 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 | decl.src_index = src_index; | 2016 | decl.src_index = src_index; |
| 2048 | try self.ensureDeclAnalyzed(decl); | 2017 | try self.ensureDeclAnalyzed(decl); |
| 2049 | return decl; | 2018 | return decl; |
| ... | @@ -2148,8 +2117,8 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const | ... | @@ -2148,8 +2117,8 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2148 | else => return self.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), | 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); | 2120 | try self.decl_exports.ensureCapacity(self.decl_exports.items().len + 1); |
| 2152 | try self.export_owners.ensureCapacity(self.export_owners.size + 1); | 2121 | try self.export_owners.ensureCapacity(self.export_owners.items().len + 1); |
| 2153 | 2122 | ||
| 2154 | const new_export = try self.allocator.create(Export); | 2123 | const new_export = try self.allocator.create(Export); |
| 2155 | errdefer self.allocator.destroy(new_export); | 2124 | errdefer self.allocator.destroy(new_export); |
| ... | @@ -2168,23 +2137,23 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const | ... | @@ -2168,23 +2137,23 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2168 | // Add to export_owners table. | 2137 | // Add to export_owners table. |
| 2169 | const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable; | 2138 | const eo_gop = self.export_owners.getOrPut(owner_decl) catch unreachable; |
| 2170 | if (!eo_gop.found_existing) { | 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); | 2142 | eo_gop.entry.value = try self.allocator.realloc(eo_gop.entry.value, eo_gop.entry.value.len + 1); |
| 2174 | eo_gop.kv.value[eo_gop.kv.value.len - 1] = new_export; | 2143 | eo_gop.entry.value[eo_gop.entry.value.len - 1] = new_export; |
| 2175 | errdefer eo_gop.kv.value = self.allocator.shrink(eo_gop.kv.value, eo_gop.kv.value.len - 1); | 2144 | errdefer eo_gop.entry.value = self.allocator.shrink(eo_gop.entry.value, eo_gop.entry.value.len - 1); |
| 2176 | 2145 | ||
| 2177 | // Add to exported_decl table. | 2146 | // Add to exported_decl table. |
| 2178 | const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable; | 2147 | const de_gop = self.decl_exports.getOrPut(exported_decl) catch unreachable; |
| 2179 | if (!de_gop.found_existing) { | 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); | 2151 | de_gop.entry.value = try self.allocator.realloc(de_gop.entry.value, de_gop.entry.value.len + 1); |
| 2183 | de_gop.kv.value[de_gop.kv.value.len - 1] = new_export; | 2152 | de_gop.entry.value[de_gop.entry.value.len - 1] = new_export; |
| 2184 | errdefer de_gop.kv.value = self.allocator.shrink(de_gop.kv.value, de_gop.kv.value.len - 1); | 2153 | errdefer de_gop.entry.value = self.allocator.shrink(de_gop.entry.value, de_gop.entry.value.len - 1); |
| 2185 | 2154 | ||
| 2186 | if (self.symbol_exports.get(symbol_name)) |_| { | 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 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( | 2157 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( |
| 2189 | self.allocator, | 2158 | self.allocator, |
| 2190 | src, | 2159 | src, |
| ... | @@ -2197,10 +2166,10 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const | ... | @@ -2197,10 +2166,10 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const |
| 2197 | } | 2166 | } |
| 2198 | 2167 | ||
| 2199 | try self.symbol_exports.putNoClobber(symbol_name, new_export); | 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 | error.OutOfMemory => return error.OutOfMemory, | 2170 | error.OutOfMemory => return error.OutOfMemory, |
| 2202 | else => { | 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 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( | 2173 | self.failed_exports.putAssumeCapacityNoClobber(new_export, try ErrorMsg.create( |
| 2205 | self.allocator, | 2174 | self.allocator, |
| 2206 | src, | 2175 | src, |
| ... | @@ -2494,7 +2463,7 @@ fn getNextAnonNameIndex(self: *Module) usize { | ... | @@ -2494,7 +2463,7 @@ fn getNextAnonNameIndex(self: *Module) usize { |
| 2494 | fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { | 2463 | fn lookupDeclName(self: *Module, scope: *Scope, ident_name: []const u8) ?*Decl { |
| 2495 | const namespace = scope.namespace(); | 2464 | const namespace = scope.namespace(); |
| 2496 | const name_hash = namespace.fullyQualifiedNameHash(ident_name); | 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 | fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { | 2469 | fn analyzeInstExport(self: *Module, scope: *Scope, export_inst: *zir.Inst.Export) InnerError!*Inst { |
| ... | @@ -3489,8 +3458,8 @@ fn failNode( | ... | @@ -3489,8 +3458,8 @@ fn failNode( |
| 3489 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { | 3458 | fn failWithOwnedErrorMsg(self: *Module, scope: *Scope, src: usize, err_msg: *ErrorMsg) InnerError { |
| 3490 | { | 3459 | { |
| 3491 | errdefer err_msg.destroy(self.allocator); | 3460 | errdefer err_msg.destroy(self.allocator); |
| 3492 | try self.failed_decls.ensureCapacity(self.failed_decls.size + 1); | 3461 | try self.failed_decls.ensureCapacity(self.failed_decls.items().len + 1); |
| 3493 | try self.failed_files.ensureCapacity(self.failed_files.size + 1); | 3462 | try self.failed_files.ensureCapacity(self.failed_files.items().len + 1); |
| 3494 | } | 3463 | } |
| 3495 | switch (scope.tag) { | 3464 | switch (scope.tag) { |
| 3496 | .decl => { | 3465 | .decl => { |
src-self-hosted/codegen.zig+2-2| ... | @@ -705,7 +705,7 @@ const Function = struct { | ... | @@ -705,7 +705,7 @@ const Function = struct { |
| 705 | } | 705 | } |
| 706 | 706 | ||
| 707 | fn resolveInst(self: *Function, inst: *ir.Inst) !MCValue { | 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 | return mcv; | 709 | return mcv; |
| 710 | } | 710 | } |
| 711 | if (inst.cast(ir.Inst.Constant)) |const_inst| { | 711 | if (inst.cast(ir.Inst.Constant)) |const_inst| { |
| ... | @@ -713,7 +713,7 @@ const Function = struct { | ... | @@ -713,7 +713,7 @@ const Function = struct { |
| 713 | try self.inst_table.putNoClobber(inst, mcvalue); | 713 | try self.inst_table.putNoClobber(inst, mcvalue); |
| 714 | return mcvalue; | 714 | return mcvalue; |
| 715 | } else { | 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,7 +1071,7 @@ pub const ElfFile = struct { |
| 1071 | try self.file.?.pwriteAll(code, file_offset); | 1071 | try self.file.?.pwriteAll(code, file_offset); |
| 1072 | 1072 | ||
| 1073 | // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. | 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 | return self.updateDeclExports(module, decl, decl_exports); | 1075 | return self.updateDeclExports(module, decl, decl_exports); |
| 1076 | } | 1076 | } |
| 1077 | 1077 | ||
| ... | @@ -1093,7 +1093,7 @@ pub const ElfFile = struct { | ... | @@ -1093,7 +1093,7 @@ pub const ElfFile = struct { |
| 1093 | for (exports) |exp| { | 1093 | for (exports) |exp| { |
| 1094 | if (exp.options.section) |section_name| { | 1094 | if (exp.options.section) |section_name| { |
| 1095 | if (!mem.eql(u8, section_name, ".text")) { | 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 | module.failed_exports.putAssumeCapacityNoClobber( | 1097 | module.failed_exports.putAssumeCapacityNoClobber( |
| 1098 | exp, | 1098 | exp, |
| 1099 | try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}), | 1099 | try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: ExportOptions.section", .{}), |
| ... | @@ -1111,7 +1111,7 @@ pub const ElfFile = struct { | ... | @@ -1111,7 +1111,7 @@ pub const ElfFile = struct { |
| 1111 | }, | 1111 | }, |
| 1112 | .Weak => elf.STB_WEAK, | 1112 | .Weak => elf.STB_WEAK, |
| 1113 | .LinkOnce => { | 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 | module.failed_exports.putAssumeCapacityNoClobber( | 1115 | module.failed_exports.putAssumeCapacityNoClobber( |
| 1116 | exp, | 1116 | exp, |
| 1117 | try Module.ErrorMsg.create(self.allocator, 0, "Unimplemented: GlobalLinkage.LinkOnce", .{}), | 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,7 +720,7 @@ fn fmtPathDir( |
| 720 | defer dir.close(); | 720 | defer dir.close(); |
| 721 | 721 | ||
| 722 | const stat = try dir.stat(); | 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 | var dir_it = dir.iterate(); | 725 | var dir_it = dir.iterate(); |
| 726 | while (try dir_it.next()) |entry| { | 726 | while (try dir_it.next()) |entry| { |
| ... | @@ -768,7 +768,7 @@ fn fmtPathFile( | ... | @@ -768,7 +768,7 @@ fn fmtPathFile( |
| 768 | defer fmt.gpa.free(source_code); | 768 | defer fmt.gpa.free(source_code); |
| 769 | 769 | ||
| 770 | // Add to set after no longer possible to get error.IsDir. | 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 | const tree = try std.zig.parse(fmt.gpa, source_code); | 773 | const tree = try std.zig.parse(fmt.gpa, source_code); |
| 774 | defer tree.deinit(); | 774 | defer tree.deinit(); |
src-self-hosted/translate_c.zig+13-14| ... | @@ -20,7 +20,7 @@ pub const Error = error{OutOfMemory}; | ... | @@ -20,7 +20,7 @@ pub const Error = error{OutOfMemory}; |
| 20 | const TypeError = Error || error{UnsupportedType}; | 20 | const TypeError = Error || error{UnsupportedType}; |
| 21 | const TransError = TypeError || error{UnsupportedTranslation}; | 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 | fn addrHash(x: usize) u32 { | 25 | fn addrHash(x: usize) u32 { |
| 26 | switch (@typeInfo(usize).Int.bits) { | 26 | switch (@typeInfo(usize).Int.bits) { |
| ... | @@ -776,8 +776,8 @@ fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 { | ... | @@ -776,8 +776,8 @@ fn checkForBuiltinTypedef(checked_name: []const u8) ?[]const u8 { |
| 776 | } | 776 | } |
| 777 | 777 | ||
| 778 | fn transTypeDef(c: *Context, typedef_decl: *const ZigClangTypedefNameDecl, top_level_visit: bool) Error!?*ast.Node { | 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| | 779 | if (c.decl_table.get(@ptrToInt(ZigClangTypedefNameDecl_getCanonicalDecl(typedef_decl)))) |name| |
| 780 | return transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice | 780 | return transCreateNodeIdentifier(c, name); // Avoid processing this decl twice |
| 781 | const rp = makeRestorePoint(c); | 781 | const rp = makeRestorePoint(c); |
| 782 | 782 | ||
| 783 | const typedef_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, typedef_decl))); | 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,8 +818,8 @@ fn transCreateNodeTypedef(rp: RestorePoint, typedef_decl: *const ZigClangTypedef |
| 818 | } | 818 | } |
| 819 | 819 | ||
| 820 | fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node { | 820 | fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?*ast.Node { |
| 821 | if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |kv| | 821 | if (c.decl_table.get(@ptrToInt(ZigClangRecordDecl_getCanonicalDecl(record_decl)))) |name| |
| 822 | return try transCreateNodeIdentifier(c, kv.value); // Avoid processing this decl twice | 822 | return try transCreateNodeIdentifier(c, name); // Avoid processing this decl twice |
| 823 | const record_loc = ZigClangRecordDecl_getLocation(record_decl); | 823 | const record_loc = ZigClangRecordDecl_getLocation(record_decl); |
| 824 | 824 | ||
| 825 | var bare_name = try c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, record_decl))); | 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,7 +969,7 @@ fn transRecordDecl(c: *Context, record_decl: *const ZigClangRecordDecl) Error!?* |
| 969 | 969 | ||
| 970 | fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node { | 970 | fn transEnumDecl(c: *Context, enum_decl: *const ZigClangEnumDecl) Error!?*ast.Node { |
| 971 | if (c.decl_table.get(@ptrToInt(ZigClangEnumDecl_getCanonicalDecl(enum_decl)))) |name| | 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 | const rp = makeRestorePoint(c); | 973 | const rp = makeRestorePoint(c); |
| 974 | const enum_loc = ZigClangEnumDecl_getLocation(enum_decl); | 974 | const enum_loc = ZigClangEnumDecl_getLocation(enum_decl); |
| 975 | 975 | ||
| ... | @@ -2130,7 +2130,7 @@ fn transInitListExprRecord( | ... | @@ -2130,7 +2130,7 @@ fn transInitListExprRecord( |
| 2130 | var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl))); | 2130 | var raw_name = try rp.c.str(ZigClangNamedDecl_getName_bytes_begin(@ptrCast(*const ZigClangNamedDecl, field_decl))); |
| 2131 | if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { | 2131 | if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { |
| 2132 | const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?; | 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 | const field_name_tok = try appendIdentifier(rp.c, raw_name); | 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,7 +2855,7 @@ fn transMemberExpr(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangMemberE |
| 2855 | const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl); | 2855 | const field_decl = @ptrCast(*const struct_ZigClangFieldDecl, member_decl); |
| 2856 | if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { | 2856 | if (ZigClangFieldDecl_isAnonymousStructOrUnion(field_decl)) { |
| 2857 | const name = rp.c.decl_table.get(@ptrToInt(ZigClangFieldDecl_getCanonicalDecl(field_decl))).?; | 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 | const decl = @ptrCast(*const ZigClangNamedDecl, member_decl); | 2861 | const decl = @ptrCast(*const ZigClangNamedDecl, member_decl); |
| ... | @@ -6040,8 +6040,8 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { | ... | @@ -6040,8 +6040,8 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6040 | } else if (node.id == .PrefixOp) { | 6040 | } else if (node.id == .PrefixOp) { |
| 6041 | return node; | 6041 | return node; |
| 6042 | } else if (node.cast(ast.Node.Identifier)) |ident| { | 6042 | } else if (node.cast(ast.Node.Identifier)) |ident| { |
| 6043 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { | 6043 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { |
| 6044 | if (kv.value.cast(ast.Node.VarDecl)) |var_decl| | 6044 | if (value.cast(ast.Node.VarDecl)) |var_decl| |
| 6045 | return getContainer(c, var_decl.init_node.?); | 6045 | return getContainer(c, var_decl.init_node.?); |
| 6046 | } | 6046 | } |
| 6047 | } else if (node.cast(ast.Node.InfixOp)) |infix| { | 6047 | } else if (node.cast(ast.Node.InfixOp)) |infix| { |
| ... | @@ -6064,8 +6064,8 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { | ... | @@ -6064,8 +6064,8 @@ fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { |
| 6064 | 6064 | ||
| 6065 | fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { | 6065 | fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { |
| 6066 | if (ref.cast(ast.Node.Identifier)) |ident| { | 6066 | if (ref.cast(ast.Node.Identifier)) |ident| { |
| 6067 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |kv| { | 6067 | if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { |
| 6068 | if (kv.value.cast(ast.Node.VarDecl)) |var_decl| { | 6068 | if (value.cast(ast.Node.VarDecl)) |var_decl| { |
| 6069 | if (var_decl.type_node) |ty| | 6069 | if (var_decl.type_node) |ty| |
| 6070 | return getContainer(c, ty); | 6070 | return getContainer(c, ty); |
| 6071 | } | 6071 | } |
| ... | @@ -6104,8 +6104,7 @@ fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { | ... | @@ -6104,8 +6104,7 @@ fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { |
| 6104 | } | 6104 | } |
| 6105 | 6105 | ||
| 6106 | fn addMacros(c: *Context) !void { | 6106 | fn addMacros(c: *Context) !void { |
| 6107 | var macro_it = c.global_scope.macro_table.iterator(); | 6107 | for (c.global_scope.macro_table.items()) |kv| { |
| 6108 | while (macro_it.next()) |kv| { | ||
| 6109 | if (getFnProto(c, kv.value)) |proto_node| { | 6108 | if (getFnProto(c, kv.value)) |proto_node| { |
| 6110 | // If a macro aliases a global variable which is a function pointer, we conclude that | 6109 | // If a macro aliases a global variable which is a function pointer, we conclude that |
| 6111 | // the macro is intended to represent a function that assumes the function pointer | 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,7 +758,7 @@ pub const Module = struct { |
| 758 | } | 758 | } |
| 759 | 759 | ||
| 760 | fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void { | 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 | if (info.index) |i| { | 762 | if (info.index) |i| { |
| 763 | try stream.print("%{}", .{info.index}); | 763 | try stream.print("%{}", .{info.index}); |
| 764 | } else { | 764 | } else { |
| ... | @@ -843,7 +843,7 @@ const Parser = struct { | ... | @@ -843,7 +843,7 @@ const Parser = struct { |
| 843 | skipSpace(self); | 843 | skipSpace(self); |
| 844 | const decl = try parseInstruction(self, &body_context, ident); | 844 | const decl = try parseInstruction(self, &body_context, ident); |
| 845 | const ident_index = body_context.instructions.items.len; | 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 | return self.fail("redefinition of identifier '{}'", .{ident}); | 847 | return self.fail("redefinition of identifier '{}'", .{ident}); |
| 848 | } | 848 | } |
| 849 | try body_context.instructions.append(decl.inst); | 849 | try body_context.instructions.append(decl.inst); |
| ... | @@ -929,7 +929,7 @@ const Parser = struct { | ... | @@ -929,7 +929,7 @@ const Parser = struct { |
| 929 | skipSpace(self); | 929 | skipSpace(self); |
| 930 | const decl = try parseInstruction(self, null, ident); | 930 | const decl = try parseInstruction(self, null, ident); |
| 931 | const ident_index = self.decls.items.len; | 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 | return self.fail("redefinition of identifier '{}'", .{ident}); | 933 | return self.fail("redefinition of identifier '{}'", .{ident}); |
| 934 | } | 934 | } |
| 935 | try self.decls.append(self.allocator, decl); | 935 | try self.decls.append(self.allocator, decl); |
| ... | @@ -1153,7 +1153,7 @@ const Parser = struct { | ... | @@ -1153,7 +1153,7 @@ const Parser = struct { |
| 1153 | else => continue, | 1153 | else => continue, |
| 1154 | }; | 1154 | }; |
| 1155 | const ident = self.source[name_start..self.i]; | 1155 | const ident = self.source[name_start..self.i]; |
| 1156 | const kv = map.get(ident) orelse { | 1156 | return map.get(ident) orelse { |
| 1157 | const bad_name = self.source[name_start - 1 .. self.i]; | 1157 | const bad_name = self.source[name_start - 1 .. self.i]; |
| 1158 | const src = name_start - 1; | 1158 | const src = name_start - 1; |
| 1159 | if (local_ref) { | 1159 | if (local_ref) { |
| ... | @@ -1172,7 +1172,6 @@ const Parser = struct { | ... | @@ -1172,7 +1172,6 @@ const Parser = struct { |
| 1172 | return &declval.base; | 1172 | return &declval.base; |
| 1173 | } | 1173 | } |
| 1174 | }; | 1174 | }; |
| 1175 | return kv.value; | ||
| 1176 | } | 1175 | } |
| 1177 | 1176 | ||
| 1178 | fn generateName(self: *Parser) ![]u8 { | 1177 | fn generateName(self: *Parser) ![]u8 { |
| ... | @@ -1219,13 +1218,12 @@ const EmitZIR = struct { | ... | @@ -1219,13 +1218,12 @@ const EmitZIR = struct { |
| 1219 | // by the hash table. | 1218 | // by the hash table. |
| 1220 | var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator); | 1219 | var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator); |
| 1221 | defer src_decls.deinit(); | 1220 | defer src_decls.deinit(); |
| 1222 | try src_decls.ensureCapacity(self.old_module.decl_table.size); | 1221 | try src_decls.ensureCapacity(self.old_module.decl_table.items().len); |
| 1223 | try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size); | 1222 | try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.items().len); |
| 1224 | try self.names.ensureCapacity(self.old_module.decl_table.size); | 1223 | try self.names.ensureCapacity(self.old_module.decl_table.items().len); |
| 1225 | 1224 | ||
| 1226 | var decl_it = self.old_module.decl_table.iterator(); | 1225 | for (self.old_module.decl_table.items()) |entry| { |
| 1227 | while (decl_it.next()) |kv| { | 1226 | const decl = entry.value; |
| 1228 | const decl = kv.value; | ||
| 1229 | src_decls.appendAssumeCapacity(decl); | 1227 | src_decls.appendAssumeCapacity(decl); |
| 1230 | self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {}); | 1228 | self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {}); |
| 1231 | } | 1229 | } |
| ... | @@ -1248,7 +1246,7 @@ const EmitZIR = struct { | ... | @@ -1248,7 +1246,7 @@ const EmitZIR = struct { |
| 1248 | .codegen_failure, | 1246 | .codegen_failure, |
| 1249 | .dependency_failure, | 1247 | .dependency_failure, |
| 1250 | .codegen_failure_retryable, | 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 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); | 1250 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1253 | fail_inst.* = .{ | 1251 | fail_inst.* = .{ |
| 1254 | .base = .{ | 1252 | .base = .{ |
| ... | @@ -1270,7 +1268,7 @@ const EmitZIR = struct { | ... | @@ -1270,7 +1268,7 @@ const EmitZIR = struct { |
| 1270 | continue; | 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 | for (exports) |module_export| { | 1272 | for (exports) |module_export| { |
| 1275 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); | 1273 | const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); |
| 1276 | const export_inst = try self.arena.allocator.create(Inst.Export); | 1274 | const export_inst = try self.arena.allocator.create(Inst.Export); |
| ... | @@ -1314,7 +1312,7 @@ const EmitZIR = struct { | ... | @@ -1314,7 +1312,7 @@ const EmitZIR = struct { |
| 1314 | try new_body.inst_table.putNoClobber(inst, new_inst); | 1312 | try new_body.inst_table.putNoClobber(inst, new_inst); |
| 1315 | return new_inst; | 1313 | return new_inst; |
| 1316 | } else { | 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,7 +1422,7 @@ const EmitZIR = struct { |
| 1424 | try self.emitBody(body, &inst_table, &instructions); | 1422 | try self.emitBody(body, &inst_table, &instructions); |
| 1425 | }, | 1423 | }, |
| 1426 | .sema_failure => { | 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 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); | 1426 | const fail_inst = try self.arena.allocator.create(Inst.CompileError); |
| 1429 | fail_inst.* = .{ | 1427 | fail_inst.* = .{ |
| 1430 | .base = .{ | 1428 | .base = .{ |
| ... | @@ -1841,7 +1839,7 @@ const EmitZIR = struct { | ... | @@ -1841,7 +1839,7 @@ const EmitZIR = struct { |
| 1841 | self.next_auto_name += 1; | 1839 | self.next_auto_name += 1; |
| 1842 | const gop = try self.names.getOrPut(proposed_name); | 1840 | const gop = try self.names.getOrPut(proposed_name); |
| 1843 | if (!gop.found_existing) { | 1841 | if (!gop.found_existing) { |
| 1844 | gop.kv.value = {}; | 1842 | gop.entry.value = {}; |
| 1845 | return proposed_name; | 1843 | return proposed_name; |
| 1846 | } | 1844 | } |
| 1847 | } | 1845 | } |
| ... | @@ -1861,9 +1859,9 @@ const EmitZIR = struct { | ... | @@ -1861,9 +1859,9 @@ const EmitZIR = struct { |
| 1861 | }, | 1859 | }, |
| 1862 | .kw_args = .{}, | 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 | fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl { | 1867 | fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Decl { |