rippled
SHAMapInnerNode.cpp
1 //------------------------------------------------------------------------------
2 /*
3  This file is part of rippled: https://github.com/ripple/rippled
4  Copyright (c) 2012, 2013 Ripple Labs Inc.
5 
6  Permission to use, copy, modify, and/or distribute this software for any
7  purpose with or without fee is hereby granted, provided that the above
8  copyright notice and this permission notice appear in all copies.
9 
10  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18 //==============================================================================
19 
20 #include <ripple/shamap/SHAMapInnerNode.h>
21 
22 #include <ripple/basics/Log.h>
23 #include <ripple/basics/Slice.h>
24 #include <ripple/basics/contract.h>
25 #include <ripple/beast/core/LexicalCast.h>
26 #include <ripple/protocol/HashPrefix.h>
27 #include <ripple/protocol/digest.h>
28 #include <ripple/shamap/SHAMapTreeNode.h>
29 #include <ripple/shamap/impl/TaggedPointer.ipp>
30 
31 #include <openssl/sha.h>
32 
33 #include <algorithm>
34 #include <iterator>
35 #include <utility>
36 
37 // This is used for the _mm_pause instruction:
38 #include <immintrin.h>
39 
40 namespace ripple {
41 
51 {
52 private:
55 
56 public:
58  {
59  }
60 
62  : bits_(lock), mask_(1 << index)
63  {
64  assert(index >= 0 && index < 16);
65  }
66 
67  [[nodiscard]] bool
69  {
70  // If we want to grab all the individual bitlocks at once we cannot
71  // use `fetch_or`! To see why, imagine that `lock_ == 0x0020` which
72  // means that the `fetch_or` would return `0x0020` but all the bits
73  // would already be (incorrectly!) set. Oops!
74  std::uint16_t expected = 0;
75 
76  if (mask_ != 0xFFFF)
77  return (bits_.fetch_or(mask_, std::memory_order_acquire) & mask_) ==
78  expected;
79 
81  expected,
82  mask_,
83  std::memory_order_acquire,
84  std::memory_order_relaxed);
85  }
86 
87  void
88  lock()
89  {
90  // Testing suggests that 99.9999% of the time this will succeed, so
91  // we try to optimize the fast path.
92  if (try_lock())
93  return;
94 
95  do
96  {
97  // We try to spin for a few times:
98  for (int i = 0; i != 100; ++i)
99  {
100  if (try_lock())
101  return;
102 
103  _mm_pause();
104  }
105 
107  } while ((bits_.load(std::memory_order_relaxed) & mask_) == 0);
108  }
109 
110  void
112  {
113  bits_.fetch_and(~mask_, std::memory_order_release);
114  }
115 };
116 
118  std::uint32_t cowid,
119  std::uint8_t numAllocatedChildren)
120  : SHAMapTreeNode(cowid), hashesAndChildren_(numAllocatedChildren)
121 {
122 }
123 
125 
126 template <class F>
127 void
129 {
130  hashesAndChildren_.iterChildren(isBranch_, std::forward<F>(f));
131 }
132 
133 template <class F>
134 void
136 {
138 }
139 
140 void
142 {
144  TaggedPointer(std::move(hashesAndChildren_), isBranch_, toAllocate);
145 }
146 
149 {
151 }
152 
155 {
156  auto const branchCount = getBranchCount();
157  auto const thisIsSparse = !hashesAndChildren_.isDense();
158  auto p = std::make_shared<SHAMapInnerNode>(cowid, branchCount);
159  p->hash_ = hash_;
160  p->isBranch_ = isBranch_;
161  p->fullBelowGen_ = fullBelowGen_;
162  SHAMapHash *cloneHashes, *thisHashes;
163  std::shared_ptr<SHAMapTreeNode>*cloneChildren, *thisChildren;
164  // structured bindings can't be captured in c++ 17; use tie instead
165  std::tie(std::ignore, cloneHashes, cloneChildren) =
166  p->hashesAndChildren_.getHashesAndChildren();
167  std::tie(std::ignore, thisHashes, thisChildren) =
169 
170  if (thisIsSparse)
171  {
172  int cloneChildIndex = 0;
173  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
174  cloneHashes[cloneChildIndex++] = thisHashes[indexNum];
175  });
176  }
177  else
178  {
179  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
180  cloneHashes[branchNum] = thisHashes[indexNum];
181  });
182  }
183 
184  SpinBitlock sl(lock_);
185  std::lock_guard lock(sl);
186 
187  if (thisIsSparse)
188  {
189  int cloneChildIndex = 0;
190  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
191  cloneChildren[cloneChildIndex++] = thisChildren[indexNum];
192  });
193  }
194  else
195  {
196  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
197  cloneChildren[branchNum] = thisChildren[indexNum];
198  });
199  }
200 
201  return p;
202 }
203 
206  Slice data,
207  SHAMapHash const& hash,
208  bool hashValid)
209 {
210  // A full inner node is serialized as 16 256-bit hashes, back to back:
211  if (data.size() != branchFactor * uint256::bytes)
212  Throw<std::runtime_error>("Invalid FI node");
213 
214  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
215 
216  SerialIter si(data);
217 
218  auto hashes = ret->hashesAndChildren_.getHashes();
219 
220  for (int i = 0; i < branchFactor; ++i)
221  {
222  hashes[i].as_uint256() = si.getBitString<256>();
223 
224  if (hashes[i].isNonZero())
225  ret->isBranch_ |= (1 << i);
226  }
227 
228  ret->resizeChildArrays(ret->getBranchCount());
229 
230  if (hashValid)
231  ret->hash_ = hash;
232  else
233  ret->updateHash();
234 
235  return ret;
236 }
237 
240 {
241  // A compressed inner node is serialized as a series of 33 byte chunks,
242  // representing a one byte "position" and a 256-bit hash:
243  constexpr std::size_t chunkSize = uint256::bytes + 1;
244 
245  if (auto const s = data.size();
246  (s % chunkSize != 0) || (s > chunkSize * branchFactor))
247  Throw<std::runtime_error>("Invalid CI node");
248 
249  SerialIter si(data);
250 
251  auto ret = std::make_shared<SHAMapInnerNode>(0, branchFactor);
252 
253  auto hashes = ret->hashesAndChildren_.getHashes();
254 
255  while (!si.empty())
256  {
257  auto const hash = si.getBitString<256>();
258  auto const pos = si.get8();
259 
260  if (pos >= branchFactor)
261  Throw<std::runtime_error>("invalid CI node");
262 
263  hashes[pos].as_uint256() = hash;
264 
265  if (hashes[pos].isNonZero())
266  ret->isBranch_ |= (1 << pos);
267  }
268 
269  ret->resizeChildArrays(ret->getBranchCount());
270  ret->updateHash();
271  return ret;
272 }
273 
274 void
276 {
277  uint256 nh;
278  if (isBranch_ != 0)
279  {
281  using beast::hash_append;
283  iterChildren([&](SHAMapHash const& hh) { hash_append(h, hh); });
284  nh = static_cast<typename sha512_half_hasher::result_type>(h);
285  }
286  hash_ = SHAMapHash{nh};
287 }
288 
289 void
291 {
292  SHAMapHash* hashes;
294  // structured bindings can't be captured in c++ 17; use tie instead
295  std::tie(std::ignore, hashes, children) =
297  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
298  if (children[indexNum] != nullptr)
299  hashes[indexNum] = children[indexNum]->getHash();
300  });
301  updateHash();
302 }
303 
304 void
306 {
307  assert(!isEmpty());
308 
309  // If the node is sparse, then only send non-empty branches:
310  if (getBranchCount() < 12)
311  {
312  // compressed node
313  auto hashes = hashesAndChildren_.getHashes();
314  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
315  s.addBitString(hashes[indexNum].as_uint256());
316  s.add8(branchNum);
317  });
319  }
320  else
321  {
322  iterChildren(
323  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
324  s.add8(wireTypeInner);
325  }
326 }
327 
328 void
330 {
331  assert(!isEmpty());
332 
334  iterChildren(
335  [&](SHAMapHash const& hh) { s.addBitString(hh.as_uint256()); });
336 }
337 
338 bool
340 {
341  return isBranch_ == 0;
342 }
343 
344 int
346 {
347  return popcnt16(isBranch_);
348 }
349 
352 {
354  auto hashes = hashesAndChildren_.getHashes();
355  iterNonEmptyChildIndexes([&](auto branchNum, auto indexNum) {
356  ret += "\nb";
357  ret += std::to_string(branchNum);
358  ret += " = ";
359  ret += to_string(hashes[indexNum]);
360  });
361  return ret;
362 }
363 
364 // We are modifying an inner node
365 void
367 {
368  assert((m >= 0) && (m < branchFactor));
369  assert(cowid_ != 0);
370  assert(child.get() != this);
371 
372  auto const dstIsBranch = [&] {
373  if (child)
374  return isBranch_ | (1 << m);
375  else
376  return isBranch_ & ~(1 << m);
377  }();
378 
379  auto const dstToAllocate = popcnt16(dstIsBranch);
380  // change hashesAndChildren to remove the element, or make room for the
381  // added element, if necessary
383  std::move(hashesAndChildren_), isBranch_, dstIsBranch, dstToAllocate);
384 
385  isBranch_ = dstIsBranch;
386 
387  if (child)
388  {
389  auto const childIndex = *getChildIndex(m);
390  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
391  hashes[childIndex].zero();
392  children[childIndex] = child;
393  }
394 
395  hash_.zero();
396 
398 }
399 
400 // finished modifying, now make shareable
401 void
403 {
404  assert((m >= 0) && (m < branchFactor));
405  assert(cowid_ != 0);
406  assert(child);
407  assert(child.get() != this);
408 
409  assert(!isEmptyBranch(m));
411 }
412 
415 {
416  assert(branch >= 0 && branch < branchFactor);
417  assert(!isEmptyBranch(branch));
418 
419  auto const index = *getChildIndex(branch);
420 
421  SpinBitlock sl(lock_, index);
422  std::lock_guard lock(sl);
423  return hashesAndChildren_.getChildren()[index].get();
424 }
425 
428 {
429  assert(branch >= 0 && branch < branchFactor);
430  assert(!isEmptyBranch(branch));
431 
432  auto const index = *getChildIndex(branch);
433 
434  SpinBitlock sl(lock_, index);
435  std::lock_guard lock(sl);
436  return hashesAndChildren_.getChildren()[index];
437 }
438 
439 SHAMapHash const&
441 {
442  assert((m >= 0) && (m < branchFactor));
443  if (auto const i = getChildIndex(m))
444  return hashesAndChildren_.getHashes()[*i];
445 
446  return zeroSHAMapHash;
447 }
448 
451  int branch,
453 {
454  assert(branch >= 0 && branch < branchFactor);
455  assert(node);
456  assert(!isEmptyBranch(branch));
457  auto const childIndex = *getChildIndex(branch);
458  auto [_, hashes, children] = hashesAndChildren_.getHashesAndChildren();
459  assert(node->getHash() == hashes[childIndex]);
460 
461  SpinBitlock sl(lock_, childIndex);
462  std::lock_guard lock(sl);
463 
464  if (children[childIndex])
465  {
466  // There is already a node hooked up, return it
467  node = children[childIndex];
468  }
469  else
470  {
471  // Hook this node up
472  children[childIndex] = node;
473  }
474  return node;
475 }
476 
477 void
478 SHAMapInnerNode::invariants(bool is_root) const
479 {
480  unsigned count = 0;
481  auto [numAllocated, hashes, children] =
483 
484  if (numAllocated != branchFactor)
485  {
486  auto const branchCount = getBranchCount();
487  for (int i = 0; i < branchCount; ++i)
488  {
489  assert(hashes[i].isNonZero());
490  if (children[i] != nullptr)
491  children[i]->invariants();
492  ++count;
493  }
494  }
495  else
496  {
497  for (int i = 0; i < branchFactor; ++i)
498  {
499  if (hashes[i].isNonZero())
500  {
501  assert((isBranch_ & (1 << i)) != 0);
502  if (children[i] != nullptr)
503  children[i]->invariants();
504  ++count;
505  }
506  else
507  {
508  assert((isBranch_ & (1 << i)) == 0);
509  }
510  }
511  }
512 
513  if (!is_root)
514  {
515  assert(hash_.isNonZero());
516  assert(count >= 1);
517  }
518  assert((count == 0) ? hash_.isZero() : hash_.isNonZero());
519 }
520 
521 } // namespace ripple
ripple::SHAMapTreeNode::cowid
std::uint32_t cowid() const
Returns the SHAMap that owns this node.
Definition: SHAMapTreeNode.h:116
ripple::SHAMapInnerNode::serializeWithPrefix
void serializeWithPrefix(Serializer &) const override
Serialize the node in a format appropriate for hashing.
Definition: SHAMapInnerNode.cpp:329
ripple::SHAMapInnerNode::setChild
void setChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:366
ripple::SpinBitlock::SpinBitlock
SpinBitlock(std::atomic< std::uint16_t > &lock, int index)
Definition: SHAMapInnerNode.cpp:61
ripple::SHAMapInnerNode::clone
std::shared_ptr< SHAMapTreeNode > clone(std::uint32_t cowid) const override
Make a copy of this node, setting the owner.
Definition: SHAMapInnerNode.cpp:154
ripple::TaggedPointer::getChildIndex
std::optional< int > getChildIndex(std::uint16_t isBranch, int i) const
Get the child's index inside the hashes or children array (which may or may not be sparse).
std::string
STL class.
std::shared_ptr
STL class.
utility
ripple::SHAMapInnerNode::hashesAndChildren_
TaggedPointer hashesAndChildren_
Opaque type that contains the hashes array (array of type SHAMapHash) and the children array (array o...
Definition: SHAMapInnerNode.h:53
ripple::detail::basic_sha512_half_hasher
Returns the SHA512-Half digest of a message.
Definition: digest.h:166
ripple::SHAMapInnerNode::getChild
std::shared_ptr< SHAMapTreeNode > getChild(int branch)
Definition: SHAMapInnerNode.cpp:427
ripple::Slice
An immutable linear range of bytes.
Definition: Slice.h:44
ripple::SHAMapInnerNode::makeFullInner
static std::shared_ptr< SHAMapTreeNode > makeFullInner(Slice data, SHAMapHash const &hash, bool hashValid)
Definition: SHAMapInnerNode.cpp:205
ripple::SHAMapInnerNode::canonicalizeChild
std::shared_ptr< SHAMapTreeNode > canonicalizeChild(int branch, std::shared_ptr< SHAMapTreeNode > node)
Definition: SHAMapInnerNode.cpp:450
ripple::SHAMapTreeNode::cowid_
std::uint32_t cowid_
Determines the owning SHAMap, if any.
Definition: SHAMapTreeNode.h:64
ripple::Serializer::add8
int add8(unsigned char i)
Definition: Serializer.cpp:166
ripple::SHAMapInnerNode::updateHash
void updateHash() override
Recalculate the hash of this node.
Definition: SHAMapInnerNode.cpp:275
iterator
std::lock_guard
STL class.
ripple::SHAMapHash::isZero
bool isZero() const
Definition: SHAMapHash.h:53
ripple::SHAMapInnerNode::branchFactor
static constexpr unsigned int branchFactor
Each inner node has 16 children (the 'radix tree' part of the map)
Definition: SHAMapInnerNode.h:46
ripple::SHAMapInnerNode::shareChild
void shareChild(int m, std::shared_ptr< SHAMapTreeNode > const &child)
Definition: SHAMapInnerNode.cpp:402
ripple::wireTypeInner
static constexpr unsigned const char wireTypeInner
Definition: SHAMapTreeNode.h:42
ripple::SHAMapNodeID
Identifies a node inside a SHAMap.
Definition: SHAMapNodeID.h:33
ripple::SHAMapHash::isNonZero
bool isNonZero() const
Definition: SHAMapHash.h:58
std::atomic::compare_exchange_weak
T compare_exchange_weak(T... args)
ripple::SHAMapTreeNode::getString
virtual std::string getString(SHAMapNodeID const &) const
Definition: SHAMapTreeNode.cpp:184
ripple::SHAMapTreeNode::hash_
SHAMapHash hash_
Definition: SHAMapTreeNode.h:56
ripple::SHAMapInnerNode::iterChildren
void iterChildren(F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
Definition: SHAMapInnerNode.cpp:128
ripple::TaggedPointer
TaggedPointer is a combination of a pointer and a mask stored in the lowest two bits.
Definition: TaggedPointer.h:57
ripple::SHAMapHash
Definition: SHAMapHash.h:32
algorithm
ripple::SpinBitlock::SpinBitlock
SpinBitlock(std::atomic< std::uint16_t > &lock)
Definition: SHAMapInnerNode.cpp:57
ripple::SpinBitlock::try_lock
bool try_lock()
Definition: SHAMapInnerNode.cpp:68
std::tie
T tie(T... args)
ripple::SHAMapInnerNode::isEmptyBranch
bool isEmptyBranch(int m) const
Definition: SHAMapInnerNode.h:198
ripple::SHAMapInnerNode::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(F &&f) const
Call the f callback for all non-empty branches.
Definition: SHAMapInnerNode.cpp:135
ripple::base_uint< 256 >
ripple::SHAMapInnerNode::getString
std::string getString(SHAMapNodeID const &) const override
Definition: SHAMapInnerNode.cpp:351
ripple::HashPrefix::innerNode
@ innerNode
inner node in V1 tree
ripple::SHAMapInnerNode::getChildHash
SHAMapHash const & getChildHash(int m) const
Definition: SHAMapInnerNode.cpp:440
ripple::base_uint< 256 >::bytes
static constexpr std::size_t bytes
Definition: base_uint.h:98
ripple::SerialIter::get8
unsigned char get8()
Definition: Serializer.cpp:362
std::atomic::load
T load(T... args)
ripple::SHAMapInnerNode::resizeChildArrays
void resizeChildArrays(std::uint8_t toAllocate)
Convert arrays stored in hashesAndChildren_ so they can store the requested number of children.
Definition: SHAMapInnerNode.cpp:141
ripple::SerialIter::empty
std::size_t empty() const noexcept
Definition: Serializer.h:332
ripple::TaggedPointer::getHashes
SHAMapHash * getHashes() const
Get the hashes array.
ripple::TaggedPointer::isDense
bool isDense() const
Check if the arrays have a dense format.
ripple::SHAMapTreeNode
Definition: SHAMapTreeNode.h:53
std::to_string
T to_string(T... args)
ripple::SHAMapInnerNode::getChildIndex
std::optional< int > getChildIndex(int i) const
Get the child's index inside the hashes or children array (stored in hashesAndChildren_.
Definition: SHAMapInnerNode.cpp:148
ripple::SpinBitlock
A specialized 16-way spinlock used to protect inner node branches.
Definition: SHAMapInnerNode.cpp:50
ripple::SHAMapInnerNode::updateHashDeep
void updateHashDeep()
Recalculate the hash of all children and this node.
Definition: SHAMapInnerNode.cpp:290
ripple::SHAMapInnerNode::SHAMapInnerNode
SHAMapInnerNode(std::uint32_t cowid, std::uint8_t numAllocatedChildren=2)
Definition: SHAMapInnerNode.cpp:117
ripple::SpinBitlock::bits_
std::atomic< std::uint16_t > & bits_
Definition: SHAMapInnerNode.cpp:53
ripple::SHAMapInnerNode::isBranch_
std::uint16_t isBranch_
Definition: SHAMapInnerNode.h:56
ripple::SerialIter
Definition: Serializer.h:310
ripple::SHAMapInnerNode::getBranchCount
int getBranchCount() const
Definition: SHAMapInnerNode.cpp:345
std::uint16_t
std::atomic< std::uint16_t >
std::atomic::fetch_and
T fetch_and(T... args)
ripple::SHAMapInnerNode::~SHAMapInnerNode
~SHAMapInnerNode()
ripple::TaggedPointer::iterNonEmptyChildIndexes
void iterNonEmptyChildIndexes(std::uint16_t isBranch, F &&f) const
Call the f callback for all non-empty branches.
ripple::SpinBitlock::mask_
std::uint16_t mask_
Definition: SHAMapInnerNode.cpp:54
ripple::SHAMapTreeNode::getHash
SHAMapHash const & getHash() const
Return the hash of this node.
Definition: SHAMapTreeNode.h:143
ripple::Serializer
Definition: Serializer.h:39
ripple::wireTypeCompressedInner
static constexpr unsigned const char wireTypeCompressedInner
Definition: SHAMapTreeNode.h:43
ripple::SerialIter::getBitString
base_uint< Bits, Tag > getBitString()
Definition: Serializer.h:414
ripple
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition: RCLCensorshipDetector.h:29
ripple::SHAMapInnerNode::serializeForWire
void serializeForWire(Serializer &) const override
Serialize the node in a format appropriate for sending over the wire.
Definition: SHAMapInnerNode.cpp:305
ripple::Serializer::addBitString
int addBitString(base_uint< Bits, Tag > const &v)
Definition: Serializer.h:97
ripple::SHAMapHash::zero
void zero()
Definition: SHAMapHash.h:68
beast::hash_append
std::enable_if_t< is_contiguously_hashable< T, Hasher >::value > hash_append(Hasher &h, T const &t) noexcept
Logically concatenate input data to a Hasher.
Definition: hash_append.h:236
std::atomic::fetch_or
T fetch_or(T... args)
ripple::SpinBitlock::unlock
void unlock()
Definition: SHAMapInnerNode.cpp:111
std::optional< int >
ripple::TaggedPointer::getChildren
std::shared_ptr< SHAMapTreeNode > * getChildren() const
Get the children array.
std::size_t
ripple::to_string
std::string to_string(Manifest const &m)
Format the specified manifest to a string for debugging purposes.
Definition: app/misc/impl/Manifest.cpp:41
ripple::Serializer::add32
int add32(std::uint32_t i)
Definition: Serializer.cpp:38
ripple::TaggedPointer::iterChildren
void iterChildren(std::uint16_t isBranch, F &&f) const
Call the f callback for all 16 (branchFactor) branches - even if the branch is empty.
ripple::SHAMapInnerNode::isEmpty
bool isEmpty() const
Definition: SHAMapInnerNode.cpp:339
ripple::SHAMapInnerNode::makeCompressedInner
static std::shared_ptr< SHAMapTreeNode > makeCompressedInner(Slice data)
Definition: SHAMapInnerNode.cpp:239
ripple::TaggedPointer::getHashesAndChildren
std::tuple< std::uint8_t, SHAMapHash *, std::shared_ptr< SHAMapTreeNode > * > getHashesAndChildren() const
Get the number of elements in each array and a pointer to the start of each array.
ripple::SHAMapHash::as_uint256
uint256 const & as_uint256() const
Definition: SHAMapHash.h:43
ripple::SpinBitlock::lock
void lock()
Definition: SHAMapInnerNode.cpp:88
ripple::SHAMapInnerNode::getChildPointer
SHAMapTreeNode * getChildPointer(int branch)
Definition: SHAMapInnerNode.cpp:414
ripple::TaggedPointer::capacity
std::uint8_t capacity() const
Get the number of elements allocated for each array.
ripple::hash_append
void hash_append(Hasher &h, ValidatorBlobInfo const &blobInfo)
Definition: ValidatorList.h:897
ripple::SHAMapInnerNode::lock_
std::atomic< std::uint16_t > lock_
A bitlock for the children of this node, with one bit per child.
Definition: SHAMapInnerNode.h:59
ripple::SHAMapInnerNode::invariants
void invariants(bool is_root=false) const override
Definition: SHAMapInnerNode.cpp:478
std::this_thread::yield
T yield(T... args)
ripple::SHAMapInnerNode::fullBelowGen_
std::uint32_t fullBelowGen_
Definition: SHAMapInnerNode.h:55