-
-
Notifications
You must be signed in to change notification settings - Fork 82
/
Copy pathHashCode.cs
148 lines (127 loc) · 4.68 KB
/
HashCode.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
namespace Schema.NET;
using System;
using System.Collections.Generic;
using System.ComponentModel;
/// <summary>
/// A hash code used to help with implementing <see cref="object.GetHashCode()"/>.
/// </summary>
public struct HashCode : IEquatable<HashCode>
{
private const int EmptyCollectionPrimeNumber = 19;
private readonly int value;
/// <summary>
/// Initializes a new instance of the <see cref="HashCode"/> struct.
/// </summary>
/// <param name="value">The value.</param>
private HashCode(int value) => this.value = value;
/// <summary>
/// Performs an implicit conversion from <see cref="HashCode"/> to <see cref="int"/>.
/// </summary>
/// <param name="hashCode">The hash code.</param>
/// <returns>
/// The result of the conversion.
/// </returns>
public static implicit operator int(HashCode hashCode) => hashCode.value;
/// <summary>
/// Implements the operator ==.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator ==(HashCode left, HashCode right) => left.Equals(right);
/// <summary>
/// Implements the operator !=.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <returns>
/// The result of the operator.
/// </returns>
public static bool operator !=(HashCode left, HashCode right) => !(left == right);
/// <summary>
/// Takes the hash code of the specified item.
/// </summary>
/// <typeparam name="T">The type of the item.</typeparam>
/// <param name="item">The item.</param>
/// <returns>The new hash code.</returns>
public static HashCode Of<T>(T? item) => new(GetHashCode(item));
/// <summary>
/// Takes the hash code of the specified items.
/// </summary>
/// <typeparam name="T">The type of the items.</typeparam>
/// <param name="items">The collection.</param>
/// <returns>The new hash code.</returns>
public static HashCode OfEach<T>(IEnumerable<T>? items) =>
items is null ? new HashCode(0) : new HashCode(GetHashCode(items, 0));
/// <summary>
/// Adds the hash code of the specified item.
/// </summary>
/// <typeparam name="T">The type of the item.</typeparam>
/// <param name="item">The item.</param>
/// <returns>The new hash code.</returns>
public HashCode And<T>(T? item) => new(CombineHashCodes(this.value, GetHashCode(item)));
/// <summary>
/// Adds the hash code of the specified items in the collection.
/// </summary>
/// <typeparam name="T">The type of the items.</typeparam>
/// <param name="items">The collection.</param>
/// <returns>The new hash code.</returns>
public HashCode AndEach<T>(IEnumerable<T>? items)
{
if (items is null)
{
return new HashCode(this.value);
}
return new HashCode(GetHashCode(items, this.value));
}
/// <inheritdoc />
public bool Equals(HashCode other) => this.value.Equals(other.value);
/// <inheritdoc />
public override bool Equals(object? obj)
{
if (obj is HashCode hashCode)
{
return this.Equals(hashCode);
}
return false;
}
/// <summary>
/// Throws <see cref="NotSupportedException" />.
/// </summary>
/// <returns>
/// Does not return.
/// </returns>
/// <exception cref="NotSupportedException">Implicitly convert this struct to an <see cref="int" /> to get the hash code.</exception>
[EditorBrowsable(EditorBrowsableState.Never)]
public override int GetHashCode() =>
throw new NotSupportedException("Implicitly convert this struct to an int to get the hash code.");
private static int CombineHashCodes(int h1, int h2)
{
unchecked
{
// Code copied from System.Tuple so it must be the best way to combine hash codes or at least a good one.
return ((h1 << 5) + h1) ^ h2;
}
}
private static int GetHashCode<T>(T? item) => item?.GetHashCode() ?? 0;
private static int GetHashCode<T>(IEnumerable<T> items, int startHashCode)
{
var temp = startHashCode;
var enumerator = items.GetEnumerator();
if (enumerator.MoveNext())
{
temp = CombineHashCodes(temp, GetHashCode(enumerator.Current));
while (enumerator.MoveNext())
{
temp = CombineHashCodes(temp, GetHashCode(enumerator.Current));
}
}
else
{
temp = CombineHashCodes(temp, EmptyCollectionPrimeNumber);
}
return temp;
}
}