はじめに
前回xxHash3.Hash64
とxxHash3.Hash128
を紹介したのですが、今回はいくつかのデータをそれぞれ計算した後にハッシュ値を取得できるxxHash3.StreamingState
を紹介したいと思います。
www.hanachiru-blog.com
// サンプルコード // 128bits xxHash3.StreamingState streamingState = new xxHash3.StreamingState(isHash64: false); // 対象のデータ NativeArray<byte> array1 = new NativeArray<byte>(100, Allocator.Temp, NativeArrayOptions.UninitializedMemory); for (var i = 0; i < array1.Length; i++) array1[i] = (byte)(i % 50); NativeArray<byte> array2 = new NativeArray<byte>(100, Allocator.Temp, NativeArrayOptions.UninitializedMemory); for (var i = 0; i < array1.Length; i++) array1[i] = (byte)(i % 100); NativeArray<byte> array3 = new NativeArray<byte>(100, Allocator.Temp, NativeArrayOptions.UninitializedMemory); for (var i = 0; i < array1.Length; i++) array1[i] = (byte)(i % 200); unsafe { // ハッシュ値を計算する streamingState.Update(array1.GetUnsafePtr(), array1.Length); streamingState.Update(array2.GetUnsafePtr(), array2.Length); streamingState.Update(array3.GetUnsafePtr(), array3.Length); uint4 hash128 = streamingState.DigestHash128(); // uint4(4123142207, 1991187312, 1036281432, 761385101) Debug.Log(hash128); } array1.Dispose(); array2.Dispose(); array3.Dispose();
概要
xxHash3.Hash64
とxxHash3.Hash128
は引数に渡したデータからハッシュ値を計算します。
使い方としては直感的で良いのですが、例えば数GBのファイルのハッシュ値を計算したい場合を考えてください。xxHash3.Hash64
やxxHash3.Hash128
を実行するために一度メモリにファイルを読み取らないといけないわけですが、数GBもメモリを確保してしまうとスマホなんかではメモリが足らなくなってしまいそうです。
そこで今回紹介するxxHash3.StreamingState
を利用するとこの問題に対処でき、ファイルを少し読み取った後に計算を行い、またファイルを少し読み取って計算を繰り返し、最後にハッシュ値を取り出すことができます。
↓ざっくりとした流れ
xxHash3.StreamingState
を生成- 「データを読み取り
Update
メソッドを実行」を繰り返す Digesthash64
もしくはDigestHash128
メソッドを用いて結果を取り出すxxHash3.StreamingState
が自動でリセットされる
例えば10KBのバッファを用意し、ファイルを少し読み込んでバッファに格納&Update
メソッドで計算を繰り返し、最後にDigesthash64(128)
で結果を取得します。
API概要
public struct StreamingState // Constructors public StreamingState(bool isHash64, ulong seed = 0UL) // Methods public uint4 DigestHash128() public uint2 DigestHash64() public void Reset(bool isHash64, ulong seed = 0UL) public void Update(void *input, int length) public void Update<T>(in T input) where T : struct
詳細は公式ドキュメントを見て欲しいですが、気をつけて欲しい箇所を列挙しておきます。
64bit
のハッシュ値を計算するときはisHash64
をtrue
、128bit
のハッシュ値を計算するときはfalse
にするDigestHash64
・DigestHash128
を実行するとStreamingState
の状態がリセットされる
使い方
public void Start() { // 128bits xxHash3.StreamingState streamingState = new xxHash3.StreamingState(isHash64: false); // ハッシュを計算するデータ NativeArray<byte> array1 = new NativeArray<byte>(100, Allocator.Temp, NativeArrayOptions.UninitializedMemory); for (var i = 0; i < array1.Length; i++) array1[i] = (byte)(i % 50); NativeArray<byte> array2 = new NativeArray<byte>(100, Allocator.Temp, NativeArrayOptions.UninitializedMemory); for (var i = 0; i < array1.Length; i++) array1[i] = (byte)(i % 100); NativeArray<byte> array3 = new NativeArray<byte>(100, Allocator.Temp, NativeArrayOptions.UninitializedMemory); for (var i = 0; i < array1.Length; i++) array1[i] = (byte)(i % 200); // パターン1 unsafe { // ハッシュ値を計算する // NOTE: このサンプルだとあまり意味がないが、一括でやる場合と比較してバッファを小さくすることができる // 例. 「ファイルを一定サイズ読み取りUpdateを実行」を繰り返す streamingState.Update(array1.GetUnsafePtr(), array1.Length); streamingState.Update(array2.GetUnsafePtr(), array2.Length); streamingState.Update(array3.GetUnsafePtr(), array3.Length); uint4 hash128 = streamingState.DigestHash128(); // uint4(4123142207, 1991187312, 1036281432, 761385101) Debug.Log(hash128); } // パターン2 : パターン1と入力したデータが同じならハッシュ値も一致する unsafe { NativeArray<byte> array1And2 = new NativeArray<byte>(array1.Concat(array2).ToArray(), Allocator.Temp); streamingState.Update(array1And2.GetUnsafePtr(), array1And2.Length); streamingState.Update(array3.GetUnsafePtr(), array1.Length); uint4 hash128 = streamingState.DigestHash128(); // uint4(4123142207, 1991187312, 1036281432, 761385101) Debug.Log(hash128); array1And2.Dispose(); } // パターン3 : パターン1と入力したデータが同じならハッシュ値も一致する unsafe { NativeArray<byte> array1And2And3 = new NativeArray<byte>(array1.Concat(array2).Concat(array3).ToArray(), Allocator.Temp); NativeArray<byte> arrayA = new NativeArray<byte>(array1And2And3.Slice(0, 77).ToArray(), Allocator.Temp); NativeArray<byte> arrayB = new NativeArray<byte>(array1And2And3.Slice(77, 100).ToArray(), Allocator.Temp); NativeArray<byte> arrayC = new NativeArray<byte>(array1And2And3.Slice(177, array1And2And3.Length - 177).ToArray(), Allocator.Temp); streamingState.Update(arrayA.GetUnsafePtr(), arrayA.Length); streamingState.Update(arrayB.GetUnsafePtr(), arrayB.Length); streamingState.Update(arrayC.GetUnsafePtr(), arrayC.Length); uint4 hash128 = streamingState.DigestHash128(); // uint4(4123142207, 1991187312, 1036281432, 761385101) Debug.Log(hash128); array1And2And3.Dispose(); arrayA.Dispose(); arrayB.Dispose(); arrayC.Dispose(); } // パターン4 unsafe { NativeArray<byte> array1And2And3 = new NativeArray<byte>(array1.Concat(array2).Concat(array3).ToArray(), Allocator.Temp); uint4 hash128 = xxHash3.Hash128(array1And2And3.GetUnsafePtr(), array1And2And3.Length); // uint4(4123142207, 1991187312, 1036281432, 761385101) Debug.Log(hash128); array1And2And3.Dispose(); } array1.Dispose(); array2.Dispose(); array3.Dispose(); }