Skip to content
This repository has been archived by the owner on Oct 12, 2022. It is now read-only.

Commit

Permalink
Fix Issue 19072 - Object.toHash and typeid(void*).getHash(&ptr) shoul…
Browse files Browse the repository at this point in the history
…d be more varied in their low bits

The low bits of Object.toHash are insufficiently varied. Depending on
the platform the bottom 4, 3, or 2 bits of the result will always be
zero. This is bad because the low bits of a hash code are typically the
most significant for hashtable implementations. D's builtin AA deals
with this and other potential defects by rehashing the hash codes it
receives. Some 3rd party hashtable implementations do the same, but
others assume that keys with a defined toHash have reasonable hashcodes
that can be used without need for further mixing.

Fixing this is not hard and not computationally expensive. This PR also
does a similar thing for raw pointers. Although it will not always be
necessary for them the cost is low and it will frequently be a benefit.
  • Loading branch information
n8sh committed Jul 10, 2018
1 parent f2f1a0a commit 710adf3
Showing 1 changed file with 15 additions and 2 deletions.
17 changes: 15 additions & 2 deletions src/object.d
Expand Up @@ -871,7 +871,15 @@ class Object
size_t toHash() @trusted nothrow
{
// BUG: this prevents a compacting GC from working, needs to be fixed
return cast(size_t)cast(void*)this;
size_t addr = cast(size_t) cast(void*) this;
// The bottom log2((void*).alignof) bits of the address will always
// be 0. Moreover it is likely that each Object is allocated with a
// separate call to malloc. The alignment of malloc differs from
// platform to platform, but rather than having special cases for
// each platform it is safe to use a shift of 4. To minimize
// collisions in the low bits it is more important for the shift to
// not be too small than for the shift to not be too big.
return addr ^ (addr >>> 4);
}

/**
Expand Down Expand Up @@ -1183,7 +1191,12 @@ class TypeInfo_Pointer : TypeInfo

override size_t getHash(scope const void* p) @trusted const
{
return cast(size_t)*cast(void**)p;
// If the target of `p` is n-byte aligned then the
// bottom log2(n) bits of `p` will always be 0.
// Performing a constant magnitude xor-shift is
// inexpensive and will generally pay off.
size_t addr = cast(size_t) *cast(const void**) p;
return addr ^ (addr >>> 4);
}

override bool equals(in void* p1, in void* p2) const
Expand Down

0 comments on commit 710adf3

Please sign in to comment.