| ... | ... | @@ -773,6 +773,11 @@ pub fn HashMapUnmanaged( |
| 773 | 773 | self.used = 0; |
| 774 | 774 | self.fingerprint = tombstone; |
| 775 | 775 | } |
| 776 | |
| 777 | pub fn reset(self: *Metadata) void { |
| 778 | self.used = 0; |
| 779 | self.fingerprint = free; |
| 780 | } |
| 776 | 781 | }; |
| 777 | 782 | |
| 778 | 783 | comptime { |
| ... | ... | @@ -1110,12 +1115,24 @@ pub fn HashMapUnmanaged( |
| 1110 | 1115 | } |
| 1111 | 1116 | const mask = self.capacity() - 1; |
| 1112 | 1117 | const fingerprint = Metadata.takeFingerprint(hash); |
| 1113 | | // Don't loop indefinitely when there are no empty slots. |
| 1114 | | var limit = self.capacity(); |
| 1115 | | var idx = @truncate(usize, hash & mask); |
| 1118 | var start = @truncate(usize, hash & mask); |
| 1119 | var idx = start; |
| 1116 | 1120 | |
| 1117 | 1121 | var metadata = self.metadata.? + idx; |
| 1118 | | while ((metadata[0].isUsed() or metadata[0].isTombstone()) and limit != 0) { |
| 1122 | if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { |
| 1123 | const test_key = &self.keys()[idx]; |
| 1124 | const eql = ctx.eql(key, test_key.*); |
| 1125 | if (eql) return idx; |
| 1126 | } |
| 1127 | // Temporarily mark the start position as "free" so that the probe loop |
| 1128 | // doesn't infinitely loop when all other slots are filled or tombstones. |
| 1129 | const saved = metadata[0]; |
| 1130 | metadata[0].reset(); |
| 1131 | defer self.metadata.?[start] = saved; |
| 1132 | idx = (idx + 1) & mask; // initial idx was already checked |
| 1133 | metadata = self.metadata.? + idx; |
| 1134 | |
| 1135 | while (metadata[0].isUsed() or metadata[0].isTombstone()) { |
| 1119 | 1136 | if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { |
| 1120 | 1137 | const test_key = &self.keys()[idx]; |
| 1121 | 1138 | // If you get a compile error on this line, it means that your generic eql |
| ... | ... | @@ -1131,7 +1148,6 @@ pub fn HashMapUnmanaged( |
| 1131 | 1148 | } |
| 1132 | 1149 | } |
| 1133 | 1150 | |
| 1134 | | limit -= 1; |
| 1135 | 1151 | idx = (idx + 1) & mask; |
| 1136 | 1152 | metadata = self.metadata.? + idx; |
| 1137 | 1153 | } |
| ... | ... | @@ -1289,12 +1305,39 @@ pub fn HashMapUnmanaged( |
| 1289 | 1305 | } |
| 1290 | 1306 | const mask = self.capacity() - 1; |
| 1291 | 1307 | const fingerprint = Metadata.takeFingerprint(hash); |
| 1292 | | var limit = self.capacity(); |
| 1293 | | var idx = @truncate(usize, hash & mask); |
| 1308 | var start = @truncate(usize, hash & mask); |
| 1309 | var idx = start; |
| 1294 | 1310 | |
| 1295 | 1311 | var first_tombstone_idx: usize = self.capacity(); // invalid index |
| 1296 | 1312 | var metadata = self.metadata.? + idx; |
| 1297 | | while ((metadata[0].isUsed() or metadata[0].isTombstone()) and limit != 0) { |
| 1313 | if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { |
| 1314 | const test_key = &self.keys()[idx]; |
| 1315 | const eql = ctx.eql(key, test_key.*); |
| 1316 | if (eql) { |
| 1317 | return GetOrPutResult{ |
| 1318 | .key_ptr = test_key, |
| 1319 | .value_ptr = &self.values()[idx], |
| 1320 | .found_existing = true, |
| 1321 | }; |
| 1322 | } |
| 1323 | } else if (metadata[0].isTombstone()) { |
| 1324 | first_tombstone_idx = idx; |
| 1325 | } |
| 1326 | // Temporarily mark the start position as "free" so that the probe loop |
| 1327 | // doesn't infinitely loop when all other slots are filled or tombstones. |
| 1328 | const saved = metadata[0]; |
| 1329 | metadata[0].reset(); |
| 1330 | defer { |
| 1331 | // Don't restore when the 'home' slot was originally a tombstone |
| 1332 | // and the probe missed, since it is now occupied by the new key. |
| 1333 | const home = &self.metadata.?[start]; |
| 1334 | assert(!home.isUsed() or home.fingerprint == fingerprint); |
| 1335 | if (!home.isUsed()) home.* = saved; |
| 1336 | } |
| 1337 | idx = (idx + 1) & mask; // initial idx was already checked |
| 1338 | metadata = self.metadata.? + idx; |
| 1339 | |
| 1340 | while (metadata[0].isUsed() or metadata[0].isTombstone()) { |
| 1298 | 1341 | if (metadata[0].isUsed() and metadata[0].fingerprint == fingerprint) { |
| 1299 | 1342 | const test_key = &self.keys()[idx]; |
| 1300 | 1343 | // If you get a compile error on this line, it means that your generic eql |
| ... | ... | @@ -1316,7 +1359,6 @@ pub fn HashMapUnmanaged( |
| 1316 | 1359 | first_tombstone_idx = idx; |
| 1317 | 1360 | } |
| 1318 | 1361 | |
| 1319 | | limit -= 1; |
| 1320 | 1362 | idx = (idx + 1) & mask; |
| 1321 | 1363 | metadata = self.metadata.? + idx; |
| 1322 | 1364 | } |
| ... | ... | @@ -1902,6 +1944,7 @@ test "std.hash_map repeat putAssumeCapacity/remove" { |
| 1902 | 1944 | |
| 1903 | 1945 | try map.ensureTotalCapacity(20); |
| 1904 | 1946 | const limit = map.unmanaged.available; |
| 1947 | const cycles = 100; |
| 1905 | 1948 | |
| 1906 | 1949 | var i: u32 = 0; |
| 1907 | 1950 | while (i < limit) : (i += 1) { |
| ... | ... | @@ -1911,7 +1954,7 @@ test "std.hash_map repeat putAssumeCapacity/remove" { |
| 1911 | 1954 | // Repeatedly delete/insert an entry without resizing the map. |
| 1912 | 1955 | // Put to different keys so entries don't land in the just-freed slot. |
| 1913 | 1956 | i = 0; |
| 1914 | | while (i < 10 * limit) : (i += 1) { |
| 1957 | while (i < cycles * limit) : (i += 1) { |
| 1915 | 1958 | try testing.expect(map.remove(i)); |
| 1916 | 1959 | if (i % 2 == 0) { |
| 1917 | 1960 | map.putAssumeCapacityNoClobber(limit + i, i); |
| ... | ... | @@ -1920,9 +1963,13 @@ test "std.hash_map repeat putAssumeCapacity/remove" { |
| 1920 | 1963 | } |
| 1921 | 1964 | } |
| 1922 | 1965 | |
| 1923 | | i = 9 * limit; |
| 1924 | | while (i < 10 * limit) : (i += 1) { |
| 1925 | | try expectEqual(map.get(limit + i), i); |
| 1966 | i = (cycles - 1) * limit; |
| 1967 | while (i < cycles * limit) : (i += 1) { |
| 1968 | try expectEqual(map.get(i), null); // (removed) key miss |
| 1969 | try expectEqual(map.get(limit + i), i); // key hit |
| 1970 | const gop = map.getOrPutAssumeCapacity(limit + i); |
| 1971 | try testing.expect(gop.found_existing); |
| 1972 | try testing.expectEqual(gop.value_ptr.*, i); |
| 1926 | 1973 | } |
| 1927 | 1974 | try expectEqual(map.unmanaged.available, 0); |
| 1928 | 1975 | try expectEqual(map.unmanaged.count(), limit); |