Hash128.cs 25 KB

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