Bitcoin ABC 0.26.3
P2P Digital Currency
Loading...
Searching...
No Matches
radix.h
Go to the documentation of this file.
1// Copyright (c) 2019 The Bitcoin developers
2// Distributed under the MIT software license, see the accompanying
3// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5#ifndef BITCOIN_RADIX_H
6#define BITCOIN_RADIX_H
7
8#include <common/system.h>
9#include <rcu.h>
10
11#include <array>
12#include <atomic>
13#include <cstdint>
14#include <memory>
15#include <type_traits>
16
17template <typename T> struct PassthroughAdapter {
18 auto &&getId(const T &e) const { return e.getId(); }
19};
20
39template <typename T, typename Adapter = PassthroughAdapter<T>>
40struct RadixTree : private Adapter {
41private:
42 static const int BITS = 4;
43 static const int MASK = (1 << BITS) - 1;
44 static const size_t CHILD_PER_LEVEL = 1 << BITS;
45
46 using KeyType =
47 typename std::remove_reference<decltype(std::declval<Adapter &>().getId(
48 std::declval<T &>()))>::type;
49 static const size_t KEY_BITS = 8 * sizeof(KeyType);
50 static const uint32_t TOP_LEVEL = (KEY_BITS - 1) / BITS;
51
52 struct RadixElement;
53 struct RadixNode;
54
55 std::atomic<RadixElement> root;
56
57public:
59 ~RadixTree() { root.load().decrementRefCount(); }
60
64 RadixTree(const RadixTree &src) : RadixTree() {
65 {
66 RCULock lock;
67 RadixElement e = src.root.load();
68 e.incrementRefCount();
69 root = e;
70 }
71
72 // Make sure we the writes in the tree are behind us so
73 // this copy won't mutate behind our back.
75 }
76
78 {
79 RCULock lock;
80 RadixElement e = rhs.root.load();
82 root.load().decrementRefCount();
83 root = e;
84 }
85
86 // Make sure we the writes in the tree are behind us so
87 // this copy won't mutate behind our back.
89
90 return *this;
91 }
92
96 RadixTree(RadixTree &&src) : RadixTree() { *this = std::move(src); }
98 {
99 RCULock lock;
100 RadixElement e = rhs.root.load();
101 rhs.root = root.load();
102 root = e;
103 }
104
105 return *this;
106 }
107
112 bool insert(const RCUPtr<T> &value) { return insert(getId(*value), value); }
113
118 RCUPtr<T> get(const KeyType &key) {
120
121 RCULock lock;
122 RadixElement e = root.load();
123
124 // Find a leaf.
125 while (e.isNode()) {
126 e = e.getNode()->get(level--, key)->load();
127 }
128
129 T *leaf = e.getLeaf();
130 if (leaf == nullptr || getId(*leaf) != key) {
131 // We failed to find the proper element.
132 return RCUPtr<T>();
133 }
134
135 // The leaf is non-null and the keys match. We have our guy.
136 return RCUPtr<T>::copy(leaf);
137 }
138
139 RCUPtr<const T> get(const KeyType &key) const {
140 T const *ptr = const_cast<RadixTree *>(this)->get(key).release();
141 return RCUPtr<const T>::acquire(ptr);
142 }
143
144 template <typename Callable> bool forEachLeaf(Callable &&func) const {
145 RCULock lock;
146 return forEachLeaf(root.load(), std::move(func));
147 }
148
149#define SEEK_LEAF_LOOP() \
150 RadixElement e = eptr->load(); \
151 \
152 /* Walk down the tree until we find a leaf for our node. */ \
153 do { \
154 while (e.isNode()) { \
155 Node: \
156 auto nptr = e.getNode(); \
157 if (!nptr->isShared()) { \
158 eptr = nptr->get(level--, key); \
159 e = eptr->load(); \
160 continue; \
161 } \
162 \
163 auto copy = std::make_unique<RadixNode>(*nptr); \
164 if (!eptr->compare_exchange_strong(e, RadixElement(copy.get()))) { \
165 /* We failed to insert our subtree, just try again. */ \
166 continue; \
167 } \
168 \
169 /* We have a subtree, resume normal operations from there. */ \
170 e.decrementRefCount(); \
171 eptr = copy->get(level--, key); \
172 e = eptr->load(); \
173 copy.release(); \
174 } \
175 } while (0)
176
183
184 RCULock lock;
185 std::atomic<RadixElement> *eptr = &root;
186
188
189 T *leaf = e.getLeaf();
190 if (leaf == nullptr || getId(*leaf) != key) {
191 // We failed to find the proper element.
192 return RCUPtr<T>();
193 }
194
195 // We have the proper element, try to delete it.
196 if (eptr->compare_exchange_strong(e, RadixElement())) {
197 return RCUPtr<T>::acquire(leaf);
198 }
199
200 // The element was replaced, either by a subtree or another element.
201 if (e.isNode()) {
202 goto Node;
203 }
204
205 // The element in the slot is not the one we are looking for.
206 return RCUPtr<T>();
207 }
208
209private:
210 KeyType getId(const T &value) const { return Adapter::getId(value); }
211
212 bool insert(const KeyType &key, RCUPtr<T> value) {
214
215 RCULock lock;
216 std::atomic<RadixElement> *eptr = &root;
217
218 while (true) {
220
221 // If the slot is empty, try to insert right there.
222 if (e.getLeaf() == nullptr) {
223 if (eptr->compare_exchange_strong(e,
224 RadixElement(value.get()))) {
225 value.release();
226 return true;
227 }
228
229 // CAS failed, we may have a node in there now.
230 if (e.isNode()) {
231 goto Node;
232 }
233 }
234
235 // The element was already in the tree.
236 const KeyType &leafKey = getId(*e.getLeaf());
237 if (key == leafKey) {
238 return false;
239 }
240
241 // There is an element there, but it isn't a subtree. We need to
242 // convert it into a subtree and resume insertion into that subtree.
243 auto newChild = std::make_unique<RadixNode>(level, leafKey, e);
244 if (eptr->compare_exchange_strong(e,
245 RadixElement(newChild.get()))) {
246 // We have a subtree, resume normal operations from there.
247 newChild.release();
248 } else {
249 // We failed to insert our subtree, clean it before it is freed.
250 newChild->get(level, leafKey)->store(RadixElement());
251 }
252 }
253 }
254
255#undef SEEK_LEAF_LOOP
256
257 template <typename Callable>
258 bool forEachLeaf(RadixElement e, Callable &&func) const {
259 if (e.isLeaf()) {
260 T *leaf = e.getLeaf();
261 if (leaf != nullptr) {
262 return func(RCUPtr<T>::copy(leaf));
263 }
264
265 return true;
266 }
267
268 return e.getNode()->forEachChild(
269 [&](const std::atomic<RadixElement> *pElement) {
270 return forEachLeaf(pElement->load(), func);
271 });
272 }
273
275 private:
276 union {
280 };
281
282 static const uintptr_t DISCRIMINANT = 0x01;
283 bool getDiscriminant() const { return (raw & DISCRIMINANT) != 0; }
284
285 public:
286 explicit RadixElement() noexcept : raw(DISCRIMINANT) {}
287 explicit RadixElement(RadixNode *nodeIn) noexcept : node(nodeIn) {}
288 explicit RadixElement(T *leafIn) noexcept : leaf(leafIn) {
289 raw |= DISCRIMINANT;
290 }
291
297 if (isNode()) {
299 } else {
301 }
302 }
303
305 if (isNode()) {
306 RadixNode *ptr = getNode();
308 } else {
309 T *ptr = getLeaf();
311 }
312 }
313
317 bool isNode() const { return !getDiscriminant(); }
318
320 assert(isNode());
321 return node;
322 }
323
324 const RadixNode *getNode() const {
325 assert(isNode());
326 return node;
327 }
328
332 bool isLeaf() const { return getDiscriminant(); }
333
334 T *getLeaf() {
335 assert(isLeaf());
336 return reinterpret_cast<T *>(raw & ~DISCRIMINANT);
337 }
338
339 const T *getLeaf() const {
340 assert(isLeaf());
341 return const_cast<RadixElement *>(this)->getLeaf();
342 }
343 };
344
345 struct RadixNode {
347
348 private:
349 union {
350 std::array<std::atomic<RadixElement>, CHILD_PER_LEVEL> children;
351 std::array<RadixElement, CHILD_PER_LEVEL>
353 };
354
355 public:
358 get(level, key)->store(e);
359 }
360
363 e.decrementRefCount();
364 }
365 }
366
368 for (size_t i = 0; i < CHILD_PER_LEVEL; i++) {
369 auto e = rhs.children[i].load();
370 e.incrementRefCount();
372 }
373 }
374
375 RadixNode &operator=(const RadixNode &) = delete;
376
377 std::atomic<RadixElement> *get(uint32_t level, const KeyType &key) {
378 return &children[(key >> uint32_t(level * BITS)) & MASK];
379 }
380
381 bool isShared() const { return refcount > 0; }
382
383 template <typename Callable> bool forEachChild(Callable &&func) const {
384 for (size_t i = 0; i < CHILD_PER_LEVEL; i++) {
385 if (!func(&children[i])) {
386 return false;
387 }
388 }
389 return true;
390 }
391 };
392
393 // Make sure the alignment works for T and RadixElement.
394 static_assert(alignof(T) > 1, "T's alignment must be 2 or more.");
395 static_assert(alignof(RadixNode) > 1,
396 "RadixNode alignment must be 2 or more.");
397};
398
399#endif // BITCOIN_RADIX_H
Definition rcu.h:61
static void synchronize()
Definition rcu.h:82
Definition rcu.h:85
T * get()
Get allows to access the undelying pointer.
Definition rcu.h:170
static RCUPtr acquire(T *&ptrIn)
Acquire ownership of some pointer.
Definition rcu.h:103
T * release()
Release transfers ownership of the pointer from RCUPtr to the caller.
Definition rcu.h:176
static RCUPtr copy(T *ptr)
Construct a new RCUPtr without transferring owership.
Definition rcu.h:119
#define SEEK_LEAF_LOOP()
Definition radix.h:149
T GetRand(T nMax=std::numeric_limits< T >::max()) noexcept
Generate a uniform random integer of type T in the range [0..nMax) nMax defaults to std::numeric_limi...
Definition random.h:85
auto && getId(const T &e) const
Definition radix.h:18
bool getDiscriminant() const
Definition radix.h:283
bool isNode() const
Node features.
Definition radix.h:317
static const uintptr_t DISCRIMINANT
Definition radix.h:282
RadixNode * getNode()
Definition radix.h:319
const T * getLeaf() const
Definition radix.h:339
bool isLeaf() const
Leaf features.
Definition radix.h:332
RadixElement(RadixNode *nodeIn) noexcept
Definition radix.h:287
void incrementRefCount()
RadixElement is designed to be a dumb wrapper.
Definition radix.h:296
RadixNode * node
Definition radix.h:277
const RadixNode * getNode() const
Definition radix.h:324
RadixElement(T *leafIn) noexcept
Definition radix.h:288
RadixElement() noexcept
Definition radix.h:286
bool forEachChild(Callable &&func) const
Definition radix.h:383
RadixNode & operator=(const RadixNode &)=delete
std::atomic< RadixElement > * get(uint32_t level, const KeyType &key)
Definition radix.h:377
RadixNode(uint32_t level, const KeyType &key, RadixElement e)
Definition radix.h:356
std::array< RadixElement, CHILD_PER_LEVEL > non_atomic_children_DO_NOT_USE
Definition radix.h:352
bool isShared() const
Definition radix.h:381
RadixNode(const RadixNode &rhs)
Definition radix.h:367
std::array< std::atomic< RadixElement >, CHILD_PER_LEVEL > children
Definition radix.h:350
IMPLEMENT_RCU_REFCOUNT(uint64_t)
This is a radix tree storing values identified by a unique key.
Definition radix.h:40
static const int BITS
Definition radix.h:42
static const uint32_t TOP_LEVEL
Definition radix.h:50
RCUPtr< T > remove(const KeyType &key)
Remove an element from the tree.
Definition radix.h:181
RadixTree()
Definition radix.h:58
KeyType getId(const T &value) const
Definition radix.h:210
static const size_t CHILD_PER_LEVEL
Definition radix.h:44
~RadixTree()
Definition radix.h:59
RadixTree(RadixTree &&src)
Move semantic.
Definition radix.h:96
typename std::remove_reference< decltype(std::declval< Adapter & >().getId(std::declval< T & >()))>::type KeyType
Definition radix.h:48
bool forEachLeaf(RadixElement e, Callable &&func) const
Definition radix.h:258
RadixTree & operator=(const RadixTree &rhs)
Definition radix.h:77
static const size_t KEY_BITS
Definition radix.h:49
static const int MASK
Definition radix.h:43
std::atomic< RadixElement > root
Definition radix.h:55
RadixTree(const RadixTree &src)
Copy semantic.
Definition radix.h:64
RCUPtr< const T > get(const KeyType &key) const
Definition radix.h:139
bool insert(const KeyType &key, RCUPtr< T > value)
Definition radix.h:212
RCUPtr< T > get(const KeyType &key)
Get the value corresponding to a key.
Definition radix.h:118
RadixTree & operator=(RadixTree &&rhs)
Definition radix.h:97
bool forEachLeaf(Callable &&func) const
Definition radix.h:144
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
Definition radix.h:112
assert(!tx.IsCoinBase())