XXHash128.cs 23 KB

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