souffle  2.0.2-371-g6315b36
LambdaBTree.h
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2018, Souffle Developers
4  * Licensed under the Universal Permissive License v 1.0 as shown at:
5  * - https://opensource.org/licenses/UPL
6  * - <souffle root>/licenses/SOUFFLE-UPL.txt
7  */
8 
9 /************************************************************************
10  *
11  * @file LambdaBTree.h
12  *
13  * An implementation of a generic B-tree data structure including
14  * interfaces for utilizing instances as set or multiset containers.
15  * Allows the user to provide a function to execute on successful insert
16  * Be careful using this, it currently expects a pair as the key.
17  *
18  ***********************************************************************/
19 
20 #pragma once
21 
25 #include <atomic>
26 #include <cassert>
27 #include <typeinfo>
28 #include <vector>
29 
30 namespace souffle {
31 
32 namespace detail {
33 /**
34  * The actual implementation of a b-tree data structure.
35  *
36  * @tparam Key .. the element type to be stored in this tree
37  * @tparam Comparator .. a class defining an order on the stored elements
38  * @tparam Allocator .. utilized for allocating memory for required nodes
39  * @tparam blockSize .. determines the number of bytes/block utilized by leaf nodes
40  * @tparam SearchStrategy .. enables switching between linear, binary or any other search strategy
41  * @tparam isSet .. true = set, false = multiset
42  * @tparam Functor .. a std::function that is called on successful (new) insert
43  */
44 template <typename Key, typename Comparator,
45  typename Allocator, // is ignored so far - TODO: add support
46  unsigned blockSize, typename SearchStrategy, bool isSet, typename Functor,
47  typename WeakComparator = Comparator, typename Updater = detail::updater<Key>>
48 class LambdaBTree : public btree<Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, WeakComparator,
49  Updater> {
50 public:
51  using parenttype =
52  btree<Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, WeakComparator, Updater>;
53 
54  LambdaBTree(const Comparator& comp = Comparator(), const WeakComparator& weak_comp = WeakComparator())
56 
57  /**
58  * Inserts the given key into this tree.
59  */
60  typename Functor::result_type insert(Key& k, const Functor& f) {
61  typename parenttype::operation_hints hints;
62  return insert(k, hints, f);
63  }
64 
65  // rewriting this because of david's changes
66  typename Functor::result_type insert(
67  Key& k, typename parenttype::operation_hints& hints, const Functor& f) {
68 #ifdef IS_PARALLEL
69 
70  // special handling for inserting first element
71  while (this->root == nullptr) {
72  // try obtaining root-lock
73  if (!this->root_lock.try_start_write()) {
74  // somebody else was faster => re-check
75  continue;
76  }
77 
78  // check loop condition again
79  if (this->root != nullptr) {
80  // somebody else was faster => normal insert
82  break;
83  }
84 
85  // create new node
86  this->leftmost = new typename parenttype::leaf_node();
87  this->leftmost->numElements = 1;
88  // call the functor as we've successfully inserted
89  typename Functor::result_type res = f(k);
90 
91  this->leftmost->keys[0] = k;
92  this->root = this->leftmost;
93 
94  // operation complete => we can release the root lock
95  this->root_lock.end_write();
96 
97  hints.last_insert.access(this->leftmost);
98 
99  return res;
100  }
101 
102  // insert using iterative implementation
103 
104  typename parenttype::node* cur = nullptr;
105 
106  // test last insert hints
107  typename parenttype::lock_type::Lease cur_lease;
108 
109  auto checkHint = [&](typename parenttype::node* last_insert) {
110  // ignore null pointer
111  if (!last_insert) return false;
112  // get a read lease on indicated node
113  auto hint_lease = last_insert->lock.start_read();
114  // check whether it covers the key
115  if (!this->weak_covers(last_insert, k)) return false;
116  // and if there was no concurrent modification
117  if (!last_insert->lock.validate(hint_lease)) return false;
118  // use hinted location
119  cur = last_insert;
120  // and keep lease
121  cur_lease = hint_lease;
122  // we found a hit
123  return true;
124  };
125 
126  if (hints.last_insert.any(checkHint)) {
127  // register this as a hit
128  this->hint_stats.inserts.addHit();
129  } else {
130  // register this as a miss
131  this->hint_stats.inserts.addMiss();
132  }
133 
134  // if there is no valid hint ..
135  if (!cur) {
136  do {
137  // get root - access lock
138  auto root_lease = this->root_lock.start_read();
139 
140  // start with root
141  cur = this->root;
142 
143  // get lease of the next node to be accessed
144  cur_lease = cur->lock.start_read();
145 
146  // check validity of root pointer
147  if (this->root_lock.end_read(root_lease)) {
148  break;
149  }
150 
151  } while (true);
152  }
153 
154  while (true) {
155  // handle inner nodes
156  if (cur->inner) {
157  auto a = &(cur->keys[0]);
158  auto b = &(cur->keys[cur->numElements]);
159 
160  auto pos = this->search.lower_bound(k, a, b, this->weak_comp);
161  auto idx = pos - a;
162 
163  // early exit for sets
164  if (isSet && pos != b && this->weak_equal(*pos, k)) {
165  // validate results
166  if (!cur->lock.validate(cur_lease)) {
167  // start over again
168  return insert(k, hints, f);
169  }
170 
171  // update provenance information
172  if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *pos)) {
173  if (!cur->lock.try_upgrade_to_write(cur_lease)) {
174  // start again
175  return insert(k, hints, f);
176  }
177  this->update(*pos, k);
178 
179  // get result before releasing lock
180  auto res = (*pos).second;
181 
182  cur->lock.end_write();
183  return res;
184  }
185 
186  // get the result before releasing lock
187  auto res = (*pos).second;
188 
189  // check validity
190  if (!cur->lock.validate(cur_lease)) {
191  // start over again
192  return insert(k, hints, f);
193  }
194 
195  // we found the element => return the result
196  return res;
197  }
198 
199  // get next pointer
200  auto next = cur->getChild(idx);
201 
202  // get lease on next level
203  auto next_lease = next->lock.start_read();
204 
205  // check whether there was a write
206  if (!cur->lock.end_read(cur_lease)) {
207  // start over
208  return insert(k, hints, f);
209  }
210 
211  // go to next
212  cur = next;
213 
214  // move on lease
215  cur_lease = next_lease;
216 
217  continue;
218  }
219 
220  // the rest is for leaf nodes
221  assert(!cur->inner);
222 
223  // -- insert node in leaf node --
224 
225  auto a = &(cur->keys[0]);
226  auto b = &(cur->keys[cur->numElements]);
227 
228  auto pos = this->search.upper_bound(k, a, b, this->weak_comp);
229  auto idx = pos - a;
230 
231  // early exit for sets
232  if (isSet && pos != a && this->weak_equal(*(pos - 1), k)) {
233  // validate result
234  if (!cur->lock.validate(cur_lease)) {
235  // start over again
236  return insert(k, hints, f);
237  }
238 
239  // TODO (pnappa): remove provenance from LambdaBTree - no use for it
240  // update provenance information
241  if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *(pos - 1))) {
242  if (!cur->lock.try_upgrade_to_write(cur_lease)) {
243  // start again
244  return insert(k, hints, f);
245  }
246  this->update(*(pos - 1), k);
247 
248  // retrieve result before releasing lock
249  auto res = (*(pos - 1)).second;
250 
251  cur->lock.end_write();
252  return res;
253  }
254 
255  // read result (atomic) -- just as a proof of concept, this is actually not valid!!
256  std::atomic<typename Functor::result_type>& loc =
257  *reinterpret_cast<std::atomic<typename Functor::result_type>*>(&(*(pos - 1)).second);
258  auto res = loc.load(std::memory_order_relaxed);
259 
260  // check validity
261  if (!cur->lock.validate(cur_lease)) {
262  // start over again
263  return insert(k, hints, f);
264  }
265 
266  // we found the element => done
267  return res;
268  }
269 
270  // upgrade to write-permission
271  if (!cur->lock.try_upgrade_to_write(cur_lease)) {
272  // something has changed => restart
273  hints.last_insert.access(cur);
274  return insert(k, hints, f);
275  }
276 
277  if (cur->numElements >= parenttype::node::maxKeys) {
278  // -- lock parents --
279  auto priv = cur;
280  auto parent = priv->parent;
281  std::vector<typename parenttype::node*> parents;
282  do {
283  if (parent) {
284  parent->lock.start_write();
285  while (true) {
286  // check whether parent is correct
287  if (parent == priv->parent) {
288  break;
289  }
290  // switch parent
291  parent->lock.abort_write();
292  parent = priv->parent;
293  parent->lock.start_write();
294  }
295  } else {
296  // lock root lock => since cur is root
297  this->root_lock.start_write();
298  }
299 
300  // record locked node
301  parents.push_back(parent);
302 
303  // stop at "sphere of influence"
304  if (!parent || !parent->isFull()) {
305  break;
306  }
307 
308  // go one step higher
309  priv = parent;
310  parent = parent->parent;
311 
312  } while (true);
313 
314  // split this node
315  auto old_root = this->root;
316  idx -= cur->rebalance_or_split(
317  const_cast<typename parenttype::node**>(&this->root), this->root_lock, idx, parents);
318 
319  // release parent lock
320  for (auto it = parents.rbegin(); it != parents.rend(); ++it) {
321  auto parent = *it;
322 
323  // release this lock
324  if (parent) {
325  parent->lock.end_write();
326  } else {
327  if (old_root != this->root) {
328  this->root_lock.end_write();
329  } else {
330  this->root_lock.abort_write();
331  }
332  }
333  }
334 
335  // insert element in right fragment
336  if (((typename parenttype::size_type)idx) > cur->numElements) {
337  // release current lock
338  cur->lock.end_write();
339 
340  // insert in sibling
341  return insert(k, hints, f);
342  }
343  }
344 
345  // ok - no split necessary
346  assert(cur->numElements < parenttype::node::maxKeys && "Split required!");
347 
348  // move keys
349  for (int j = cur->numElements; j > idx; --j) {
350  cur->keys[j] = cur->keys[j - 1];
351  }
352 
353  // insert new element
354  typename Functor::result_type res = f(k);
355  cur->keys[idx] = k;
356  cur->numElements++;
357 
358  // release lock on current node
359  cur->lock.end_write();
360 
361  // remember last insertion position
362  hints.last_insert.access(cur);
363  return res;
364  }
365 
366 #else
367  // special handling for inserting first element
368  if (this->empty()) {
369  // create new node
370  this->leftmost = new typename parenttype::leaf_node();
371  this->leftmost->numElements = 1;
372  // call the functor as we've successfully inserted
373  typename Functor::result_type res = f(k);
374  this->leftmost->keys[0] = k;
375  this->root = this->leftmost;
376 
377  hints.last_insert.access(this->leftmost);
378 
379  return res;
380  }
381 
382  // insert using iterative implementation
383  typename parenttype::node* cur = this->root;
384 
385  auto checkHints = [&](typename parenttype::node* last_insert) {
386  if (!last_insert) return false;
387  if (!this->weak_covers(last_insert, k)) return false;
388  cur = last_insert;
389  return true;
390  };
391 
392  // test last insert
393  if (hints.last_insert.any(checkHints)) {
394  this->hint_stats.inserts.addHit();
395  } else {
396  this->hint_stats.inserts.addMiss();
397  }
398 
399  while (true) {
400  // handle inner nodes
401  if (cur->inner) {
402  auto a = &(cur->keys[0]);
403  auto b = &(cur->keys[cur->numElements]);
404 
405  auto pos = this->search.lower_bound(k, a, b, this->weak_comp);
406  auto idx = pos - a;
407 
408  // early exit for sets
409  if (isSet && pos != b && this->weak_equal(*pos, k)) {
410  // update provenance information
411  if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *pos)) {
412  this->update(*pos, k);
413  return (*pos).second;
414  }
415 
416  return (*pos).second;
417  }
418 
419  cur = cur->getChild(idx);
420  continue;
421  }
422 
423  // the rest is for leaf nodes
424  assert(!cur->inner);
425 
426  // -- insert node in leaf node --
427 
428  auto a = &(cur->keys[0]);
429  auto b = &(cur->keys[cur->numElements]);
430 
431  auto pos = this->search.upper_bound(k, a, b, this->weak_comp);
432  auto idx = pos - a;
433 
434  // early exit for sets
435  if (isSet && pos != a && this->weak_equal(*(pos - 1), k)) {
436  // update provenance information
437  if (typeid(Comparator) != typeid(WeakComparator) && this->less(k, *(pos - 1))) {
438  this->update(*(pos - 1), k);
439  return (*(pos - 1)).second;
440  }
441 
442  return (*(pos - 1)).second;
443  }
444 
445  if (cur->numElements >= parenttype::node::maxKeys) {
446  // split this node
447  idx -= cur->rebalance_or_split(
448  const_cast<typename parenttype::node**>(&this->root), this->root_lock, idx);
449 
450  // insert element in right fragment
451  if (((typename parenttype::size_type)idx) > cur->numElements) {
452  idx -= cur->numElements + 1;
453  cur = cur->parent->getChild(cur->position + 1);
454  }
455  }
456 
457  // ok - no split necessary
458  assert(cur->numElements < parenttype::node::maxKeys && "Split required!");
459 
460  // move keys
461  for (int j = cur->numElements; j > idx; --j) {
462  cur->keys[j] = cur->keys[j - 1];
463  }
464 
465  // call the functor as we've successfully inserted
466  typename Functor::result_type res = f(k);
467  // insert new element
468  cur->keys[idx] = k;
469  cur->numElements++;
470 
471  // remember last insertion position
472  hints.last_insert.access(cur);
473  return res;
474  }
475 #endif
476  }
477 
478  /**
479  * Inserts the given range of elements into this tree.
480  */
481  template <typename Iter>
482  void insert(const Iter& a, const Iter& b) {
483  // TODO: improve this beyond a naive insert
484  typename parenttype::operation_hints hints;
485  // a naive insert so far .. seems to work fine
486  for (auto it = a; it != b; ++it) {
487  // use insert with hint
488  insert(*it, hints);
489  }
490  }
491 
492  /**
493  * Swaps the content of this tree with the given tree. This
494  * is a much more efficient operation than creating a copy and
495  * realizing the swap utilizing assignment operations.
496  */
497  void swap(LambdaBTree& other) {
498  // swap the content
499  std::swap(this->root, other.root);
500  std::swap(this->leftmost, other.leftmost);
501  }
502 
503  // Implementation of the assignment operation for trees.
504  LambdaBTree& operator=(const LambdaBTree& other) {
505  // check identity
506  if (this == &other) {
507  return *this;
508  }
509 
510  // create a deep-copy of the content of the other tree
511  // shortcut for empty sets
512  if (other.empty()) {
513  return *this;
514  }
515 
516  // clone content (deep copy)
517  this->root = other.root->clone();
518 
519  // update leftmost reference
520  auto tmp = this->root;
521  while (!tmp->isLeaf()) {
522  tmp = tmp->getChild(0);
523  }
524  this->leftmost = static_cast<typename parenttype::leaf_node*>(tmp);
525 
526  // done
527  return *this;
528  }
529 
530  // Implementation of an equality operation for trees.
531  bool operator==(const LambdaBTree& other) const {
532  // check identity
533  if (this == &other) {
534  return true;
535  }
536 
537  // check size
538  if (this->size() != other.size()) {
539  return false;
540  }
541  if (this->size() < other.size()) {
542  return other == *this;
543  }
544 
545  // check content
546  for (const auto& key : other) {
547  if (!contains(key)) {
548  return false;
549  }
550  }
551  return true;
552  }
553 
554  // Implementation of an inequality operation for trees.
555  bool operator!=(const LambdaBTree& other) const {
556  return !(*this == other);
557  }
558 };
559 
560 } // end namespace detail
561 
562 /**
563  * A b-tree based set implementation.
564  *
565  * @tparam Key .. the element type to be stored in this set
566  * @tparam Functor .. a std::function that is invoked on successful insert
567  * @tparam Comparator .. a class defining an order on the stored elements
568  * @tparam Allocator .. utilized for allocating memory for required nodes
569  * @tparam blockSize .. determines the number of bytes/block utilized by leaf nodes
570  * @tparam SearchStrategy .. enables switching between linear, binary or any other search strategy
571  */
572 template <typename Key, typename Functor, typename Comparator = detail::comparator<Key>,
573  typename Allocator = std::allocator<Key>, // is ignored so far
574  unsigned blockSize = 256, typename SearchStrategy = typename detail::default_strategy<Key>::type>
575 class LambdaBTreeSet
576  : public detail::LambdaBTree<Key, Comparator, Allocator, blockSize, SearchStrategy, true, Functor> {
578 
579  friend class detail::LambdaBTree<Key, Comparator, Allocator, blockSize, SearchStrategy, true, Functor>;
580 
581 public:
582  /**
583  * A default constructor creating an empty set.
584  */
586 
587  /**
588  * A constructor creating a set based on the given range.
589  */
590  template <typename Iter>
591  LambdaBTreeSet(const Iter& a, const Iter& b) {
592  this->insert(a, b);
593  }
594 
595  // A copy constructor.
596  LambdaBTreeSet(const LambdaBTreeSet& other) : super(other) {}
597 
598  // A move constructor.
599  LambdaBTreeSet(LambdaBTreeSet&& other) : super(std::move(other)) {}
600 
601 private:
602  // A constructor required by the bulk-load facility.
603  template <typename s, typename n, typename l>
605 
606 public:
607  // Support for the assignment operator.
609  super::operator=(other);
610  return *this;
611  }
612 
613  // Support for the bulk-load operator.
614  template <typename Iter>
615  static LambdaBTreeSet load(const Iter& a, const Iter& b) {
616  return super::template load<LambdaBTreeSet>(a, b);
617  }
618 };
619 
620 } // end of namespace souffle
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::root_lock
lock_type root_lock
Definition: BTree.h:1245
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::empty
bool empty() const
Definition: BTree.h:1316
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::search
const static SearchStrategy search
Definition: BTree.h:277
souffle::detail::LambdaBTree
The actual implementation of a b-tree data structure.
Definition: LambdaBTree.h:66
BTree.h
souffle::detail::btree< StorePair, souffle::EqrelMapComparator< StorePair >, std::allocator< StorePair >, blockSize, typename detail::default_strategy< StorePair >::type, isSet, souffle::EqrelMapComparator< StorePair >, detail::updater< StorePair > >
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::update
void update(Key &old_k, const Key &new_k)
Definition: BTree.h:304
souffle::detail::btree::btree_operation_hints::last_insert
node_cache last_insert
Definition: BTree.h:1208
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::leftmost
leaf_node * leftmost
Definition: BTree.h:1249
souffle::OptimisticReadWriteLock::start_read
Lease start_read()
Definition: ParallelUtil.h:532
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::weak_comp
Comparator weak_comp
Definition: BTree.h:291
ParallelUtil.h
souffle::LambdaBTreeSet::super
detail::LambdaBTree< Key, Comparator, Allocator, blockSize, SearchStrategy, true, Functor > super
Definition: LambdaBTree.h:586
souffle::detail::LambdaBTree::operator=
LambdaBTree & operator=(const LambdaBTree &other)
Definition: LambdaBTree.h:531
Comparator
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::less
bool less(const Key &a, const Key &b) const
Definition: BTree.h:283
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::comp
Comparator comp
Definition: BTree.h:281
souffle::detail::btree::size_type
std::size_t size_type
Definition: BTree.h:310
n
var n
Definition: htmlJsChartistMin.h:15
souffle::detail::LambdaBTree::insert
Functor::result_type insert(Key &k, const Functor &f)
Inserts the given key into this tree.
Definition: LambdaBTree.h:87
j
var j
Definition: htmlJsChartistMin.h:15
souffle::OptimisticReadWriteLock::abort_write
void abort_write()
Definition: ParallelUtil.h:554
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::weak_equal
bool weak_equal(const Key &a, const Key &b) const
Definition: BTree.h:297
l
var l
Definition: htmlJsChartistMin.h:15
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::weak_covers
bool weak_covers(const node *node, const Key &k) const
Determines whether the range covered by the given node is also covering the given key value.
Definition: BTree.h:2146
ContainerUtil.h
souffle::LambdaBTreeSet::load
static LambdaBTreeSet load(const Iter &a, const Iter &b)
Definition: LambdaBTree.h:624
souffle::LambdaBTreeSet::LambdaBTreeSet
LambdaBTreeSet(const Comparator &comp=Comparator())
A default constructor creating an empty set.
Definition: LambdaBTree.h:594
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::root
node * root
Definition: BTree.h:1242
souffle::detail::LambdaBTree::swap
void swap(LambdaBTree &other)
Swaps the content of this tree with the given tree.
Definition: LambdaBTree.h:524
souffle::OptimisticReadWriteLock::start_write
void start_write()
Definition: ParallelUtil.h:544
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::hint_stats
hint_statistics hint_stats
Definition: BTree.h:1269
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::size
size_type size() const
Definition: BTree.h:1321
souffle::OptimisticReadWriteLock::try_start_write
bool try_start_write()
Definition: ParallelUtil.h:546
k
var k
Definition: htmlJsChartistMin.h:15
souffle::OptimisticReadWriteLock::end_write
void end_write()
Definition: ParallelUtil.h:556
souffle::detail::LambdaBTree::operator==
bool operator==(const LambdaBTree &other) const
Definition: LambdaBTree.h:558
souffle::LRUCache::any
bool any(const Op &op) const
Definition: CacheUtil.h:142
souffle::detail::LambdaBTree::LambdaBTree
LambdaBTree(const Comparator &comp=Comparator(), const WeakComparator &weak_comp=WeakComparator())
Definition: LambdaBTree.h:81
b
l j a showGridBackground &&c b raw series this eventEmitter b
Definition: htmlJsChartistMin.h:15
std
Definition: Brie.h:3053
souffle::OptimisticReadWriteLock::end_read
bool end_read(const Lease &)
Definition: ParallelUtil.h:540
souffle::detail::LambdaBTree::parenttype
btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, WeakComparator, Updater > parenttype
Definition: LambdaBTree.h:79
souffle::detail::btree::btree_operation_hints
A collection of operation hints speeding up some of the involved operations by exploiting temporal lo...
Definition: BTree.h:1204
souffle::detail::btree< Key, Comparator, Allocator, blockSize, SearchStrategy, isSet, Comparator, detail::updater< Key > >::contains
bool contains(const Key &k) const
Determines whether the given element is a member of this tree.
Definition: BTree.h:1756
souffle
Definition: AggregateOp.h:25
souffle::LambdaBTreeSet::operator=
LambdaBTreeSet & operator=(const LambdaBTreeSet &other)
Definition: LambdaBTree.h:617
souffle::LRUCache::access
void access(const T &val)
Definition: CacheUtil.h:76
souffle::detail::btree::operation_hints
btree_operation_hints< 1 > operation_hints
Definition: BTree.h:1231
souffle::LambdaBTreeSet
A b-tree based set implementation.
Definition: LambdaBTree.h:584
souffle::detail::LambdaBTree::operator!=
bool operator!=(const LambdaBTree &other) const
Definition: LambdaBTree.h:582
souffle::detail::btree::node::maxKeys
static constexpr size_t maxKeys
The actual number of keys/node corrected by functional requirements.
Definition: BTree.h:390