• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

loresoft / FluentCommand / 8850661434

26 Apr 2024 03:30PM UTC coverage: 54.48% (+2.6%) from 51.881%
8850661434

push

github

pwelter34
update debug settings

1152 of 2717 branches covered (42.4%)

Branch coverage included in aggregate %.

3682 of 6156 relevant lines covered (59.81%)

701.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/FluentCommand.Generators/Internal/HashCode.cs
1
using System.ComponentModel;
2
using System.Runtime.CompilerServices;
3

4
namespace FluentCommand.Generators.Internal;
5

6
/// <summary>
7
/// Polyfill for .NET 6 HashCode
8
/// </summary>
9
internal struct HashCode
10
{
11
    private static readonly uint s_seed = GenerateGlobalSeed();
×
12

13
    private const uint Prime1 = 2654435761U;
14
    private const uint Prime2 = 2246822519U;
15
    private const uint Prime3 = 3266489917U;
16
    private const uint Prime4 = 668265263U;
17
    private const uint Prime5 = 374761393U;
18

19
    private uint _v1, _v2, _v3, _v4;
20
    private uint _queue1, _queue2, _queue3;
21
    private uint _length;
22

23
    private static uint GenerateGlobalSeed()
24
    {
25
        var buffer = new byte[sizeof(uint)];
×
26
        new Random().NextBytes(buffer);
×
27
        return BitConverter.ToUInt32(buffer, 0);
×
28
    }
29

30
    public static int Combine<T1>(T1 value1)
31
    {
32
        // Provide a way of diffusing bits from something with a limited
33
        // input hash space. For example, many enums only have a few
34
        // possible hashes, only using the bottom few bits of the code. Some
35
        // collections are built on the assumption that hashes are spread
36
        // over a larger space, so diffusing the bits may help the
37
        // collection work more efficiently.
38

39
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
40

41
        uint hash = MixEmptyState();
×
42
        hash += 4;
×
43

44
        hash = QueueRound(hash, hc1);
×
45

46
        hash = MixFinal(hash);
×
47
        return (int)hash;
×
48
    }
49

50
    public static int Combine<T1, T2>(T1 value1, T2 value2)
51
    {
52
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
53
        uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
×
54

55
        uint hash = MixEmptyState();
×
56
        hash += 8;
×
57

58
        hash = QueueRound(hash, hc1);
×
59
        hash = QueueRound(hash, hc2);
×
60

61
        hash = MixFinal(hash);
×
62
        return (int)hash;
×
63
    }
64

65
    public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
66
    {
67
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
68
        uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
×
69
        uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
×
70

71
        uint hash = MixEmptyState();
×
72
        hash += 12;
×
73

74
        hash = QueueRound(hash, hc1);
×
75
        hash = QueueRound(hash, hc2);
×
76
        hash = QueueRound(hash, hc3);
×
77

78
        hash = MixFinal(hash);
×
79
        return (int)hash;
×
80
    }
81

82
    public static int Combine<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4)
83
    {
84
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
85
        uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
×
86
        uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
×
87
        uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
×
88

89
        Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
×
90

91
        v1 = Round(v1, hc1);
×
92
        v2 = Round(v2, hc2);
×
93
        v3 = Round(v3, hc3);
×
94
        v4 = Round(v4, hc4);
×
95

96
        uint hash = MixState(v1, v2, v3, v4);
×
97
        hash += 16;
×
98

99
        hash = MixFinal(hash);
×
100
        return (int)hash;
×
101
    }
102

103
    public static int Combine<T1, T2, T3, T4, T5>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5)
104
    {
105
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
106
        uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
×
107
        uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
×
108
        uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
×
109
        uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
×
110

111
        Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
×
112

113
        v1 = Round(v1, hc1);
×
114
        v2 = Round(v2, hc2);
×
115
        v3 = Round(v3, hc3);
×
116
        v4 = Round(v4, hc4);
×
117

118
        uint hash = MixState(v1, v2, v3, v4);
×
119
        hash += 20;
×
120

121
        hash = QueueRound(hash, hc5);
×
122

123
        hash = MixFinal(hash);
×
124
        return (int)hash;
×
125
    }
126

127
    public static int Combine<T1, T2, T3, T4, T5, T6>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 value6)
128
    {
129
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
130
        uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
×
131
        uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
×
132
        uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
×
133
        uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
×
134
        uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
×
135

136
        Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
×
137

138
        v1 = Round(v1, hc1);
×
139
        v2 = Round(v2, hc2);
×
140
        v3 = Round(v3, hc3);
×
141
        v4 = Round(v4, hc4);
×
142

143
        uint hash = MixState(v1, v2, v3, v4);
×
144
        hash += 24;
×
145

146
        hash = QueueRound(hash, hc5);
×
147
        hash = QueueRound(hash, hc6);
×
148

149
        hash = MixFinal(hash);
×
150
        return (int)hash;
×
151
    }
152

153
    public static int Combine<T1, T2, T3, T4, T5, T6, T7>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5,
154
        T6 value6, T7 value7)
155
    {
156
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
157
        uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
×
158
        uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
×
159
        uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
×
160
        uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
×
161
        uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
×
162
        uint hc7 = (uint)(value7?.GetHashCode() ?? 0);
×
163

164
        Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
×
165

166
        v1 = Round(v1, hc1);
×
167
        v2 = Round(v2, hc2);
×
168
        v3 = Round(v3, hc3);
×
169
        v4 = Round(v4, hc4);
×
170

171
        uint hash = MixState(v1, v2, v3, v4);
×
172
        hash += 28;
×
173

174
        hash = QueueRound(hash, hc5);
×
175
        hash = QueueRound(hash, hc6);
×
176
        hash = QueueRound(hash, hc7);
×
177

178
        hash = MixFinal(hash);
×
179
        return (int)hash;
×
180
    }
181

182
    public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5,
183
        T6 value6, T7 value7, T8 value8)
184
    {
185
        uint hc1 = (uint)(value1?.GetHashCode() ?? 0);
×
186
        uint hc2 = (uint)(value2?.GetHashCode() ?? 0);
×
187
        uint hc3 = (uint)(value3?.GetHashCode() ?? 0);
×
188
        uint hc4 = (uint)(value4?.GetHashCode() ?? 0);
×
189
        uint hc5 = (uint)(value5?.GetHashCode() ?? 0);
×
190
        uint hc6 = (uint)(value6?.GetHashCode() ?? 0);
×
191
        uint hc7 = (uint)(value7?.GetHashCode() ?? 0);
×
192
        uint hc8 = (uint)(value8?.GetHashCode() ?? 0);
×
193

194
        Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
×
195

196
        v1 = Round(v1, hc1);
×
197
        v2 = Round(v2, hc2);
×
198
        v3 = Round(v3, hc3);
×
199
        v4 = Round(v4, hc4);
×
200

201
        v1 = Round(v1, hc5);
×
202
        v2 = Round(v2, hc6);
×
203
        v3 = Round(v3, hc7);
×
204
        v4 = Round(v4, hc8);
×
205

206
        uint hash = MixState(v1, v2, v3, v4);
×
207
        hash += 32;
×
208

209
        hash = MixFinal(hash);
×
210
        return (int)hash;
×
211
    }
212

213
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
214
    private static void Initialize(out uint v1, out uint v2, out uint v3, out uint v4)
215
    {
216
        v1 = s_seed + Prime1 + Prime2;
×
217
        v2 = s_seed + Prime2;
×
218
        v3 = s_seed;
×
219
        v4 = s_seed - Prime1;
×
220
    }
×
221

222
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
223
    private static uint Round(uint hash, uint input)
224
    {
225
        return RotateLeft(hash + input * Prime2, 13) * Prime1;
×
226
    }
227

228
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
229
    private static uint QueueRound(uint hash, uint queuedValue)
230
    {
231
        return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4;
×
232
    }
233

234
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
235
    private static uint MixState(uint v1, uint v2, uint v3, uint v4)
236
    {
237
        return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18);
×
238
    }
239

240
    private static uint MixEmptyState()
241
    {
242
        return s_seed + Prime5;
×
243
    }
244

245
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
246
    private static uint MixFinal(uint hash)
247
    {
248
        hash ^= hash >> 15;
×
249
        hash *= Prime2;
×
250
        hash ^= hash >> 13;
×
251
        hash *= Prime3;
×
252
        hash ^= hash >> 16;
×
253
        return hash;
×
254
    }
255

256
    public void Add<T>(T value)
257
    {
258
        Add(value?.GetHashCode() ?? 0);
×
259
    }
×
260

261
    public void Add<T>(T value, IEqualityComparer<T> comparer)
262
    {
263
        Add(value is null ? 0 : (comparer?.GetHashCode(value) ?? value.GetHashCode()));
×
264
    }
×
265

266
    private void Add(int value)
267
    {
268
        // The original xxHash works as follows:
269
        // 0. Initialize immediately. We can't do this in a struct (no
270
        //    default ctor).
271
        // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators.
272
        // 2. Accumulate remaining blocks of length 4 (1 uint) into the
273
        //    hash.
274
        // 3. Accumulate remaining blocks of length 1 into the hash.
275

276
        // There is no need for #3 as this type only accepts ints. _queue1,
277
        // _queue2 and _queue3 are basically a buffer so that when
278
        // ToHashCode is called we can execute #2 correctly.
279

280
        // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see
281
        // #0) nd the last place that can be done if you look at the
282
        // original code is just before the first block of 16 bytes is mixed
283
        // in. The xxHash32 state is never used for streams containing fewer
284
        // than 16 bytes.
285

286
        // To see what's really going on here, have a look at the Combine
287
        // methods.
288

289
        uint val = (uint)value;
×
290

291
        // Storing the value of _length locally shaves of quite a few bytes
292
        // in the resulting machine code.
293
        uint previousLength = _length++;
×
294
        uint position = previousLength % 4;
×
295

296
        // Switch can't be inlined.
297

298
        if (position == 0)
×
299
            _queue1 = val;
×
300
        else if (position == 1)
×
301
            _queue2 = val;
×
302
        else if (position == 2)
×
303
            _queue3 = val;
×
304
        else // position == 3
305
        {
306
            if (previousLength == 3)
×
307
                Initialize(out _v1, out _v2, out _v3, out _v4);
×
308

309
            _v1 = Round(_v1, _queue1);
×
310
            _v2 = Round(_v2, _queue2);
×
311
            _v3 = Round(_v3, _queue3);
×
312
            _v4 = Round(_v4, val);
×
313
        }
314
    }
×
315

316
    public int ToHashCode()
317
    {
318
        // Storing the value of _length locally shaves of quite a few bytes
319
        // in the resulting machine code.
320
        uint length = _length;
×
321

322
        // position refers to the *next* queue position in this method, so
323
        // position == 1 means that _queue1 is populated; _queue2 would have
324
        // been populated on the next call to Add.
325
        uint position = length % 4;
×
326

327
        // If the length is less than 4, _v1 to _v4 don't contain anything
328
        // yet. xxHash32 treats this differently.
329

330
        uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4);
×
331

332
        // _length is incremented once per Add(Int32) and is therefore 4
333
        // times too small (xxHash length is in bytes, not ints).
334

335
        hash += length * 4;
×
336

337
        // Mix what remains in the queue
338

339
        // Switch can't be inlined right now, so use as few branches as
340
        // possible by manually excluding impossible scenarios (position > 1
341
        // is always false if position is not > 0).
342
        if (position > 0)
×
343
        {
344
            hash = QueueRound(hash, _queue1);
×
345
            if (position > 1)
×
346
            {
347
                hash = QueueRound(hash, _queue2);
×
348
                if (position > 2)
×
349
                    hash = QueueRound(hash, _queue3);
×
350
            }
351
        }
352

353
        hash = MixFinal(hash);
×
354
        return (int)hash;
×
355
    }
356

357
#pragma warning disable 0809
358
    // Obsolete member 'memberA' overrides non-obsolete member 'memberB'.
359
    // Disallowing GetHashCode and Equals is by design
360

361
    // * We decided to not override GetHashCode() to produce the hash code
362
    //   as this would be weird, both naming-wise as well as from a
363
    //   behavioral standpoint (GetHashCode() should return the object's
364
    //   hash code, not the one being computed).
365

366
    // * Even though ToHashCode() can be called safely multiple times on
367
    //   this implementation, it is not part of the contract. If the
368
    //   implementation has to change in the future we don't want to worry
369
    //   about people who might have incorrectly used this type.
370

371
    [Obsolete(
372
        "HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.",
373
        error: true)]
374
    [EditorBrowsable(EditorBrowsableState.Never)]
375
    public override int GetHashCode() => throw new NotSupportedException("Hash code not supported");
×
376

377
    [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)]
378
    [EditorBrowsable(EditorBrowsableState.Never)]
379
    public override bool Equals(object obj) => throw new NotSupportedException("Equality not supported");
×
380
#pragma warning restore 0809
381

382
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
383
    private static uint RotateLeft(uint value, int offset)
384
        => (value << offset) | (value >> (32 - offset));
×
385
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc