1
// This file is covered by an MIT Copyright in the same directory.
3
// Its project home is https://code.google.com/p/rdestl/ where it
4
// exists as a small STL replacement. It has been modified slightly
5
// by jmarantz@google.com to make it work better with normal STL:
7
// 1. references to rdp::pair has been replaced by std::pair as that
8
// class is exposed at the interface.
9
// 2. Tabs & trailing whitespace were removed and the file re-indented by Emacs.
11
#ifndef RDESTL_HASH_MAP_H
12
#define RDESTL_HASH_MAP_H
14
#include <inttypes.h> // for uintptr_t
15
#include <utility> // for std::pair
16
#include "algorithm.h"
17
#include "allocator.h"
18
#include "functional.h"
24
// TLoadFactor4 - controls hash map load. 4 means 100% load, ie. hashmap will grow
25
// when number of items == capacity. Default value of 6 means it grows when
26
// number of items == capacity * 3/2 (6/4). Higher load == tighter maps, but bigger
27
// risk of collisions.
28
template<typename TKey, typename TValue,
29
class THashFunc = rde::hash<TKey>,
31
class TKeyEqualFunc = rde::equal_to<TKey>,
32
class TAllocator = rde::allocator>
36
typedef std::pair<TKey, TValue> value_type;
41
static const hash_value_t kUnusedHash = 0xFFFFFFFF;
42
static const hash_value_t kDeletedHash = 0xFFFFFFFE;
44
node(): hash(kUnusedHash) {}
46
RDE_FORCEINLINE bool is_unused() const { return hash == kUnusedHash; }
47
RDE_FORCEINLINE bool is_deleted() const { return hash == kDeletedHash; }
48
RDE_FORCEINLINE bool is_occupied() const { return hash < kDeletedHash; }
53
template<typename TNodePtr, typename TPtr, typename TRef>
56
friend class hash_map;
58
typedef forward_iterator_tag iterator_category;
60
explicit node_iterator(TNodePtr node, const hash_map* map)
64
template<typename UNodePtr, typename UPtr, typename URef>
65
node_iterator(const node_iterator<UNodePtr, UPtr, URef>& rhs)
70
TRef operator*() const
72
RDE_ASSERT(m_node != 0);
75
TPtr operator->() const
79
RDE_FORCEINLINE TNodePtr node() const
84
node_iterator& operator++()
86
RDE_ASSERT(m_node != 0);
88
move_to_next_occupied_node();
91
node_iterator operator++(int)
93
node_iterator copy(*this);
98
RDE_FORCEINLINE bool operator==(const node_iterator& rhs) const
100
return rhs.m_node == m_node;
102
bool operator!=(const node_iterator& rhs) const
104
return !(rhs == *this);
107
const hash_map* get_map() const { return m_map; }
109
void move_to_next_occupied_node()
111
// @todo: save nodeEnd in constructor?
112
TNodePtr nodeEnd = m_map->m_nodes + m_map->bucket_count();
113
for (/**/; m_node < nodeEnd; ++m_node)
115
if (m_node->is_occupied())
120
const hash_map* m_map;
124
typedef TKey key_type;
125
typedef TValue mapped_type;
126
typedef TAllocator allocator_type;
127
typedef node_iterator<node*, value_type*, value_type&> iterator;
128
typedef node_iterator<const node*, const value_type*, const value_type&> const_iterator;
129
typedef int size_type;
130
static const size_type kNodeSize = sizeof(node);
131
static const size_type kInitialCapacity = 64;
134
: m_nodes(&ms_emptyNode),
140
RDE_ASSERT((kInitialCapacity & (kInitialCapacity - 1)) == 0); // Must be power-of-two
142
explicit hash_map(const allocator_type& allocator)
143
: m_nodes(&ms_emptyNode),
148
m_allocator(allocator)
152
explicit hash_map(size_type initial_bucket_count,
153
const allocator_type& allocator = allocator_type())
154
: m_nodes(&ms_emptyNode),
159
m_allocator(allocator)
161
reserve(initial_bucket_count);
163
hash_map(size_type initial_bucket_count,
164
const THashFunc& hashFunc,
165
const allocator_type& allocator = allocator_type())
166
: m_nodes(&ms_emptyNode),
171
m_hashFunc(hashFunc),
172
m_allocator(allocator)
174
reserve(initial_bucket_count);
176
hash_map(const hash_map& rhs, const allocator_type& allocator = allocator_type())
177
: m_nodes(&ms_emptyNode),
182
m_allocator(allocator)
186
explicit hash_map(e_noinitialize)
197
iterator it(m_nodes, this);
198
it.move_to_next_occupied_node();
201
const_iterator begin() const
203
const_iterator it(m_nodes, this);
204
it.move_to_next_occupied_node();
207
iterator end() { return iterator(m_nodes + m_capacity, this); }
208
const_iterator end() const { return const_iterator(m_nodes + m_capacity, this); }
210
// @note: Added for compatiblity sake.
211
// Personally, I consider it "risky". Use find/insert for more
212
// explicit operations.
213
mapped_type& operator[](const key_type& key)
216
node* n = find_for_insert(key, &hash);
217
if (n == 0 || !n->is_occupied())
219
return insert_at(value_type(key, TValue()), n, hash).first->second;
221
return n->data.second;
223
// @note: Doesn't copy allocator.
224
hash_map& operator=(const hash_map& rhs)
226
RDE_ASSERT(invariant());
230
if (m_capacity < rhs.bucket_count())
233
m_nodes = allocate_nodes(rhs.bucket_count());
234
m_capacity = rhs.bucket_count();
235
m_capacityMask = m_capacity - 1;
237
rehash(m_capacity, m_nodes, rhs.m_capacity, rhs.m_nodes, false);
239
m_numUsed = rhs.m_numUsed;
241
RDE_ASSERT(invariant());
244
void swap(hash_map& rhs)
248
RDE_ASSERT(invariant());
249
RDE_ASSERT(m_allocator == rhs.m_allocator);
250
rde::swap(m_nodes, rhs.m_nodes);
251
rde::swap(m_size, rhs.m_size);
252
rde::swap(m_capacity, rhs.m_capacity);
253
rde::swap(m_capacityMask, rhs.m_capacityMask);
254
rde::swap(m_numUsed, rhs.m_numUsed);
255
rde::swap(m_hashFunc, rhs.m_hashFunc);
256
rde::swap(m_keyEqualFunc, rhs.m_keyEqualFunc);
257
RDE_ASSERT(invariant());
261
std::pair<iterator, bool> insert(const value_type& v)
263
typedef std::pair<iterator, bool> ret_type_t;
264
RDE_ASSERT(invariant());
265
if (m_numUsed * TLoadFactor4 >= m_capacity * 4)
268
hash_value_t hash = 0;
269
node* n = find_for_insert(v.first, &hash);
270
if (n->is_occupied())
272
RDE_ASSERT(hash == n->hash && m_keyEqualFunc(v.first, n->data.first));
273
return ret_type_t(iterator(n, this), false);
277
rde::copy_construct(&n->data, v);
280
RDE_ASSERT(invariant());
281
return ret_type_t(iterator(n, this), true);
284
size_type erase(const key_type& key)
286
node* n = lookup(key);
287
if (n != (m_nodes + m_capacity) && n->is_occupied())
294
void erase(iterator it)
296
RDE_ASSERT(it.get_map() == this);
299
RDE_ASSERT(!empty());
300
erase_node(it.node());
303
void erase(iterator from, iterator to)
305
for (/**/; from != to; ++from)
307
node* n = from.node();
308
if (n->is_occupied())
313
iterator find(const key_type& key)
315
node* n = lookup(key);
316
return iterator(n, this);
318
const_iterator find(const key_type& key) const
320
const node* n = lookup(key);
321
return const_iterator(n, this);
326
node* endNode = m_nodes + m_capacity;
327
for (node* iter = m_nodes; iter != endNode; ++iter)
331
if (iter->is_occupied())
333
rde::destruct(&iter->data);
335
// We can make them unused, because we clear whole hash_map,
336
// so we can guarantee there'll be no holes.
337
iter->hash = node::kUnusedHash;
344
void reserve(size_type min_size)
346
size_type newCapacity = (m_capacity == 0 ? kInitialCapacity : m_capacity);
347
while (newCapacity < min_size)
349
if (newCapacity > m_capacity)
353
size_type bucket_count() const { return m_capacity; }
354
size_type size() const { return m_size; }
355
size_type empty() const { return size() == 0; }
356
size_type nonempty_bucket_count() const { return m_numUsed; }
357
size_type used_memory() const
359
return bucket_count() * kNodeSize;
362
const allocator_type& get_allocator() const { return m_allocator; }
363
void set_allocator(const allocator_type& allocator)
365
m_allocator = allocator;
371
const int newCapacity = (m_capacity == 0 ? kInitialCapacity : m_capacity * 2);
374
void grow(int new_capacity)
376
RDE_ASSERT((new_capacity & (new_capacity - 1)) == 0); // Must be power-of-two
377
node* newNodes = allocate_nodes(new_capacity);
378
rehash(new_capacity, newNodes, m_capacity, m_nodes, true);
379
if (m_nodes != &ms_emptyNode)
380
m_allocator.deallocate(m_nodes, sizeof(node) * m_capacity);
381
m_capacity = new_capacity;
382
m_capacityMask = new_capacity - 1;
385
RDE_ASSERT(m_numUsed < m_capacity);
387
std::pair<iterator, bool> insert_at(const value_type& v, node* n,
390
RDE_ASSERT(invariant());
391
if (n == 0 || m_numUsed * TLoadFactor4 >= m_capacity * 4)
394
RDE_ASSERT(!n->is_occupied());
397
rde::copy_construct(&n->data, v);
400
RDE_ASSERT(invariant());
401
return std::pair<iterator, bool>(iterator(n, this), true);
403
node* find_for_insert(const key_type& key, hash_value_t* out_hash)
408
const hash_value_t hash = hash_func(key);
410
uint32 i = hash & m_capacityMask;
412
node* n = m_nodes + i;
413
if (n->hash == hash && m_keyEqualFunc(key, n->data.first))
420
// Guarantees loop termination.
421
RDE_ASSERT(m_numUsed < m_capacity);
422
while (!n->is_unused())
425
i = (i + numProbes) & m_capacityMask;
427
if (compare_key(n, key, hash))
429
if (n->is_deleted() && freeNode == 0)
432
return freeNode ? freeNode : n;
434
node* lookup(const key_type& key) const
436
const hash_value_t hash = hash_func(key);
437
uint32 i = hash & m_capacityMask;
438
node* n = m_nodes + i;
439
if (n->hash == hash && m_keyEqualFunc(key, n->data.first))
443
// Guarantees loop termination.
444
RDE_ASSERT(m_capacity == 0 || m_numUsed < m_capacity);
445
while (!n->is_unused())
448
i = (i + numProbes) & m_capacityMask;
451
if (compare_key(n, key, hash))
454
return m_nodes + m_capacity;
457
static void rehash(int new_capacity, node* new_nodes,
458
int capacity, const node* nodes, bool destruct_original)
460
//if (nodes == &ms_emptyNode || new_nodes == &ms_emptyNode)
463
const node* it = nodes;
464
const node* itEnd = nodes + capacity;
465
const uint32 mask = new_capacity - 1;
468
if (it->is_occupied())
470
const hash_value_t hash = it->hash;
471
uint32 i = hash & mask;
473
node* n = new_nodes + i;
475
while (!n->is_unused())
478
i = (i + numProbes) & mask;
481
rde::copy_construct(&n->data, it->data);
483
if (destruct_original)
484
rde::destruct(&it->data);
490
node* allocate_nodes(int n)
492
node* buckets = static_cast<node*>(m_allocator.allocate(n * sizeof(node)));
493
node* iterBuckets(buckets);
494
node* end = iterBuckets + n;
495
for (/**/; iterBuckets != end; ++iterBuckets)
496
iterBuckets->hash = node::kUnusedHash;
503
node* itEnd = it + m_capacity;
506
if (it && it->is_occupied())
507
rde::destruct(&it->data);
510
if (m_nodes != &ms_emptyNode)
511
m_allocator.deallocate(m_nodes, sizeof(node) * m_capacity);
517
void erase_node(node* n)
519
RDE_ASSERT(!empty());
520
RDE_ASSERT(n->is_occupied());
521
rde::destruct(&n->data);
522
n->hash = node::kDeletedHash;
526
RDE_FORCEINLINE hash_value_t hash_func(const key_type& key) const
528
const hash_value_t h = m_hashFunc(key) & 0xFFFFFFFD;
529
//RDE_ASSERT(h < node::kDeletedHash);
532
bool invariant() const
534
RDE_ASSERT((m_capacity & (m_capacity - 1)) == 0);
535
RDE_ASSERT(m_numUsed >= m_size);
539
RDE_FORCEINLINE bool compare_key(const node* n, const key_type& key, hash_value_t hash) const
541
return (n->hash == hash && m_keyEqualFunc(key, n->data.first));
547
uint32 m_capacityMask;
549
THashFunc m_hashFunc;
550
TKeyEqualFunc m_keyEqualFunc;
551
TAllocator m_allocator;
553
static node ms_emptyNode;
558
template<typename TKey, typename TValue,
563
typename hash_map<TKey, TValue, THashFunc, TLoadFactor4, TKeyEqualFunc, TAllocator>::node hash_map<TKey, TValue, THashFunc, TLoadFactor4, TKeyEqualFunc, TAllocator>::ms_emptyNode;