1
0
mirror of https://frontier.innolan.net/github/AmigaExamples.git synced 2026-05-10 09:28:00 +00:00
Files
AmigaExamples/tools/external/shrinkler/CuckooHash.h
2016-03-15 16:25:03 +11:00

240 lines
5.1 KiB
C++

// Copyright 1999-2015 Aske Simon Christensen. See LICENSE.txt for usage terms.
/*
Cuckoo hash map. Used for mapping offsets to edges in the LZ parser.
*/
#pragma once
#include <utility>
#include <algorithm>
#include <new>
using std::pair;
template <typename V> class CuckooHash;
template <typename V>
class CuckooHashIterator {
const CuckooHash<V>* table;
int index;
CuckooHashIterator(const CuckooHash<V>* table, int index) : table(table), index(index)
{}
void find() {
while (table->element_array[index].first == CuckooHash<V>::UNUSED) index++;
}
friend class CuckooHash<V>;
public:
pair<int, V>& operator*() {
find();
return table->element_array[index];
}
pair<int, V>* operator->() {
find();
return &table->element_array[index];
}
CuckooHashIterator<V> operator++(int) {
find();
return CuckooHashIterator<V>(table, index++);
}
bool operator!=(const CuckooHashIterator<V>& other) {
return index != other.index;
}
};
template <typename V>
class CuckooHash {
public:
typedef int key_type;
typedef pair<key_type, V> value_type;
typedef CuckooHashIterator<V> iterator;
private:
friend class CuckooHashIterator<V>;
typedef unsigned hash_type;
static const key_type UNUSED = 0x80000000;
static const hash_type HASH1_MUL = 0xF230D3A1;
static const hash_type HASH2_MUL = 0x8084027F;
static const int INITIAL_SIZE_LOG = 2;
value_type* element_array;
unsigned n_elements:26;
unsigned hash_shift:6;
int array_size() const {
return 1 << (sizeof(hash_type) * 8 - hash_shift);
}
void init_array() {
int size = array_size();
element_array = new value_type[size];
for (int i = 0 ; i < size ; i++) {
element_array[i].first = UNUSED;
element_array[i].second = V();
}
}
value_type* get_array() {
if (element_array == NULL) {
init_array();
}
return element_array;
}
void init() {
n_elements = 0;
hash_shift = sizeof(hash_type) * 8 - INITIAL_SIZE_LOG;
element_array = NULL;
}
void hashes(key_type key, hash_type& hash1, hash_type& hash2) const {
hash_type f = (key << 1) + 1;
hash1 = (f * HASH1_MUL) >> hash_shift;
hash2 = (f * HASH2_MUL) >> hash_shift;
}
void rehash() {
int old_size = array_size();
value_type* old_array = get_array();
n_elements = 0;
hash_shift--;
init_array();
for (int i = 0 ; i < old_size ; i++) {
if (old_array[i].first != UNUSED) {
(*this)[old_array[i].first] = old_array[i].second;
}
}
delete[] old_array;
}
void insert(hash_type hash, int key, V value, int n) {
value_type* array = get_array();
while (array[hash].first != UNUSED) {
if (--n < 0) {
rehash();
(*this)[key] = value;
return;
}
std::swap(key, array[hash].first);
std::swap(value, array[hash].second);
hash_type hash1;
hash_type hash2;
hashes(key, hash1, hash2);
hash ^= hash1 ^ hash2;
}
array[hash].first = key;
array[hash].second = value;
n_elements++;
}
public:
CuckooHash() {
init();
}
CuckooHash(const CuckooHash& source) {
// We only use copy for array initialization, so just create an empty map
init();
}
~CuckooHash() {
delete[] element_array;
}
void clear() {
delete[] element_array;
init();
}
iterator begin() const {
return CuckooHashIterator<V>(this, 0);
}
iterator end() const {
if (element_array == NULL) {
// Empty
return CuckooHashIterator<V>(this, 0);
}
int index = array_size();
assert(element_array != NULL);
value_type* array = element_array;
while (index > 0 && array[index - 1].first == UNUSED) index--;
return CuckooHashIterator<V>(this, index);
}
int size() const {
return n_elements;
}
bool empty() const {
return size() == 0;
}
int count(int key) const {
if (empty()) return 0;
hash_type hash1;
hash_type hash2;
hashes(key, hash1, hash2);
assert(element_array != NULL);
value_type* array = element_array;
if (array[hash1].first == key || array[hash2].first == key) return 1;
return 0;
}
void erase(int key) {
hash_type hash1;
hash_type hash2;
hashes(key, hash1, hash2);
value_type* array = get_array();
hash_type hash;
if (array[hash1].first == key) {
hash = hash1;
} else if (array[hash2].first == key) {
hash = hash2;
} else {
return;
}
array[hash].first = UNUSED;
array[hash].second = V();
n_elements--;
}
V& operator[](int key) {
hash_type hash1;
hash_type hash2;
hashes(key, hash1, hash2);
value_type* array = get_array();
if (array[hash1].first == key) return array[hash1].second;
if (array[hash2].first == key) return array[hash2].second;
if (array[hash1].first == UNUSED) {
array[hash1].first = key;
array[hash1].second = V();
n_elements++;
return array[hash1].second;
}
if (array[hash2].first == UNUSED) {
array[hash2].first = key;
array[hash2].second = V();
n_elements++;
return array[hash2].second;
}
insert(hash1, key, V(), n_elements);
return (*this)[key];
}
};