Skip to content

Commit

Permalink
Better hashing
Browse files Browse the repository at this point in the history
  • Loading branch information
badeend committed Mar 16, 2024
1 parent 4187ea9 commit 27d2b57
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 14 deletions.
41 changes: 40 additions & 1 deletion Badeend.ValueCollections/ValueList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ public static ValueListBuilder<T> Builder<T>(ReadOnlySpan<T> items)
[CollectionBuilder(typeof(ValueList), nameof(ValueList.Create))]
public sealed class ValueList<T> : IReadOnlyList<T>, IList<T>, IEquatable<ValueList<T>>
{
private const int UninitializedHashCode = 0;

/// <summary>
/// Get a new empty list.
///
Expand All @@ -79,6 +81,11 @@ public sealed class ValueList<T> : IReadOnlyList<T>, IList<T>, IEquatable<ValueL
private readonly T[] items;
private readonly int count;

/// <summary>
/// Warning! This class promises to be thread-safe, yet this is a mutable field.
/// </summary>
private int hashCode = UninitializedHashCode;

internal T[] Items
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand Down Expand Up @@ -289,7 +296,39 @@ void ICollection<T>.CopyTo(T[] array, int arrayIndex)
}

/// <inheritdoc/>
public sealed override int GetHashCode() => this.AsValueSlice().GetHashCode();
public sealed override int GetHashCode()
{
var hashCode = Volatile.Read(ref this.hashCode);
if (hashCode != UninitializedHashCode)
{
return hashCode;
}

hashCode = this.ComputeHashCode();
Volatile.Write(ref this.hashCode, hashCode);
return hashCode;
}

private int ComputeHashCode()
{
var hasher = new HashCode();
hasher.Add(typeof(ValueList<T>));
hasher.Add(this.Count);

foreach (var item in this)
{
hasher.Add(item);
}

var hashCode = hasher.ToHashCode();
if (hashCode == UninitializedHashCode)
{
// Never return 0, as that is our placeholder value.
hashCode = 1;
}

return hashCode;
}

/// <summary>
/// Returns <see langword="true"/> when the two lists have identical length
Expand Down
2 changes: 1 addition & 1 deletion Badeend.ValueCollections/ValueSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ private int ComputeHashCode()
}

var hasher = new HashCode();
hasher.Add(typeof(T));
hasher.Add(typeof(ValueSet<T>));
hasher.Add(this.Count);
hasher.AddUnordered(ref contentHasher);

Expand Down
18 changes: 6 additions & 12 deletions Badeend.ValueCollections/ValueSlice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,22 +378,16 @@ public readonly ref readonly T Current
/// <inheritdoc/>
public override int GetHashCode()
{
int hashCode = typeof(T).GetHashCode();
hashCode = (hashCode * -1521134295) + this.length;
var hasher = new HashCode();
hasher.Add(typeof(ValueSlice<T>));
hasher.Add(this.Length);

// Include first item
if (this.length >= 1)
{
hashCode = (hashCode * -1521134295) + (this[0]?.GetHashCode() ?? 0);
}

// Include last item
if (this.length >= 2)
foreach (var item in this)
{
hashCode = (hashCode * -1521134295) + (this[this.length - 1]?.GetHashCode() ?? 0);
hasher.Add(item);
}

return hashCode;
return hasher.ToHashCode();
}

/// <summary>
Expand Down

0 comments on commit 27d2b57

Please sign in to comment.