authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2020-11-11 18:50:34+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-11-11 18:50:34+02:00
log93844a5ef9f3c4f1ae9a079ec32c681e3ffed410
tree380b591f2dfd2863cf210a078792b112eb8adeef
parent08270d72b404cab5c7cc2f5239c21f9fe3823ca3
parentae6f3291c0e6a3f66250072d2ec648277cddfdda
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #7061 from Vexu/std

std: fix HashMap.putAssumeCapacity

1 files changed, 41 insertions(+), 35 deletions(-)

lib/std/hash_map.zig+41-35
......@@ -428,7 +428,7 @@ pub fn HashMapUnmanaged(
428428 if (self.metadata) |_| {
429429 self.initMetadatas();
430430 self.size = 0;
431 self.available = 0;
431 self.available = @truncate(u32, (self.capacity() * MaxLoadPercentage) / 100);
432432 }
433433 }
434434
......@@ -468,41 +468,12 @@ pub fn HashMapUnmanaged(
468468 self.putAssumeCapacityNoClobber(key, value);
469469 }
470470
471 /// Asserts there is enough capacity to store the new key-value pair.
472 /// Clobbers any existing data. To detect if a put would clobber
473 /// existing data, see `getOrPutAssumeCapacity`.
471474 pub fn putAssumeCapacity(self: *Self, key: K, value: V) void {
472 const hash = hashFn(key);
473 const mask = self.capacity() - 1;
474 const fingerprint = Metadata.takeFingerprint(hash);
475 var idx = @truncate(usize, hash & mask);
476
477 var first_tombstone_idx: usize = self.capacity(); // invalid index
478 var metadata = self.metadata.? + idx;
479 while (metadata[0].isUsed() or metadata[0].isTombstone()) {
480 if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) {
481 const entry = &self.entries()[idx];
482 if (eqlFn(entry.key, key)) {
483 return;
484 }
485 } else if (first_tombstone_idx == self.capacity() and metadata[0].isTombstone()) {
486 first_tombstone_idx = idx;
487 }
488
489 idx = (idx + 1) & mask;
490 metadata = self.metadata.? + idx;
491 }
492
493 if (first_tombstone_idx < self.capacity()) {
494 // Cheap try to lower probing lengths after deletions. Recycle a tombstone.
495 idx = first_tombstone_idx;
496 metadata = self.metadata.? + idx;
497 } else {
498 // We're using a slot previously free.
499 self.available -= 1;
500 }
501
502 metadata[0].fill(fingerprint);
503 const entry = &self.entries()[idx];
504 entry.* = .{ .key = key, .value = undefined };
505 self.size += 1;
475 const gop = self.getOrPutAssumeCapacity(key);
476 gop.entry.value = value;
506477 }
507478
508479 /// Insert an entry in the map. Assumes it is not already present,
......@@ -893,6 +864,11 @@ test "std.hash_map clearRetainingCapacity" {
893864 expectEqual(map.get(1).?, 1);
894865 expectEqual(map.count(), 1);
895866
867 map.clearRetainingCapacity();
868 map.putAssumeCapacity(1, 1);
869 expectEqual(map.get(1).?, 1);
870 expectEqual(map.count(), 1);
871
896872 const cap = map.capacity();
897873 expect(cap > 0);
898874
......@@ -1148,6 +1124,36 @@ test "std.hash_map put" {
11481124 }
11491125}
11501126
1127test "std.hash_map putAssumeCapacity" {
1128 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
1129 defer map.deinit();
1130
1131 try map.ensureCapacity(20);
1132 var i: u32 = 0;
1133 while (i < 20) : (i += 1) {
1134 map.putAssumeCapacityNoClobber(i, i);
1135 }
1136
1137 i = 0;
1138 var sum = i;
1139 while (i < 20) : (i += 1) {
1140 sum += map.get(i).?;
1141 }
1142 expectEqual(sum, 190);
1143
1144 i = 0;
1145 while (i < 20) : (i += 1) {
1146 map.putAssumeCapacity(i, 1);
1147 }
1148
1149 i = 0;
1150 sum = i;
1151 while (i < 20) : (i += 1) {
1152 sum += map.get(i).?;
1153 }
1154 expectEqual(sum, 20);
1155}
1156
11511157test "std.hash_map getOrPut" {
11521158 var map = AutoHashMap(u32, u32).init(std.testing.allocator);
11531159 defer map.deinit();