// This file is not GPL. It may be used for educational purposes only. #pragma once #include template class MatchHasher { public: MatchHasher() {} enum { NumHash = _NumHash, DualHash = _DualHash, }; ~MatchHasher() { free(malloced_ptr_); } void AllocateHash(int bits, int k) { hash_bits_ = bits; hash_mask_ = (1 << bits) - NumHash; k = std::max(std::min(k > 0 ? k : 4, 8), 1); hashmult_ = 0xCF1BBCDCB7A56463ull << (8 * (8 - k)); malloced_ptr_ = malloc(sizeof(uint32) * (1 << bits) + 64); hash_ptr_ = (uint32*)(((uintptr_t)malloced_ptr_ + 63) & ~63); memset(hash_ptr_, 0, sizeof(uint32) * (1 << bits)); } struct HashPos { uint32 *ptr1, *ptr2; uint32 pos, hi; }; HashPos GetHashPos(const uint8 *src) { HashPos hp = {hashentry_ptr_next_, hashentry2_ptr_next_, (uint32)(src - src_base_), hashtable_high_bits_}; return hp; } void SetBaseWithoutPreload(const uint8 *src_base) { src_base_ = src_base; } void SetBaseAndPreload(const uint8 *src_base, const uint8 *src_start, int max_preload_len) { src_base_ = src_base; if (src_base == src_start) return; int preload_len = src_start - src_base; const uint8 *src = src_base; assert(preload_len > 0); if (preload_len > max_preload_len) { preload_len = max_preload_len; src = src_start - preload_len; } int step = std::max(preload_len >> 18, 2); int rounds_until_next_step = (preload_len >> 1) / step; SetHashPos(src); for (;;) { if (--rounds_until_next_step <= 0) { if (src >= src_start) break; step >>= 1; assert(step >= 1); rounds_until_next_step = (src_start - src) / step; if (step > 1) rounds_until_next_step >>= 1; } HashPos hp = GetHashPos(src); src += step; SetHashPos(src); Insert(hp); } } static uint32 MakeHashValue(uint32 cur_high, uint32 cur_pos) { return (cur_high & ~0x3FFFFFF) | (cur_pos & 0x3FFFFFF); } inline void InsertOne(uint32 *h, uint32 hval) { if (NumHash == 1) { h[0] = hval; } else if (NumHash == 2) { h[1] = h[0]; h[0] = hval; } else if (NumHash == 4) { uint32 a = h[2], b = h[1], c = h[0]; h[3] = a; h[2] = b; h[1] = c; h[0] = hval; } else if (NumHash == 16) { __m128i a0 = _mm_load_si128((__m128i*)h); __m128i a1 = _mm_loadu_si128((__m128i*)(h + 3)); __m128i a2 = _mm_loadu_si128((__m128i*)(h + 7)); __m128i a3 = _mm_loadu_si128((__m128i*)(h + 11)); _mm_storeu_si128((__m128i*)(h + 1), a0); _mm_store_si128((__m128i*)(h + 4), a1); _mm_store_si128((__m128i*)(h + 8), a2); _mm_store_si128((__m128i*)(h + 12), a3); h[0] = hval; } else { assert(0); } } inline void Insert(uint32 *h, uint32 *h2, uint32 he) { InsertOne(h, he); if (DualHash) InsertOne(h2, he); } inline void Insert(const HashPos &hp) { Insert(hp.ptr1, hp.ptr2, MakeHashValue(hp.hi, hp.pos)); } inline void SetHashPos(const uint8 *p) { src_cur_ = p; uint64 at_src = *(uint64*)p; uint32 hash1 = Rol32((uint32)((hashmult_ * at_src) >> 32), hash_bits_); hashtable_high_bits_ = hash1; hashentry_ptr_next_ = &hash_ptr_[hash1 & hash_mask_]; if (DualHash) { uint32 hash2 = (0xCF1BBCDCB7A56463ull * at_src) >> (64 - hash_bits_); hashentry2_ptr_next_ = &hash_ptr_[hash2 & ~(NumHash - 1)]; } } inline void SetHashPosPrefetch(const uint8 *p) { SetHashPos(p); _mm_prefetch((char*)hashentry_ptr_next_, _MM_HINT_T0); if (DualHash) _mm_prefetch((char*)hashentry2_ptr_next_, _MM_HINT_T0); } void Reset(const uint8 *p) { memset(hash_ptr_, 0, sizeof(uint32) * (1 << hash_bits_)); src_base_ = p; } void InsertRange(const uint8 *p, size_t len) { if (src_cur_ < p + len) { const uint8 *src_base = src_base_; Insert(hashentry_ptr_next_, hashentry2_ptr_next_, MakeHashValue(hashtable_high_bits_, src_cur_ - src_base)); for (int i = src_cur_ - p + 1; i < len; i *= 2) { uint32 hash = Rol32((uint32)((hashmult_ * *(uint64*)(p + i)) >> 32), hash_bits_); InsertOne(&hash_ptr_[hash & hash_mask_], MakeHashValue(hash, p + i - src_base)); } SetHashPos(p + len); } else if (src_cur_ != p + len) { SetHashPos(p + len); } } public: void *malloced_ptr_ = 0; uint32 *hash_ptr_ = 0; int hash_bits_; uint32 hash_mask_; const uint8 *src_base_ = 0; const uint8 *src_cur_ = 0; uint32 *hashentry_ptr_next_ = 0; uint32 *hashentry2_ptr_next_ = 0; uint64 hashmult_; uint32 hashtable_high_bits_; }; class MatchHasher2 { public: MatchHasher2() {} ~MatchHasher2() { delete[] firsthash_; delete[] longhash_; delete[] nexthash_; } void AllocateHash(int bits, int min_match_len) { int a_bits = bits, b_bits = bits, c_bits = 16; firsthash_bits_ = a_bits = std::min(a_bits, 19); longhash_bits_ = b_bits = std::min(b_bits, 19); firsthash_mask_ = (1 << a_bits) - 1; longhash_mask_ = (1 << b_bits) - 1; nexthash_mask_ = (1 << c_bits) - 1; firsthash_ = new uint32[1 << a_bits]; longhash_ = new uint32[1 << b_bits]; nexthash_ = new uint16[1 << c_bits]; memset(firsthash_, 0, sizeof(uint32) * (1 << a_bits)); memset(longhash_, 0, sizeof(uint32) * (1 << b_bits)); memset(nexthash_, 0, sizeof(uint16) * (1 << c_bits)); } struct HashPos { uint32 pos; uint32 hash_a, hash_b, hash_b_hi; const uint8 *next_p; }; HashPos GetHashPos(const uint8 *src) { uint64 at_src = *(uint64*)src; uint32 hash_a = (uint32)((0xB7A5646300000000ull * at_src) >> 32); uint32 hash_b = (uint32)((0xCF1BBCDCB7A56463ull * at_src) >> 32); HashPos hp = { (uint32)(src - src_base_), hash_a >> (32 - firsthash_bits_), hash_b >> (32 - longhash_bits_), hash_b, src + 1 }; return hp; } void SetBaseWithoutPreload(const uint8 *src_base) { src_base_ = src_base; } void SetBaseAndPreload(const uint8 *src_base, const uint8 *src_start, int max_preload_len) { src_base_ = src_base; if (src_base == src_start) return; int preload_len = src_start - src_base; const uint8 *src = src_base; assert(preload_len > 0); if (preload_len > max_preload_len) { preload_len = max_preload_len; src = src_start - preload_len; } int step = std::max(preload_len >> 18, 2); int rounds_until_next_step = (preload_len >> 1) / step; SetHashPos(src); for (;;) { if (--rounds_until_next_step <= 0) { if (src >= src_start) break; step >>= 1; assert(step >= 1); rounds_until_next_step = (src_start - src) / step; if (step > 1) rounds_until_next_step >>= 1; } HashPos hp = GetHashPos(src); src += step; SetHashPos(src); firsthash_[hp.hash_a] = hp.pos; longhash_[hp.hash_b] = (hp.hash_b_hi & 0x3F) | (hp.pos << 6); } } static uint32 MakeHashValue(uint32 cur_high, uint32 cur_pos) { return (cur_high & ~0x3FFFFFF) | (cur_pos & 0x3FFFFFF); } inline void Insert(const HashPos &hp) { nexthash_[hp.pos & nexthash_mask_] = firsthash_[hp.hash_a]; firsthash_[hp.hash_a] = hp.pos; src_cur_ = hp.next_p; } inline void SetHashPos(const uint8 *p) { src_cur_ = p; } inline void SetHashPosPrefetch(const uint8 *p) { HashPos hp = GetHashPos(p); _mm_prefetch((char*)&firsthash_[hp.hash_a], _MM_HINT_T0); _mm_prefetch((char*)&longhash_[hp.hash_b], _MM_HINT_T0); } void InsertRange(const uint8 *p, size_t len) { for (int i = 0; i < len; i = 2 * i + 1) { uint32 hash_b = (uint32)((0xCF1BBCDCB7A56463ull * *(uint64*)(p + i)) >> 32); longhash_[hash_b >> (32 - longhash_bits_)] = (hash_b & 0x3F) | ((p + i - src_base_) << 6); } const uint8 *p_end = p + len; while (src_cur_ < p_end) { uint32 hash_a = (uint32)((0xB7A5646300000000ull * *(uint64*)src_cur_) >> 32) >> (32 - firsthash_bits_); uint32 pos = src_cur_ - src_base_; nexthash_[pos & nexthash_mask_] = firsthash_[hash_a]; firsthash_[hash_a] = pos; src_cur_++; } } public: uint32 *firsthash_ = 0; uint32 *longhash_ = 0; uint16 *nexthash_ = 0; const uint8 *src_base_ = 0; const uint8 *src_cur_ = 0; uint32 firsthash_mask_; uint32 longhash_mask_; uint32 nexthash_mask_; uint8 firsthash_bits_; uint8 longhash_bits_; }; template class FastMatchHasher { public: typedef T ElemType; void AllocateHash(int bits, int k) { hash_bits_ = bits; if (k == 0) k = 4; if (k >= 5 && k <= 8) { hashmult_ = 0xCF1BBCDCB7A56463ull << (8 * (8 - k)); } else { hashmult_ = 0x9E3779B100000000ull; } malloced_ptr_ = malloc(sizeof(T) * (1 << bits) + 64); hash_ptr_ = (T*)(((uintptr_t)malloced_ptr_ + 63) & ~63); memset(hash_ptr_, 0, sizeof(T) * (1 << bits)); } void SetBaseWithoutPreload(const uint8 *p) { src_base_ = p; } void SetBaseAndPreload(const uint8 *src_base, const uint8 *src_start, int max_preload_len) { src_base_ = src_base; if (src_base == src_start) return; const uint8 *src = src_base; int preload_len = src_start - src_base; assert(preload_len > 0); if (preload_len > max_preload_len) { preload_len = max_preload_len; src = src_start - preload_len; } int step = std::max(preload_len >> 18, 2); int rounds_until_next_step = (preload_len >> 1) / step; T *hash_ptr = hash_ptr_; uint64 hashmult = hashmult_; int hashshift = 64 - hash_bits_; for (;;) { if (--rounds_until_next_step <= 0) { if (src >= src_start) break; step >>= 1; assert(step >= 1); rounds_until_next_step = (src_start - src) / step; if (step > 1) rounds_until_next_step >>= 1; } hash_ptr[(size_t)(*(uint64*)src * hashmult >> hashshift)] = src - src_base; src += step; } } void *malloced_ptr_ = NULL; T *hash_ptr_ = NULL; const uint8 *src_base_; uint64 hashmult_; int hash_bits_; };