| ... | ... | @@ -382,22 +382,24 @@ pub fn HashMapUnmanaged( |
| 382 | 382 | try self.entries.ensureCapacity(allocator, new_capacity); |
| 383 | 383 | if (new_capacity <= linear_scan_max) return; |
| 384 | 384 | |
| 385 | | // Resize if indexes would be more than 60% full. |
| 385 | // Ensure that the indexes will be at most 60% full if |
| 386 | // `new_capacity` items are put into it. |
| 386 | 387 | const needed_len = new_capacity * 5 / 3; |
| 387 | 388 | if (self.index_header) |header| { |
| 388 | 389 | if (needed_len > header.indexes_len) { |
| 389 | | var new_indexes_len = header.indexes_len; |
| 390 | | while (true) { |
| 391 | | new_indexes_len *= new_indexes_len / 2 + 8; |
| 392 | | if (new_indexes_len >= needed_len) break; |
| 393 | | } |
| 390 | // An overflow here would mean the amount of memory required would not |
| 391 | // be representable in the address space. |
| 392 | const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable; |
| 394 | 393 | const new_header = try IndexHeader.alloc(allocator, new_indexes_len); |
| 395 | 394 | self.insertAllEntriesIntoNewHeader(new_header); |
| 396 | 395 | header.free(allocator); |
| 397 | 396 | self.index_header = new_header; |
| 398 | 397 | } |
| 399 | 398 | } else { |
| 400 | | const header = try IndexHeader.alloc(allocator, needed_len); |
| 399 | // An overflow here would mean the amount of memory required would not |
| 400 | // be representable in the address space. |
| 401 | const new_indexes_len = math.ceilPowerOfTwo(usize, needed_len) catch unreachable; |
| 402 | const header = try IndexHeader.alloc(allocator, new_indexes_len); |
| 401 | 403 | self.insertAllEntriesIntoNewHeader(header); |
| 402 | 404 | self.index_header = header; |
| 403 | 405 | } |
| ... | ... | @@ -540,10 +542,10 @@ pub fn HashMapUnmanaged( |
| 540 | 542 | fn removeInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) ?Entry { |
| 541 | 543 | const indexes = header.indexes(I); |
| 542 | 544 | const h = hash(key); |
| 543 | | const start_index = header.hashToIndex(h); |
| 545 | const start_index = header.constrainIndex(h); |
| 544 | 546 | var roll_over: usize = 0; |
| 545 | 547 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { |
| 546 | | const index_index = (start_index + roll_over) % header.indexes_len; |
| 548 | const index_index = header.constrainIndex(start_index + roll_over); |
| 547 | 549 | var index = &indexes[index_index]; |
| 548 | 550 | if (index.isEmpty()) |
| 549 | 551 | return null; |
| ... | ... | @@ -564,7 +566,7 @@ pub fn HashMapUnmanaged( |
| 564 | 566 | // Now we have to shift over the following indexes. |
| 565 | 567 | roll_over += 1; |
| 566 | 568 | while (roll_over < header.indexes_len) : (roll_over += 1) { |
| 567 | | const next_index_index = (start_index + roll_over) % header.indexes_len; |
| 569 | const next_index_index = header.constrainIndex(start_index + roll_over); |
| 568 | 570 | const next_index = &indexes[next_index_index]; |
| 569 | 571 | if (next_index.isEmpty() or next_index.distance_from_start_index == 0) { |
| 570 | 572 | index.setEmpty(); |
| ... | ... | @@ -588,10 +590,10 @@ pub fn HashMapUnmanaged( |
| 588 | 590 | indexes: []Index(I), |
| 589 | 591 | ) void { |
| 590 | 592 | const h = if (store_hash) self.entries.items[new_entry_index].hash else hash(self.entries.items[new_entry_index].key); |
| 591 | | const start_index = header.hashToIndex(h); |
| 593 | const start_index = header.constrainIndex(h); |
| 592 | 594 | var roll_over: usize = 0; |
| 593 | 595 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { |
| 594 | | const index_index = (start_index + roll_over) % header.indexes_len; |
| 596 | const index_index = header.constrainIndex(start_index + roll_over); |
| 595 | 597 | const index = &indexes[index_index]; |
| 596 | 598 | if (index.entry_index == old_entry_index) { |
| 597 | 599 | index.entry_index = @intCast(I, new_entry_index); |
| ... | ... | @@ -605,14 +607,14 @@ pub fn HashMapUnmanaged( |
| 605 | 607 | fn getOrPutInternal(self: *Self, key: K, header: *IndexHeader, comptime I: type) GetOrPutResult { |
| 606 | 608 | const indexes = header.indexes(I); |
| 607 | 609 | const h = hash(key); |
| 608 | | const start_index = header.hashToIndex(h); |
| 610 | const start_index = header.constrainIndex(h); |
| 609 | 611 | var roll_over: usize = 0; |
| 610 | 612 | var distance_from_start_index: usize = 0; |
| 611 | 613 | while (roll_over <= header.indexes_len) : ({ |
| 612 | 614 | roll_over += 1; |
| 613 | 615 | distance_from_start_index += 1; |
| 614 | 616 | }) { |
| 615 | | const index_index = (start_index + roll_over) % header.indexes_len; |
| 617 | const index_index = header.constrainIndex(start_index + roll_over); |
| 616 | 618 | const index = indexes[index_index]; |
| 617 | 619 | if (index.isEmpty()) { |
| 618 | 620 | indexes[index_index] = .{ |
| ... | ... | @@ -670,7 +672,7 @@ pub fn HashMapUnmanaged( |
| 670 | 672 | roll_over += 1; |
| 671 | 673 | distance_from_start_index += 1; |
| 672 | 674 | }) { |
| 673 | | const next_index_index = (start_index + roll_over) % header.indexes_len; |
| 675 | const next_index_index = header.constrainIndex(start_index + roll_over); |
| 674 | 676 | const next_index = indexes[next_index_index]; |
| 675 | 677 | if (next_index.isEmpty()) { |
| 676 | 678 | header.maybeBumpMax(distance_from_start_index); |
| ... | ... | @@ -702,10 +704,10 @@ pub fn HashMapUnmanaged( |
| 702 | 704 | fn getInternal(self: Self, key: K, header: *IndexHeader, comptime I: type) ?*Entry { |
| 703 | 705 | const indexes = header.indexes(I); |
| 704 | 706 | const h = hash(key); |
| 705 | | const start_index = header.hashToIndex(h); |
| 707 | const start_index = header.constrainIndex(h); |
| 706 | 708 | var roll_over: usize = 0; |
| 707 | 709 | while (roll_over <= header.max_distance_from_start_index) : (roll_over += 1) { |
| 708 | | const index_index = (start_index + roll_over) % header.indexes_len; |
| 710 | const index_index = header.constrainIndex(start_index + roll_over); |
| 709 | 711 | const index = indexes[index_index]; |
| 710 | 712 | if (index.isEmpty()) |
| 711 | 713 | return null; |
| ... | ... | @@ -731,7 +733,7 @@ pub fn HashMapUnmanaged( |
| 731 | 733 | const indexes = header.indexes(I); |
| 732 | 734 | entry_loop: for (self.entries.items) |entry, i| { |
| 733 | 735 | const h = if (store_hash) entry.hash else hash(entry.key); |
| 734 | | const start_index = header.hashToIndex(h); |
| 736 | const start_index = header.constrainIndex(h); |
| 735 | 737 | var entry_index = i; |
| 736 | 738 | var roll_over: usize = 0; |
| 737 | 739 | var distance_from_start_index: usize = 0; |
| ... | ... | @@ -739,7 +741,7 @@ pub fn HashMapUnmanaged( |
| 739 | 741 | roll_over += 1; |
| 740 | 742 | distance_from_start_index += 1; |
| 741 | 743 | }) { |
| 742 | | const index_index = (start_index + roll_over) % header.indexes_len; |
| 744 | const index_index = header.constrainIndex(start_index + roll_over); |
| 743 | 745 | const next_index = indexes[index_index]; |
| 744 | 746 | if (next_index.isEmpty()) { |
| 745 | 747 | header.maybeBumpMax(distance_from_start_index); |
| ... | ... | @@ -814,8 +816,10 @@ const IndexHeader = struct { |
| 814 | 816 | max_distance_from_start_index: usize, |
| 815 | 817 | indexes_len: usize, |
| 816 | 818 | |
| 817 | | fn hashToIndex(header: IndexHeader, h: u32) usize { |
| 818 | | return @as(usize, h) % header.indexes_len; |
| 819 | fn constrainIndex(header: IndexHeader, i: usize) usize { |
| 820 | // This is an optimization for modulo of power of two integers; |
| 821 | // it requires `indexes_len` to always be a power of two. |
| 822 | return i & (header.indexes_len - 1); |
| 819 | 823 | } |
| 820 | 824 | |
| 821 | 825 | fn indexes(header: *IndexHeader, comptime I: type) []Index(I) { |