Commit f04e65bc09

sentientwaffle <sentientwaffle@gmail.com>
2023-06-09 21:52:36
std.hash_map: fetchRemove increment available
To avoid leaking slots, `fetchRemove` must increment `available` (when the "fetch" succeeds). Without the `available += 1`, the added test `"std.hash_map repeat fetchRemove"` fails with run test std-x86-linux-none-Debug: error: thread 432734 panic: integer overflow .../zig/lib/std/hash_map.zig:1365:28: 0x6471d5 in getOrPutAssumeCapacityAdapted__anon_47495 (test) self.available -= 1; ^ .../zig/lib/std/hash_map.zig:1308:62: 0x616950 in getOrPutAssumeCapacityContext (test) const result = self.getOrPutAssumeCapacityAdapted(key, ctx); ^ Alternatively, `fetchRemove` could call `removeByIndex`, though that would entail calling `header()` twice instead of once.
1 parent 99fe2a2
Changed files (1)
lib
lib/std/hash_map.zig
@@ -1113,6 +1113,7 @@ pub fn HashMapUnmanaged(
                 old_key.* = undefined;
                 old_val.* = undefined;
                 self.size -= 1;
+                self.available += 1;
                 return result;
             }
 
@@ -2193,3 +2194,27 @@ test "std.hash_map removeByPtr 0 sized key" {
 
     try testing.expect(map.count() == 0);
 }
+
+test "std.hash_map repeat fetchRemove" {
+    var map = AutoHashMapUnmanaged(u64, void){};
+    defer map.deinit(testing.allocator);
+
+    try map.ensureTotalCapacity(testing.allocator, 4);
+
+    map.putAssumeCapacity(0, {});
+    map.putAssumeCapacity(1, {});
+    map.putAssumeCapacity(2, {});
+    map.putAssumeCapacity(3, {});
+
+    // fetchRemove() should make slots available.
+    var i: usize = 0;
+    while (i < 10) : (i += 1) {
+        try testing.expect(map.fetchRemove(3) != null);
+        map.putAssumeCapacity(3, {});
+    }
+
+    try testing.expect(map.get(0) != null);
+    try testing.expect(map.get(1) != null);
+    try testing.expect(map.get(2) != null);
+    try testing.expect(map.get(3) != null);
+}