< Summary

Class:Azure.Core.HashCodeBuilder
Assembly:Azure.Messaging.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\core\Azure.Core\src\Shared\HashCodeBuilder.cs
Covered lines:0
Uncovered lines:159
Coverable lines:159
Total lines:361
Line coverage:0% (0 of 159)
Covered branches:0
Total branches:94
Branch coverage:0% (0 of 94)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-0%100%
GenerateGlobalSeed()-0%100%
Combine(...)-0%0%
Combine(...)-0%0%
Combine(...)-0%0%
Combine(...)-0%0%
Combine(...)-0%0%
Combine(...)-0%0%
Combine(...)-0%0%
Combine(...)-0%0%
Initialize(...)-0%100%
RotateLeft(...)-0%100%
Round(...)-0%100%
QueueRound(...)-0%100%
MixState(...)-0%100%
MixEmptyState()-0%100%
MixFinal(...)-0%100%
Add(...)-0%0%
Add(...)-0%0%
Add(...)-0%0%
ToHashCode()-0%0%

File(s)

C:\Git\azure-sdk-for-net\sdk\core\Azure.Core\src\Shared\HashCodeBuilder.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.Runtime.CompilerServices;
 7
 8namespace Azure.Core
 9{
 10    /// <summary>
 11    /// Copied from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/HashCode.cs.
 12    /// </summary>
 13    internal struct HashCodeBuilder
 14    {
 015        private static readonly uint s_seed = GenerateGlobalSeed();
 16
 17        private const uint Prime1 = 2654435761U;
 18        private const uint Prime2 = 2246822519U;
 19        private const uint Prime3 = 3266489917U;
 20        private const uint Prime4 = 668265263U;
 21        private const uint Prime5 = 374761393U;
 22
 23        private uint _v1, _v2, _v3, _v4;
 24        private uint _queue1, _queue2, _queue3;
 25        private uint _length;
 26
 27        private static uint GenerateGlobalSeed()
 28        {
 029            return (uint)new Random().Next();
 30        }
 31
 32        public static int Combine<T1>(T1 value1)
 33        {
 34            // Provide a way of diffusing bits from something with a limited
 35            // input hash space. For example, many enums only have a few
 36            // possible hashes, only using the bottom few bits of the code. Some
 37            // collections are built on the assumption that hashes are spread
 38            // over a larger space, so diffusing the bits may help the
 39            // collection work more efficiently.
 40
 041            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 42
 043            uint hash = MixEmptyState();
 044            hash += 4;
 45
 046            hash = QueueRound(hash, hc1);
 47
 048            hash = MixFinal(hash);
 049            return (int)hash;
 50        }
 51
 52        public static int Combine<T1, T2>(T1 value1, T2 value2)
 53        {
 054            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 055            var hc2 = (uint)(value2?.GetHashCode() ?? 0);
 56
 057            uint hash = MixEmptyState();
 058            hash += 8;
 59
 060            hash = QueueRound(hash, hc1);
 061            hash = QueueRound(hash, hc2);
 62
 063            hash = MixFinal(hash);
 064            return (int)hash;
 65        }
 66
 67        public static int Combine<T1, T2, T3>(T1 value1, T2 value2, T3 value3)
 68        {
 069            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 070            var hc2 = (uint)(value2?.GetHashCode() ?? 0);
 071            var hc3 = (uint)(value3?.GetHashCode() ?? 0);
 72
 073            uint hash = MixEmptyState();
 074            hash += 12;
 75
 076            hash = QueueRound(hash, hc1);
 077            hash = QueueRound(hash, hc2);
 078            hash = QueueRound(hash, hc3);
 79
 080            hash = MixFinal(hash);
 081            return (int)hash;
 82        }
 83
 84        public static int Combine<T1, T2, T3, T4>(T1 value1, T2 value2, T3 value3, T4 value4)
 85        {
 086            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 087            var hc2 = (uint)(value2?.GetHashCode() ?? 0);
 088            var hc3 = (uint)(value3?.GetHashCode() ?? 0);
 089            var hc4 = (uint)(value4?.GetHashCode() ?? 0);
 90
 091            Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
 92
 093            v1 = Round(v1, hc1);
 094            v2 = Round(v2, hc2);
 095            v3 = Round(v3, hc3);
 096            v4 = Round(v4, hc4);
 97
 098            uint hash = MixState(v1, v2, v3, v4);
 099            hash += 16;
 100
 0101            hash = MixFinal(hash);
 0102            return (int)hash;
 103        }
 104
 105        public static int Combine<T1, T2, T3, T4, T5>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5)
 106        {
 0107            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 0108            var hc2 = (uint)(value2?.GetHashCode() ?? 0);
 0109            var hc3 = (uint)(value3?.GetHashCode() ?? 0);
 0110            var hc4 = (uint)(value4?.GetHashCode() ?? 0);
 0111            var hc5 = (uint)(value5?.GetHashCode() ?? 0);
 112
 0113            Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
 114
 0115            v1 = Round(v1, hc1);
 0116            v2 = Round(v2, hc2);
 0117            v3 = Round(v3, hc3);
 0118            v4 = Round(v4, hc4);
 119
 0120            uint hash = MixState(v1, v2, v3, v4);
 0121            hash += 20;
 122
 0123            hash = QueueRound(hash, hc5);
 124
 0125            hash = MixFinal(hash);
 0126            return (int)hash;
 127        }
 128
 129        public static int Combine<T1, T2, T3, T4, T5, T6>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 valu
 130        {
 0131            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 0132            var hc2 = (uint)(value2?.GetHashCode() ?? 0);
 0133            var hc3 = (uint)(value3?.GetHashCode() ?? 0);
 0134            var hc4 = (uint)(value4?.GetHashCode() ?? 0);
 0135            var hc5 = (uint)(value5?.GetHashCode() ?? 0);
 0136            var hc6 = (uint)(value6?.GetHashCode() ?? 0);
 137
 0138            Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
 139
 0140            v1 = Round(v1, hc1);
 0141            v2 = Round(v2, hc2);
 0142            v3 = Round(v3, hc3);
 0143            v4 = Round(v4, hc4);
 144
 0145            uint hash = MixState(v1, v2, v3, v4);
 0146            hash += 24;
 147
 0148            hash = QueueRound(hash, hc5);
 0149            hash = QueueRound(hash, hc6);
 150
 0151            hash = MixFinal(hash);
 0152            return (int)hash;
 153        }
 154
 155        public static int Combine<T1, T2, T3, T4, T5, T6, T7>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5, T6 
 156        {
 0157            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 0158            var hc2 = (uint)(value2?.GetHashCode() ?? 0);
 0159            var hc3 = (uint)(value3?.GetHashCode() ?? 0);
 0160            var hc4 = (uint)(value4?.GetHashCode() ?? 0);
 0161            var hc5 = (uint)(value5?.GetHashCode() ?? 0);
 0162            var hc6 = (uint)(value6?.GetHashCode() ?? 0);
 0163            var hc7 = (uint)(value7?.GetHashCode() ?? 0);
 164
 0165            Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
 166
 0167            v1 = Round(v1, hc1);
 0168            v2 = Round(v2, hc2);
 0169            v3 = Round(v3, hc3);
 0170            v4 = Round(v4, hc4);
 171
 0172            uint hash = MixState(v1, v2, v3, v4);
 0173            hash += 28;
 174
 0175            hash = QueueRound(hash, hc5);
 0176            hash = QueueRound(hash, hc6);
 0177            hash = QueueRound(hash, hc7);
 178
 0179            hash = MixFinal(hash);
 0180            return (int)hash;
 181        }
 182
 183        public static int Combine<T1, T2, T3, T4, T5, T6, T7, T8>(T1 value1, T2 value2, T3 value3, T4 value4, T5 value5,
 184        {
 0185            var hc1 = (uint)(value1?.GetHashCode() ?? 0);
 0186            var hc2 = (uint)(value2?.GetHashCode() ?? 0);
 0187            var hc3 = (uint)(value3?.GetHashCode() ?? 0);
 0188            var hc4 = (uint)(value4?.GetHashCode() ?? 0);
 0189            var hc5 = (uint)(value5?.GetHashCode() ?? 0);
 0190            var hc6 = (uint)(value6?.GetHashCode() ?? 0);
 0191            var hc7 = (uint)(value7?.GetHashCode() ?? 0);
 0192            var hc8 = (uint)(value8?.GetHashCode() ?? 0);
 193
 0194            Initialize(out uint v1, out uint v2, out uint v3, out uint v4);
 195
 0196            v1 = Round(v1, hc1);
 0197            v2 = Round(v2, hc2);
 0198            v3 = Round(v3, hc3);
 0199            v4 = Round(v4, hc4);
 200
 0201            v1 = Round(v1, hc5);
 0202            v2 = Round(v2, hc6);
 0203            v3 = Round(v3, hc7);
 0204            v4 = Round(v4, hc8);
 205
 0206            uint hash = MixState(v1, v2, v3, v4);
 0207            hash += 32;
 208
 0209            hash = MixFinal(hash);
 0210            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        {
 0216            v1 = s_seed + Prime1 + Prime2;
 0217            v2 = s_seed + Prime2;
 0218            v3 = s_seed;
 0219            v4 = s_seed - Prime1;
 0220        }
 221
 222        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 223        public static uint RotateLeft(uint value, int offset)
 0224            => (value << offset) | (value >> (64 - offset));
 225
 226        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 227        private static uint Round(uint hash, uint input)
 228        {
 0229            return RotateLeft(hash + input * Prime2, 13) * Prime1;
 230        }
 231
 232        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 233        private static uint QueueRound(uint hash, uint queuedValue)
 234        {
 0235            return RotateLeft(hash + queuedValue * Prime3, 17) * Prime4;
 236        }
 237
 238        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 239        private static uint MixState(uint v1, uint v2, uint v3, uint v4)
 240        {
 0241            return RotateLeft(v1, 1) + RotateLeft(v2, 7) + RotateLeft(v3, 12) + RotateLeft(v4, 18);
 242        }
 243
 244        private static uint MixEmptyState()
 245        {
 0246            return s_seed + Prime5;
 247        }
 248
 249        [MethodImpl(MethodImplOptions.AggressiveInlining)]
 250        private static uint MixFinal(uint hash)
 251        {
 0252            hash ^= hash >> 15;
 0253            hash *= Prime2;
 0254            hash ^= hash >> 13;
 0255            hash *= Prime3;
 0256            hash ^= hash >> 16;
 0257            return hash;
 258        }
 259
 260        public void Add<T>(T value)
 261        {
 0262            Add(value?.GetHashCode() ?? 0);
 0263        }
 264
 265        public void Add<T>(T value, IEqualityComparer<T> comparer)
 266        {
 0267            Add(comparer != null ? comparer.GetHashCode(value) : (value?.GetHashCode() ?? 0));
 0268        }
 269
 270        private void Add(int value)
 271        {
 272            // The original xxHash works as follows:
 273            // 0. Initialize immediately. We can't do this in a struct (no
 274            //    default ctor).
 275            // 1. Accumulate blocks of length 16 (4 uints) into 4 accumulators.
 276            // 2. Accumulate remaining blocks of length 4 (1 uint) into the
 277            //    hash.
 278            // 3. Accumulate remaining blocks of length 1 into the hash.
 279
 280            // There is no need for #3 as this type only accepts ints. _queue1,
 281            // _queue2 and _queue3 are basically a buffer so that when
 282            // ToHashCode is called we can execute #2 correctly.
 283
 284            // We need to initialize the xxHash32 state (_v1 to _v4) lazily (see
 285            // #0) nd the last place that can be done if you look at the
 286            // original code is just before the first block of 16 bytes is mixed
 287            // in. The xxHash32 state is never used for streams containing fewer
 288            // than 16 bytes.
 289
 290            // To see what's really going on here, have a look at the Combine
 291            // methods.
 292
 0293            var val = (uint)value;
 294
 295            // Storing the value of _length locally shaves of quite a few bytes
 296            // in the resulting machine code.
 0297            uint previousLength = _length++;
 0298            uint position = previousLength % 4;
 299
 300            // Switch can't be inlined.
 301
 0302            if (position == 0)
 0303                _queue1 = val;
 0304            else if (position == 1)
 0305                _queue2 = val;
 0306            else if (position == 2)
 0307                _queue3 = val;
 308            else // position == 3
 309            {
 0310                if (previousLength == 3)
 0311                    Initialize(out _v1, out _v2, out _v3, out _v4);
 312
 0313                _v1 = Round(_v1, _queue1);
 0314                _v2 = Round(_v2, _queue2);
 0315                _v3 = Round(_v3, _queue3);
 0316                _v4 = Round(_v4, val);
 317            }
 0318        }
 319
 320        public int ToHashCode()
 321        {
 322            // Storing the value of _length locally shaves of quite a few bytes
 323            // in the resulting machine code.
 0324            uint length = _length;
 325
 326            // position refers to the *next* queue position in this method, so
 327            // position == 1 means that _queue1 is populated; _queue2 would have
 328            // been populated on the next call to Add.
 0329            uint position = length % 4;
 330
 331            // If the length is less than 4, _v1 to _v4 don't contain anything
 332            // yet. xxHash32 treats this differently.
 333
 0334            uint hash = length < 4 ? MixEmptyState() : MixState(_v1, _v2, _v3, _v4);
 335
 336            // _length is incremented once per Add(Int32) and is therefore 4
 337            // times too small (xxHash length is in bytes, not ints).
 338
 0339            hash += length * 4;
 340
 341            // Mix what remains in the queue
 342
 343            // Switch can't be inlined right now, so use as few branches as
 344            // possible by manually excluding impossible scenarios (position > 1
 345            // is always false if position is not > 0).
 0346            if (position > 0)
 347            {
 0348                hash = QueueRound(hash, _queue1);
 0349                if (position > 1)
 350                {
 0351                    hash = QueueRound(hash, _queue2);
 0352                    if (position > 2)
 0353                        hash = QueueRound(hash, _queue3);
 354                }
 355            }
 356
 0357            hash = MixFinal(hash);
 0358            return (int)hash;
 359        }
 360    }
 361}