| ... | @@ -468,41 +468,12 @@ pub fn HashMapUnmanaged( | ... | @@ -468,41 +468,12 @@ pub fn HashMapUnmanaged( |
| 468 | self.putAssumeCapacityNoClobber(key, value); | 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 | pub fn putAssumeCapacity(self: *Self, key: K, value: V) void { | 474 | pub fn putAssumeCapacity(self: *Self, key: K, value: V) void { |
| 472 | const hash = hashFn(key); | 475 | const gop = self.getOrPutAssumeCapacity(key); |
| 473 | const mask = self.capacity() - 1; | 476 | gop.entry.value = value; |
| 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; | | |
| 506 | } | 477 | } |
| 507 | | 478 | |
| 508 | /// Insert an entry in the map. Assumes it is not already present, | 479 | /// Insert an entry in the map. Assumes it is not already present, |
| ... | @@ -1148,6 +1119,36 @@ test "std.hash_map put" { | ... | @@ -1148,6 +1119,36 @@ test "std.hash_map put" { |
| 1148 | } | 1119 | } |
| 1149 | } | 1120 | } |
| 1150 | | 1121 | |
| | 1122 | test "std.hash_map putAssumeCapacity" { |
| | 1123 | var map = AutoHashMap(u32, u32).init(std.testing.allocator); |
| | 1124 | defer map.deinit(); |
| | 1125 | |
| | 1126 | try map.ensureCapacity(20); |
| | 1127 | var i: u32 = 0; |
| | 1128 | while (i < 20) : (i += 1) { |
| | 1129 | map.putAssumeCapacityNoClobber(i, i); |
| | 1130 | } |
| | 1131 | |
| | 1132 | i = 0; |
| | 1133 | var sum = i; |
| | 1134 | while (i < 20) : (i += 1) { |
| | 1135 | sum += map.get(i).?; |
| | 1136 | } |
| | 1137 | expectEqual(sum, 190); |
| | 1138 | |
| | 1139 | i = 0; |
| | 1140 | while (i < 20) : (i += 1) { |
| | 1141 | map.putAssumeCapacity(i, 1); |
| | 1142 | } |
| | 1143 | |
| | 1144 | i = 0; |
| | 1145 | sum = i; |
| | 1146 | while (i < 20) : (i += 1) { |
| | 1147 | sum += map.get(i).?; |
| | 1148 | } |
| | 1149 | expectEqual(sum, 20); |
| | 1150 | } |
| | 1151 | |
| 1151 | test "std.hash_map getOrPut" { | 1152 | test "std.hash_map getOrPut" { |
| 1152 | var map = AutoHashMap(u32, u32).init(std.testing.allocator); | 1153 | var map = AutoHashMap(u32, u32).init(std.testing.allocator); |
| 1153 | defer map.deinit(); | 1154 | defer map.deinit(); |