motiejus/zig

fork of https://codeberg.org/ziglang/zig
git clone https://git.jakstys.lt/motiejus/zig.git
Log | Tree | Refs | README | LICENSE

lib/std/array_hash_map.zig (117107B) - Raw


      1 const std = @import("std.zig");
      2 const debug = std.debug;
      3 const assert = debug.assert;
      4 const testing = std.testing;
      5 const math = std.math;
      6 const mem = std.mem;
      7 const autoHash = std.hash.autoHash;
      8 const Wyhash = std.hash.Wyhash;
      9 const Allocator = mem.Allocator;
     10 const hash_map = @This();
     11 
     12 /// An `ArrayHashMap` with default hash and equal functions.
     13 ///
     14 /// See `AutoContext` for a description of the hash and equal implementations.
     15 pub fn AutoArrayHashMap(comptime K: type, comptime V: type) type {
     16     return ArrayHashMap(K, V, AutoContext(K), !autoEqlIsCheap(K));
     17 }
     18 
     19 /// An `ArrayHashMapUnmanaged` with default hash and equal functions.
     20 ///
     21 /// See `AutoContext` for a description of the hash and equal implementations.
     22 pub fn AutoArrayHashMapUnmanaged(comptime K: type, comptime V: type) type {
     23     return ArrayHashMapUnmanaged(K, V, AutoContext(K), !autoEqlIsCheap(K));
     24 }
     25 
     26 /// An `ArrayHashMap` with strings as keys.
     27 pub fn StringArrayHashMap(comptime V: type) type {
     28     return ArrayHashMap([]const u8, V, StringContext, true);
     29 }
     30 
     31 /// An `ArrayHashMapUnmanaged` with strings as keys.
     32 pub fn StringArrayHashMapUnmanaged(comptime V: type) type {
     33     return ArrayHashMapUnmanaged([]const u8, V, StringContext, true);
     34 }
     35 
     36 pub const StringContext = struct {
     37     pub fn hash(self: @This(), s: []const u8) u32 {
     38         _ = self;
     39         return hashString(s);
     40     }
     41     pub fn eql(self: @This(), a: []const u8, b: []const u8, b_index: usize) bool {
     42         _ = self;
     43         _ = b_index;
     44         return eqlString(a, b);
     45     }
     46 };
     47 
     48 pub fn eqlString(a: []const u8, b: []const u8) bool {
     49     return mem.eql(u8, a, b);
     50 }
     51 
     52 pub fn hashString(s: []const u8) u32 {
     53     return @as(u32, @truncate(std.hash.Wyhash.hash(0, s)));
     54 }
     55 
     56 /// Deprecated in favor of `ArrayHashMapWithAllocator` (no code changes needed)
     57 /// or `ArrayHashMapUnmanaged` (will need to update callsites to pass an
     58 /// allocator). After Zig 0.14.0 is released, `ArrayHashMapWithAllocator` will
     59 /// be removed and `ArrayHashMapUnmanaged` will be a deprecated alias. After
     60 /// Zig 0.15.0 is released, the deprecated alias `ArrayHashMapUnmanaged` will
     61 /// be removed.
     62 pub const ArrayHashMap = ArrayHashMapWithAllocator;
     63 
     64 /// A hash table of keys and values, each stored sequentially.
     65 ///
     66 /// Insertion order is preserved. In general, this data structure supports the same
     67 /// operations as `std.ArrayList`.
     68 ///
     69 /// Deletion operations:
     70 /// * `swapRemove` - O(1)
     71 /// * `orderedRemove` - O(N)
     72 ///
     73 /// Modifying the hash map while iterating is allowed, however, one must understand
     74 /// the (well defined) behavior when mixing insertions and deletions with iteration.
     75 ///
     76 /// See `ArrayHashMapUnmanaged` for a variant of this data structure that accepts an
     77 /// `Allocator` as a parameter when needed rather than storing it.
     78 pub fn ArrayHashMapWithAllocator(
     79     comptime K: type,
     80     comptime V: type,
     81     /// A namespace that provides these two functions:
     82     /// * `pub fn hash(self, K) u32`
     83     /// * `pub fn eql(self, K, K, usize) bool`
     84     ///
     85     /// The final `usize` in the `eql` function represents the index of the key
     86     /// that's already inside the map.
     87     comptime Context: type,
     88     /// When `false`, this data structure is biased towards cheap `eql`
     89     /// functions and avoids storing each key's hash in the table. Setting
     90     /// `store_hash` to `true` incurs more memory cost but limits `eql` to
     91     /// being called only once per insertion/deletion (provided there are no
     92     /// hash collisions).
     93     comptime store_hash: bool,
     94 ) type {
     95     return struct {
     96         unmanaged: Unmanaged,
     97         allocator: Allocator,
     98         ctx: Context,
     99 
    100         /// The ArrayHashMapUnmanaged type using the same settings as this managed map.
    101         pub const Unmanaged = ArrayHashMapUnmanaged(K, V, Context, store_hash);
    102 
    103         /// Pointers to a key and value in the backing store of this map.
    104         /// Modifying the key is allowed only if it does not change the hash.
    105         /// Modifying the value is allowed.
    106         /// Entry pointers become invalid whenever this ArrayHashMap is modified,
    107         /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
    108         pub const Entry = Unmanaged.Entry;
    109 
    110         /// A KV pair which has been copied out of the backing store
    111         pub const KV = Unmanaged.KV;
    112 
    113         /// The Data type used for the MultiArrayList backing this map
    114         pub const Data = Unmanaged.Data;
    115         /// The MultiArrayList type backing this map
    116         pub const DataList = Unmanaged.DataList;
    117 
    118         /// The stored hash type, either u32 or void.
    119         pub const Hash = Unmanaged.Hash;
    120 
    121         /// getOrPut variants return this structure, with pointers
    122         /// to the backing store and a flag to indicate whether an
    123         /// existing entry was found.
    124         /// Modifying the key is allowed only if it does not change the hash.
    125         /// Modifying the value is allowed.
    126         /// Entry pointers become invalid whenever this ArrayHashMap is modified,
    127         /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
    128         pub const GetOrPutResult = Unmanaged.GetOrPutResult;
    129 
    130         /// An Iterator over Entry pointers.
    131         pub const Iterator = Unmanaged.Iterator;
    132 
    133         const Self = @This();
    134 
    135         /// Create an ArrayHashMap instance which will use a specified allocator.
    136         pub fn init(allocator: Allocator) Self {
    137             if (@sizeOf(Context) != 0)
    138                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call initContext instead.");
    139             return initContext(allocator, undefined);
    140         }
    141         pub fn initContext(allocator: Allocator, ctx: Context) Self {
    142             return .{
    143                 .unmanaged = .empty,
    144                 .allocator = allocator,
    145                 .ctx = ctx,
    146             };
    147         }
    148 
    149         /// Frees the backing allocation and leaves the map in an undefined state.
    150         /// Note that this does not free keys or values.  You must take care of that
    151         /// before calling this function, if it is needed.
    152         pub fn deinit(self: *Self) void {
    153             self.unmanaged.deinit(self.allocator);
    154             self.* = undefined;
    155         }
    156 
    157         /// Puts the hash map into a state where any method call that would
    158         /// cause an existing key or value pointer to become invalidated will
    159         /// instead trigger an assertion.
    160         ///
    161         /// An additional call to `lockPointers` in such state also triggers an
    162         /// assertion.
    163         ///
    164         /// `unlockPointers` returns the hash map to the previous state.
    165         pub fn lockPointers(self: *Self) void {
    166             self.unmanaged.lockPointers();
    167         }
    168 
    169         /// Undoes a call to `lockPointers`.
    170         pub fn unlockPointers(self: *Self) void {
    171             self.unmanaged.unlockPointers();
    172         }
    173 
    174         /// Clears the map but retains the backing allocation for future use.
    175         pub fn clearRetainingCapacity(self: *Self) void {
    176             return self.unmanaged.clearRetainingCapacity();
    177         }
    178 
    179         /// Clears the map and releases the backing allocation
    180         pub fn clearAndFree(self: *Self) void {
    181             return self.unmanaged.clearAndFree(self.allocator);
    182         }
    183 
    184         /// Returns the number of KV pairs stored in this map.
    185         pub fn count(self: Self) usize {
    186             return self.unmanaged.count();
    187         }
    188 
    189         /// Returns the backing array of keys in this map. Modifying the map may
    190         /// invalidate this array. Modifying this array in a way that changes
    191         /// key hashes or key equality puts the map into an unusable state until
    192         /// `reIndex` is called.
    193         pub fn keys(self: Self) []K {
    194             return self.unmanaged.keys();
    195         }
    196         /// Returns the backing array of values in this map. Modifying the map
    197         /// may invalidate this array. It is permitted to modify the values in
    198         /// this array.
    199         pub fn values(self: Self) []V {
    200             return self.unmanaged.values();
    201         }
    202 
    203         /// Returns an iterator over the pairs in this map.
    204         /// Modifying the map may invalidate this iterator.
    205         pub fn iterator(self: *const Self) Iterator {
    206             return self.unmanaged.iterator();
    207         }
    208 
    209         /// If key exists this function cannot fail.
    210         /// If there is an existing item with `key`, then the result
    211         /// `Entry` pointer points to it, and found_existing is true.
    212         /// Otherwise, puts a new item with undefined value, and
    213         /// the `Entry` pointer points to it. Caller should then initialize
    214         /// the value (but not the key).
    215         pub fn getOrPut(self: *Self, key: K) !GetOrPutResult {
    216             return self.unmanaged.getOrPutContext(self.allocator, key, self.ctx);
    217         }
    218         pub fn getOrPutAdapted(self: *Self, key: anytype, ctx: anytype) !GetOrPutResult {
    219             return self.unmanaged.getOrPutContextAdapted(self.allocator, key, ctx, self.ctx);
    220         }
    221 
    222         /// If there is an existing item with `key`, then the result
    223         /// `Entry` pointer points to it, and found_existing is true.
    224         /// Otherwise, puts a new item with undefined value, and
    225         /// the `Entry` pointer points to it. Caller should then initialize
    226         /// the value (but not the key).
    227         /// If a new entry needs to be stored, this function asserts there
    228         /// is enough capacity to store it.
    229         pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult {
    230             return self.unmanaged.getOrPutAssumeCapacityContext(key, self.ctx);
    231         }
    232         pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult {
    233             return self.unmanaged.getOrPutAssumeCapacityAdapted(key, ctx);
    234         }
    235         pub fn getOrPutValue(self: *Self, key: K, value: V) !GetOrPutResult {
    236             return self.unmanaged.getOrPutValueContext(self.allocator, key, value, self.ctx);
    237         }
    238 
    239         /// Increases capacity, guaranteeing that insertions up until the
    240         /// `expected_count` will not cause an allocation, and therefore cannot fail.
    241         pub fn ensureTotalCapacity(self: *Self, new_capacity: usize) !void {
    242             return self.unmanaged.ensureTotalCapacityContext(self.allocator, new_capacity, self.ctx);
    243         }
    244 
    245         /// Increases capacity, guaranteeing that insertions up until
    246         /// `additional_count` **more** items will not cause an allocation, and
    247         /// therefore cannot fail.
    248         pub fn ensureUnusedCapacity(self: *Self, additional_count: usize) !void {
    249             return self.unmanaged.ensureUnusedCapacityContext(self.allocator, additional_count, self.ctx);
    250         }
    251 
    252         /// Returns the number of total elements which may be present before it is
    253         /// no longer guaranteed that no allocations will be performed.
    254         pub fn capacity(self: Self) usize {
    255             return self.unmanaged.capacity();
    256         }
    257 
    258         /// Clobbers any existing data. To detect if a put would clobber
    259         /// existing data, see `getOrPut`.
    260         pub fn put(self: *Self, key: K, value: V) !void {
    261             return self.unmanaged.putContext(self.allocator, key, value, self.ctx);
    262         }
    263 
    264         /// Inserts a key-value pair into the hash map, asserting that no previous
    265         /// entry with the same key is already present
    266         pub fn putNoClobber(self: *Self, key: K, value: V) !void {
    267             return self.unmanaged.putNoClobberContext(self.allocator, key, value, self.ctx);
    268         }
    269 
    270         /// Asserts there is enough capacity to store the new key-value pair.
    271         /// Clobbers any existing data. To detect if a put would clobber
    272         /// existing data, see `getOrPutAssumeCapacity`.
    273         pub fn putAssumeCapacity(self: *Self, key: K, value: V) void {
    274             return self.unmanaged.putAssumeCapacityContext(key, value, self.ctx);
    275         }
    276 
    277         /// Asserts there is enough capacity to store the new key-value pair.
    278         /// Asserts that it does not clobber any existing data.
    279         /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`.
    280         pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
    281             return self.unmanaged.putAssumeCapacityNoClobberContext(key, value, self.ctx);
    282         }
    283 
    284         /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
    285         pub fn fetchPut(self: *Self, key: K, value: V) !?KV {
    286             return self.unmanaged.fetchPutContext(self.allocator, key, value, self.ctx);
    287         }
    288 
    289         /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
    290         /// If insertion happuns, asserts there is enough capacity without allocating.
    291         pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?KV {
    292             return self.unmanaged.fetchPutAssumeCapacityContext(key, value, self.ctx);
    293         }
    294 
    295         /// Finds pointers to the key and value storage associated with a key.
    296         pub fn getEntry(self: Self, key: K) ?Entry {
    297             return self.unmanaged.getEntryContext(key, self.ctx);
    298         }
    299         pub fn getEntryAdapted(self: Self, key: anytype, ctx: anytype) ?Entry {
    300             return self.unmanaged.getEntryAdapted(key, ctx);
    301         }
    302 
    303         /// Finds the index in the `entries` array where a key is stored
    304         pub fn getIndex(self: Self, key: K) ?usize {
    305             return self.unmanaged.getIndexContext(key, self.ctx);
    306         }
    307         pub fn getIndexAdapted(self: Self, key: anytype, ctx: anytype) ?usize {
    308             return self.unmanaged.getIndexAdapted(key, ctx);
    309         }
    310 
    311         /// Find the value associated with a key
    312         pub fn get(self: Self, key: K) ?V {
    313             return self.unmanaged.getContext(key, self.ctx);
    314         }
    315         pub fn getAdapted(self: Self, key: anytype, ctx: anytype) ?V {
    316             return self.unmanaged.getAdapted(key, ctx);
    317         }
    318 
    319         /// Find a pointer to the value associated with a key
    320         pub fn getPtr(self: Self, key: K) ?*V {
    321             return self.unmanaged.getPtrContext(key, self.ctx);
    322         }
    323         pub fn getPtrAdapted(self: Self, key: anytype, ctx: anytype) ?*V {
    324             return self.unmanaged.getPtrAdapted(key, ctx);
    325         }
    326 
    327         /// Find the actual key associated with an adapted key
    328         pub fn getKey(self: Self, key: K) ?K {
    329             return self.unmanaged.getKeyContext(key, self.ctx);
    330         }
    331         pub fn getKeyAdapted(self: Self, key: anytype, ctx: anytype) ?K {
    332             return self.unmanaged.getKeyAdapted(key, ctx);
    333         }
    334 
    335         /// Find a pointer to the actual key associated with an adapted key
    336         pub fn getKeyPtr(self: Self, key: K) ?*K {
    337             return self.unmanaged.getKeyPtrContext(key, self.ctx);
    338         }
    339         pub fn getKeyPtrAdapted(self: Self, key: anytype, ctx: anytype) ?*K {
    340             return self.unmanaged.getKeyPtrAdapted(key, ctx);
    341         }
    342 
    343         /// Check whether a key is stored in the map
    344         pub fn contains(self: Self, key: K) bool {
    345             return self.unmanaged.containsContext(key, self.ctx);
    346         }
    347         pub fn containsAdapted(self: Self, key: anytype, ctx: anytype) bool {
    348             return self.unmanaged.containsAdapted(key, ctx);
    349         }
    350 
    351         /// If there is an `Entry` with a matching key, it is deleted from
    352         /// the hash map, and then returned from this function. The entry is
    353         /// removed from the underlying array by swapping it with the last
    354         /// element.
    355         pub fn fetchSwapRemove(self: *Self, key: K) ?KV {
    356             return self.unmanaged.fetchSwapRemoveContext(key, self.ctx);
    357         }
    358         pub fn fetchSwapRemoveAdapted(self: *Self, key: anytype, ctx: anytype) ?KV {
    359             return self.unmanaged.fetchSwapRemoveContextAdapted(key, ctx, self.ctx);
    360         }
    361 
    362         /// If there is an `Entry` with a matching key, it is deleted from
    363         /// the hash map, and then returned from this function. The entry is
    364         /// removed from the underlying array by shifting all elements forward
    365         /// thereby maintaining the current ordering.
    366         pub fn fetchOrderedRemove(self: *Self, key: K) ?KV {
    367             return self.unmanaged.fetchOrderedRemoveContext(key, self.ctx);
    368         }
    369         pub fn fetchOrderedRemoveAdapted(self: *Self, key: anytype, ctx: anytype) ?KV {
    370             return self.unmanaged.fetchOrderedRemoveContextAdapted(key, ctx, self.ctx);
    371         }
    372 
    373         /// If there is an `Entry` with a matching key, it is deleted from
    374         /// the hash map. The entry is removed from the underlying array
    375         /// by swapping it with the last element.  Returns true if an entry
    376         /// was removed, false otherwise.
    377         pub fn swapRemove(self: *Self, key: K) bool {
    378             return self.unmanaged.swapRemoveContext(key, self.ctx);
    379         }
    380         pub fn swapRemoveAdapted(self: *Self, key: anytype, ctx: anytype) bool {
    381             return self.unmanaged.swapRemoveContextAdapted(key, ctx, self.ctx);
    382         }
    383 
    384         /// If there is an `Entry` with a matching key, it is deleted from
    385         /// the hash map. The entry is removed from the underlying array
    386         /// by shifting all elements forward, thereby maintaining the
    387         /// current ordering.  Returns true if an entry was removed, false otherwise.
    388         pub fn orderedRemove(self: *Self, key: K) bool {
    389             return self.unmanaged.orderedRemoveContext(key, self.ctx);
    390         }
    391         pub fn orderedRemoveAdapted(self: *Self, key: anytype, ctx: anytype) bool {
    392             return self.unmanaged.orderedRemoveContextAdapted(key, ctx, self.ctx);
    393         }
    394 
    395         /// Deletes the item at the specified index in `entries` from
    396         /// the hash map. The entry is removed from the underlying array
    397         /// by swapping it with the last element.
    398         pub fn swapRemoveAt(self: *Self, index: usize) void {
    399             self.unmanaged.swapRemoveAtContext(index, self.ctx);
    400         }
    401 
    402         /// Deletes the item at the specified index in `entries` from
    403         /// the hash map. The entry is removed from the underlying array
    404         /// by shifting all elements forward, thereby maintaining the
    405         /// current ordering.
    406         pub fn orderedRemoveAt(self: *Self, index: usize) void {
    407             self.unmanaged.orderedRemoveAtContext(index, self.ctx);
    408         }
    409 
    410         /// Create a copy of the hash map which can be modified separately.
    411         /// The copy uses the same context and allocator as this instance.
    412         pub fn clone(self: Self) !Self {
    413             var other = try self.unmanaged.cloneContext(self.allocator, self.ctx);
    414             return other.promoteContext(self.allocator, self.ctx);
    415         }
    416         /// Create a copy of the hash map which can be modified separately.
    417         /// The copy uses the same context as this instance, but the specified
    418         /// allocator.
    419         pub fn cloneWithAllocator(self: Self, allocator: Allocator) !Self {
    420             var other = try self.unmanaged.cloneContext(allocator, self.ctx);
    421             return other.promoteContext(allocator, self.ctx);
    422         }
    423         /// Create a copy of the hash map which can be modified separately.
    424         /// The copy uses the same allocator as this instance, but the
    425         /// specified context.
    426         pub fn cloneWithContext(self: Self, ctx: anytype) !ArrayHashMap(K, V, @TypeOf(ctx), store_hash) {
    427             var other = try self.unmanaged.cloneContext(self.allocator, ctx);
    428             return other.promoteContext(self.allocator, ctx);
    429         }
    430         /// Create a copy of the hash map which can be modified separately.
    431         /// The copy uses the specified allocator and context.
    432         pub fn cloneWithAllocatorAndContext(self: Self, allocator: Allocator, ctx: anytype) !ArrayHashMap(K, V, @TypeOf(ctx), store_hash) {
    433             var other = try self.unmanaged.cloneContext(allocator, ctx);
    434             return other.promoteContext(allocator, ctx);
    435         }
    436 
    437         /// Set the map to an empty state, making deinitialization a no-op, and
    438         /// returning a copy of the original.
    439         pub fn move(self: *Self) Self {
    440             self.unmanaged.pointer_stability.assertUnlocked();
    441             const result = self.*;
    442             self.unmanaged = .empty;
    443             return result;
    444         }
    445 
    446         /// Recomputes stored hashes and rebuilds the key indexes. If the
    447         /// underlying keys have been modified directly, call this method to
    448         /// recompute the denormalized metadata necessary for the operation of
    449         /// the methods of this map that lookup entries by key.
    450         ///
    451         /// One use case for this is directly calling `entries.resize()` to grow
    452         /// the underlying storage, and then setting the `keys` and `values`
    453         /// directly without going through the methods of this map.
    454         ///
    455         /// The time complexity of this operation is O(n).
    456         pub fn reIndex(self: *Self) !void {
    457             return self.unmanaged.reIndexContext(self.allocator, self.ctx);
    458         }
    459 
    460         /// Sorts the entries and then rebuilds the index.
    461         /// `sort_ctx` must have this method:
    462         /// `fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool`
    463         pub fn sort(self: *Self, sort_ctx: anytype) void {
    464             return self.unmanaged.sortContext(sort_ctx, self.ctx);
    465         }
    466 
    467         /// Shrinks the underlying `Entry` array to `new_len` elements and
    468         /// discards any associated index entries. Keeps capacity the same.
    469         ///
    470         /// Asserts the discarded entries remain initialized and capable of
    471         /// performing hash and equality checks. Any deinitialization of
    472         /// discarded entries must take place *after* calling this function.
    473         pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
    474             return self.unmanaged.shrinkRetainingCapacityContext(new_len, self.ctx);
    475         }
    476 
    477         /// Shrinks the underlying `Entry` array to `new_len` elements and
    478         /// discards any associated index entries. Reduces allocated capacity.
    479         ///
    480         /// Asserts the discarded entries remain initialized and capable of
    481         /// performing hash and equality checks. It is a bug to call this
    482         /// function if the discarded entries require deinitialization. For
    483         /// that use case, `shrinkRetainingCapacity` can be used instead.
    484         pub fn shrinkAndFree(self: *Self, new_len: usize) void {
    485             return self.unmanaged.shrinkAndFreeContext(self.allocator, new_len, self.ctx);
    486         }
    487 
    488         /// Removes the last inserted `Entry` in the hash map and returns it.
    489         pub fn pop(self: *Self) KV {
    490             return self.unmanaged.popContext(self.ctx);
    491         }
    492 
    493         /// Removes the last inserted `Entry` in the hash map and returns it if count is nonzero.
    494         /// Otherwise returns null.
    495         pub fn popOrNull(self: *Self) ?KV {
    496             return self.unmanaged.popOrNullContext(self.ctx);
    497         }
    498     };
    499 }
    500 
    501 /// A hash table of keys and values, each stored sequentially.
    502 ///
    503 /// Insertion order is preserved. In general, this data structure supports the same
    504 /// operations as `std.ArrayListUnmanaged`.
    505 ///
    506 /// Deletion operations:
    507 /// * `swapRemove` - O(1)
    508 /// * `orderedRemove` - O(N)
    509 ///
    510 /// Modifying the hash map while iterating is allowed, however, one must understand
    511 /// the (well defined) behavior when mixing insertions and deletions with iteration.
    512 ///
    513 /// This type does not store an `Allocator` field - the `Allocator` must be passed in
    514 /// with each function call that requires it. See `ArrayHashMap` for a type that stores
    515 /// an `Allocator` field for convenience.
    516 ///
    517 /// Can be initialized directly using the default field values.
    518 ///
    519 /// This type is designed to have low overhead for small numbers of entries. When
    520 /// `store_hash` is `false` and the number of entries in the map is less than 9,
    521 /// the overhead cost of using `ArrayHashMapUnmanaged` rather than `std.ArrayList` is
    522 /// only a single pointer-sized integer.
    523 ///
    524 /// Default initialization of this struct is deprecated; use `.empty` instead.
    525 pub fn ArrayHashMapUnmanaged(
    526     comptime K: type,
    527     comptime V: type,
    528     /// A namespace that provides these two functions:
    529     /// * `pub fn hash(self, K) u32`
    530     /// * `pub fn eql(self, K, K, usize) bool`
    531     ///
    532     /// The final `usize` in the `eql` function represents the index of the key
    533     /// that's already inside the map.
    534     comptime Context: type,
    535     /// When `false`, this data structure is biased towards cheap `eql`
    536     /// functions and avoids storing each key's hash in the table. Setting
    537     /// `store_hash` to `true` incurs more memory cost but limits `eql` to
    538     /// being called only once per insertion/deletion (provided there are no
    539     /// hash collisions).
    540     comptime store_hash: bool,
    541 ) type {
    542     return struct {
    543         /// It is permitted to access this field directly.
    544         /// After any modification to the keys, consider calling `reIndex`.
    545         entries: DataList = .{},
    546 
    547         /// When entries length is less than `linear_scan_max`, this remains `null`.
    548         /// Once entries length grows big enough, this field is allocated. There is
    549         /// an IndexHeader followed by an array of Index(I) structs, where I is defined
    550         /// by how many total indexes there are.
    551         index_header: ?*IndexHeader = null,
    552 
    553         /// Used to detect memory safety violations.
    554         pointer_stability: std.debug.SafetyLock = .{},
    555 
    556         /// A map containing no keys or values.
    557         pub const empty: Self = .{
    558             .entries = .{},
    559             .index_header = null,
    560         };
    561 
    562         /// Modifying the key is allowed only if it does not change the hash.
    563         /// Modifying the value is allowed.
    564         /// Entry pointers become invalid whenever this ArrayHashMap is modified,
    565         /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
    566         pub const Entry = struct {
    567             key_ptr: *K,
    568             value_ptr: *V,
    569         };
    570 
    571         /// A KV pair which has been copied out of the backing store
    572         pub const KV = struct {
    573             key: K,
    574             value: V,
    575         };
    576 
    577         /// The Data type used for the MultiArrayList backing this map
    578         pub const Data = struct {
    579             hash: Hash,
    580             key: K,
    581             value: V,
    582         };
    583 
    584         /// The MultiArrayList type backing this map
    585         pub const DataList = std.MultiArrayList(Data);
    586 
    587         /// The stored hash type, either u32 or void.
    588         pub const Hash = if (store_hash) u32 else void;
    589 
    590         /// getOrPut variants return this structure, with pointers
    591         /// to the backing store and a flag to indicate whether an
    592         /// existing entry was found.
    593         /// Modifying the key is allowed only if it does not change the hash.
    594         /// Modifying the value is allowed.
    595         /// Entry pointers become invalid whenever this ArrayHashMap is modified,
    596         /// unless `ensureTotalCapacity`/`ensureUnusedCapacity` was previously used.
    597         pub const GetOrPutResult = struct {
    598             key_ptr: *K,
    599             value_ptr: *V,
    600             found_existing: bool,
    601             index: usize,
    602         };
    603 
    604         /// The ArrayHashMap type using the same settings as this managed map.
    605         pub const Managed = ArrayHashMap(K, V, Context, store_hash);
    606 
    607         /// Some functions require a context only if hashes are not stored.
    608         /// To keep the api simple, this type is only used internally.
    609         const ByIndexContext = if (store_hash) void else Context;
    610 
    611         const Self = @This();
    612 
    613         const linear_scan_max = 8;
    614 
    615         const RemovalType = enum {
    616             swap,
    617             ordered,
    618         };
    619 
    620         const Oom = Allocator.Error;
    621 
    622         /// Convert from an unmanaged map to a managed map.  After calling this,
    623         /// the promoted map should no longer be used.
    624         pub fn promote(self: Self, gpa: Allocator) Managed {
    625             if (@sizeOf(Context) != 0)
    626                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call promoteContext instead.");
    627             return self.promoteContext(gpa, undefined);
    628         }
    629         pub fn promoteContext(self: Self, gpa: Allocator, ctx: Context) Managed {
    630             return .{
    631                 .unmanaged = self,
    632                 .allocator = gpa,
    633                 .ctx = ctx,
    634             };
    635         }
    636 
    637         pub fn init(gpa: Allocator, key_list: []const K, value_list: []const V) Oom!Self {
    638             var self: Self = .{};
    639             errdefer self.deinit(gpa);
    640             try self.reinit(gpa, key_list, value_list);
    641             return self;
    642         }
    643 
    644         pub fn reinit(self: *Self, gpa: Allocator, key_list: []const K, value_list: []const V) Oom!void {
    645             try self.entries.resize(gpa, key_list.len);
    646             @memcpy(self.keys(), key_list);
    647             if (@sizeOf(V) != 0) {
    648                 assert(key_list.len == value_list.len);
    649                 @memcpy(self.values(), value_list);
    650             }
    651             try self.reIndex(gpa);
    652         }
    653 
    654         /// Frees the backing allocation and leaves the map in an undefined state.
    655         /// Note that this does not free keys or values.  You must take care of that
    656         /// before calling this function, if it is needed.
    657         pub fn deinit(self: *Self, gpa: Allocator) void {
    658             self.pointer_stability.assertUnlocked();
    659             self.entries.deinit(gpa);
    660             if (self.index_header) |header| {
    661                 header.free(gpa);
    662             }
    663             self.* = undefined;
    664         }
    665 
    666         /// Puts the hash map into a state where any method call that would
    667         /// cause an existing key or value pointer to become invalidated will
    668         /// instead trigger an assertion.
    669         ///
    670         /// An additional call to `lockPointers` in such state also triggers an
    671         /// assertion.
    672         ///
    673         /// `unlockPointers` returns the hash map to the previous state.
    674         pub fn lockPointers(self: *Self) void {
    675             self.pointer_stability.lock();
    676         }
    677 
    678         /// Undoes a call to `lockPointers`.
    679         pub fn unlockPointers(self: *Self) void {
    680             self.pointer_stability.unlock();
    681         }
    682 
    683         /// Clears the map but retains the backing allocation for future use.
    684         pub fn clearRetainingCapacity(self: *Self) void {
    685             self.pointer_stability.lock();
    686             defer self.pointer_stability.unlock();
    687 
    688             self.entries.len = 0;
    689             if (self.index_header) |header| {
    690                 switch (header.capacityIndexType()) {
    691                     .u8 => @memset(header.indexes(u8), Index(u8).empty),
    692                     .u16 => @memset(header.indexes(u16), Index(u16).empty),
    693                     .u32 => @memset(header.indexes(u32), Index(u32).empty),
    694                 }
    695             }
    696         }
    697 
    698         /// Clears the map and releases the backing allocation
    699         pub fn clearAndFree(self: *Self, gpa: Allocator) void {
    700             self.pointer_stability.lock();
    701             defer self.pointer_stability.unlock();
    702 
    703             self.entries.shrinkAndFree(gpa, 0);
    704             if (self.index_header) |header| {
    705                 header.free(gpa);
    706                 self.index_header = null;
    707             }
    708         }
    709 
    710         /// Returns the number of KV pairs stored in this map.
    711         pub fn count(self: Self) usize {
    712             return self.entries.len;
    713         }
    714 
    715         /// Returns the backing array of keys in this map. Modifying the map may
    716         /// invalidate this array. Modifying this array in a way that changes
    717         /// key hashes or key equality puts the map into an unusable state until
    718         /// `reIndex` is called.
    719         pub fn keys(self: Self) []K {
    720             return self.entries.items(.key);
    721         }
    722         /// Returns the backing array of values in this map. Modifying the map
    723         /// may invalidate this array. It is permitted to modify the values in
    724         /// this array.
    725         pub fn values(self: Self) []V {
    726             return self.entries.items(.value);
    727         }
    728 
    729         /// Returns an iterator over the pairs in this map.
    730         /// Modifying the map may invalidate this iterator.
    731         pub fn iterator(self: Self) Iterator {
    732             const slice = self.entries.slice();
    733             return .{
    734                 .keys = slice.items(.key).ptr,
    735                 .values = slice.items(.value).ptr,
    736                 .len = @as(u32, @intCast(slice.len)),
    737             };
    738         }
    739         pub const Iterator = struct {
    740             keys: [*]K,
    741             values: [*]V,
    742             len: u32,
    743             index: u32 = 0,
    744 
    745             pub fn next(it: *Iterator) ?Entry {
    746                 if (it.index >= it.len) return null;
    747                 const result = Entry{
    748                     .key_ptr = &it.keys[it.index],
    749                     // workaround for #6974
    750                     .value_ptr = if (@sizeOf(*V) == 0) undefined else &it.values[it.index],
    751                 };
    752                 it.index += 1;
    753                 return result;
    754             }
    755 
    756             /// Reset the iterator to the initial index
    757             pub fn reset(it: *Iterator) void {
    758                 it.index = 0;
    759             }
    760         };
    761 
    762         /// If key exists this function cannot fail.
    763         /// If there is an existing item with `key`, then the result
    764         /// `Entry` pointer points to it, and found_existing is true.
    765         /// Otherwise, puts a new item with undefined value, and
    766         /// the `Entry` pointer points to it. Caller should then initialize
    767         /// the value (but not the key).
    768         pub fn getOrPut(self: *Self, gpa: Allocator, key: K) Oom!GetOrPutResult {
    769             if (@sizeOf(Context) != 0)
    770                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getOrPutContext instead.");
    771             return self.getOrPutContext(gpa, key, undefined);
    772         }
    773         pub fn getOrPutContext(self: *Self, gpa: Allocator, key: K, ctx: Context) Oom!GetOrPutResult {
    774             const gop = try self.getOrPutContextAdapted(gpa, key, ctx, ctx);
    775             if (!gop.found_existing) {
    776                 gop.key_ptr.* = key;
    777             }
    778             return gop;
    779         }
    780         pub fn getOrPutAdapted(self: *Self, gpa: Allocator, key: anytype, key_ctx: anytype) Oom!GetOrPutResult {
    781             if (@sizeOf(Context) != 0)
    782                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getOrPutContextAdapted instead.");
    783             return self.getOrPutContextAdapted(gpa, key, key_ctx, undefined);
    784         }
    785         pub fn getOrPutContextAdapted(self: *Self, gpa: Allocator, key: anytype, key_ctx: anytype, ctx: Context) Oom!GetOrPutResult {
    786             self.ensureTotalCapacityContext(gpa, self.entries.len + 1, ctx) catch |err| {
    787                 // "If key exists this function cannot fail."
    788                 const index = self.getIndexAdapted(key, key_ctx) orelse return err;
    789                 const slice = self.entries.slice();
    790                 return GetOrPutResult{
    791                     .key_ptr = &slice.items(.key)[index],
    792                     // workaround for #6974
    793                     .value_ptr = if (@sizeOf(*V) == 0) undefined else &slice.items(.value)[index],
    794                     .found_existing = true,
    795                     .index = index,
    796                 };
    797             };
    798             return self.getOrPutAssumeCapacityAdapted(key, key_ctx);
    799         }
    800 
    801         /// If there is an existing item with `key`, then the result
    802         /// `Entry` pointer points to it, and found_existing is true.
    803         /// Otherwise, puts a new item with undefined value, and
    804         /// the `Entry` pointer points to it. Caller should then initialize
    805         /// the value (but not the key).
    806         /// If a new entry needs to be stored, this function asserts there
    807         /// is enough capacity to store it.
    808         pub fn getOrPutAssumeCapacity(self: *Self, key: K) GetOrPutResult {
    809             if (@sizeOf(Context) != 0)
    810                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getOrPutAssumeCapacityContext instead.");
    811             return self.getOrPutAssumeCapacityContext(key, undefined);
    812         }
    813         pub fn getOrPutAssumeCapacityContext(self: *Self, key: K, ctx: Context) GetOrPutResult {
    814             const gop = self.getOrPutAssumeCapacityAdapted(key, ctx);
    815             if (!gop.found_existing) {
    816                 gop.key_ptr.* = key;
    817             }
    818             return gop;
    819         }
    820         /// If there is an existing item with `key`, then the result
    821         /// `Entry` pointers point to it, and found_existing is true.
    822         /// Otherwise, puts a new item with undefined key and value, and
    823         /// the `Entry` pointers point to it. Caller must then initialize
    824         /// both the key and the value.
    825         /// If a new entry needs to be stored, this function asserts there
    826         /// is enough capacity to store it.
    827         pub fn getOrPutAssumeCapacityAdapted(self: *Self, key: anytype, ctx: anytype) GetOrPutResult {
    828             const header = self.index_header orelse {
    829                 // Linear scan.
    830                 const h = if (store_hash) checkedHash(ctx, key) else {};
    831                 const slice = self.entries.slice();
    832                 const hashes_array = slice.items(.hash);
    833                 const keys_array = slice.items(.key);
    834                 for (keys_array, 0..) |*item_key, i| {
    835                     if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*, i)) {
    836                         return GetOrPutResult{
    837                             .key_ptr = item_key,
    838                             // workaround for #6974
    839                             .value_ptr = if (@sizeOf(*V) == 0) undefined else &slice.items(.value)[i],
    840                             .found_existing = true,
    841                             .index = i,
    842                         };
    843                     }
    844                 }
    845 
    846                 const index = self.entries.addOneAssumeCapacity();
    847                 // The slice length changed, so we directly index the pointer.
    848                 if (store_hash) hashes_array.ptr[index] = h;
    849 
    850                 return GetOrPutResult{
    851                     .key_ptr = &keys_array.ptr[index],
    852                     // workaround for #6974
    853                     .value_ptr = if (@sizeOf(*V) == 0) undefined else &slice.items(.value).ptr[index],
    854                     .found_existing = false,
    855                     .index = index,
    856                 };
    857             };
    858 
    859             switch (header.capacityIndexType()) {
    860                 .u8 => return self.getOrPutInternal(key, ctx, header, u8),
    861                 .u16 => return self.getOrPutInternal(key, ctx, header, u16),
    862                 .u32 => return self.getOrPutInternal(key, ctx, header, u32),
    863             }
    864         }
    865 
    866         pub fn getOrPutValue(self: *Self, gpa: Allocator, key: K, value: V) Oom!GetOrPutResult {
    867             if (@sizeOf(Context) != 0)
    868                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getOrPutValueContext instead.");
    869             return self.getOrPutValueContext(gpa, key, value, undefined);
    870         }
    871         pub fn getOrPutValueContext(self: *Self, gpa: Allocator, key: K, value: V, ctx: Context) Oom!GetOrPutResult {
    872             const res = try self.getOrPutContextAdapted(gpa, key, ctx, ctx);
    873             if (!res.found_existing) {
    874                 res.key_ptr.* = key;
    875                 res.value_ptr.* = value;
    876             }
    877             return res;
    878         }
    879 
    880         /// Increases capacity, guaranteeing that insertions up until the
    881         /// `expected_count` will not cause an allocation, and therefore cannot fail.
    882         pub fn ensureTotalCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Oom!void {
    883             if (@sizeOf(ByIndexContext) != 0)
    884                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureTotalCapacityContext instead.");
    885             return self.ensureTotalCapacityContext(gpa, new_capacity, undefined);
    886         }
    887         pub fn ensureTotalCapacityContext(self: *Self, gpa: Allocator, new_capacity: usize, ctx: Context) Oom!void {
    888             self.pointer_stability.lock();
    889             defer self.pointer_stability.unlock();
    890 
    891             if (new_capacity <= linear_scan_max) {
    892                 try self.entries.ensureTotalCapacity(gpa, new_capacity);
    893                 return;
    894             }
    895 
    896             if (self.index_header) |header| {
    897                 if (new_capacity <= header.capacity()) {
    898                     try self.entries.ensureTotalCapacity(gpa, new_capacity);
    899                     return;
    900                 }
    901             }
    902 
    903             try self.entries.ensureTotalCapacity(gpa, new_capacity);
    904             const new_bit_index = try IndexHeader.findBitIndex(new_capacity);
    905             const new_header = try IndexHeader.alloc(gpa, new_bit_index);
    906 
    907             if (self.index_header) |old_header| old_header.free(gpa);
    908             self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);
    909             self.index_header = new_header;
    910         }
    911 
    912         /// Increases capacity, guaranteeing that insertions up until
    913         /// `additional_count` **more** items will not cause an allocation, and
    914         /// therefore cannot fail.
    915         pub fn ensureUnusedCapacity(
    916             self: *Self,
    917             gpa: Allocator,
    918             additional_capacity: usize,
    919         ) Oom!void {
    920             if (@sizeOf(Context) != 0)
    921                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call ensureTotalCapacityContext instead.");
    922             return self.ensureUnusedCapacityContext(gpa, additional_capacity, undefined);
    923         }
    924         pub fn ensureUnusedCapacityContext(
    925             self: *Self,
    926             gpa: Allocator,
    927             additional_capacity: usize,
    928             ctx: Context,
    929         ) Oom!void {
    930             return self.ensureTotalCapacityContext(gpa, self.count() + additional_capacity, ctx);
    931         }
    932 
    933         /// Returns the number of total elements which may be present before it is
    934         /// no longer guaranteed that no allocations will be performed.
    935         pub fn capacity(self: Self) usize {
    936             const entry_cap = self.entries.capacity;
    937             const header = self.index_header orelse return @min(linear_scan_max, entry_cap);
    938             const indexes_cap = header.capacity();
    939             return @min(entry_cap, indexes_cap);
    940         }
    941 
    942         /// Clobbers any existing data. To detect if a put would clobber
    943         /// existing data, see `getOrPut`.
    944         pub fn put(self: *Self, gpa: Allocator, key: K, value: V) Oom!void {
    945             if (@sizeOf(Context) != 0)
    946                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call putContext instead.");
    947             return self.putContext(gpa, key, value, undefined);
    948         }
    949         pub fn putContext(self: *Self, gpa: Allocator, key: K, value: V, ctx: Context) Oom!void {
    950             const result = try self.getOrPutContext(gpa, key, ctx);
    951             result.value_ptr.* = value;
    952         }
    953 
    954         /// Inserts a key-value pair into the hash map, asserting that no previous
    955         /// entry with the same key is already present
    956         pub fn putNoClobber(self: *Self, gpa: Allocator, key: K, value: V) Oom!void {
    957             if (@sizeOf(Context) != 0)
    958                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call putNoClobberContext instead.");
    959             return self.putNoClobberContext(gpa, key, value, undefined);
    960         }
    961         pub fn putNoClobberContext(self: *Self, gpa: Allocator, key: K, value: V, ctx: Context) Oom!void {
    962             const result = try self.getOrPutContext(gpa, key, ctx);
    963             assert(!result.found_existing);
    964             result.value_ptr.* = value;
    965         }
    966 
    967         /// Asserts there is enough capacity to store the new key-value pair.
    968         /// Clobbers any existing data. To detect if a put would clobber
    969         /// existing data, see `getOrPutAssumeCapacity`.
    970         pub fn putAssumeCapacity(self: *Self, key: K, value: V) void {
    971             if (@sizeOf(Context) != 0)
    972                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call putAssumeCapacityContext instead.");
    973             return self.putAssumeCapacityContext(key, value, undefined);
    974         }
    975         pub fn putAssumeCapacityContext(self: *Self, key: K, value: V, ctx: Context) void {
    976             const result = self.getOrPutAssumeCapacityContext(key, ctx);
    977             result.value_ptr.* = value;
    978         }
    979 
    980         /// Asserts there is enough capacity to store the new key-value pair.
    981         /// Asserts that it does not clobber any existing data.
    982         /// To detect if a put would clobber existing data, see `getOrPutAssumeCapacity`.
    983         pub fn putAssumeCapacityNoClobber(self: *Self, key: K, value: V) void {
    984             if (@sizeOf(Context) != 0)
    985                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call putAssumeCapacityNoClobberContext instead.");
    986             return self.putAssumeCapacityNoClobberContext(key, value, undefined);
    987         }
    988         pub fn putAssumeCapacityNoClobberContext(self: *Self, key: K, value: V, ctx: Context) void {
    989             const result = self.getOrPutAssumeCapacityContext(key, ctx);
    990             assert(!result.found_existing);
    991             result.value_ptr.* = value;
    992         }
    993 
    994         /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
    995         pub fn fetchPut(self: *Self, gpa: Allocator, key: K, value: V) Oom!?KV {
    996             if (@sizeOf(Context) != 0)
    997                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call fetchPutContext instead.");
    998             return self.fetchPutContext(gpa, key, value, undefined);
    999         }
   1000         pub fn fetchPutContext(self: *Self, gpa: Allocator, key: K, value: V, ctx: Context) Oom!?KV {
   1001             const gop = try self.getOrPutContext(gpa, key, ctx);
   1002             var result: ?KV = null;
   1003             if (gop.found_existing) {
   1004                 result = KV{
   1005                     .key = gop.key_ptr.*,
   1006                     .value = gop.value_ptr.*,
   1007                 };
   1008             }
   1009             gop.value_ptr.* = value;
   1010             return result;
   1011         }
   1012 
   1013         /// Inserts a new `Entry` into the hash map, returning the previous one, if any.
   1014         /// If insertion happens, asserts there is enough capacity without allocating.
   1015         pub fn fetchPutAssumeCapacity(self: *Self, key: K, value: V) ?KV {
   1016             if (@sizeOf(Context) != 0)
   1017                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call fetchPutAssumeCapacityContext instead.");
   1018             return self.fetchPutAssumeCapacityContext(key, value, undefined);
   1019         }
   1020         pub fn fetchPutAssumeCapacityContext(self: *Self, key: K, value: V, ctx: Context) ?KV {
   1021             const gop = self.getOrPutAssumeCapacityContext(key, ctx);
   1022             var result: ?KV = null;
   1023             if (gop.found_existing) {
   1024                 result = KV{
   1025                     .key = gop.key_ptr.*,
   1026                     .value = gop.value_ptr.*,
   1027                 };
   1028             }
   1029             gop.value_ptr.* = value;
   1030             return result;
   1031         }
   1032 
   1033         /// Finds pointers to the key and value storage associated with a key.
   1034         pub fn getEntry(self: Self, key: K) ?Entry {
   1035             if (@sizeOf(Context) != 0)
   1036                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getEntryContext instead.");
   1037             return self.getEntryContext(key, undefined);
   1038         }
   1039         pub fn getEntryContext(self: Self, key: K, ctx: Context) ?Entry {
   1040             return self.getEntryAdapted(key, ctx);
   1041         }
   1042         pub fn getEntryAdapted(self: Self, key: anytype, ctx: anytype) ?Entry {
   1043             const index = self.getIndexAdapted(key, ctx) orelse return null;
   1044             const slice = self.entries.slice();
   1045             return Entry{
   1046                 .key_ptr = &slice.items(.key)[index],
   1047                 // workaround for #6974
   1048                 .value_ptr = if (@sizeOf(*V) == 0) undefined else &slice.items(.value)[index],
   1049             };
   1050         }
   1051 
   1052         /// Finds the index in the `entries` array where a key is stored
   1053         pub fn getIndex(self: Self, key: K) ?usize {
   1054             if (@sizeOf(Context) != 0)
   1055                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getIndexContext instead.");
   1056             return self.getIndexContext(key, undefined);
   1057         }
   1058         pub fn getIndexContext(self: Self, key: K, ctx: Context) ?usize {
   1059             return self.getIndexAdapted(key, ctx);
   1060         }
   1061         pub fn getIndexAdapted(self: Self, key: anytype, ctx: anytype) ?usize {
   1062             const header = self.index_header orelse {
   1063                 // Linear scan.
   1064                 const h = if (store_hash) checkedHash(ctx, key) else {};
   1065                 const slice = self.entries.slice();
   1066                 const hashes_array = slice.items(.hash);
   1067                 const keys_array = slice.items(.key);
   1068                 for (keys_array, 0..) |*item_key, i| {
   1069                     if (hashes_array[i] == h and checkedEql(ctx, key, item_key.*, i)) {
   1070                         return i;
   1071                     }
   1072                 }
   1073                 return null;
   1074             };
   1075             switch (header.capacityIndexType()) {
   1076                 .u8 => return self.getIndexWithHeaderGeneric(key, ctx, header, u8),
   1077                 .u16 => return self.getIndexWithHeaderGeneric(key, ctx, header, u16),
   1078                 .u32 => return self.getIndexWithHeaderGeneric(key, ctx, header, u32),
   1079             }
   1080         }
   1081         fn getIndexWithHeaderGeneric(self: Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type) ?usize {
   1082             const indexes = header.indexes(I);
   1083             const slot = self.getSlotByKey(key, ctx, header, I, indexes) orelse return null;
   1084             return indexes[slot].entry_index;
   1085         }
   1086 
   1087         /// Find the value associated with a key
   1088         pub fn get(self: Self, key: K) ?V {
   1089             if (@sizeOf(Context) != 0)
   1090                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getContext instead.");
   1091             return self.getContext(key, undefined);
   1092         }
   1093         pub fn getContext(self: Self, key: K, ctx: Context) ?V {
   1094             return self.getAdapted(key, ctx);
   1095         }
   1096         pub fn getAdapted(self: Self, key: anytype, ctx: anytype) ?V {
   1097             const index = self.getIndexAdapted(key, ctx) orelse return null;
   1098             return self.values()[index];
   1099         }
   1100 
   1101         /// Find a pointer to the value associated with a key
   1102         pub fn getPtr(self: Self, key: K) ?*V {
   1103             if (@sizeOf(Context) != 0)
   1104                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getPtrContext instead.");
   1105             return self.getPtrContext(key, undefined);
   1106         }
   1107         pub fn getPtrContext(self: Self, key: K, ctx: Context) ?*V {
   1108             return self.getPtrAdapted(key, ctx);
   1109         }
   1110         pub fn getPtrAdapted(self: Self, key: anytype, ctx: anytype) ?*V {
   1111             const index = self.getIndexAdapted(key, ctx) orelse return null;
   1112             // workaround for #6974
   1113             return if (@sizeOf(*V) == 0) @as(*V, undefined) else &self.values()[index];
   1114         }
   1115 
   1116         /// Find the actual key associated with an adapted key
   1117         pub fn getKey(self: Self, key: K) ?K {
   1118             if (@sizeOf(Context) != 0)
   1119                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getKeyContext instead.");
   1120             return self.getKeyContext(key, undefined);
   1121         }
   1122         pub fn getKeyContext(self: Self, key: K, ctx: Context) ?K {
   1123             return self.getKeyAdapted(key, ctx);
   1124         }
   1125         pub fn getKeyAdapted(self: Self, key: anytype, ctx: anytype) ?K {
   1126             const index = self.getIndexAdapted(key, ctx) orelse return null;
   1127             return self.keys()[index];
   1128         }
   1129 
   1130         /// Find a pointer to the actual key associated with an adapted key
   1131         pub fn getKeyPtr(self: Self, key: K) ?*K {
   1132             if (@sizeOf(Context) != 0)
   1133                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call getKeyPtrContext instead.");
   1134             return self.getKeyPtrContext(key, undefined);
   1135         }
   1136         pub fn getKeyPtrContext(self: Self, key: K, ctx: Context) ?*K {
   1137             return self.getKeyPtrAdapted(key, ctx);
   1138         }
   1139         pub fn getKeyPtrAdapted(self: Self, key: anytype, ctx: anytype) ?*K {
   1140             const index = self.getIndexAdapted(key, ctx) orelse return null;
   1141             return &self.keys()[index];
   1142         }
   1143 
   1144         /// Check whether a key is stored in the map
   1145         pub fn contains(self: Self, key: K) bool {
   1146             if (@sizeOf(Context) != 0)
   1147                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call containsContext instead.");
   1148             return self.containsContext(key, undefined);
   1149         }
   1150         pub fn containsContext(self: Self, key: K, ctx: Context) bool {
   1151             return self.containsAdapted(key, ctx);
   1152         }
   1153         pub fn containsAdapted(self: Self, key: anytype, ctx: anytype) bool {
   1154             return self.getIndexAdapted(key, ctx) != null;
   1155         }
   1156 
   1157         /// If there is an `Entry` with a matching key, it is deleted from
   1158         /// the hash map, and then returned from this function. The entry is
   1159         /// removed from the underlying array by swapping it with the last
   1160         /// element.
   1161         pub fn fetchSwapRemove(self: *Self, key: K) ?KV {
   1162             if (@sizeOf(Context) != 0)
   1163                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call fetchSwapRemoveContext instead.");
   1164             return self.fetchSwapRemoveContext(key, undefined);
   1165         }
   1166         pub fn fetchSwapRemoveContext(self: *Self, key: K, ctx: Context) ?KV {
   1167             return self.fetchSwapRemoveContextAdapted(key, ctx, ctx);
   1168         }
   1169         pub fn fetchSwapRemoveAdapted(self: *Self, key: anytype, ctx: anytype) ?KV {
   1170             if (@sizeOf(ByIndexContext) != 0)
   1171                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call fetchSwapRemoveContextAdapted instead.");
   1172             return self.fetchSwapRemoveContextAdapted(key, ctx, undefined);
   1173         }
   1174         pub fn fetchSwapRemoveContextAdapted(self: *Self, key: anytype, key_ctx: anytype, ctx: Context) ?KV {
   1175             self.pointer_stability.lock();
   1176             defer self.pointer_stability.unlock();
   1177 
   1178             return self.fetchRemoveByKey(key, key_ctx, if (store_hash) {} else ctx, .swap);
   1179         }
   1180 
   1181         /// If there is an `Entry` with a matching key, it is deleted from
   1182         /// the hash map, and then returned from this function. The entry is
   1183         /// removed from the underlying array by shifting all elements forward
   1184         /// thereby maintaining the current ordering.
   1185         pub fn fetchOrderedRemove(self: *Self, key: K) ?KV {
   1186             if (@sizeOf(Context) != 0)
   1187                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call fetchOrderedRemoveContext instead.");
   1188             return self.fetchOrderedRemoveContext(key, undefined);
   1189         }
   1190         pub fn fetchOrderedRemoveContext(self: *Self, key: K, ctx: Context) ?KV {
   1191             return self.fetchOrderedRemoveContextAdapted(key, ctx, ctx);
   1192         }
   1193         pub fn fetchOrderedRemoveAdapted(self: *Self, key: anytype, ctx: anytype) ?KV {
   1194             if (@sizeOf(ByIndexContext) != 0)
   1195                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call fetchOrderedRemoveContextAdapted instead.");
   1196             return self.fetchOrderedRemoveContextAdapted(key, ctx, undefined);
   1197         }
   1198         pub fn fetchOrderedRemoveContextAdapted(self: *Self, key: anytype, key_ctx: anytype, ctx: Context) ?KV {
   1199             self.pointer_stability.lock();
   1200             defer self.pointer_stability.unlock();
   1201 
   1202             return self.fetchRemoveByKey(key, key_ctx, if (store_hash) {} else ctx, .ordered);
   1203         }
   1204 
   1205         /// If there is an `Entry` with a matching key, it is deleted from
   1206         /// the hash map. The entry is removed from the underlying array
   1207         /// by swapping it with the last element.  Returns true if an entry
   1208         /// was removed, false otherwise.
   1209         pub fn swapRemove(self: *Self, key: K) bool {
   1210             if (@sizeOf(Context) != 0)
   1211                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call swapRemoveContext instead.");
   1212             return self.swapRemoveContext(key, undefined);
   1213         }
   1214         pub fn swapRemoveContext(self: *Self, key: K, ctx: Context) bool {
   1215             return self.swapRemoveContextAdapted(key, ctx, ctx);
   1216         }
   1217         pub fn swapRemoveAdapted(self: *Self, key: anytype, ctx: anytype) bool {
   1218             if (@sizeOf(ByIndexContext) != 0)
   1219                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call swapRemoveContextAdapted instead.");
   1220             return self.swapRemoveContextAdapted(key, ctx, undefined);
   1221         }
   1222         pub fn swapRemoveContextAdapted(self: *Self, key: anytype, key_ctx: anytype, ctx: Context) bool {
   1223             self.pointer_stability.lock();
   1224             defer self.pointer_stability.unlock();
   1225 
   1226             return self.removeByKey(key, key_ctx, if (store_hash) {} else ctx, .swap);
   1227         }
   1228 
   1229         /// If there is an `Entry` with a matching key, it is deleted from
   1230         /// the hash map. The entry is removed from the underlying array
   1231         /// by shifting all elements forward, thereby maintaining the
   1232         /// current ordering.  Returns true if an entry was removed, false otherwise.
   1233         pub fn orderedRemove(self: *Self, key: K) bool {
   1234             if (@sizeOf(Context) != 0)
   1235                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call orderedRemoveContext instead.");
   1236             return self.orderedRemoveContext(key, undefined);
   1237         }
   1238         pub fn orderedRemoveContext(self: *Self, key: K, ctx: Context) bool {
   1239             return self.orderedRemoveContextAdapted(key, ctx, ctx);
   1240         }
   1241         pub fn orderedRemoveAdapted(self: *Self, key: anytype, ctx: anytype) bool {
   1242             if (@sizeOf(ByIndexContext) != 0)
   1243                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call orderedRemoveContextAdapted instead.");
   1244             return self.orderedRemoveContextAdapted(key, ctx, undefined);
   1245         }
   1246         pub fn orderedRemoveContextAdapted(self: *Self, key: anytype, key_ctx: anytype, ctx: Context) bool {
   1247             self.pointer_stability.lock();
   1248             defer self.pointer_stability.unlock();
   1249 
   1250             return self.removeByKey(key, key_ctx, if (store_hash) {} else ctx, .ordered);
   1251         }
   1252 
   1253         /// Deletes the item at the specified index in `entries` from
   1254         /// the hash map. The entry is removed from the underlying array
   1255         /// by swapping it with the last element.
   1256         pub fn swapRemoveAt(self: *Self, index: usize) void {
   1257             if (@sizeOf(ByIndexContext) != 0)
   1258                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call swapRemoveAtContext instead.");
   1259             return self.swapRemoveAtContext(index, undefined);
   1260         }
   1261         pub fn swapRemoveAtContext(self: *Self, index: usize, ctx: Context) void {
   1262             self.pointer_stability.lock();
   1263             defer self.pointer_stability.unlock();
   1264 
   1265             self.removeByIndex(index, if (store_hash) {} else ctx, .swap);
   1266         }
   1267 
   1268         /// Deletes the item at the specified index in `entries` from
   1269         /// the hash map. The entry is removed from the underlying array
   1270         /// by shifting all elements forward, thereby maintaining the
   1271         /// current ordering.
   1272         pub fn orderedRemoveAt(self: *Self, index: usize) void {
   1273             if (@sizeOf(ByIndexContext) != 0)
   1274                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call orderedRemoveAtContext instead.");
   1275             return self.orderedRemoveAtContext(index, undefined);
   1276         }
   1277         pub fn orderedRemoveAtContext(self: *Self, index: usize, ctx: Context) void {
   1278             self.pointer_stability.lock();
   1279             defer self.pointer_stability.unlock();
   1280 
   1281             self.removeByIndex(index, if (store_hash) {} else ctx, .ordered);
   1282         }
   1283 
   1284         /// Create a copy of the hash map which can be modified separately.
   1285         /// The copy uses the same context as this instance, but is allocated
   1286         /// with the provided allocator.
   1287         pub fn clone(self: Self, gpa: Allocator) Oom!Self {
   1288             if (@sizeOf(ByIndexContext) != 0)
   1289                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call cloneContext instead.");
   1290             return self.cloneContext(gpa, undefined);
   1291         }
   1292         pub fn cloneContext(self: Self, gpa: Allocator, ctx: Context) Oom!Self {
   1293             var other: Self = .{};
   1294             other.entries = try self.entries.clone(gpa);
   1295             errdefer other.entries.deinit(gpa);
   1296 
   1297             if (self.index_header) |header| {
   1298                 // TODO: I'm pretty sure this could be memcpy'd instead of
   1299                 // doing all this work.
   1300                 const new_header = try IndexHeader.alloc(gpa, header.bit_index);
   1301                 other.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);
   1302                 other.index_header = new_header;
   1303             }
   1304             return other;
   1305         }
   1306 
   1307         /// Set the map to an empty state, making deinitialization a no-op, and
   1308         /// returning a copy of the original.
   1309         pub fn move(self: *Self) Self {
   1310             self.pointer_stability.assertUnlocked();
   1311             const result = self.*;
   1312             self.* = .empty;
   1313             return result;
   1314         }
   1315 
   1316         /// Recomputes stored hashes and rebuilds the key indexes. If the
   1317         /// underlying keys have been modified directly, call this method to
   1318         /// recompute the denormalized metadata necessary for the operation of
   1319         /// the methods of this map that lookup entries by key.
   1320         ///
   1321         /// One use case for this is directly calling `entries.resize()` to grow
   1322         /// the underlying storage, and then setting the `keys` and `values`
   1323         /// directly without going through the methods of this map.
   1324         ///
   1325         /// The time complexity of this operation is O(n).
   1326         pub fn reIndex(self: *Self, gpa: Allocator) Oom!void {
   1327             if (@sizeOf(ByIndexContext) != 0)
   1328                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call reIndexContext instead.");
   1329             return self.reIndexContext(gpa, undefined);
   1330         }
   1331 
   1332         pub fn reIndexContext(self: *Self, gpa: Allocator, ctx: Context) Oom!void {
   1333             // Recompute all hashes.
   1334             if (store_hash) {
   1335                 for (self.keys(), self.entries.items(.hash)) |key, *hash| {
   1336                     const h = checkedHash(ctx, key);
   1337                     hash.* = h;
   1338                 }
   1339             }
   1340             // Rebuild the index.
   1341             if (self.entries.capacity > linear_scan_max) {
   1342                 // We're going to rebuild the index header and replace the existing one (if any). The
   1343                 // indexes should sized such that they will be at most 60% full.
   1344                 const bit_index = try IndexHeader.findBitIndex(self.entries.capacity);
   1345                 const new_header = try IndexHeader.alloc(gpa, bit_index);
   1346                 if (self.index_header) |header| header.free(gpa);
   1347                 self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, new_header);
   1348                 self.index_header = new_header;
   1349             }
   1350         }
   1351 
   1352         /// Sorts the entries and then rebuilds the index.
   1353         /// `sort_ctx` must have this method:
   1354         /// `fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool`
   1355         /// Uses a stable sorting algorithm.
   1356         pub inline fn sort(self: *Self, sort_ctx: anytype) void {
   1357             if (@sizeOf(ByIndexContext) != 0)
   1358                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call sortContext instead.");
   1359             return sortContextInternal(self, .stable, sort_ctx, undefined);
   1360         }
   1361 
   1362         /// Sorts the entries and then rebuilds the index.
   1363         /// `sort_ctx` must have this method:
   1364         /// `fn lessThan(ctx: @TypeOf(ctx), a_index: usize, b_index: usize) bool`
   1365         /// Uses an unstable sorting algorithm.
   1366         pub inline fn sortUnstable(self: *Self, sort_ctx: anytype) void {
   1367             if (@sizeOf(ByIndexContext) != 0)
   1368                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call sortUnstableContext instead.");
   1369             return self.sortContextInternal(.unstable, sort_ctx, undefined);
   1370         }
   1371 
   1372         pub inline fn sortContext(self: *Self, sort_ctx: anytype, ctx: Context) void {
   1373             return sortContextInternal(self, .stable, sort_ctx, ctx);
   1374         }
   1375 
   1376         pub inline fn sortUnstableContext(self: *Self, sort_ctx: anytype, ctx: Context) void {
   1377             return sortContextInternal(self, .unstable, sort_ctx, ctx);
   1378         }
   1379 
   1380         fn sortContextInternal(
   1381             self: *Self,
   1382             comptime mode: std.sort.Mode,
   1383             sort_ctx: anytype,
   1384             ctx: Context,
   1385         ) void {
   1386             self.pointer_stability.lock();
   1387             defer self.pointer_stability.unlock();
   1388 
   1389             switch (mode) {
   1390                 .stable => self.entries.sort(sort_ctx),
   1391                 .unstable => self.entries.sortUnstable(sort_ctx),
   1392             }
   1393             const header = self.index_header orelse return;
   1394             header.reset();
   1395             self.insertAllEntriesIntoNewHeader(if (store_hash) {} else ctx, header);
   1396         }
   1397 
   1398         /// Shrinks the underlying `Entry` array to `new_len` elements and
   1399         /// discards any associated index entries. Keeps capacity the same.
   1400         ///
   1401         /// Asserts the discarded entries remain initialized and capable of
   1402         /// performing hash and equality checks. Any deinitialization of
   1403         /// discarded entries must take place *after* calling this function.
   1404         pub fn shrinkRetainingCapacity(self: *Self, new_len: usize) void {
   1405             if (@sizeOf(ByIndexContext) != 0)
   1406                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call shrinkRetainingCapacityContext instead.");
   1407             return self.shrinkRetainingCapacityContext(new_len, undefined);
   1408         }
   1409 
   1410         /// Shrinks the underlying `Entry` array to `new_len` elements and
   1411         /// discards any associated index entries. Keeps capacity the same.
   1412         ///
   1413         /// Asserts the discarded entries remain initialized and capable of
   1414         /// performing hash and equality checks. Any deinitialization of
   1415         /// discarded entries must take place *after* calling this function.
   1416         pub fn shrinkRetainingCapacityContext(self: *Self, new_len: usize, ctx: Context) void {
   1417             self.pointer_stability.lock();
   1418             defer self.pointer_stability.unlock();
   1419 
   1420             // Remove index entries from the new length onwards.
   1421             // Explicitly choose to ONLY remove index entries and not the underlying array list
   1422             // entries as we're going to remove them in the subsequent shrink call.
   1423             if (self.index_header) |header| {
   1424                 var i: usize = new_len;
   1425                 while (i < self.entries.len) : (i += 1)
   1426                     self.removeFromIndexByIndex(i, if (store_hash) {} else ctx, header);
   1427             }
   1428             self.entries.shrinkRetainingCapacity(new_len);
   1429         }
   1430 
   1431         /// Shrinks the underlying `Entry` array to `new_len` elements and
   1432         /// discards any associated index entries. Reduces allocated capacity.
   1433         ///
   1434         /// Asserts the discarded entries remain initialized and capable of
   1435         /// performing hash and equality checks. It is a bug to call this
   1436         /// function if the discarded entries require deinitialization. For
   1437         /// that use case, `shrinkRetainingCapacity` can be used instead.
   1438         pub fn shrinkAndFree(self: *Self, gpa: Allocator, new_len: usize) void {
   1439             if (@sizeOf(ByIndexContext) != 0)
   1440                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call shrinkAndFreeContext instead.");
   1441             return self.shrinkAndFreeContext(gpa, new_len, undefined);
   1442         }
   1443 
   1444         /// Shrinks the underlying `Entry` array to `new_len` elements and
   1445         /// discards any associated index entries. Reduces allocated capacity.
   1446         ///
   1447         /// Asserts the discarded entries remain initialized and capable of
   1448         /// performing hash and equality checks. It is a bug to call this
   1449         /// function if the discarded entries require deinitialization. For
   1450         /// that use case, `shrinkRetainingCapacityContext` can be used
   1451         /// instead.
   1452         pub fn shrinkAndFreeContext(self: *Self, gpa: Allocator, new_len: usize, ctx: Context) void {
   1453             self.pointer_stability.lock();
   1454             defer self.pointer_stability.unlock();
   1455 
   1456             // Remove index entries from the new length onwards.
   1457             // Explicitly choose to ONLY remove index entries and not the underlying array list
   1458             // entries as we're going to remove them in the subsequent shrink call.
   1459             if (self.index_header) |header| {
   1460                 var i: usize = new_len;
   1461                 while (i < self.entries.len) : (i += 1)
   1462                     self.removeFromIndexByIndex(i, if (store_hash) {} else ctx, header);
   1463             }
   1464             self.entries.shrinkAndFree(gpa, new_len);
   1465         }
   1466 
   1467         /// Removes the last inserted `Entry` in the hash map and returns it.
   1468         pub fn pop(self: *Self) KV {
   1469             if (@sizeOf(ByIndexContext) != 0)
   1470                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call popContext instead.");
   1471             return self.popContext(undefined);
   1472         }
   1473         pub fn popContext(self: *Self, ctx: Context) KV {
   1474             self.pointer_stability.lock();
   1475             defer self.pointer_stability.unlock();
   1476 
   1477             const item = self.entries.get(self.entries.len - 1);
   1478             if (self.index_header) |header|
   1479                 self.removeFromIndexByIndex(self.entries.len - 1, if (store_hash) {} else ctx, header);
   1480             self.entries.len -= 1;
   1481             return .{
   1482                 .key = item.key,
   1483                 .value = item.value,
   1484             };
   1485         }
   1486 
   1487         /// Removes the last inserted `Entry` in the hash map and returns it if count is nonzero.
   1488         /// Otherwise returns null.
   1489         pub fn popOrNull(self: *Self) ?KV {
   1490             if (@sizeOf(ByIndexContext) != 0)
   1491                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call popContext instead.");
   1492             return self.popOrNullContext(undefined);
   1493         }
   1494         pub fn popOrNullContext(self: *Self, ctx: Context) ?KV {
   1495             return if (self.entries.len == 0) null else self.popContext(ctx);
   1496         }
   1497 
   1498         fn fetchRemoveByKey(
   1499             self: *Self,
   1500             key: anytype,
   1501             key_ctx: anytype,
   1502             ctx: ByIndexContext,
   1503             comptime removal_type: RemovalType,
   1504         ) ?KV {
   1505             const header = self.index_header orelse {
   1506                 // Linear scan.
   1507                 const key_hash = if (store_hash) key_ctx.hash(key) else {};
   1508                 const slice = self.entries.slice();
   1509                 const hashes_array = if (store_hash) slice.items(.hash) else {};
   1510                 const keys_array = slice.items(.key);
   1511                 for (keys_array, 0..) |*item_key, i| {
   1512                     const hash_match = if (store_hash) hashes_array[i] == key_hash else true;
   1513                     if (hash_match and key_ctx.eql(key, item_key.*, i)) {
   1514                         const removed_entry: KV = .{
   1515                             .key = keys_array[i],
   1516                             .value = slice.items(.value)[i],
   1517                         };
   1518                         switch (removal_type) {
   1519                             .swap => self.entries.swapRemove(i),
   1520                             .ordered => self.entries.orderedRemove(i),
   1521                         }
   1522                         return removed_entry;
   1523                     }
   1524                 }
   1525                 return null;
   1526             };
   1527             return switch (header.capacityIndexType()) {
   1528                 .u8 => self.fetchRemoveByKeyGeneric(key, key_ctx, ctx, header, u8, removal_type),
   1529                 .u16 => self.fetchRemoveByKeyGeneric(key, key_ctx, ctx, header, u16, removal_type),
   1530                 .u32 => self.fetchRemoveByKeyGeneric(key, key_ctx, ctx, header, u32, removal_type),
   1531             };
   1532         }
   1533         fn fetchRemoveByKeyGeneric(
   1534             self: *Self,
   1535             key: anytype,
   1536             key_ctx: anytype,
   1537             ctx: ByIndexContext,
   1538             header: *IndexHeader,
   1539             comptime I: type,
   1540             comptime removal_type: RemovalType,
   1541         ) ?KV {
   1542             const indexes = header.indexes(I);
   1543             const entry_index = self.removeFromIndexByKey(key, key_ctx, header, I, indexes) orelse return null;
   1544             const slice = self.entries.slice();
   1545             const removed_entry: KV = .{
   1546                 .key = slice.items(.key)[entry_index],
   1547                 .value = slice.items(.value)[entry_index],
   1548             };
   1549             self.removeFromArrayAndUpdateIndex(entry_index, ctx, header, I, indexes, removal_type);
   1550             return removed_entry;
   1551         }
   1552 
   1553         fn removeByKey(
   1554             self: *Self,
   1555             key: anytype,
   1556             key_ctx: anytype,
   1557             ctx: ByIndexContext,
   1558             comptime removal_type: RemovalType,
   1559         ) bool {
   1560             const header = self.index_header orelse {
   1561                 // Linear scan.
   1562                 const key_hash = if (store_hash) key_ctx.hash(key) else {};
   1563                 const slice = self.entries.slice();
   1564                 const hashes_array = if (store_hash) slice.items(.hash) else {};
   1565                 const keys_array = slice.items(.key);
   1566                 for (keys_array, 0..) |*item_key, i| {
   1567                     const hash_match = if (store_hash) hashes_array[i] == key_hash else true;
   1568                     if (hash_match and key_ctx.eql(key, item_key.*, i)) {
   1569                         switch (removal_type) {
   1570                             .swap => self.entries.swapRemove(i),
   1571                             .ordered => self.entries.orderedRemove(i),
   1572                         }
   1573                         return true;
   1574                     }
   1575                 }
   1576                 return false;
   1577             };
   1578             return switch (header.capacityIndexType()) {
   1579                 .u8 => self.removeByKeyGeneric(key, key_ctx, ctx, header, u8, removal_type),
   1580                 .u16 => self.removeByKeyGeneric(key, key_ctx, ctx, header, u16, removal_type),
   1581                 .u32 => self.removeByKeyGeneric(key, key_ctx, ctx, header, u32, removal_type),
   1582             };
   1583         }
   1584         fn removeByKeyGeneric(self: *Self, key: anytype, key_ctx: anytype, ctx: ByIndexContext, header: *IndexHeader, comptime I: type, comptime removal_type: RemovalType) bool {
   1585             const indexes = header.indexes(I);
   1586             const entry_index = self.removeFromIndexByKey(key, key_ctx, header, I, indexes) orelse return false;
   1587             self.removeFromArrayAndUpdateIndex(entry_index, ctx, header, I, indexes, removal_type);
   1588             return true;
   1589         }
   1590 
   1591         fn removeByIndex(self: *Self, entry_index: usize, ctx: ByIndexContext, comptime removal_type: RemovalType) void {
   1592             assert(entry_index < self.entries.len);
   1593             const header = self.index_header orelse {
   1594                 switch (removal_type) {
   1595                     .swap => self.entries.swapRemove(entry_index),
   1596                     .ordered => self.entries.orderedRemove(entry_index),
   1597                 }
   1598                 return;
   1599             };
   1600             switch (header.capacityIndexType()) {
   1601                 .u8 => self.removeByIndexGeneric(entry_index, ctx, header, u8, removal_type),
   1602                 .u16 => self.removeByIndexGeneric(entry_index, ctx, header, u16, removal_type),
   1603                 .u32 => self.removeByIndexGeneric(entry_index, ctx, header, u32, removal_type),
   1604             }
   1605         }
   1606         fn removeByIndexGeneric(self: *Self, entry_index: usize, ctx: ByIndexContext, header: *IndexHeader, comptime I: type, comptime removal_type: RemovalType) void {
   1607             const indexes = header.indexes(I);
   1608             self.removeFromIndexByIndexGeneric(entry_index, ctx, header, I, indexes);
   1609             self.removeFromArrayAndUpdateIndex(entry_index, ctx, header, I, indexes, removal_type);
   1610         }
   1611 
   1612         fn removeFromArrayAndUpdateIndex(self: *Self, entry_index: usize, ctx: ByIndexContext, header: *IndexHeader, comptime I: type, indexes: []Index(I), comptime removal_type: RemovalType) void {
   1613             const last_index = self.entries.len - 1; // overflow => remove from empty map
   1614             switch (removal_type) {
   1615                 .swap => {
   1616                     if (last_index != entry_index) {
   1617                         // Because of the swap remove, now we need to update the index that was
   1618                         // pointing to the last entry and is now pointing to this removed item slot.
   1619                         self.updateEntryIndex(header, last_index, entry_index, ctx, I, indexes);
   1620                     }
   1621                     // updateEntryIndex reads from the old entry index,
   1622                     // so it needs to run before removal.
   1623                     self.entries.swapRemove(entry_index);
   1624                 },
   1625                 .ordered => {
   1626                     var i: usize = entry_index;
   1627                     while (i < last_index) : (i += 1) {
   1628                         // Because of the ordered remove, everything from the entry index onwards has
   1629                         // been shifted forward so we'll need to update the index entries.
   1630                         self.updateEntryIndex(header, i + 1, i, ctx, I, indexes);
   1631                     }
   1632                     // updateEntryIndex reads from the old entry index,
   1633                     // so it needs to run before removal.
   1634                     self.entries.orderedRemove(entry_index);
   1635                 },
   1636             }
   1637         }
   1638 
   1639         fn updateEntryIndex(
   1640             self: *Self,
   1641             header: *IndexHeader,
   1642             old_entry_index: usize,
   1643             new_entry_index: usize,
   1644             ctx: ByIndexContext,
   1645             comptime I: type,
   1646             indexes: []Index(I),
   1647         ) void {
   1648             const slot = self.getSlotByIndex(old_entry_index, ctx, header, I, indexes);
   1649             indexes[slot].entry_index = @as(I, @intCast(new_entry_index));
   1650         }
   1651 
   1652         fn removeFromIndexByIndex(self: *Self, entry_index: usize, ctx: ByIndexContext, header: *IndexHeader) void {
   1653             switch (header.capacityIndexType()) {
   1654                 .u8 => self.removeFromIndexByIndexGeneric(entry_index, ctx, header, u8, header.indexes(u8)),
   1655                 .u16 => self.removeFromIndexByIndexGeneric(entry_index, ctx, header, u16, header.indexes(u16)),
   1656                 .u32 => self.removeFromIndexByIndexGeneric(entry_index, ctx, header, u32, header.indexes(u32)),
   1657             }
   1658         }
   1659         fn removeFromIndexByIndexGeneric(self: *Self, entry_index: usize, ctx: ByIndexContext, header: *IndexHeader, comptime I: type, indexes: []Index(I)) void {
   1660             const slot = self.getSlotByIndex(entry_index, ctx, header, I, indexes);
   1661             removeSlot(slot, header, I, indexes);
   1662         }
   1663 
   1664         fn removeFromIndexByKey(self: *Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type, indexes: []Index(I)) ?usize {
   1665             const slot = self.getSlotByKey(key, ctx, header, I, indexes) orelse return null;
   1666             const removed_entry_index = indexes[slot].entry_index;
   1667             removeSlot(slot, header, I, indexes);
   1668             return removed_entry_index;
   1669         }
   1670 
   1671         fn removeSlot(removed_slot: usize, header: *IndexHeader, comptime I: type, indexes: []Index(I)) void {
   1672             const start_index = removed_slot +% 1;
   1673             const end_index = start_index +% indexes.len;
   1674 
   1675             var last_slot = removed_slot;
   1676             var index: usize = start_index;
   1677             while (index != end_index) : (index +%= 1) {
   1678                 const slot = header.constrainIndex(index);
   1679                 const slot_data = indexes[slot];
   1680                 if (slot_data.isEmpty() or slot_data.distance_from_start_index == 0) {
   1681                     indexes[last_slot].setEmpty();
   1682                     return;
   1683                 }
   1684                 indexes[last_slot] = .{
   1685                     .entry_index = slot_data.entry_index,
   1686                     .distance_from_start_index = slot_data.distance_from_start_index - 1,
   1687                 };
   1688                 last_slot = slot;
   1689             }
   1690             unreachable;
   1691         }
   1692 
   1693         fn getSlotByIndex(self: *Self, entry_index: usize, ctx: ByIndexContext, header: *IndexHeader, comptime I: type, indexes: []Index(I)) usize {
   1694             const slice = self.entries.slice();
   1695             const h = if (store_hash) slice.items(.hash)[entry_index] else checkedHash(ctx, slice.items(.key)[entry_index]);
   1696             const start_index = safeTruncate(usize, h);
   1697             const end_index = start_index +% indexes.len;
   1698 
   1699             var index = start_index;
   1700             var distance_from_start_index: I = 0;
   1701             while (index != end_index) : ({
   1702                 index +%= 1;
   1703                 distance_from_start_index += 1;
   1704             }) {
   1705                 const slot = header.constrainIndex(index);
   1706                 const slot_data = indexes[slot];
   1707 
   1708                 // This is the fundamental property of the array hash map index.  If this
   1709                 // assert fails, it probably means that the entry was not in the index.
   1710                 assert(!slot_data.isEmpty());
   1711                 assert(slot_data.distance_from_start_index >= distance_from_start_index);
   1712 
   1713                 if (slot_data.entry_index == entry_index) {
   1714                     return slot;
   1715                 }
   1716             }
   1717             unreachable;
   1718         }
   1719 
   1720         /// Must `ensureTotalCapacity`/`ensureUnusedCapacity` before calling this.
   1721         fn getOrPutInternal(self: *Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type) GetOrPutResult {
   1722             const slice = self.entries.slice();
   1723             const hashes_array = if (store_hash) slice.items(.hash) else {};
   1724             const keys_array = slice.items(.key);
   1725             const values_array = slice.items(.value);
   1726             const indexes = header.indexes(I);
   1727 
   1728             const h = checkedHash(ctx, key);
   1729             const start_index = safeTruncate(usize, h);
   1730             const end_index = start_index +% indexes.len;
   1731 
   1732             var index = start_index;
   1733             var distance_from_start_index: I = 0;
   1734             while (index != end_index) : ({
   1735                 index +%= 1;
   1736                 distance_from_start_index += 1;
   1737             }) {
   1738                 var slot = header.constrainIndex(index);
   1739                 var slot_data = indexes[slot];
   1740 
   1741                 // If the slot is empty, there can be no more items in this run.
   1742                 // We didn't find a matching item, so this must be new.
   1743                 // Put it in the empty slot.
   1744                 if (slot_data.isEmpty()) {
   1745                     const new_index = self.entries.addOneAssumeCapacity();
   1746                     indexes[slot] = .{
   1747                         .distance_from_start_index = distance_from_start_index,
   1748                         .entry_index = @as(I, @intCast(new_index)),
   1749                     };
   1750 
   1751                     // update the hash if applicable
   1752                     if (store_hash) hashes_array.ptr[new_index] = h;
   1753 
   1754                     return .{
   1755                         .found_existing = false,
   1756                         .key_ptr = &keys_array.ptr[new_index],
   1757                         // workaround for #6974
   1758                         .value_ptr = if (@sizeOf(*V) == 0) undefined else &values_array.ptr[new_index],
   1759                         .index = new_index,
   1760                     };
   1761                 }
   1762 
   1763                 // This pointer survives the following append because we call
   1764                 // entries.ensureTotalCapacity before getOrPutInternal.
   1765                 const i = slot_data.entry_index;
   1766                 const hash_match = if (store_hash) h == hashes_array[i] else true;
   1767                 if (hash_match and checkedEql(ctx, key, keys_array[i], i)) {
   1768                     return .{
   1769                         .found_existing = true,
   1770                         .key_ptr = &keys_array[slot_data.entry_index],
   1771                         // workaround for #6974
   1772                         .value_ptr = if (@sizeOf(*V) == 0) undefined else &values_array[slot_data.entry_index],
   1773                         .index = slot_data.entry_index,
   1774                     };
   1775                 }
   1776 
   1777                 // If the entry is closer to its target than our current distance,
   1778                 // the entry we are looking for does not exist.  It would be in
   1779                 // this slot instead if it was here.  So stop looking, and switch
   1780                 // to insert mode.
   1781                 if (slot_data.distance_from_start_index < distance_from_start_index) {
   1782                     // In this case, we did not find the item. We will put a new entry.
   1783                     // However, we will use this index for the new entry, and move
   1784                     // the previous index down the line, to keep the max distance_from_start_index
   1785                     // as small as possible.
   1786                     const new_index = self.entries.addOneAssumeCapacity();
   1787                     if (store_hash) hashes_array.ptr[new_index] = h;
   1788                     indexes[slot] = .{
   1789                         .entry_index = @as(I, @intCast(new_index)),
   1790                         .distance_from_start_index = distance_from_start_index,
   1791                     };
   1792                     distance_from_start_index = slot_data.distance_from_start_index;
   1793                     var displaced_index = slot_data.entry_index;
   1794 
   1795                     // Find somewhere to put the index we replaced by shifting
   1796                     // following indexes backwards.
   1797                     index +%= 1;
   1798                     distance_from_start_index += 1;
   1799                     while (index != end_index) : ({
   1800                         index +%= 1;
   1801                         distance_from_start_index += 1;
   1802                     }) {
   1803                         slot = header.constrainIndex(index);
   1804                         slot_data = indexes[slot];
   1805                         if (slot_data.isEmpty()) {
   1806                             indexes[slot] = .{
   1807                                 .entry_index = displaced_index,
   1808                                 .distance_from_start_index = distance_from_start_index,
   1809                             };
   1810                             return .{
   1811                                 .found_existing = false,
   1812                                 .key_ptr = &keys_array.ptr[new_index],
   1813                                 // workaround for #6974
   1814                                 .value_ptr = if (@sizeOf(*V) == 0) undefined else &values_array.ptr[new_index],
   1815                                 .index = new_index,
   1816                             };
   1817                         }
   1818 
   1819                         if (slot_data.distance_from_start_index < distance_from_start_index) {
   1820                             indexes[slot] = .{
   1821                                 .entry_index = displaced_index,
   1822                                 .distance_from_start_index = distance_from_start_index,
   1823                             };
   1824                             displaced_index = slot_data.entry_index;
   1825                             distance_from_start_index = slot_data.distance_from_start_index;
   1826                         }
   1827                     }
   1828                     unreachable;
   1829                 }
   1830             }
   1831             unreachable;
   1832         }
   1833 
   1834         fn getSlotByKey(self: Self, key: anytype, ctx: anytype, header: *IndexHeader, comptime I: type, indexes: []Index(I)) ?usize {
   1835             const slice = self.entries.slice();
   1836             const hashes_array = if (store_hash) slice.items(.hash) else {};
   1837             const keys_array = slice.items(.key);
   1838             const h = checkedHash(ctx, key);
   1839 
   1840             const start_index = safeTruncate(usize, h);
   1841             const end_index = start_index +% indexes.len;
   1842 
   1843             var index = start_index;
   1844             var distance_from_start_index: I = 0;
   1845             while (index != end_index) : ({
   1846                 index +%= 1;
   1847                 distance_from_start_index += 1;
   1848             }) {
   1849                 const slot = header.constrainIndex(index);
   1850                 const slot_data = indexes[slot];
   1851                 if (slot_data.isEmpty() or slot_data.distance_from_start_index < distance_from_start_index)
   1852                     return null;
   1853 
   1854                 const i = slot_data.entry_index;
   1855                 const hash_match = if (store_hash) h == hashes_array[i] else true;
   1856                 if (hash_match and checkedEql(ctx, key, keys_array[i], i))
   1857                     return slot;
   1858             }
   1859             unreachable;
   1860         }
   1861 
   1862         fn insertAllEntriesIntoNewHeader(self: *Self, ctx: ByIndexContext, header: *IndexHeader) void {
   1863             switch (header.capacityIndexType()) {
   1864                 .u8 => return self.insertAllEntriesIntoNewHeaderGeneric(ctx, header, u8),
   1865                 .u16 => return self.insertAllEntriesIntoNewHeaderGeneric(ctx, header, u16),
   1866                 .u32 => return self.insertAllEntriesIntoNewHeaderGeneric(ctx, header, u32),
   1867             }
   1868         }
   1869         fn insertAllEntriesIntoNewHeaderGeneric(self: *Self, ctx: ByIndexContext, header: *IndexHeader, comptime I: type) void {
   1870             const slice = self.entries.slice();
   1871             const items = if (store_hash) slice.items(.hash) else slice.items(.key);
   1872             const indexes = header.indexes(I);
   1873 
   1874             entry_loop: for (items, 0..) |key, i| {
   1875                 const h = if (store_hash) key else checkedHash(ctx, key);
   1876                 const start_index = safeTruncate(usize, h);
   1877                 const end_index = start_index +% indexes.len;
   1878                 var index = start_index;
   1879                 var entry_index = @as(I, @intCast(i));
   1880                 var distance_from_start_index: I = 0;
   1881                 while (index != end_index) : ({
   1882                     index +%= 1;
   1883                     distance_from_start_index += 1;
   1884                 }) {
   1885                     const slot = header.constrainIndex(index);
   1886                     const next_index = indexes[slot];
   1887                     if (next_index.isEmpty()) {
   1888                         indexes[slot] = .{
   1889                             .distance_from_start_index = distance_from_start_index,
   1890                             .entry_index = entry_index,
   1891                         };
   1892                         continue :entry_loop;
   1893                     }
   1894                     if (next_index.distance_from_start_index < distance_from_start_index) {
   1895                         indexes[slot] = .{
   1896                             .distance_from_start_index = distance_from_start_index,
   1897                             .entry_index = entry_index,
   1898                         };
   1899                         distance_from_start_index = next_index.distance_from_start_index;
   1900                         entry_index = next_index.entry_index;
   1901                     }
   1902                 }
   1903                 unreachable;
   1904             }
   1905         }
   1906 
   1907         fn checkedHash(ctx: anytype, key: anytype) u32 {
   1908             // If you get a compile error on the next line, it means that your
   1909             // generic hash function doesn't accept your key.
   1910             return ctx.hash(key);
   1911         }
   1912 
   1913         fn checkedEql(ctx: anytype, a: anytype, b: K, b_index: usize) bool {
   1914             // If you get a compile error on the next line, it means that your
   1915             // generic eql function doesn't accept (self, adapt key, K, index).
   1916             return ctx.eql(a, b, b_index);
   1917         }
   1918 
   1919         fn dumpState(self: Self, comptime keyFmt: []const u8, comptime valueFmt: []const u8) void {
   1920             if (@sizeOf(ByIndexContext) != 0)
   1921                 @compileError("Cannot infer context " ++ @typeName(Context) ++ ", call dumpStateContext instead.");
   1922             self.dumpStateContext(keyFmt, valueFmt, undefined);
   1923         }
   1924         fn dumpStateContext(self: Self, comptime keyFmt: []const u8, comptime valueFmt: []const u8, ctx: Context) void {
   1925             const p = std.debug.print;
   1926             p("{s}:\n", .{@typeName(Self)});
   1927             const slice = self.entries.slice();
   1928             const hash_status = if (store_hash) "stored" else "computed";
   1929             p("  len={} capacity={} hashes {s}\n", .{ slice.len, slice.capacity, hash_status });
   1930             var i: usize = 0;
   1931             const mask: u32 = if (self.index_header) |header| header.mask() else ~@as(u32, 0);
   1932             while (i < slice.len) : (i += 1) {
   1933                 const hash = if (store_hash) slice.items(.hash)[i] else checkedHash(ctx, slice.items(.key)[i]);
   1934                 if (store_hash) {
   1935                     p(
   1936                         "  [{}]: key=" ++ keyFmt ++ " value=" ++ valueFmt ++ " hash=0x{x} slot=[0x{x}]\n",
   1937                         .{ i, slice.items(.key)[i], slice.items(.value)[i], hash, hash & mask },
   1938                     );
   1939                 } else {
   1940                     p(
   1941                         "  [{}]: key=" ++ keyFmt ++ " value=" ++ valueFmt ++ " slot=[0x{x}]\n",
   1942                         .{ i, slice.items(.key)[i], slice.items(.value)[i], hash & mask },
   1943                     );
   1944                 }
   1945             }
   1946             if (self.index_header) |header| {
   1947                 p("\n", .{});
   1948                 switch (header.capacityIndexType()) {
   1949                     .u8 => dumpIndex(header, u8),
   1950                     .u16 => dumpIndex(header, u16),
   1951                     .u32 => dumpIndex(header, u32),
   1952                 }
   1953             }
   1954         }
   1955         fn dumpIndex(header: *IndexHeader, comptime I: type) void {
   1956             const p = std.debug.print;
   1957             p("  index len=0x{x} type={}\n", .{ header.length(), header.capacityIndexType() });
   1958             const indexes = header.indexes(I);
   1959             if (indexes.len == 0) return;
   1960             var is_empty = false;
   1961             for (indexes, 0..) |idx, i| {
   1962                 if (idx.isEmpty()) {
   1963                     is_empty = true;
   1964                 } else {
   1965                     if (is_empty) {
   1966                         is_empty = false;
   1967                         p("  ...\n", .{});
   1968                     }
   1969                     p("  [0x{x}]: [{}] +{}\n", .{ i, idx.entry_index, idx.distance_from_start_index });
   1970                 }
   1971             }
   1972             if (is_empty) {
   1973                 p("  ...\n", .{});
   1974             }
   1975         }
   1976     };
   1977 }
   1978 
   1979 const CapacityIndexType = enum { u8, u16, u32 };
   1980 
   1981 fn capacityIndexType(bit_index: u8) CapacityIndexType {
   1982     if (bit_index <= 8)
   1983         return .u8;
   1984     if (bit_index <= 16)
   1985         return .u16;
   1986     assert(bit_index <= 32);
   1987     return .u32;
   1988 }
   1989 
   1990 fn capacityIndexSize(bit_index: u8) usize {
   1991     switch (capacityIndexType(bit_index)) {
   1992         .u8 => return @sizeOf(Index(u8)),
   1993         .u16 => return @sizeOf(Index(u16)),
   1994         .u32 => return @sizeOf(Index(u32)),
   1995     }
   1996 }
   1997 
   1998 /// @truncate fails if the target type is larger than the
   1999 /// target value.  This causes problems when one of the types
   2000 /// is usize, which may be larger or smaller than u32 on different
   2001 /// systems.  This version of truncate is safe to use if either
   2002 /// parameter has dynamic size, and will perform widening conversion
   2003 /// when needed.  Both arguments must have the same signedness.
   2004 fn safeTruncate(comptime T: type, val: anytype) T {
   2005     if (@bitSizeOf(T) >= @bitSizeOf(@TypeOf(val)))
   2006         return val;
   2007     return @as(T, @truncate(val));
   2008 }
   2009 
   2010 /// A single entry in the lookup acceleration structure.  These structs
   2011 /// are found in an array after the IndexHeader.  Hashes index into this
   2012 /// array, and linear probing is used for collisions.
   2013 fn Index(comptime I: type) type {
   2014     return extern struct {
   2015         const Self = @This();
   2016 
   2017         /// The index of this entry in the backing store.  If the index is
   2018         /// empty, this is empty_sentinel.
   2019         entry_index: I,
   2020 
   2021         /// The distance between this slot and its ideal placement.  This is
   2022         /// used to keep maximum scan length small.  This value is undefined
   2023         /// if the index is empty.
   2024         distance_from_start_index: I,
   2025 
   2026         /// The special entry_index value marking an empty slot.
   2027         const empty_sentinel = ~@as(I, 0);
   2028 
   2029         /// A constant empty index
   2030         const empty = Self{
   2031             .entry_index = empty_sentinel,
   2032             .distance_from_start_index = undefined,
   2033         };
   2034 
   2035         /// Checks if a slot is empty
   2036         fn isEmpty(idx: Self) bool {
   2037             return idx.entry_index == empty_sentinel;
   2038         }
   2039 
   2040         /// Sets a slot to empty
   2041         fn setEmpty(idx: *Self) void {
   2042             idx.entry_index = empty_sentinel;
   2043             idx.distance_from_start_index = undefined;
   2044         }
   2045     };
   2046 }
   2047 
   2048 /// the byte size of the index must fit in a usize.  This is a power of two
   2049 /// length * the size of an Index(u32).  The index is 8 bytes (3 bits repr)
   2050 /// and max_usize + 1 is not representable, so we need to subtract out 4 bits.
   2051 const max_representable_index_len = @bitSizeOf(usize) - 4;
   2052 const max_bit_index = @min(32, max_representable_index_len);
   2053 const min_bit_index = 5;
   2054 const max_capacity = (1 << max_bit_index) - 1;
   2055 const index_capacities = blk: {
   2056     var caps: [max_bit_index + 1]u32 = undefined;
   2057     for (caps[0..max_bit_index], 0..) |*item, i| {
   2058         item.* = (1 << i) * 3 / 5;
   2059     }
   2060     caps[max_bit_index] = max_capacity;
   2061     break :blk caps;
   2062 };
   2063 
   2064 /// This struct is trailed by two arrays of length indexes_len
   2065 /// of integers, whose integer size is determined by indexes_len.
   2066 /// These arrays are indexed by constrainIndex(hash).  The
   2067 /// entryIndexes array contains the index in the dense backing store
   2068 /// where the entry's data can be found.  Entries which are not in
   2069 /// use have their index value set to emptySentinel(I).
   2070 /// The entryDistances array stores the distance between an entry
   2071 /// and its ideal hash bucket.  This is used when adding elements
   2072 /// to balance the maximum scan length.
   2073 const IndexHeader = struct {
   2074     /// This field tracks the total number of items in the arrays following
   2075     /// this header.  It is the bit index of the power of two number of indices.
   2076     /// This value is between min_bit_index and max_bit_index, inclusive.
   2077     bit_index: u8 align(@alignOf(u32)),
   2078 
   2079     /// Map from an incrementing index to an index slot in the attached arrays.
   2080     fn constrainIndex(header: IndexHeader, i: usize) usize {
   2081         // This is an optimization for modulo of power of two integers;
   2082         // it requires `indexes_len` to always be a power of two.
   2083         return @as(usize, @intCast(i & header.mask()));
   2084     }
   2085 
   2086     /// Returns the attached array of indexes.  I must match the type
   2087     /// returned by capacityIndexType.
   2088     fn indexes(header: *IndexHeader, comptime I: type) []Index(I) {
   2089         const start_ptr: [*]Index(I) = @alignCast(@ptrCast(@as([*]u8, @ptrCast(header)) + @sizeOf(IndexHeader)));
   2090         return start_ptr[0..header.length()];
   2091     }
   2092 
   2093     /// Returns the type used for the index arrays.
   2094     fn capacityIndexType(header: IndexHeader) CapacityIndexType {
   2095         return hash_map.capacityIndexType(header.bit_index);
   2096     }
   2097 
   2098     fn capacity(self: IndexHeader) u32 {
   2099         return index_capacities[self.bit_index];
   2100     }
   2101     fn length(self: IndexHeader) usize {
   2102         return @as(usize, 1) << @as(math.Log2Int(usize), @intCast(self.bit_index));
   2103     }
   2104     fn mask(self: IndexHeader) u32 {
   2105         return @as(u32, @intCast(self.length() - 1));
   2106     }
   2107 
   2108     fn findBitIndex(desired_capacity: usize) Allocator.Error!u8 {
   2109         if (desired_capacity > max_capacity) return error.OutOfMemory;
   2110         var new_bit_index = @as(u8, @intCast(std.math.log2_int_ceil(usize, desired_capacity)));
   2111         if (desired_capacity > index_capacities[new_bit_index]) new_bit_index += 1;
   2112         if (new_bit_index < min_bit_index) new_bit_index = min_bit_index;
   2113         assert(desired_capacity <= index_capacities[new_bit_index]);
   2114         return new_bit_index;
   2115     }
   2116 
   2117     /// Allocates an index header, and fills the entryIndexes array with empty.
   2118     /// The distance array contents are undefined.
   2119     fn alloc(gpa: Allocator, new_bit_index: u8) Allocator.Error!*IndexHeader {
   2120         const len = @as(usize, 1) << @as(math.Log2Int(usize), @intCast(new_bit_index));
   2121         const index_size = hash_map.capacityIndexSize(new_bit_index);
   2122         const nbytes = @sizeOf(IndexHeader) + index_size * len;
   2123         const bytes = try gpa.alignedAlloc(u8, @alignOf(IndexHeader), nbytes);
   2124         @memset(bytes[@sizeOf(IndexHeader)..], 0xff);
   2125         const result: *IndexHeader = @alignCast(@ptrCast(bytes.ptr));
   2126         result.* = .{
   2127             .bit_index = new_bit_index,
   2128         };
   2129         return result;
   2130     }
   2131 
   2132     /// Releases the memory for a header and its associated arrays.
   2133     fn free(header: *IndexHeader, gpa: Allocator) void {
   2134         const index_size = hash_map.capacityIndexSize(header.bit_index);
   2135         const ptr: [*]align(@alignOf(IndexHeader)) u8 = @ptrCast(header);
   2136         const slice = ptr[0 .. @sizeOf(IndexHeader) + header.length() * index_size];
   2137         gpa.free(slice);
   2138     }
   2139 
   2140     /// Puts an IndexHeader into the state that it would be in after being freshly allocated.
   2141     fn reset(header: *IndexHeader) void {
   2142         const index_size = hash_map.capacityIndexSize(header.bit_index);
   2143         const ptr: [*]align(@alignOf(IndexHeader)) u8 = @ptrCast(header);
   2144         const nbytes = @sizeOf(IndexHeader) + header.length() * index_size;
   2145         @memset(ptr[@sizeOf(IndexHeader)..nbytes], 0xff);
   2146     }
   2147 
   2148     // Verify that the header has sufficient alignment to produce aligned arrays.
   2149     comptime {
   2150         if (@alignOf(u32) > @alignOf(IndexHeader))
   2151             @compileError("IndexHeader must have a larger alignment than its indexes!");
   2152     }
   2153 };
   2154 
   2155 test "basic hash map usage" {
   2156     var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2157     defer map.deinit();
   2158 
   2159     try testing.expect((try map.fetchPut(1, 11)) == null);
   2160     try testing.expect((try map.fetchPut(2, 22)) == null);
   2161     try testing.expect((try map.fetchPut(3, 33)) == null);
   2162     try testing.expect((try map.fetchPut(4, 44)) == null);
   2163 
   2164     try map.putNoClobber(5, 55);
   2165     try testing.expect((try map.fetchPut(5, 66)).?.value == 55);
   2166     try testing.expect((try map.fetchPut(5, 55)).?.value == 66);
   2167 
   2168     const gop1 = try map.getOrPut(5);
   2169     try testing.expect(gop1.found_existing == true);
   2170     try testing.expect(gop1.value_ptr.* == 55);
   2171     try testing.expect(gop1.index == 4);
   2172     gop1.value_ptr.* = 77;
   2173     try testing.expect(map.getEntry(5).?.value_ptr.* == 77);
   2174 
   2175     const gop2 = try map.getOrPut(99);
   2176     try testing.expect(gop2.found_existing == false);
   2177     try testing.expect(gop2.index == 5);
   2178     gop2.value_ptr.* = 42;
   2179     try testing.expect(map.getEntry(99).?.value_ptr.* == 42);
   2180 
   2181     const gop3 = try map.getOrPutValue(5, 5);
   2182     try testing.expect(gop3.value_ptr.* == 77);
   2183 
   2184     const gop4 = try map.getOrPutValue(100, 41);
   2185     try testing.expect(gop4.value_ptr.* == 41);
   2186 
   2187     try testing.expect(map.contains(2));
   2188     try testing.expect(map.getEntry(2).?.value_ptr.* == 22);
   2189     try testing.expect(map.get(2).? == 22);
   2190 
   2191     const rmv1 = map.fetchSwapRemove(2);
   2192     try testing.expect(rmv1.?.key == 2);
   2193     try testing.expect(rmv1.?.value == 22);
   2194     try testing.expect(map.fetchSwapRemove(2) == null);
   2195     try testing.expect(map.swapRemove(2) == false);
   2196     try testing.expect(map.getEntry(2) == null);
   2197     try testing.expect(map.get(2) == null);
   2198 
   2199     // Since we've used `swapRemove` above, the index of this entry should remain unchanged.
   2200     try testing.expect(map.getIndex(100).? == 1);
   2201     const gop5 = try map.getOrPut(5);
   2202     try testing.expect(gop5.found_existing == true);
   2203     try testing.expect(gop5.value_ptr.* == 77);
   2204     try testing.expect(gop5.index == 4);
   2205 
   2206     // Whereas, if we do an `orderedRemove`, it should move the index forward one spot.
   2207     const rmv2 = map.fetchOrderedRemove(100);
   2208     try testing.expect(rmv2.?.key == 100);
   2209     try testing.expect(rmv2.?.value == 41);
   2210     try testing.expect(map.fetchOrderedRemove(100) == null);
   2211     try testing.expect(map.orderedRemove(100) == false);
   2212     try testing.expect(map.getEntry(100) == null);
   2213     try testing.expect(map.get(100) == null);
   2214     const gop6 = try map.getOrPut(5);
   2215     try testing.expect(gop6.found_existing == true);
   2216     try testing.expect(gop6.value_ptr.* == 77);
   2217     try testing.expect(gop6.index == 3);
   2218 
   2219     try testing.expect(map.swapRemove(3));
   2220 }
   2221 
   2222 test "iterator hash map" {
   2223     var reset_map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2224     defer reset_map.deinit();
   2225 
   2226     // test ensureTotalCapacity with a 0 parameter
   2227     try reset_map.ensureTotalCapacity(0);
   2228 
   2229     try reset_map.putNoClobber(0, 11);
   2230     try reset_map.putNoClobber(1, 22);
   2231     try reset_map.putNoClobber(2, 33);
   2232 
   2233     const keys = [_]i32{
   2234         0, 2, 1,
   2235     };
   2236 
   2237     const values = [_]i32{
   2238         11, 33, 22,
   2239     };
   2240 
   2241     var buffer = [_]i32{
   2242         0, 0, 0,
   2243     };
   2244 
   2245     var it = reset_map.iterator();
   2246     const first_entry = it.next().?;
   2247     it.reset();
   2248 
   2249     var count: usize = 0;
   2250     while (it.next()) |entry| : (count += 1) {
   2251         buffer[@as(usize, @intCast(entry.key_ptr.*))] = entry.value_ptr.*;
   2252     }
   2253     try testing.expect(count == 3);
   2254     try testing.expect(it.next() == null);
   2255 
   2256     for (buffer, 0..) |_, i| {
   2257         try testing.expect(buffer[@as(usize, @intCast(keys[i]))] == values[i]);
   2258     }
   2259 
   2260     it.reset();
   2261     count = 0;
   2262     while (it.next()) |entry| {
   2263         buffer[@as(usize, @intCast(entry.key_ptr.*))] = entry.value_ptr.*;
   2264         count += 1;
   2265         if (count >= 2) break;
   2266     }
   2267 
   2268     for (buffer[0..2], 0..) |_, i| {
   2269         try testing.expect(buffer[@as(usize, @intCast(keys[i]))] == values[i]);
   2270     }
   2271 
   2272     it.reset();
   2273     const entry = it.next().?;
   2274     try testing.expect(entry.key_ptr.* == first_entry.key_ptr.*);
   2275     try testing.expect(entry.value_ptr.* == first_entry.value_ptr.*);
   2276 }
   2277 
   2278 test "ensure capacity" {
   2279     var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2280     defer map.deinit();
   2281 
   2282     try map.ensureTotalCapacity(20);
   2283     const initial_capacity = map.capacity();
   2284     try testing.expect(initial_capacity >= 20);
   2285     var i: i32 = 0;
   2286     while (i < 20) : (i += 1) {
   2287         try testing.expect(map.fetchPutAssumeCapacity(i, i + 10) == null);
   2288     }
   2289     // shouldn't resize from putAssumeCapacity
   2290     try testing.expect(initial_capacity == map.capacity());
   2291 }
   2292 
   2293 test "ensure capacity leak" {
   2294     try testing.checkAllAllocationFailures(std.testing.allocator, struct {
   2295         pub fn f(allocator: Allocator) !void {
   2296             var map = AutoArrayHashMap(i32, i32).init(allocator);
   2297             defer map.deinit();
   2298 
   2299             var i: i32 = 0;
   2300             // put more than `linear_scan_max` in so index_header gets allocated.
   2301             while (i <= 20) : (i += 1) try map.put(i, i);
   2302         }
   2303     }.f, .{});
   2304 }
   2305 
   2306 test "big map" {
   2307     var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2308     defer map.deinit();
   2309 
   2310     var i: i32 = 0;
   2311     while (i < 8) : (i += 1) {
   2312         try map.put(i, i + 10);
   2313     }
   2314 
   2315     i = 0;
   2316     while (i < 8) : (i += 1) {
   2317         try testing.expectEqual(@as(?i32, i + 10), map.get(i));
   2318     }
   2319     while (i < 16) : (i += 1) {
   2320         try testing.expectEqual(@as(?i32, null), map.get(i));
   2321     }
   2322 
   2323     i = 4;
   2324     while (i < 12) : (i += 1) {
   2325         try map.put(i, i + 12);
   2326     }
   2327 
   2328     i = 0;
   2329     while (i < 4) : (i += 1) {
   2330         try testing.expectEqual(@as(?i32, i + 10), map.get(i));
   2331     }
   2332     while (i < 12) : (i += 1) {
   2333         try testing.expectEqual(@as(?i32, i + 12), map.get(i));
   2334     }
   2335     while (i < 16) : (i += 1) {
   2336         try testing.expectEqual(@as(?i32, null), map.get(i));
   2337     }
   2338 
   2339     i = 0;
   2340     while (i < 4) : (i += 1) {
   2341         try testing.expect(map.orderedRemove(i));
   2342     }
   2343     while (i < 8) : (i += 1) {
   2344         try testing.expect(map.swapRemove(i));
   2345     }
   2346 
   2347     i = 0;
   2348     while (i < 8) : (i += 1) {
   2349         try testing.expectEqual(@as(?i32, null), map.get(i));
   2350     }
   2351     while (i < 12) : (i += 1) {
   2352         try testing.expectEqual(@as(?i32, i + 12), map.get(i));
   2353     }
   2354     while (i < 16) : (i += 1) {
   2355         try testing.expectEqual(@as(?i32, null), map.get(i));
   2356     }
   2357 }
   2358 
   2359 test "clone" {
   2360     var original = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2361     defer original.deinit();
   2362 
   2363     // put more than `linear_scan_max` so we can test that the index header is properly cloned
   2364     var i: u8 = 0;
   2365     while (i < 10) : (i += 1) {
   2366         try original.putNoClobber(i, i * 10);
   2367     }
   2368 
   2369     var copy = try original.clone();
   2370     defer copy.deinit();
   2371 
   2372     i = 0;
   2373     while (i < 10) : (i += 1) {
   2374         try testing.expect(original.get(i).? == i * 10);
   2375         try testing.expect(copy.get(i).? == i * 10);
   2376         try testing.expect(original.getPtr(i).? != copy.getPtr(i).?);
   2377     }
   2378 
   2379     while (i < 20) : (i += 1) {
   2380         try testing.expect(original.get(i) == null);
   2381         try testing.expect(copy.get(i) == null);
   2382     }
   2383 }
   2384 
   2385 test "shrink" {
   2386     var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2387     defer map.deinit();
   2388 
   2389     // This test is more interesting if we insert enough entries to allocate the index header.
   2390     const num_entries = 20;
   2391     var i: i32 = 0;
   2392     while (i < num_entries) : (i += 1)
   2393         try testing.expect((try map.fetchPut(i, i * 10)) == null);
   2394 
   2395     try testing.expect(map.unmanaged.index_header != null);
   2396     try testing.expect(map.count() == num_entries);
   2397 
   2398     // Test `shrinkRetainingCapacity`.
   2399     map.shrinkRetainingCapacity(17);
   2400     try testing.expect(map.count() == 17);
   2401     try testing.expect(map.capacity() == 20);
   2402     i = 0;
   2403     while (i < num_entries) : (i += 1) {
   2404         const gop = try map.getOrPut(i);
   2405         if (i < 17) {
   2406             try testing.expect(gop.found_existing == true);
   2407             try testing.expect(gop.value_ptr.* == i * 10);
   2408         } else try testing.expect(gop.found_existing == false);
   2409     }
   2410 
   2411     // Test `shrinkAndFree`.
   2412     map.shrinkAndFree(15);
   2413     try testing.expect(map.count() == 15);
   2414     try testing.expect(map.capacity() == 15);
   2415     i = 0;
   2416     while (i < num_entries) : (i += 1) {
   2417         const gop = try map.getOrPut(i);
   2418         if (i < 15) {
   2419             try testing.expect(gop.found_existing == true);
   2420             try testing.expect(gop.value_ptr.* == i * 10);
   2421         } else try testing.expect(gop.found_existing == false);
   2422     }
   2423 }
   2424 
   2425 test "pop" {
   2426     var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2427     defer map.deinit();
   2428 
   2429     // Insert just enough entries so that the map expands. Afterwards,
   2430     // pop all entries out of the map.
   2431 
   2432     var i: i32 = 0;
   2433     while (i < 9) : (i += 1) {
   2434         try testing.expect((try map.fetchPut(i, i)) == null);
   2435     }
   2436 
   2437     while (i > 0) : (i -= 1) {
   2438         const pop = map.pop();
   2439         try testing.expect(pop.key == i - 1 and pop.value == i - 1);
   2440     }
   2441 }
   2442 
   2443 test "popOrNull" {
   2444     var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2445     defer map.deinit();
   2446 
   2447     // Insert just enough entries so that the map expands. Afterwards,
   2448     // pop all entries out of the map.
   2449 
   2450     var i: i32 = 0;
   2451     while (i < 9) : (i += 1) {
   2452         try testing.expect((try map.fetchPut(i, i)) == null);
   2453     }
   2454 
   2455     while (map.popOrNull()) |pop| {
   2456         try testing.expect(pop.key == i - 1 and pop.value == i - 1);
   2457         i -= 1;
   2458     }
   2459 
   2460     try testing.expect(map.count() == 0);
   2461 }
   2462 
   2463 test "reIndex" {
   2464     var map = ArrayHashMap(i32, i32, AutoContext(i32), true).init(std.testing.allocator);
   2465     defer map.deinit();
   2466 
   2467     // Populate via the API.
   2468     const num_indexed_entries = 20;
   2469     var i: i32 = 0;
   2470     while (i < num_indexed_entries) : (i += 1)
   2471         try testing.expect((try map.fetchPut(i, i * 10)) == null);
   2472 
   2473     // Make sure we allocated an index header.
   2474     try testing.expect(map.unmanaged.index_header != null);
   2475 
   2476     // Now write to the arrays directly.
   2477     const num_unindexed_entries = 20;
   2478     try map.unmanaged.entries.resize(std.testing.allocator, num_indexed_entries + num_unindexed_entries);
   2479     for (map.keys()[num_indexed_entries..], map.values()[num_indexed_entries..], num_indexed_entries..) |*key, *value, j| {
   2480         key.* = @intCast(j);
   2481         value.* = @intCast(j * 10);
   2482     }
   2483 
   2484     // After reindexing, we should see everything.
   2485     try map.reIndex();
   2486     i = 0;
   2487     while (i < num_indexed_entries + num_unindexed_entries) : (i += 1) {
   2488         const gop = try map.getOrPut(i);
   2489         try testing.expect(gop.found_existing == true);
   2490         try testing.expect(gop.value_ptr.* == i * 10);
   2491         try testing.expect(gop.index == i);
   2492     }
   2493 }
   2494 
   2495 test "auto store_hash" {
   2496     const HasCheapEql = AutoArrayHashMap(i32, i32);
   2497     const HasExpensiveEql = AutoArrayHashMap([32]i32, i32);
   2498     try testing.expect(std.meta.fieldInfo(HasCheapEql.Data, .hash).type == void);
   2499     try testing.expect(std.meta.fieldInfo(HasExpensiveEql.Data, .hash).type != void);
   2500 
   2501     const HasCheapEqlUn = AutoArrayHashMapUnmanaged(i32, i32);
   2502     const HasExpensiveEqlUn = AutoArrayHashMapUnmanaged([32]i32, i32);
   2503     try testing.expect(std.meta.fieldInfo(HasCheapEqlUn.Data, .hash).type == void);
   2504     try testing.expect(std.meta.fieldInfo(HasExpensiveEqlUn.Data, .hash).type != void);
   2505 }
   2506 
   2507 test "sort" {
   2508     var map = AutoArrayHashMap(i32, i32).init(std.testing.allocator);
   2509     defer map.deinit();
   2510 
   2511     for ([_]i32{ 8, 3, 12, 10, 2, 4, 9, 5, 6, 13, 14, 15, 16, 1, 11, 17, 7 }) |x| {
   2512         try map.put(x, x * 3);
   2513     }
   2514 
   2515     const C = struct {
   2516         keys: []i32,
   2517 
   2518         pub fn lessThan(ctx: @This(), a_index: usize, b_index: usize) bool {
   2519             return ctx.keys[a_index] < ctx.keys[b_index];
   2520         }
   2521     };
   2522 
   2523     map.sort(C{ .keys = map.keys() });
   2524 
   2525     var x: i32 = 1;
   2526     for (map.keys(), 0..) |key, i| {
   2527         try testing.expect(key == x);
   2528         try testing.expect(map.values()[i] == x * 3);
   2529         x += 1;
   2530     }
   2531 }
   2532 
   2533 test "0 sized key" {
   2534     var map = AutoArrayHashMap(u0, i32).init(std.testing.allocator);
   2535     defer map.deinit();
   2536 
   2537     try testing.expectEqual(map.get(0), null);
   2538 
   2539     try map.put(0, 5);
   2540     try testing.expectEqual(map.get(0), 5);
   2541 
   2542     try map.put(0, 10);
   2543     try testing.expectEqual(map.get(0), 10);
   2544 
   2545     try testing.expectEqual(map.swapRemove(0), true);
   2546     try testing.expectEqual(map.get(0), null);
   2547 }
   2548 
   2549 test "0 sized key and 0 sized value" {
   2550     var map = AutoArrayHashMap(u0, u0).init(std.testing.allocator);
   2551     defer map.deinit();
   2552 
   2553     try testing.expectEqual(map.get(0), null);
   2554 
   2555     try map.put(0, 0);
   2556     try testing.expectEqual(map.get(0), 0);
   2557 
   2558     try testing.expectEqual(map.swapRemove(0), true);
   2559     try testing.expectEqual(map.get(0), null);
   2560 }
   2561 
   2562 pub fn getHashPtrAddrFn(comptime K: type, comptime Context: type) (fn (Context, K) u32) {
   2563     return struct {
   2564         fn hash(ctx: Context, key: K) u32 {
   2565             _ = ctx;
   2566             return getAutoHashFn(usize, void)({}, @intFromPtr(key));
   2567         }
   2568     }.hash;
   2569 }
   2570 
   2571 pub fn getTrivialEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K) bool) {
   2572     return struct {
   2573         fn eql(ctx: Context, a: K, b: K) bool {
   2574             _ = ctx;
   2575             return a == b;
   2576         }
   2577     }.eql;
   2578 }
   2579 
   2580 pub fn AutoContext(comptime K: type) type {
   2581     return struct {
   2582         pub const hash = getAutoHashFn(K, @This());
   2583         pub const eql = getAutoEqlFn(K, @This());
   2584     };
   2585 }
   2586 
   2587 pub fn getAutoHashFn(comptime K: type, comptime Context: type) (fn (Context, K) u32) {
   2588     return struct {
   2589         fn hash(ctx: Context, key: K) u32 {
   2590             _ = ctx;
   2591             if (std.meta.hasUniqueRepresentation(K)) {
   2592                 return @truncate(Wyhash.hash(0, std.mem.asBytes(&key)));
   2593             } else {
   2594                 var hasher = Wyhash.init(0);
   2595                 autoHash(&hasher, key);
   2596                 return @truncate(hasher.final());
   2597             }
   2598         }
   2599     }.hash;
   2600 }
   2601 
   2602 pub fn getAutoEqlFn(comptime K: type, comptime Context: type) (fn (Context, K, K, usize) bool) {
   2603     return struct {
   2604         fn eql(ctx: Context, a: K, b: K, b_index: usize) bool {
   2605             _ = b_index;
   2606             _ = ctx;
   2607             return std.meta.eql(a, b);
   2608         }
   2609     }.eql;
   2610 }
   2611 
   2612 pub fn autoEqlIsCheap(comptime K: type) bool {
   2613     return switch (@typeInfo(K)) {
   2614         .bool,
   2615         .int,
   2616         .float,
   2617         .pointer,
   2618         .comptime_float,
   2619         .comptime_int,
   2620         .@"enum",
   2621         .@"fn",
   2622         .error_set,
   2623         .@"anyframe",
   2624         .enum_literal,
   2625         => true,
   2626         else => false,
   2627     };
   2628 }
   2629 
   2630 pub fn getAutoHashStratFn(comptime K: type, comptime Context: type, comptime strategy: std.hash.Strategy) (fn (Context, K) u32) {
   2631     return struct {
   2632         fn hash(ctx: Context, key: K) u32 {
   2633             _ = ctx;
   2634             var hasher = Wyhash.init(0);
   2635             std.hash.autoHashStrat(&hasher, key, strategy);
   2636             return @as(u32, @truncate(hasher.final()));
   2637         }
   2638     }.hash;
   2639 }