XXHash128.cs
1 using System; 2 using System.Buffers.Binary; 3 using System.Diagnostics; 4 using System.Numerics; 5 using System.Runtime.CompilerServices; 6 using System.Runtime.Intrinsics; 7 using System.Runtime.Intrinsics.X86; 8 9 namespace Ryujinx.Common 10 { 11 public static class XXHash128 12 { 13 private const int StripeLen = 64; 14 private const int AccNb = StripeLen / sizeof(ulong); 15 private const int SecretConsumeRate = 8; 16 private const int SecretLastAccStart = 7; 17 private const int SecretMergeAccsStart = 11; 18 private const int SecretSizeMin = 136; 19 private const int MidSizeStartOffset = 3; 20 private const int MidSizeLastOffset = 17; 21 22 private const uint Prime32_1 = 0x9E3779B1U; 23 private const uint Prime32_2 = 0x85EBCA77U; 24 private const uint Prime32_3 = 0xC2B2AE3DU; 25 private const uint Prime32_4 = 0x27D4EB2FU; 26 private const uint Prime32_5 = 0x165667B1U; 27 28 private const ulong Prime64_1 = 0x9E3779B185EBCA87UL; 29 private const ulong Prime64_2 = 0xC2B2AE3D27D4EB4FUL; 30 private const ulong Prime64_3 = 0x165667B19E3779F9UL; 31 private const ulong Prime64_4 = 0x85EBCA77C2B2AE63UL; 32 private const ulong Prime64_5 = 0x27D4EB2F165667C5UL; 33 34 private static readonly ulong[] _xxh3InitAcc = { 35 Prime32_3, 36 Prime64_1, 37 Prime64_2, 38 Prime64_3, 39 Prime64_4, 40 Prime32_2, 41 Prime64_5, 42 Prime32_1, 43 }; 44 45 private static ReadOnlySpan<byte> Xxh3KSecret => new byte[] 46 { 47 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, 48 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, 49 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, 50 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, 51 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, 52 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, 53 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, 54 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, 55 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, 56 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, 57 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, 58 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, 59 }; 60 61 [MethodImpl(MethodImplOptions.AggressiveInlining)] 62 private static ulong Mult32To64(ulong x, ulong y) 63 { 64 return (uint)x * (ulong)(uint)y; 65 } 66 67 [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 private static Hash128 Mult64To128(ulong lhs, ulong rhs) 69 { 70 ulong high = Math.BigMul(lhs, rhs, out ulong low); 71 72 return new Hash128 73 { 74 Low = low, 75 High = high, 76 }; 77 } 78 79 [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 private static ulong Mul128Fold64(ulong lhs, ulong rhs) 81 { 82 Hash128 product = Mult64To128(lhs, rhs); 83 84 return product.Low ^ product.High; 85 } 86 87 [MethodImpl(MethodImplOptions.AggressiveInlining)] 88 private static ulong XorShift64(ulong v64, int shift) 89 { 90 Debug.Assert(0 <= shift && shift < 64); 91 92 return v64 ^ (v64 >> shift); 93 } 94 95 [MethodImpl(MethodImplOptions.AggressiveInlining)] 96 private static ulong Xxh3Avalanche(ulong h64) 97 { 98 h64 = XorShift64(h64, 37); 99 h64 *= 0x165667919E3779F9UL; 100 h64 = XorShift64(h64, 32); 101 102 return h64; 103 } 104 105 [MethodImpl(MethodImplOptions.AggressiveInlining)] 106 private static ulong Xxh64Avalanche(ulong h64) 107 { 108 h64 ^= h64 >> 33; 109 h64 *= Prime64_2; 110 h64 ^= h64 >> 29; 111 h64 *= Prime64_3; 112 h64 ^= h64 >> 32; 113 114 return h64; 115 } 116 117 [MethodImpl(MethodImplOptions.AggressiveInlining)] 118 private unsafe static void Xxh3Accumulate512(Span<ulong> acc, ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret) 119 { 120 if (Avx2.IsSupported) 121 { 122 fixed (ulong* pAcc = acc) 123 { 124 fixed (byte* pInput = input, pSecret = secret) 125 { 126 Vector256<ulong>* xAcc = (Vector256<ulong>*)pAcc; 127 Vector256<byte>* xInput = (Vector256<byte>*)pInput; 128 Vector256<byte>* xSecret = (Vector256<byte>*)pSecret; 129 130 for (ulong i = 0; i < StripeLen / 32; i++) 131 { 132 Vector256<byte> dataVec = xInput[i]; 133 Vector256<byte> keyVec = xSecret[i]; 134 Vector256<byte> dataKey = Avx2.Xor(dataVec, keyVec); 135 Vector256<uint> dataKeyLo = Avx2.Shuffle(dataKey.AsUInt32(), 0b00110001); 136 Vector256<ulong> product = Avx2.Multiply(dataKey.AsUInt32(), dataKeyLo); 137 Vector256<uint> dataSwap = Avx2.Shuffle(dataVec.AsUInt32(), 0b01001110); 138 Vector256<ulong> sum = Avx2.Add(xAcc[i], dataSwap.AsUInt64()); 139 xAcc[i] = Avx2.Add(product, sum); 140 } 141 } 142 } 143 } 144 else if (Sse2.IsSupported) 145 { 146 fixed (ulong* pAcc = acc) 147 { 148 fixed (byte* pInput = input, pSecret = secret) 149 { 150 Vector128<ulong>* xAcc = (Vector128<ulong>*)pAcc; 151 Vector128<byte>* xInput = (Vector128<byte>*)pInput; 152 Vector128<byte>* xSecret = (Vector128<byte>*)pSecret; 153 154 for (ulong i = 0; i < StripeLen / 16; i++) 155 { 156 Vector128<byte> dataVec = xInput[i]; 157 Vector128<byte> keyVec = xSecret[i]; 158 Vector128<byte> dataKey = Sse2.Xor(dataVec, keyVec); 159 Vector128<uint> dataKeyLo = Sse2.Shuffle(dataKey.AsUInt32(), 0b00110001); 160 Vector128<ulong> product = Sse2.Multiply(dataKey.AsUInt32(), dataKeyLo); 161 Vector128<uint> dataSwap = Sse2.Shuffle(dataVec.AsUInt32(), 0b01001110); 162 Vector128<ulong> sum = Sse2.Add(xAcc[i], dataSwap.AsUInt64()); 163 xAcc[i] = Sse2.Add(product, sum); 164 } 165 } 166 } 167 } 168 else 169 { 170 for (int i = 0; i < AccNb; i++) 171 { 172 ulong dataVal = BinaryPrimitives.ReadUInt64LittleEndian(input[(i * sizeof(ulong))..]); 173 ulong dataKey = dataVal ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); 174 acc[i ^ 1] += dataVal; 175 acc[i] += Mult32To64((uint)dataKey, dataKey >> 32); 176 } 177 } 178 } 179 180 [MethodImpl(MethodImplOptions.AggressiveInlining)] 181 private unsafe static void Xxh3ScrambleAcc(Span<ulong> acc, ReadOnlySpan<byte> secret) 182 { 183 if (Avx2.IsSupported) 184 { 185 fixed (ulong* pAcc = acc) 186 { 187 fixed (byte* pSecret = secret) 188 { 189 Vector256<uint> prime32 = Vector256.Create(Prime32_1); 190 Vector256<ulong>* xAcc = (Vector256<ulong>*)pAcc; 191 Vector256<byte>* xSecret = (Vector256<byte>*)pSecret; 192 193 for (ulong i = 0; i < StripeLen / 32; i++) 194 { 195 Vector256<ulong> accVec = xAcc[i]; 196 Vector256<ulong> shifted = Avx2.ShiftRightLogical(accVec, 47); 197 Vector256<ulong> dataVec = Avx2.Xor(accVec, shifted); 198 199 Vector256<byte> keyVec = xSecret[i]; 200 Vector256<uint> dataKey = Avx2.Xor(dataVec.AsUInt32(), keyVec.AsUInt32()); 201 202 Vector256<uint> dataKeyHi = Avx2.Shuffle(dataKey.AsUInt32(), 0b00110001); 203 Vector256<ulong> prodLo = Avx2.Multiply(dataKey, prime32); 204 Vector256<ulong> prodHi = Avx2.Multiply(dataKeyHi, prime32); 205 206 xAcc[i] = Avx2.Add(prodLo, Avx2.ShiftLeftLogical(prodHi, 32)); 207 } 208 } 209 } 210 } 211 else if (Sse2.IsSupported) 212 { 213 fixed (ulong* pAcc = acc) 214 { 215 fixed (byte* pSecret = secret) 216 { 217 Vector128<uint> prime32 = Vector128.Create(Prime32_1); 218 Vector128<ulong>* xAcc = (Vector128<ulong>*)pAcc; 219 Vector128<byte>* xSecret = (Vector128<byte>*)pSecret; 220 221 for (ulong i = 0; i < StripeLen / 16; i++) 222 { 223 Vector128<ulong> accVec = xAcc[i]; 224 Vector128<ulong> shifted = Sse2.ShiftRightLogical(accVec, 47); 225 Vector128<ulong> dataVec = Sse2.Xor(accVec, shifted); 226 227 Vector128<byte> keyVec = xSecret[i]; 228 Vector128<uint> dataKey = Sse2.Xor(dataVec.AsUInt32(), keyVec.AsUInt32()); 229 230 Vector128<uint> dataKeyHi = Sse2.Shuffle(dataKey.AsUInt32(), 0b00110001); 231 Vector128<ulong> prodLo = Sse2.Multiply(dataKey, prime32); 232 Vector128<ulong> prodHi = Sse2.Multiply(dataKeyHi, prime32); 233 234 xAcc[i] = Sse2.Add(prodLo, Sse2.ShiftLeftLogical(prodHi, 32)); 235 } 236 } 237 } 238 } 239 else 240 { 241 for (int i = 0; i < AccNb; i++) 242 { 243 ulong key64 = BinaryPrimitives.ReadUInt64LittleEndian(secret[(i * sizeof(ulong))..]); 244 ulong acc64 = acc[i]; 245 acc64 = XorShift64(acc64, 47); 246 acc64 ^= key64; 247 acc64 *= Prime32_1; 248 acc[i] = acc64; 249 } 250 } 251 } 252 253 [MethodImpl(MethodImplOptions.AggressiveInlining)] 254 private static void Xxh3Accumulate(Span<ulong> acc, ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, int nbStripes) 255 { 256 for (int n = 0; n < nbStripes; n++) 257 { 258 ReadOnlySpan<byte> inData = input[(n * StripeLen)..]; 259 Xxh3Accumulate512(acc, inData, secret[(n * SecretConsumeRate)..]); 260 } 261 } 262 263 private static void Xxh3HashLongInternalLoop(Span<ulong> acc, ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret) 264 { 265 int nbStripesPerBlock = (secret.Length - StripeLen) / SecretConsumeRate; 266 int blockLen = StripeLen * nbStripesPerBlock; 267 int nbBlocks = (input.Length - 1) / blockLen; 268 269 Debug.Assert(secret.Length >= SecretSizeMin); 270 271 for (int n = 0; n < nbBlocks; n++) 272 { 273 Xxh3Accumulate(acc, input[(n * blockLen)..], secret, nbStripesPerBlock); 274 Xxh3ScrambleAcc(acc, secret[^StripeLen..]); 275 } 276 277 Debug.Assert(input.Length > StripeLen); 278 279 int nbStripes = (input.Length - 1 - (blockLen * nbBlocks)) / StripeLen; 280 Debug.Assert(nbStripes <= (secret.Length / SecretConsumeRate)); 281 Xxh3Accumulate(acc, input[(nbBlocks * blockLen)..], secret, nbStripes); 282 283 ReadOnlySpan<byte> p = input[^StripeLen..]; 284 Xxh3Accumulate512(acc, p, secret[(secret.Length - StripeLen - SecretLastAccStart)..]); 285 } 286 287 [MethodImpl(MethodImplOptions.AggressiveInlining)] 288 private static ulong Xxh3Mix2Accs(Span<ulong> acc, ReadOnlySpan<byte> secret) 289 { 290 return Mul128Fold64( 291 acc[0] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret), 292 acc[1] ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[8..])); 293 } 294 295 [MethodImpl(MethodImplOptions.AggressiveInlining)] 296 private static ulong Xxh3MergeAccs(Span<ulong> acc, ReadOnlySpan<byte> secret, ulong start) 297 { 298 ulong result64 = start; 299 300 for (int i = 0; i < 4; i++) 301 { 302 result64 += Xxh3Mix2Accs(acc[(2 * i)..], secret[(16 * i)..]); 303 } 304 305 return Xxh3Avalanche(result64); 306 } 307 308 [SkipLocalsInit] 309 private static Hash128 Xxh3HashLong128bInternal(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret) 310 { 311 Span<ulong> acc = stackalloc ulong[AccNb]; 312 _xxh3InitAcc.CopyTo(acc); 313 314 Xxh3HashLongInternalLoop(acc, input, secret); 315 316 Debug.Assert(acc.Length == 8); 317 Debug.Assert(secret.Length >= acc.Length * sizeof(ulong) + SecretMergeAccsStart); 318 319 return new Hash128 320 { 321 Low = Xxh3MergeAccs(acc, secret[SecretMergeAccsStart..], (ulong)input.Length * Prime64_1), 322 High = Xxh3MergeAccs( 323 acc, 324 secret[(secret.Length - acc.Length * sizeof(ulong) - SecretMergeAccsStart)..], 325 ~((ulong)input.Length * Prime64_2)), 326 }; 327 } 328 329 private static Hash128 Xxh3Len1To3128b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 330 { 331 Debug.Assert(1 <= input.Length && input.Length <= 3); 332 333 byte c1 = input[0]; 334 byte c2 = input[input.Length >> 1]; 335 byte c3 = input[^1]; 336 337 uint combinedL = ((uint)c1 << 16) | ((uint)c2 << 24) | c3 | ((uint)input.Length << 8); 338 uint combinedH = BitOperations.RotateLeft(BinaryPrimitives.ReverseEndianness(combinedL), 13); 339 ulong bitFlipL = (BinaryPrimitives.ReadUInt32LittleEndian(secret) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[4..])) + seed; 340 ulong bitFlipH = (BinaryPrimitives.ReadUInt32LittleEndian(secret[8..]) ^ BinaryPrimitives.ReadUInt32LittleEndian(secret[12..])) - seed; 341 ulong keyedLo = combinedL ^ bitFlipL; 342 ulong keyedHi = combinedH ^ bitFlipH; 343 344 return new Hash128 345 { 346 Low = Xxh64Avalanche(keyedLo), 347 High = Xxh64Avalanche(keyedHi), 348 }; 349 } 350 351 private static Hash128 Xxh3Len4To8128b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 352 { 353 Debug.Assert(4 <= input.Length && input.Length <= 8); 354 355 seed ^= BinaryPrimitives.ReverseEndianness((uint)seed) << 32; 356 357 uint inputLo = BinaryPrimitives.ReadUInt32LittleEndian(input); 358 uint inputHi = BinaryPrimitives.ReadUInt32LittleEndian(input[^4..]); 359 ulong input64 = inputLo + ((ulong)inputHi << 32); 360 ulong bitFlip = (BinaryPrimitives.ReadUInt64LittleEndian(secret[16..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[24..])) + seed; 361 ulong keyed = input64 ^ bitFlip; 362 363 Hash128 m128 = Mult64To128(keyed, Prime64_1 + ((ulong)input.Length << 2)); 364 365 m128.High += m128.Low << 1; 366 m128.Low ^= m128.High >> 3; 367 368 m128.Low = XorShift64(m128.Low, 35); 369 m128.Low *= 0x9FB21C651E98DF25UL; 370 m128.Low = XorShift64(m128.Low, 28); 371 m128.High = Xxh3Avalanche(m128.High); 372 373 return m128; 374 } 375 376 private static Hash128 Xxh3Len9To16128b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 377 { 378 Debug.Assert(9 <= input.Length && input.Length <= 16); 379 380 ulong bitFlipL = (BinaryPrimitives.ReadUInt64LittleEndian(secret[32..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[40..])) - seed; 381 ulong bitFlipH = (BinaryPrimitives.ReadUInt64LittleEndian(secret[48..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[56..])) + seed; 382 ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); 383 ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[^8..]); 384 385 Hash128 m128 = Mult64To128(inputLo ^ inputHi ^ bitFlipL, Prime64_1); 386 m128.Low += ((ulong)input.Length - 1) << 54; 387 inputHi ^= bitFlipH; 388 m128.High += inputHi + Mult32To64((uint)inputHi, Prime32_2 - 1); 389 m128.Low ^= BinaryPrimitives.ReverseEndianness(m128.High); 390 391 Hash128 h128 = Mult64To128(m128.Low, Prime64_2); 392 h128.High += m128.High * Prime64_2; 393 h128.Low = Xxh3Avalanche(h128.Low); 394 h128.High = Xxh3Avalanche(h128.High); 395 396 return h128; 397 } 398 399 private static Hash128 Xxh3Len0To16128b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 400 { 401 Debug.Assert(input.Length <= 16); 402 403 if (input.Length > 8) 404 { 405 return Xxh3Len9To16128b(input, secret, seed); 406 } 407 408 if (input.Length >= 4) 409 { 410 return Xxh3Len4To8128b(input, secret, seed); 411 } 412 413 if (input.Length != 0) 414 { 415 return Xxh3Len1To3128b(input, secret, seed); 416 } 417 418 Hash128 h128 = new(); 419 ulong bitFlipL = BinaryPrimitives.ReadUInt64LittleEndian(secret[64..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[72..]); 420 ulong bitFlipH = BinaryPrimitives.ReadUInt64LittleEndian(secret[80..]) ^ BinaryPrimitives.ReadUInt64LittleEndian(secret[88..]); 421 h128.Low = Xxh64Avalanche(seed ^ bitFlipL); 422 h128.High = Xxh64Avalanche(seed ^ bitFlipH); 423 424 return h128; 425 } 426 427 private static ulong Xxh3Mix16b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 428 { 429 ulong inputLo = BinaryPrimitives.ReadUInt64LittleEndian(input); 430 ulong inputHi = BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); 431 432 return Mul128Fold64( 433 inputLo ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret) + seed), 434 inputHi ^ (BinaryPrimitives.ReadUInt64LittleEndian(secret[8..]) - seed)); 435 } 436 437 private static Hash128 Xxh128Mix32b(Hash128 acc, ReadOnlySpan<byte> input, ReadOnlySpan<byte> input2, ReadOnlySpan<byte> secret, ulong seed) 438 { 439 acc.Low += Xxh3Mix16b(input, secret, seed); 440 acc.Low ^= BinaryPrimitives.ReadUInt64LittleEndian(input2) + BinaryPrimitives.ReadUInt64LittleEndian(input2[8..]); 441 acc.High += Xxh3Mix16b(input2, secret[16..], seed); 442 acc.High ^= BinaryPrimitives.ReadUInt64LittleEndian(input) + BinaryPrimitives.ReadUInt64LittleEndian(input[8..]); 443 444 return acc; 445 } 446 447 private static Hash128 Xxh3Len17To128128b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 448 { 449 Debug.Assert(secret.Length >= SecretSizeMin); 450 Debug.Assert(16 < input.Length && input.Length <= 128); 451 452 Hash128 acc = new() 453 { 454 Low = (ulong)input.Length * Prime64_1, 455 High = 0, 456 }; 457 458 if (input.Length > 32) 459 { 460 if (input.Length > 64) 461 { 462 if (input.Length > 96) 463 { 464 acc = Xxh128Mix32b(acc, input[48..], input[^64..], secret[96..], seed); 465 } 466 acc = Xxh128Mix32b(acc, input[32..], input[^48..], secret[64..], seed); 467 } 468 acc = Xxh128Mix32b(acc, input[16..], input[^32..], secret[32..], seed); 469 } 470 acc = Xxh128Mix32b(acc, input, input[^16..], secret, seed); 471 472 Hash128 h128 = new() 473 { 474 Low = acc.Low + acc.High, 475 High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, 476 }; 477 h128.Low = Xxh3Avalanche(h128.Low); 478 h128.High = 0UL - Xxh3Avalanche(h128.High); 479 480 return h128; 481 } 482 483 private static Hash128 Xxh3Len129To240128b(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 484 { 485 Debug.Assert(secret.Length >= SecretSizeMin); 486 Debug.Assert(128 < input.Length && input.Length <= 240); 487 488 Hash128 acc = new(); 489 490 int nbRounds = input.Length / 32; 491 acc.Low = (ulong)input.Length * Prime64_1; 492 acc.High = 0; 493 494 for (int i = 0; i < 4; i++) 495 { 496 acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(32 * i)..], seed); 497 } 498 499 acc.Low = Xxh3Avalanche(acc.Low); 500 acc.High = Xxh3Avalanche(acc.High); 501 Debug.Assert(nbRounds >= 4); 502 503 for (int i = 4; i < nbRounds; i++) 504 { 505 acc = Xxh128Mix32b(acc, input[(32 * i)..], input[(32 * i + 16)..], secret[(MidSizeStartOffset + 32 * (i - 4))..], seed); 506 } 507 508 acc = Xxh128Mix32b(acc, input[^16..], input[^32..], secret[(SecretSizeMin - MidSizeLastOffset - 16)..], 0UL - seed); 509 510 Hash128 h128 = new() 511 { 512 Low = acc.Low + acc.High, 513 High = acc.Low * Prime64_1 + acc.High * Prime64_4 + ((ulong)input.Length - seed) * Prime64_2, 514 }; 515 h128.Low = Xxh3Avalanche(h128.Low); 516 h128.High = 0UL - Xxh3Avalanche(h128.High); 517 518 return h128; 519 } 520 521 private static Hash128 Xxh3128bitsInternal(ReadOnlySpan<byte> input, ReadOnlySpan<byte> secret, ulong seed) 522 { 523 Debug.Assert(secret.Length >= SecretSizeMin); 524 525 if (input.Length <= 16) 526 { 527 return Xxh3Len0To16128b(input, secret, seed); 528 } 529 530 if (input.Length <= 128) 531 { 532 return Xxh3Len17To128128b(input, secret, seed); 533 } 534 535 if (input.Length <= 240) 536 { 537 return Xxh3Len129To240128b(input, secret, seed); 538 } 539 540 return Xxh3HashLong128bInternal(input, secret); 541 } 542 543 public static Hash128 ComputeHash(ReadOnlySpan<byte> input) 544 { 545 return Xxh3128bitsInternal(input, Xxh3KSecret, 0UL); 546 } 547 } 548 }