Fixes
-
A size at the limit produced an unusable table. Two independent faults, both at
max_size(), both found by asking whether a test would notice a bug rather than whether it ran the line:calc_shifts_for_size()walked the shift down past the point where the bucket array can still grow.calc_num_buckets()saturates atmax_bucket_count(), so abovemax_bucket_count() * max_load_factor()the capacity being compared stopped growing while the loop kept decrementing — all the way to a shift of zero, wherecalc_num_buckets()asks for1 << 64. That is undefined and in practice one: a table sized for billions of elements came back with a single bucket and a mask of zero, and the next probe read past the end of it. Reachable fromrehash(), which does not allocate the values and so has nothing to fail first —map<uint32_t, uint32_t>::rehash(3865470566)reproduced it.replace()andclear_and_fill_buckets_from_values()counted invalue_idx_type.max_size()is exactly what that type can hold, so a container of precisely that many has a size that is not representable in it: the cast wrapped to zero, the loop never ran once, and the table came back reportingsize()elements with no bucket pointing at any of them. (#192)
-
The bucket type's contract is now checked at compile time. The fingerprint must fit strictly below
dist_inc, anddist_incmust be a power of two. Both were always required — the probe arithmetic addsdist_incexpecting it to touch only the distance — but nothing said so, and a customBucketviolating either failed at run time in ways that looked like a hashing bug. This only affects code supplying its own bucket type, and only where it was already broken. (#192)
No API changes. Everything else since v4.9.1 is tests, tooling and CI, which does not reach anyone consuming the header.
Full Changelog: v4.9.1...v4.9.2