Nav apraksta
Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

xxHash3.StreamingState.cs 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. using System;
  2. using System.Diagnostics;
  3. using System.Runtime.InteropServices;
  4. using Unity.Burst;
  5. #if !NET_DOTS
  6. using Unity.Burst.Intrinsics;
  7. #endif
  8. using Unity.Collections.LowLevel.Unsafe;
  9. using Unity.Mathematics;
  10. namespace Unity.Collections
  11. {
  12. [BurstCompatible]
  13. public static partial class xxHash3
  14. {
  15. /// <summary>
  16. /// Type used to compute hash based on multiple data feed
  17. /// </summary>
  18. /// <remarks>
  19. /// Allow to feed the internal hashing accumulators with data through multiple calls to <see cref="Update"/>, then retrieving the final hash value using <see cref="DigestHash64"/> or <see cref="DigestHash128"/>.
  20. /// More info about how to use this class in its constructor.
  21. /// </remarks>
  22. [BurstCompatible]
  23. public struct StreamingState
  24. {
  25. #region Public API
  26. /// <summary>
  27. /// Create a StreamingState object, ready to be used with the streaming API
  28. /// </summary>
  29. /// <param name="isHash64">true if we are computing a 64bits hash value, false if we are computing a 128bits one</param>
  30. /// <param name="seed">A seed value to be used to compute the hash, default is 0</param>
  31. /// <remarks>
  32. /// Once the object is constructed, you can call the <see cref="Update"/> method as many times as you want to accumulate data to hash.
  33. /// When all the data has been sent, call <see cref="DigestHash64"/> or <see cref="DigestHash128"/> to retrieve the corresponding key, the <see cref="StreamingState"/>
  34. /// instance will then be reset, using the same hash key size and same Seed in order to be ready to be used again.
  35. /// </remarks>
  36. public StreamingState(bool isHash64, ulong seed=0)
  37. {
  38. State = default;
  39. Reset(isHash64, seed);
  40. }
  41. /// <summary>
  42. /// Reset the state of the streaming instance using the given seed value.
  43. /// </summary>
  44. /// <param name="isHash64"></param>
  45. /// <param name="seed">The seed value to alter the computed hash value from</param>
  46. /// <remarks> Call this method to start a new streaming session based on this instance</remarks>
  47. public unsafe void Reset(bool isHash64, ulong seed=0UL)
  48. {
  49. // Reset the whole buffer to 0
  50. var size = UnsafeUtility.SizeOf<StreamingStateData>();
  51. UnsafeUtility.MemClear(UnsafeUtility.AddressOf(ref State), size);
  52. // Set back the saved states
  53. State.IsHash64 = isHash64 ? 1 : 0;
  54. // Init the accumulator with the prime numbers
  55. var acc = Acc;
  56. acc[0] = PRIME32_3;
  57. acc[1] = PRIME64_1;
  58. acc[2] = PRIME64_2;
  59. acc[3] = PRIME64_3;
  60. acc[4] = PRIME64_4;
  61. acc[5] = PRIME32_2;
  62. acc[6] = PRIME64_5;
  63. acc[7] = PRIME32_1;
  64. State.Seed = seed;
  65. fixed (byte* secret = xxHashDefaultKey.kSecret)
  66. {
  67. if (seed != 0)
  68. {
  69. // Must encode the secret key if we're using a seed, we store it in the state object
  70. EncodeSecretKey(SecretKey, secret, seed);
  71. }
  72. else
  73. {
  74. // Otherwise just copy it
  75. UnsafeUtility.MemCpy(SecretKey, secret, SECRET_KEY_SIZE);
  76. }
  77. }
  78. }
  79. /// <summary>
  80. /// Add some data to be hashed
  81. /// </summary>
  82. /// <param name="input">The memory buffer, can't be null</param>
  83. /// <param name="length">The length of the data to accumulate, can be zero</param>
  84. /// <remarks>This API allows you to feed very small data to be hashed, avoiding you to accumulate them in a big buffer, then computing the hash value from.</remarks>
  85. public unsafe void Update(void* input, int length)
  86. {
  87. var bInput = (byte*) input;
  88. var bEnd = bInput + length;
  89. var isHash64 = State.IsHash64;
  90. var secret = SecretKey;
  91. State.TotalLength += length;
  92. if (State.BufferedSize + length <= INTERNAL_BUFFER_SIZE)
  93. {
  94. UnsafeUtility.MemCpy(Buffer + State.BufferedSize, bInput, length);
  95. State.BufferedSize += length;
  96. return;
  97. }
  98. if (State.BufferedSize != 0)
  99. {
  100. var loadSize = INTERNAL_BUFFER_SIZE - State.BufferedSize;
  101. UnsafeUtility.MemCpy(Buffer + State.BufferedSize, bInput, loadSize);
  102. bInput += loadSize;
  103. ConsumeStripes(Acc, ref State.NbStripesSoFar, Buffer, INTERNAL_BUFFER_STRIPES, secret, isHash64);
  104. State.BufferedSize = 0;
  105. }
  106. if (bInput + INTERNAL_BUFFER_SIZE < bEnd)
  107. {
  108. var limit = bEnd - INTERNAL_BUFFER_SIZE;
  109. do
  110. {
  111. ConsumeStripes(Acc, ref State.NbStripesSoFar, bInput, INTERNAL_BUFFER_STRIPES, secret, isHash64);
  112. bInput += INTERNAL_BUFFER_SIZE;
  113. } while (bInput < limit);
  114. UnsafeUtility.MemCpy(Buffer + INTERNAL_BUFFER_SIZE - STRIPE_LEN, bInput - STRIPE_LEN, STRIPE_LEN);
  115. }
  116. if (bInput < bEnd)
  117. {
  118. var newBufferedSize = bEnd - bInput;
  119. UnsafeUtility.MemCpy(Buffer, bInput, newBufferedSize);
  120. State.BufferedSize = (int) newBufferedSize;
  121. }
  122. }
  123. /// <summary>
  124. /// Add the contents of input struct to the hash.
  125. /// </summary>
  126. /// <typeparam name="T">The input type.</typeparam>
  127. /// <param name="input">The input struct that will be hashed</param>
  128. /// <remarks>This API allows you to feed very small data to be hashed, avoiding you to accumulate them in a big buffer, then computing the hash value from.</remarks>
  129. [BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
  130. public unsafe void Update<T>(in T input) where T : unmanaged
  131. {
  132. Update(UnsafeUtilityExtensions.AddressOf(input), UnsafeUtility.SizeOf<T>());
  133. }
  134. /// <summary>
  135. /// Compute the 128bits value based on all the data that have been accumulated
  136. /// </summary>
  137. /// <returns>The hash value</returns>
  138. public unsafe uint4 DigestHash128()
  139. {
  140. CheckKeySize(0);
  141. unchecked
  142. {
  143. var secret = SecretKey;
  144. uint4 hash;
  145. if (State.TotalLength > MIDSIZE_MAX)
  146. {
  147. var acc = stackalloc ulong[ACC_NB];
  148. DigestLong(acc, secret, 0);
  149. var low64 = MergeAcc(acc, secret + SECRET_MERGEACCS_START,
  150. (ulong) State.TotalLength * PRIME64_1);
  151. var high64 = MergeAcc(acc, secret + SECRET_LIMIT - SECRET_MERGEACCS_START,
  152. ~((ulong) State.TotalLength * PRIME64_2));
  153. hash = ToUint4(low64, high64);
  154. }
  155. else
  156. {
  157. hash = Hash128(Buffer, State.TotalLength, State.Seed);
  158. }
  159. Reset(State.IsHash64==1, State.Seed);
  160. return hash;
  161. }
  162. }
  163. /// <summary>
  164. /// Compute the 64bits value based on all the data that have been accumulated
  165. /// </summary>
  166. /// <returns>The hash value</returns>
  167. public unsafe uint2 DigestHash64()
  168. {
  169. CheckKeySize(1);
  170. unchecked
  171. {
  172. var secret = SecretKey;
  173. uint2 hash;
  174. if (State.TotalLength > MIDSIZE_MAX)
  175. {
  176. var acc = stackalloc ulong[ACC_NB];
  177. DigestLong(acc, secret, 1);
  178. hash = ToUint2(MergeAcc(acc, secret + SECRET_MERGEACCS_START, (ulong) State.TotalLength * PRIME64_1));
  179. }
  180. else
  181. {
  182. hash = Hash64(Buffer, State.TotalLength, State.Seed);
  183. }
  184. Reset(State.IsHash64==1, State.Seed);
  185. return hash;
  186. }
  187. }
  188. #endregion
  189. #region Constants
  190. private static readonly int SECRET_LIMIT = SECRET_KEY_SIZE - STRIPE_LEN;
  191. private static readonly int NB_STRIPES_PER_BLOCK = SECRET_LIMIT / SECRET_CONSUME_RATE;
  192. private static readonly int INTERNAL_BUFFER_SIZE = 256;
  193. private static readonly int INTERNAL_BUFFER_STRIPES = INTERNAL_BUFFER_SIZE / STRIPE_LEN;
  194. #endregion
  195. #region Wrapper to internal data storage
  196. unsafe ulong* Acc
  197. {
  198. [DebuggerStepThrough]
  199. get => (ulong*) UnsafeUtility.AddressOf(ref State.Acc);
  200. }
  201. unsafe byte* Buffer
  202. {
  203. [DebuggerStepThrough]
  204. get => (byte*) UnsafeUtility.AddressOf(ref State.Buffer);
  205. }
  206. unsafe byte* SecretKey
  207. {
  208. [DebuggerStepThrough]
  209. get => (byte*) UnsafeUtility.AddressOf(ref State.SecretKey);
  210. }
  211. #endregion
  212. #region Data storage
  213. private StreamingStateData State;
  214. [StructLayout(LayoutKind.Explicit)]
  215. struct StreamingStateData
  216. {
  217. [FieldOffset(0)] public ulong Acc; // 64 bytes
  218. [FieldOffset(64)] public byte Buffer; // 256 bytes
  219. [FieldOffset(320)] public int IsHash64; // 4 bytes
  220. [FieldOffset(324)] public int BufferedSize; // 4 bytes
  221. [FieldOffset(328)] public int NbStripesSoFar; // 4 bytes + 4 padding
  222. [FieldOffset(336)] public long TotalLength; // 8 bytes
  223. [FieldOffset(344)] public ulong Seed; // 8 bytes
  224. [FieldOffset(352)] public byte SecretKey; // 192 bytes
  225. [FieldOffset(540)] public byte _PadEnd;
  226. }
  227. #endregion
  228. #region Internals
  229. private unsafe void DigestLong(ulong* acc, byte* secret, int isHash64)
  230. {
  231. UnsafeUtility.MemCpy(acc, Acc, STRIPE_LEN);
  232. if (State.BufferedSize >= STRIPE_LEN)
  233. {
  234. var totalNbStripes = (State.BufferedSize - 1) / STRIPE_LEN;
  235. ConsumeStripes(acc, ref State.NbStripesSoFar, Buffer, totalNbStripes, secret, isHash64);
  236. #if !NET_DOTS
  237. if (X86.Avx2.IsAvx2Supported)
  238. {
  239. Avx2Accumulate512(acc, Buffer + State.BufferedSize - STRIPE_LEN, null,
  240. secret + SECRET_LIMIT - SECRET_LASTACC_START);
  241. }
  242. else
  243. #endif
  244. {
  245. DefaultAccumulate512(acc, Buffer + State.BufferedSize - STRIPE_LEN, null,
  246. secret + SECRET_LIMIT - SECRET_LASTACC_START, isHash64);
  247. }
  248. }
  249. else
  250. {
  251. var lastStripe = stackalloc byte[STRIPE_LEN];
  252. var catchupSize = STRIPE_LEN - State.BufferedSize;
  253. UnsafeUtility.MemCpy(lastStripe, Buffer + INTERNAL_BUFFER_SIZE - catchupSize, catchupSize);
  254. UnsafeUtility.MemCpy(lastStripe + catchupSize, Buffer, State.BufferedSize);
  255. #if !NET_DOTS
  256. if (X86.Avx2.IsAvx2Supported)
  257. {
  258. Avx2Accumulate512(acc, lastStripe, null, secret+SECRET_LIMIT-SECRET_LASTACC_START);
  259. }
  260. else
  261. #endif
  262. {
  263. DefaultAccumulate512(acc, lastStripe, null, secret+SECRET_LIMIT-SECRET_LASTACC_START, isHash64);
  264. }
  265. }
  266. }
  267. private unsafe void ConsumeStripes(ulong* acc, ref int nbStripesSoFar, byte* input, long totalStripes,
  268. byte* secret, int isHash64)
  269. {
  270. if (NB_STRIPES_PER_BLOCK - nbStripesSoFar <= totalStripes)
  271. {
  272. var nbStripes = NB_STRIPES_PER_BLOCK - nbStripesSoFar;
  273. #if !NET_DOTS
  274. if (X86.Avx2.IsAvx2Supported)
  275. {
  276. Avx2Accumulate(acc, input, null, secret + nbStripesSoFar * SECRET_CONSUME_RATE, nbStripes, isHash64);
  277. Avx2ScrambleAcc(acc, secret + SECRET_LIMIT);
  278. Avx2Accumulate(acc, input + nbStripes * STRIPE_LEN, null, secret, totalStripes - nbStripes, isHash64);
  279. }
  280. else
  281. #endif
  282. {
  283. DefaultAccumulate(acc, input, null, secret + nbStripesSoFar * SECRET_CONSUME_RATE, nbStripes, isHash64);
  284. DefaultScrambleAcc(acc, secret + SECRET_LIMIT);
  285. DefaultAccumulate(acc, input + nbStripes * STRIPE_LEN, null, secret, totalStripes - nbStripes, isHash64);
  286. }
  287. nbStripesSoFar = (int) totalStripes - nbStripes;
  288. }
  289. else
  290. {
  291. #if !NET_DOTS
  292. if (X86.Avx2.IsAvx2Supported)
  293. {
  294. Avx2Accumulate(acc, input, null, secret + nbStripesSoFar * SECRET_CONSUME_RATE, totalStripes, isHash64);
  295. }
  296. else
  297. #endif
  298. {
  299. DefaultAccumulate(acc, input, null, secret + nbStripesSoFar * SECRET_CONSUME_RATE, totalStripes, isHash64);
  300. }
  301. nbStripesSoFar += (int) totalStripes;
  302. }
  303. }
  304. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  305. [BurstDiscard]
  306. void CheckKeySize(int isHash64)
  307. {
  308. if (State.IsHash64 != isHash64)
  309. {
  310. var s = State.IsHash64 != 0 ? "64" : "128";
  311. throw new InvalidOperationException(
  312. $"The streaming state was create for {s} bits hash key, the calling method doesn't support this key size, please use the appropriate API");
  313. }
  314. }
  315. #endregion
  316. }
  317. }
  318. }