Roc Toolkit internal modules
Roc Toolkit: real-time audio streaming
Toggle main menu visibility
Loading...
Searching...
No Matches
hashmap.h
Go to the documentation of this file.
1
/*
2
* Copyright (c) 2020 Roc Streaming authors
3
*
4
* This Source Code Form is subject to the terms of the Mozilla Public
5
* License, v. 2.0. If a copy of the MPL was not distributed with this
6
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
7
*/
8
9
//! @file roc_core/hashmap.h
10
//! @brief Intrusive hash table.
11
12
#ifndef ROC_CORE_HASHMAP_H_
13
#define ROC_CORE_HASHMAP_H_
14
15
#include "
roc_core/aligned_storage.h
"
16
#include "
roc_core/attributes.h
"
17
#include "
roc_core/hashmap_impl.h
"
18
#include "
roc_core/hashmap_node.h
"
19
#include "
roc_core/hashsum.h
"
20
#include "
roc_core/iarena.h
"
21
#include "
roc_core/macro_helpers.h
"
22
#include "
roc_core/noncopyable.h
"
23
#include "
roc_core/ownership_policy.h
"
24
#include "
roc_core/panic.h
"
25
#include "
roc_core/stddefs.h
"
26
27
namespace
roc
{
28
namespace
core
{
29
30
//! Intrusive hash table.
31
//!
32
//! Characteristics:
33
//! 1) Intrusive. Hash table nodes are stored directly in elements. No allocations
34
//! are needed to insert a node. Arena is used only to allocate an array
35
//! of buckets.
36
//! 2) Collision-chaining. Implemented as an array of buckets, where a bucket is
37
//! the head of a doubly-linked lists of bucket elements.
38
//! 3) Controllable allocations. Allocations and deallocations are performed only
39
//! when the hash table is explicitly growed. All other operations don't touch
40
//! arena.
41
//! 4) Zero allocations for small hash tables. A fixed number of buckets can be
42
//! embedded directly into hash table object.
43
//! 5) Incremental rehashing. After hash table growth, rehashing is performed
44
//! incrementally when inserting and removing elements. The slower hash table
45
//! size growth is, the less overhead rehashing adds to each operation.
46
//! 6) Allows to iterate elements in insertion order. Implements safe iteration with
47
//! regards to element insertion and deletion. Elements deleted during iteration
48
//! won't be visited. Elements inserted during iteration will be visited.
49
//!
50
//! Incremental rehashing technique is inspired by Go's map implementation, though
51
//! there are differences. Load factor value is taken from it as well.
52
//! Prime numbers for sizes are from https://planetmath.org/goodhashtableprimes.
53
//!
54
//! @tparam T defines object type, it must inherit HashmapNode and additionally
55
//! implement three methods:
56
//!
57
//! @code
58
//! // get object key
59
//! Key key() const;
60
//!
61
//! // compute key hash
62
//! static core::hashsum_t key_hash(Key key);
63
//!
64
//! // compare two keys for equality
65
//! static bool key_equal(Key key1, Key key2);
66
//! @endcode
67
//!
68
//! "Key" can be any type. Hashmap doesn't use it directly. It is never stored and
69
//! is always accessed via the three methods above. The hash is computed for a key
70
//! only once when an object is inserted into hash table.
71
//!
72
//! @tparam EmbeddedCapacity defines the capacity embedded directly into Hashmap.
73
//! It is used instead of dynamic memory while the number of elements is smaller
74
//! than this capacity. The actual object size occupied to provide the requested
75
//! capacity is implementation defined.
76
//!
77
//! @tparam OwnershipPolicy defines ownership policy which is used to acquire an element
78
//! ownership when it's added to the hashmap and release ownership when it's removed
79
//! from the hashmap.
80
//!
81
//! @tparam Node defines base class of hashmap nodes. It is needed if HashmapNode
82
//! is used with non-default tag.
83
template
<
class
T,
84
size_t
EmbeddedCapacity = 0,
85
template
<
class
TT>
class
OwnershipPolicy =
RefCountedOwnership
,
86
class
Node =
HashmapNode<>
>
87
class
Hashmap
:
public
NonCopyable<> {
88
public
:
89
//! Pointer type.
90
//! @remarks
91
//! either raw or smart pointer depending on the ownership policy.
92
typedef
typename
OwnershipPolicy<T>::Pointer
Pointer
;
93
94
//! Initialize empty hashmap with arena.
95
//! @remarks
96
//! Hashmap capacity may grow using arena.
97
explicit
Hashmap
(
IArena
& arena)
98
: impl_(embedded_buckets_.memory(), NumEmbeddedBuckets, arena) {
99
}
100
101
//! Release ownership of all elements.
102
~Hashmap
() {
103
HashmapData
* data = impl_.front();
104
105
while
(data != NULL) {
106
impl_.remove(data,
true
);
107
T* elem = from_node_data_(data);
108
OwnershipPolicy<T>::release(*elem);
109
data = impl_.front();
110
}
111
}
112
113
//! Get maximum number of elements that can be added to hashmap before
114
//! grow() should be called.
115
size_t
capacity
()
const
{
116
return
impl_.capacity();
117
}
118
119
//! Get number of elements added to hashmap.
120
size_t
size
()
const
{
121
return
impl_.size();
122
}
123
124
//! Check if size is zero.
125
bool
is_empty
()
const
{
126
return
size
() == 0;
127
}
128
129
//! Check if element belongs to hashmap.
130
//!
131
//! @note
132
//! - has O(1) complexity
133
//! - doesn't compute key hashes
134
bool
contains
(
const
T& elem)
const
{
135
const
HashmapData
* data = to_node_data_(elem);
136
return
impl_.contains(data);
137
}
138
139
//! Find element in the hashmap by key.
140
//!
141
//! @returns
142
//! Pointer to the element with given key or NULL if it's not found.
143
//!
144
//! @note
145
//! - has O(1) complexity in average and O(n) in the worst case
146
//! - computes key hash
147
//!
148
//! @note
149
//! The worst case is achieved when the hash function produces many collisions.
150
template
<
class
Key>
Pointer
find
(
const
Key& key)
const
{
151
const
hashsum_t
hash = T::key_hash(key);
152
HashmapData
* data = impl_.find_node(
153
hash, (
const
void
*)&key,
154
&Hashmap<T, EmbeddedCapacity, OwnershipPolicy, Node>::key_equal_<Key>);
155
if
(!data) {
156
return
NULL;
157
}
158
return
from_node_data_(data);
159
}
160
161
//! Get first element in hashmap.
162
//! Elements are ordered by insertion.
163
//! @returns
164
//! first element or NULL if hashmap is empty.
165
Pointer
front
()
const
{
166
HashmapData
* data = impl_.front();
167
if
(!data) {
168
return
NULL;
169
}
170
return
from_node_data_(data);
171
}
172
173
//! Get last element in hashmap.
174
//! Elements are ordered by insertion.
175
//! @returns
176
//! last element or NULL if hashmap is empty.
177
Pointer
back
()
const
{
178
HashmapData
*
node
= impl_.back();
179
if
(!
node
) {
180
return
NULL;
181
}
182
return
from_node_data_(
node
);
183
}
184
185
//! Get hashmap element next to given one.
186
//! Elements are ordered by insertion.
187
//!
188
//! @returns
189
//! hashmap element following @p elem if @p elem is not
190
//! last, or NULL otherwise.
191
//!
192
//! @pre
193
//! @p elem should be member of this hashmap.
194
Pointer
nextof
(T& elem)
const
{
195
HashmapData
* data = to_node_data_(elem);
196
HashmapData
* next_data = impl_.nextof(data);
197
if
(!next_data) {
198
return
NULL;
199
}
200
return
from_node_data_(next_data);
201
}
202
203
//! Get hashmap element previous to given one.
204
//! Elements are ordered by insertion.
205
//!
206
//! @returns
207
//! hashmap element preceding @p elem if @p elem is not
208
//! first, or NULL otherwise.
209
//!
210
//! @pre
211
//! @p elem should be member of this hashmap.
212
Pointer
prevof
(T& elem)
const
{
213
HashmapData
* data = to_node_data_(elem);
214
HashmapData
* prev_data = impl_.prevof(data);
215
if
(!prev_data) {
216
return
NULL;
217
}
218
return
from_node_data_(prev_data);
219
}
220
221
//! Insert element into hashmap.
222
//!
223
//! @remarks
224
//! - acquires ownership of @p elem
225
//!
226
//! @returns
227
//! false if the allocation failed
228
//!
229
//! @pre
230
//! - @p elem should not be member of any hashmap
231
//! - hashmap shouldn't have an element with the same key
232
//!
233
//! @note
234
//! - has O(1) complexity in average and O(n) in the worst case
235
//! - computes key hash
236
//! - doesn't make allocations or deallocations
237
//! - proceeds lazy rehashing
238
//!
239
//! @note
240
//! Insertion speed is higher when insert to remove ratio is close to one or lower,
241
//! and slows down when it becomes higher than one. The slow down is caused by
242
//! the incremental rehashing algorithm.
243
ROC_ATTR_NODISCARD
bool
insert
(T& elem) {
244
HashmapData
* data = to_node_data_(elem);
245
if
(!insert_(elem.key(), data)) {
246
return
false
;
247
}
248
OwnershipPolicy<T>::acquire(elem);
249
return
true
;
250
}
251
252
//! Remove element from hashmap.
253
//!
254
//! @remarks
255
//! - releases ownership of @p elem
256
//!
257
//! @pre
258
//! @p elem should be member of this hashmap.
259
//!
260
//! @note
261
//! - has O(1) complexity
262
//! - doesn't compute key hash
263
//! - doesn't make allocations or deallocations
264
//! - proceeds lazy rehashing
265
void
remove
(T& elem) {
266
HashmapData
* data = to_node_data_(elem);
267
impl_.remove(data,
false
);
268
OwnershipPolicy<T>::release(elem);
269
}
270
271
//! Grow hashtable capacity.
272
//!
273
//! @remarks
274
//! Check if hash table is full (size is equal to capacity), and if so, increase
275
//! hash table capacity and initiate incremental rehashing. Rehashing will be
276
//! performed during subsequent insertions and removals.
277
//!
278
//! @returns
279
//! - true if no growth needed or growth succeeded
280
//! - false if allocation failed
281
//!
282
//! @note
283
//! - has O(1) complexity
284
//! - doesn't computes key hashes
285
//! - makes allocations and deallocations
286
//! - doesn't proceed lazy rehashing
287
ROC_ATTR_NODISCARD
bool
grow
() {
288
return
impl_.grow();
289
}
290
291
private
:
292
enum
{
293
// how much buckets are embedded directly into Hashmap object
294
NumEmbeddedBuckets = ((int)(EmbeddedCapacity == 0 ? 0
295
: EmbeddedCapacity <= 16 ? 16
296
: EmbeddedCapacity)
297
* HashmapImpl::LoadFactorDen
298
+ HashmapImpl::LoadFactorNum - 1)
299
/ HashmapImpl::LoadFactorNum * 2
300
};
301
302
static
HashmapData* to_node_data_(
const
T& elem) {
303
return
static_cast<
const
Node&
>
(elem).hashmap_data();
304
}
305
306
static
T* from_node_data_(HashmapData* data) {
307
return
static_cast<
T*
>
(
static_cast<
Node*
>
(Node::hashmap_node(data)));
308
}
309
310
template
<
class
Key>
static
bool
key_equal_(HashmapData* node,
const
void
* key) {
311
T* elem = from_node_data_(node);
312
const
Key& key_ref = *(
const
Key*)key;
313
return
T::key_equal(elem->key(), key_ref);
314
}
315
316
template
<
class
Key>
bool
insert_(
const
Key& key, HashmapData* node) {
317
const
hashsum_t
hash = T::key_hash(key);
318
return
impl_.insert(
319
node, hash, (
const
void
*)&key,
320
&
Hashmap<T, EmbeddedCapacity, OwnershipPolicy, Node>::key_equal_<Key>
);
321
}
322
323
AlignedStorage<NumEmbeddedBuckets *
sizeof
(HashmapImpl::Bucket)> embedded_buckets_;
324
325
HashmapImpl impl_;
326
};
327
328
}
// namespace core
329
}
// namespace roc
330
331
#endif
// ROC_CORE_HASHMAP_H_
aligned_storage.h
Aligned storage.
attributes.h
Compiler attributes.
ROC_ATTR_NODISCARD
#define ROC_ATTR_NODISCARD
Emit warning if function result is not checked.
Definition
attributes.h:31
roc::core::HashmapNode
Base class for Hashmap element.
Definition
hashmap_node.h:61
roc::core::Hashmap::contains
bool contains(const T &elem) const
Check if element belongs to hashmap.
Definition
hashmap.h:134
roc::core::Hashmap::remove
void remove(T &elem)
Remove element from hashmap.
Definition
hashmap.h:265
roc::core::Hashmap::front
Pointer front() const
Get first element in hashmap. Elements are ordered by insertion.
Definition
hashmap.h:165
roc::core::Hashmap::nextof
Pointer nextof(T &elem) const
Get hashmap element next to given one. Elements are ordered by insertion.
Definition
hashmap.h:194
roc::core::Hashmap::grow
ROC_ATTR_NODISCARD bool grow()
Grow hashtable capacity.
Definition
hashmap.h:287
roc::core::Hashmap::size
size_t size() const
Get number of elements added to hashmap.
Definition
hashmap.h:120
roc::core::Hashmap::is_empty
bool is_empty() const
Check if size is zero.
Definition
hashmap.h:125
roc::core::Hashmap::Pointer
OwnershipPolicy< T >::Pointer Pointer
Pointer type.
Definition
hashmap.h:92
roc::core::Hashmap::find
Pointer find(const Key &key) const
Find element in the hashmap by key.
Definition
hashmap.h:150
roc::core::Hashmap::capacity
size_t capacity() const
Get maximum number of elements that can be added to hashmap before grow() should be called.
Definition
hashmap.h:115
roc::core::Hashmap::prevof
Pointer prevof(T &elem) const
Get hashmap element previous to given one. Elements are ordered by insertion.
Definition
hashmap.h:212
roc::core::Hashmap::back
Pointer back() const
Get last element in hashmap. Elements are ordered by insertion.
Definition
hashmap.h:177
roc::core::Hashmap::~Hashmap
~Hashmap()
Release ownership of all elements.
Definition
hashmap.h:102
roc::core::Hashmap::insert
ROC_ATTR_NODISCARD bool insert(T &elem)
Insert element into hashmap.
Definition
hashmap.h:243
roc::core::Hashmap::Hashmap
Hashmap(IArena &arena)
Initialize empty hashmap with arena.
Definition
hashmap.h:97
roc::core::IArena
Memory arena interface.
Definition
iarena.h:23
hashmap_impl.h
Intrusive hash table implementation file.
hashmap_node.h
Hashmap node.
hashsum.h
Hash sum.
iarena.h
Memory arena interface.
macro_helpers.h
Helper macros.
roc::core
General-purpose building blocks and platform abstraction layer.
roc::core::hashsum_t
size_t hashsum_t
Hash type.
Definition
hashsum.h:21
roc::node
High-level sender and receiver nodes.
roc
Root namespace.
noncopyable.h
Non-copyable object.
ownership_policy.h
Ownership policies.
panic.h
Panic.
stddefs.h
Commonly used types and functions.
roc::core::HashmapData
Hashmap node internal data.
Definition
hashmap_node.h:25
roc::core::RefCountedOwnership
Reference counted object ownership.
Definition
ownership_policy.h:21
roc_core
hashmap.h
Generated by
1.17.0