| ... | ... | @@ -428,7 +428,7 @@ pub fn HashMapUnmanaged( |
| 428 | 428 | if (self.metadata) |_| { |
| 429 | 429 | self.initMetadatas(); |
| 430 | 430 | self.size = 0; |
| 431 | | self.available = 0; |
| 431 | self.available = @truncate(u32, (self.capacity() * MaxLoadPercentage) / 100); |
| 432 | 432 | } |
| 433 | 433 | } |
| 434 | 434 | |
| ... | ... | @@ -468,41 +468,12 @@ pub fn HashMapUnmanaged( |
| 468 | 468 | self.putAssumeCapacityNoClobber(key, value); |
| 469 | 469 | } |
| 470 | 470 | |
| 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`. |
| 471 | 474 | 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; |
| 506 | 477 | } |
| 507 | 478 | |
| 508 | 479 | /// Insert an entry in the map. Assumes it is not already present, |
| ... | ... | @@ -893,6 +864,11 @@ test "std.hash_map clearRetainingCapacity" { |
| 893 | 864 | expectEqual(map.get(1).?, 1); |
| 894 | 865 | expectEqual(map.count(), 1); |
| 895 | 866 | |
| 867 | map.clearRetainingCapacity(); |
| 868 | map.putAssumeCapacity(1, 1); |
| 869 | expectEqual(map.get(1).?, 1); |
| 870 | expectEqual(map.count(), 1); |
| 871 | |
| 896 | 872 | const cap = map.capacity(); |
| 897 | 873 | expect(cap > 0); |
| 898 | 874 | |
| ... | ... | @@ -1148,6 +1124,36 @@ test "std.hash_map put" { |
| 1148 | 1124 | } |
| 1149 | 1125 | } |
| 1150 | 1126 | |
| 1127 | test "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 | |
| 1151 | 1157 | test "std.hash_map getOrPut" { |
| 1152 | 1158 | var map = AutoHashMap(u32, u32).init(std.testing.allocator); |
| 1153 | 1159 | defer map.deinit(); |