From 4f9b00acb5b3d90898837812d6b91a418c1ea94d Mon Sep 17 00:00:00 2001 From: Oleksandr Melnyk Date: Thu, 2 Jun 2022 11:25:54 +0300 Subject: [PATCH] Add xxHash128 without support SIMD --- src/Standart.Hash.xxHash/Utils.cs | 23 + src/Standart.Hash.xxHash/xxHash128.XXH.cs | 118 +++++ src/Standart.Hash.xxHash/xxHash128.XXH3.cs | 463 ++++++++++++++++++++ src/Standart.Hash.xxHash/xxHash128.XXH64.cs | 20 + src/Standart.Hash.xxHash/xxHash128.cs | 104 +++++ 5 files changed, 728 insertions(+) create mode 100644 src/Standart.Hash.xxHash/Utils.cs create mode 100644 src/Standart.Hash.xxHash/xxHash128.XXH.cs create mode 100644 src/Standart.Hash.xxHash/xxHash128.XXH3.cs create mode 100644 src/Standart.Hash.xxHash/xxHash128.XXH64.cs create mode 100644 src/Standart.Hash.xxHash/xxHash128.cs diff --git a/src/Standart.Hash.xxHash/Utils.cs b/src/Standart.Hash.xxHash/Utils.cs new file mode 100644 index 0000000..af7141b --- /dev/null +++ b/src/Standart.Hash.xxHash/Utils.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.CompilerServices; + +namespace Standart.Hash.xxHash +{ + public static class Utils + { + public static Guid ToGuid(this uint128 value) + { + // allocation + return new Guid(value.ToBytes()); + } + + public static byte[] ToBytes(this uint128 value) + { + // allocation + byte[] bytes = new byte[sizeof(ulong) * 2]; + Unsafe.As(ref bytes[0]) = value.high64; + Unsafe.As(ref bytes[8]) = value.low64; + return bytes; + } + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash128.XXH.cs b/src/Standart.Hash.xxHash/xxHash128.XXH.cs new file mode 100644 index 0000000..a369cf5 --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash128.XXH.cs @@ -0,0 +1,118 @@ +// ReSharper disable InconsistentNaming + +using System.Runtime.CompilerServices; + +namespace Standart.Hash.xxHash +{ + public static partial class xxHash128 + { + private const ulong XXH_PRIME64_1 = 11400714785074694791UL; + private const ulong XXH_PRIME64_2 = 14029467366897019727UL; + private const ulong XXH_PRIME64_3 = 1609587929392839161UL; + private const ulong XXH_PRIME64_4 = 9650029242287828579UL; + private const ulong XXH_PRIME64_5 = 2870177450012600261UL; + + private const uint XXH_PRIME32_1 = 2654435761U; + private const uint XXH_PRIME32_2 = 2246822519U; + private const uint XXH_PRIME32_3 = 3266489917U; + private const uint XXH_PRIME32_4 = 668265263U; + private const uint XXH_PRIME32_5 = 374761393U; + + private const int XXH_STRIPE_LEN = 64; + private const int XXH_ACC_NB = XXH_STRIPE_LEN / 8; + private const int XXH_SECRET_CONSUME_RATE = 8; + private const int XXH_SECRET_MERGEACCS_START = 11; + private const int XXH_SECRET_DEFAULT_SIZE = 192; + private const int XXH_SECRET_LASTACC_START = 7; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint XXH_rotl32(uint x, int r) + { + return (x << r) | (x >> (32 - r)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH_rotl64(ulong x, int r) + { + return (x << r) | (x >> (64 - r)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong XXH_readLE64(byte* ptr) + { + return *(ulong*) ptr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint XXH_readLE32(byte* ptr) + { + return *(uint*) ptr; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH_xorshift64(ulong v64, int shift) + { + return v64 ^ (v64 >> shift); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint XXH_swap32(uint x) + { + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH_swap64(ulong x) + { + return ((x << 56) & 0xff00000000000000UL) | + ((x << 40) & 0x00ff000000000000UL) | + ((x << 24) & 0x0000ff0000000000UL) | + ((x << 8) & 0x000000ff00000000UL) | + ((x >> 8) & 0x00000000ff000000UL) | + ((x >> 24) & 0x0000000000ff0000UL) | + ((x >> 40) & 0x000000000000ff00UL) | + ((x >> 56) & 0x00000000000000ffUL); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH_mult32to64(ulong x, ulong y) + { + return (ulong)(uint)(x) * (ulong)(uint)(y); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint128 XXH_mult64to128(ulong lhs, ulong rhs) + { + // TODO: SIMD + + return XXH_mult64to128_scalar(lhs, rhs); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static uint128 XXH_mult64to128_scalar(ulong lhs, ulong rhs) + { + ulong lo_lo = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs & 0xFFFFFFFF); + ulong hi_lo = XXH_mult32to64(lhs >> 32, rhs & 0xFFFFFFFF); + ulong lo_hi = XXH_mult32to64(lhs & 0xFFFFFFFF, rhs >> 32); + ulong hi_hi = XXH_mult32to64(lhs >> 32, rhs >> 32); + + ulong cross = (lo_lo >> 32) + (hi_lo & 0xFFFFFFFF) + lo_hi; + ulong upper = (hi_lo >> 32) + (cross >> 32) + hi_hi; + ulong lower = (cross << 32) | (lo_lo & 0xFFFFFFFF); + + uint128 r128; + r128.low64 = lower; + r128.high64 = upper; + return r128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH_writeLE64(byte* dst, ulong v64) + { + *(ulong*) dst = v64; + } + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash128.XXH3.cs b/src/Standart.Hash.xxHash/xxHash128.XXH3.cs new file mode 100644 index 0000000..e01fd53 --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash128.XXH3.cs @@ -0,0 +1,463 @@ +// ReSharper disable InconsistentNaming + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Standart.Hash.xxHash +{ + public static partial class xxHash128 + { + private static byte[] XXH3_SECRET = + { + 0xb8, 0xfe, 0x6c, 0x39, 0x23, 0xa4, 0x4b, 0xbe, 0x7c, 0x01, 0x81, 0x2c, 0xf7, 0x21, 0xad, 0x1c, + 0xde, 0xd4, 0x6d, 0xe9, 0x83, 0x90, 0x97, 0xdb, 0x72, 0x40, 0xa4, 0xa4, 0xb7, 0xb3, 0x67, 0x1f, + 0xcb, 0x79, 0xe6, 0x4e, 0xcc, 0xc0, 0xe5, 0x78, 0x82, 0x5a, 0xd0, 0x7d, 0xcc, 0xff, 0x72, 0x21, + 0xb8, 0x08, 0x46, 0x74, 0xf7, 0x43, 0x24, 0x8e, 0xe0, 0x35, 0x90, 0xe6, 0x81, 0x3a, 0x26, 0x4c, + 0x3c, 0x28, 0x52, 0xbb, 0x91, 0xc3, 0x00, 0xcb, 0x88, 0xd0, 0x65, 0x8b, 0x1b, 0x53, 0x2e, 0xa3, + 0x71, 0x64, 0x48, 0x97, 0xa2, 0x0d, 0xf9, 0x4e, 0x38, 0x19, 0xef, 0x46, 0xa9, 0xde, 0xac, 0xd8, + 0xa8, 0xfa, 0x76, 0x3f, 0xe3, 0x9c, 0x34, 0x3f, 0xf9, 0xdc, 0xbb, 0xc7, 0xc7, 0x0b, 0x4f, 0x1d, + 0x8a, 0x51, 0xe0, 0x4b, 0xcd, 0xb4, 0x59, 0x31, 0xc8, 0x9f, 0x7e, 0xc9, 0xd9, 0x78, 0x73, 0x64, + 0xea, 0xc5, 0xac, 0x83, 0x34, 0xd3, 0xeb, 0xc3, 0xc5, 0x81, 0xa0, 0xff, 0xfa, 0x13, 0x63, 0xeb, + 0x17, 0x0d, 0xdd, 0x51, 0xb7, 0xf0, 0xda, 0x49, 0xd3, 0x16, 0x55, 0x26, 0x29, 0xd4, 0x68, 0x9e, + 0x2b, 0x16, 0xbe, 0x58, 0x7d, 0x47, 0xa1, 0xfc, 0x8f, 0xf8, 0xb8, 0xd1, 0x7a, 0xd0, 0x31, 0xce, + 0x45, 0xcb, 0x3a, 0x8f, 0x95, 0x16, 0x04, 0x28, 0xaf, 0xd7, 0xfb, 0xca, 0xbb, 0x4b, 0x40, 0x7e, + }; + + private static ulong[] XXH3_INIT_ACC = + { + XXH_PRIME32_3, XXH_PRIME64_1, XXH_PRIME64_2, XXH_PRIME64_3, + XXH_PRIME64_4, XXH_PRIME32_2, XXH_PRIME64_5, XXH_PRIME32_1 + }; + + private const int XXH3_SECRET_SIZE_MIN = 136; + private const int XXH3_SECRET_DEFAULT_SIZE = 192; + private const int XXH3_MIDSIZE_MAX = 240; + private const int XXH3_MIDSIZE_STARTOFFSET = 3; + private const int XXH3_MIDSIZE_LASTOFFSET = 17; + private const int XXH3_ACC_SIZE = 64; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_128bits_internal(byte* input, int len, ulong seed, byte* secret, int secretLen) + { + Debug.Assert(secretLen >= XXH3_SECRET_SIZE_MIN); + + if (len <= 16) + return XXH3_len_0to16_128b(input, len, secret, seed); + if (len <= 128) + return XXH3_len_17to128_128b(input, len, secret, secretLen, seed); + if (len <= XXH3_MIDSIZE_MAX) + return XXH3_len_129to240_128b(input, len, secret, secretLen, seed); + + return XXH3_hashLong_128b_withSeed(input, len, secret, secretLen, seed); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_len_0to16_128b(byte* input, int len, byte* secret, ulong seed) + { + Debug.Assert(len <= 16); + + if (len > 8) return XXH3_len_9to16_128b(input, len, secret, seed); + if (len >= 4) return XXH3_len_4to8_128b(input, len, secret, seed); + if (len != 0) return XXH3_len_1to3_128b(input, len, secret, seed); + + uint128 h128; + ulong bitflipl = XXH_readLE64(secret + 64) ^ XXH_readLE64(secret + 72); + ulong bitfliph = XXH_readLE64(secret + 80) ^ XXH_readLE64(secret + 88); + h128.low64 = XXH64_avalanche(seed ^ bitflipl); + h128.high64 = XXH64_avalanche(seed ^ bitfliph); + return h128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_len_17to128_128b(byte* input, int len, byte* secret, int secretSize, ulong seed) + { + Debug.Assert(secretSize >= XXH3_SECRET_SIZE_MIN); + Debug.Assert(16 < len && len <= 128); + + uint128 acc; + acc.low64 = (ulong) len * XXH_PRIME64_1; + acc.high64 = 0; + + if (len > 32) { + if (len > 64) { + if (len > 96) { + acc = XXH128_mix32B(acc, input+48, input+len-64, secret+96, seed); + } + acc = XXH128_mix32B(acc, input+32, input+len-48, secret+64, seed); + } + acc = XXH128_mix32B(acc, input+16, input+len-32, secret+32, seed); + } + acc = XXH128_mix32B(acc, input, input+len-16, secret, seed); + + uint128 h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + (((ulong) len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (ulong) 0 - XXH3_avalanche(h128.high64); + return h128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_len_9to16_128b(byte* input, int len, byte* secret, ulong seed) + { + Debug.Assert(input != null); + Debug.Assert(secret != null); + Debug.Assert(9 <= len && len <= 16); + + ulong bitflipl = (XXH_readLE64(secret + 32) ^ XXH_readLE64(secret + 40)) - seed; + ulong bitfliph = (XXH_readLE64(secret + 48) ^ XXH_readLE64(secret + 56)) + seed; + ulong input_lo = XXH_readLE64(input); + ulong input_hi = XXH_readLE64(input + len - 8); + uint128 m128 = XXH_mult64to128(input_lo ^ input_hi ^ bitflipl, XXH_PRIME64_1); + + m128.low64 += (ulong) (len - 1) << 54; + input_hi ^= bitfliph; + + m128.high64 += input_hi + XXH_mult32to64((uint) input_hi, XXH_PRIME32_2 - 1); + m128.low64 ^= XXH_swap64(m128.high64); + + uint128 h128 = XXH_mult64to128(m128.low64, XXH_PRIME64_2); + h128.high64 += m128.high64 * XXH_PRIME64_2; + + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = XXH3_avalanche(h128.high64); + return h128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_len_1to3_128b(byte* input, int len, byte* secret, ulong seed) + { + Debug.Assert(input != null); + Debug.Assert(1 <= len && len <= 3); + Debug.Assert(secret != null); + + byte c1 = input[0]; + byte c2 = input[len >> 1]; + byte c3 = input[len - 1]; + + uint combinedl = ((uint) c1 << 16) | + ((uint) c2 << 24) | + ((uint) c3 << 0) | + ((uint) len << 8); + uint combinedh = XXH_rotl32(XXH_swap32(combinedl), 13); + + ulong bitflipl = (XXH_readLE32(secret) ^ XXH_readLE32(secret + 4)) + seed; + ulong bitfliph = (XXH_readLE32(secret + 8) ^ XXH_readLE32(secret + 12)) - seed; + ulong keyed_lo = (ulong) combinedl ^ bitflipl; + ulong keyed_hi = (ulong) combinedh ^ bitfliph; + + uint128 h128; + h128.low64 = XXH64_avalanche(keyed_lo); + h128.high64 = XXH64_avalanche(keyed_hi); + + return h128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_len_4to8_128b(byte* input, int len, byte* secret, ulong seed) + { + Debug.Assert(input != null); + Debug.Assert(secret != null); + Debug.Assert(4 <= len && len <= 8); + + seed ^= (ulong) XXH_swap32((uint) seed) << 32; + + uint input_lo = XXH_readLE32(input); + uint input_hi = XXH_readLE32(input + len - 4); + ulong input_64 = input_lo + ((ulong) input_hi << 32); + ulong bitflip = (XXH_readLE64(secret + 16) ^ XXH_readLE64(secret + 24)) + seed; + ulong keyed = input_64 ^ bitflip; + + uint128 m128 = XXH_mult64to128(keyed, XXH_PRIME64_1 + ((ulong) len << 2)); + + m128.high64 += (m128.low64 << 1); + m128.low64 ^= (m128.high64 >> 3); + + m128.low64 = XXH_xorshift64(m128.low64, 35); + m128.low64 *= 0x9FB21C651E98DF25UL; + m128.low64 = XXH_xorshift64(m128.low64, 28); + m128.high64 = XXH3_avalanche(m128.high64); + + return m128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_len_129to240_128b(byte* input, int len, byte* secret, int secretSize, ulong seed) + { + Debug.Assert(secretSize >= XXH3_SECRET_SIZE_MIN); + Debug.Assert(128 < len && len <= XXH3_MIDSIZE_MAX); + + uint128 acc; + int nbRounds = len / 32; + + acc.low64 = (ulong) len * XXH_PRIME64_1; + acc.high64 = 0; + for (int i = 0; i < 4; i++) { + acc = XXH128_mix32B(acc, + input + (32 * i), + input + (32 * i) + 16, + secret + (32 * i), + seed); + } + + acc.low64 = XXH3_avalanche(acc.low64); + acc.high64 = XXH3_avalanche(acc.high64); + + for (int i = 4 ; i < nbRounds; i++) { + acc = XXH128_mix32B(acc, + input + (32 * i), + input + (32 * i) + 16, + secret + XXH3_MIDSIZE_STARTOFFSET + (32 * (i - 4)), + seed); + } + + acc = XXH128_mix32B(acc, + input + len - 16, + input + len - 32, + secret + XXH3_SECRET_SIZE_MIN - XXH3_MIDSIZE_LASTOFFSET - 16, + 0UL - seed); + + uint128 h128; + h128.low64 = acc.low64 + acc.high64; + h128.high64 = (acc.low64 * XXH_PRIME64_1) + + (acc.high64 * XXH_PRIME64_4) + + (((ulong)len - seed) * XXH_PRIME64_2); + h128.low64 = XXH3_avalanche(h128.low64); + h128.high64 = (ulong)0 - XXH3_avalanche(h128.high64); + return h128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH3_avalanche(ulong h64) + { + h64 = XXH_xorshift64(h64, 37); + h64 *= 0x165667919E3779F9UL; + h64 = XXH_xorshift64(h64, 32); + return h64; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH128_mix32B(uint128 acc, byte* input_1, byte* input_2, byte* secret, ulong seed) + { + acc.low64 += XXH3_mix16B(input_1, secret + 0, seed); + acc.low64 ^= XXH_readLE64(input_2) + XXH_readLE64(input_2 + 8); + acc.high64 += XXH3_mix16B(input_2, secret + 16, seed); + acc.high64 ^= XXH_readLE64(input_1) + XXH_readLE64(input_1 + 8); + return acc; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong XXH3_mix16B(byte* input, byte* secret, ulong seed) + { + ulong input_lo = XXH_readLE64(input); + ulong input_hi = XXH_readLE64(input + 8); + + return XXH3_mul128_fold64( + input_lo ^ (XXH_readLE64(secret) + seed), + input_hi ^ (XXH_readLE64(secret + 8) - seed) + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH3_mul128_fold64(ulong lhs, ulong rhs) + { + uint128 product = XXH_mult64to128(lhs, rhs); + return product.low64 ^ product.high64; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_hashLong_128b_withSeed(byte* input, int len, byte* secret, int secretSize, ulong seed) + { + if (seed == 0) + return XXH3_hashLong_128b_internal(input, len, secret, secretSize); + + int customSecretSize = XXH3_SECRET_DEFAULT_SIZE; + byte* customSecret = stackalloc byte[customSecretSize]; + + fixed (byte* ptr = &XXH3_SECRET[0]) + { + for (int i = 0; i < customSecretSize; i += 8) + { + customSecret[i] = ptr[i]; + customSecret[i+1] = ptr[i+1]; + customSecret[i+2] = ptr[i+2]; + customSecret[i+3] = ptr[i+3]; + customSecret[i+4] = ptr[i+4]; + customSecret[i+5] = ptr[i+5]; + customSecret[i+6] = ptr[i+6]; + customSecret[i+7] = ptr[i+7]; + } + } + XXH3_initCustomSecret(customSecret, seed); + + return XXH3_hashLong_128b_internal(input, len, customSecret, customSecretSize); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 XXH3_hashLong_128b_internal(byte* input, int len, byte* secret, int secretSize) + { + ulong* acc = stackalloc ulong[8]; + + fixed (ulong* ptr = &XXH3_INIT_ACC[0]) + { + acc[0] = ptr[0]; + acc[1] = ptr[1]; + acc[2] = ptr[2]; + acc[3] = ptr[3]; + acc[4] = ptr[4]; + acc[5] = ptr[5]; + acc[6] = ptr[6]; + acc[7] = ptr[7]; + } + XXH3_hashLong_internal_loop(acc, input, len, secret, secretSize); + + uint128 uint128; + uint128.low64 = XXH3_mergeAccs(acc, + secret + XXH_SECRET_MERGEACCS_START, + (ulong)len * XXH_PRIME64_1); + uint128.high64 = XXH3_mergeAccs(acc, + secret + secretSize - XXH3_ACC_SIZE - XXH_SECRET_MERGEACCS_START, + ~((ulong)len * XXH_PRIME64_2)); + + return uint128; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_hashLong_internal_loop(ulong* acc, byte* input, int len, byte* secret, int secretSize) + { + Debug.Assert(secretSize >= XXH3_SECRET_SIZE_MIN); + Debug.Assert(len > XXH_STRIPE_LEN); + + int nbStripesPerBlock = (secretSize - XXH_STRIPE_LEN) / XXH_SECRET_CONSUME_RATE; + int block_len = XXH_STRIPE_LEN * nbStripesPerBlock; + int nb_blocks = (len - 1) / block_len; + + for (int n = 0; n < nb_blocks; n++) { + XXH3_accumulate(acc, input + n * block_len, secret, nbStripesPerBlock); + XXH3_scrambleAcc(acc, secret + secretSize - XXH_STRIPE_LEN); + } + + int nbStripes = ((len - 1) - (block_len * nb_blocks)) / XXH_STRIPE_LEN; + XXH3_accumulate(acc, input + nb_blocks * block_len, secret, nbStripes); + + byte* p = input + len - XXH_STRIPE_LEN; + XXH3_accumulate_512(acc, p, secret + secretSize - XXH_STRIPE_LEN - XXH_SECRET_LASTACC_START); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong XXH3_mergeAccs(ulong* acc, byte* secret, ulong start) + { + ulong result64 = start; + + for (int i = 0; i < 4; i++) + result64 += XXH3_mix2Accs(acc + 2 * i, secret + 16 * i); + + return XXH3_avalanche(result64); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe ulong XXH3_mix2Accs(ulong* acc, byte* secret) + { + return XXH3_mul128_fold64( + acc[0] ^ XXH_readLE64(secret), + acc[1] ^ XXH_readLE64(secret+8) ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_accumulate(ulong* acc, byte* input, byte* secret, int nbStripes) + { + for (int n = 0; n < nbStripes; n++ ) { + byte* inp = input + n * XXH_STRIPE_LEN; + XXH3_accumulate_512(acc, inp, secret + n * XXH_SECRET_CONSUME_RATE); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_accumulate_512(ulong* acc, byte* input, byte* secret) + { + // TODO: SIMD + + XXH3_accumulate_512_scalar(acc, input, secret); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_accumulate_512_scalar(ulong* acc, byte* input, byte* secret) + { + for (int i = 0; i < XXH_ACC_NB; i++) + XXH3_scalarRound(acc, input, secret, i); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_scalarRound(ulong* acc, byte* input, byte* secret, int lane) + { + Debug.Assert(lane < XXH_ACC_NB); + + ulong* xacc = acc; + byte* xinput = input; + byte* xsecret = secret; + + ulong data_val = XXH_readLE64(xinput + lane * 8); + ulong data_key = data_val ^ XXH_readLE64(xsecret + lane * 8); + xacc[lane ^ 1] += data_val; + xacc[lane] += XXH_mult32to64(data_key & 0xFFFFFFFF, data_key >> 32); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_scrambleAcc(ulong* acc, byte* secret) + { + // TODO: SIMD + + XXH3_scrambleAcc_scalar(acc, secret); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_scrambleAcc_scalar(ulong* acc, byte* secret) + { + for (int i = 0; i < XXH_ACC_NB; i++) + XXH3_scalarScrambleRound(acc, secret, i); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_scalarScrambleRound(ulong* acc, byte* secret, int lane) + { + Debug.Assert(lane < XXH_ACC_NB); + + ulong* xacc = acc; + byte* xsecret = secret; + + ulong key64 = XXH_readLE64(xsecret + lane * 8); + ulong acc64 = xacc[lane]; + acc64 = XXH_xorshift64(acc64, 47); + acc64 ^= key64; + acc64 *= XXH_PRIME32_1; + xacc[lane] = acc64; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_initCustomSecret(byte* customSecret, ulong seed) + { + // TODO: SIMD + + XXH3_initCustomSecret_scalar(customSecret, seed); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe void XXH3_initCustomSecret_scalar(byte* customSecret, ulong seed) + { + fixed (byte* kSecretPtr = &XXH3_SECRET[0]) + { + int nbRounds = XXH_SECRET_DEFAULT_SIZE / 16; + + for (int i = 0; i < nbRounds; i++) + { + ulong lo = XXH_readLE64(kSecretPtr + 16 * i) + seed; + ulong hi = XXH_readLE64(kSecretPtr + 16 * i + 8) - seed; + XXH_writeLE64((byte*) customSecret + 16 * i, lo); + XXH_writeLE64((byte*) customSecret + 16 * i + 8, hi); + } + } + } + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash128.XXH64.cs b/src/Standart.Hash.xxHash/xxHash128.XXH64.cs new file mode 100644 index 0000000..31349bf --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash128.XXH64.cs @@ -0,0 +1,20 @@ +// ReSharper disable InconsistentNaming + +using System.Runtime.CompilerServices; + +namespace Standart.Hash.xxHash +{ + public static partial class xxHash128 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ulong XXH64_avalanche(ulong hash) + { + hash ^= hash >> 33; + hash *= XXH_PRIME64_2; + hash ^= hash >> 29; + hash *= XXH_PRIME64_3; + hash ^= hash >> 32; + return hash; + } + } +} \ No newline at end of file diff --git a/src/Standart.Hash.xxHash/xxHash128.cs b/src/Standart.Hash.xxHash/xxHash128.cs new file mode 100644 index 0000000..0215134 --- /dev/null +++ b/src/Standart.Hash.xxHash/xxHash128.cs @@ -0,0 +1,104 @@ +// ReSharper disable InconsistentNaming + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Standart.Hash.xxHash +{ + public static partial class xxHash128 + { + /// + /// Compute xxHash for the data byte array + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe uint128 ComputeHash(byte[] data, int length, ulong seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* ptr = &data[0]) + { + return UnsafeComputeHash(ptr, length, seed); + } + } + + /// + /// Compute xxHash for the data byte span + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe uint128 ComputeHash(Span data, int length, uint seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* ptr = &data[0]) + { + return UnsafeComputeHash(ptr, length, seed); + } + } + + /// + /// Compute xxHash for the data byte span + /// + /// The source of data + /// The length of the data for hashing + /// The seed number + /// hash + public static unsafe uint128 ComputeHash(ReadOnlySpan data, int length, uint seed = 0) + { + Debug.Assert(data != null); + Debug.Assert(length >= 0); + Debug.Assert(length <= data.Length); + + fixed (byte* ptr = &data[0]) + { + return UnsafeComputeHash(ptr, length, seed); + } + } + + /// + /// Compute xxHash for the data byte span + /// + /// The source of data + /// The seed number + /// hash + public static unsafe uint128 ComputeHash(string str, uint seed = 0) + { + Debug.Assert(str != null); + + fixed (char* c = str) + { + byte* ptr = (byte*) c; + int length = str.Length; + + return UnsafeComputeHash(ptr, length, seed); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static unsafe uint128 UnsafeComputeHash(byte* input, int len, ulong seed) + { + fixed (byte* secret = &XXH3_SECRET[0]) + { + return XXH3_128bits_internal(input, len, seed, secret, XXH3_SECRET_DEFAULT_SIZE); + } + } + } + + [StructLayout(LayoutKind.Sequential)] + public struct uint128 + { + public ulong low64; + public ulong high64; + } +} \ No newline at end of file