souffle  2.0.2-371-g6315b36
Brie.h
Go to the documentation of this file.
1 /*
2  * Souffle - A Datalog Compiler
3  * Copyright (c) 2013, 2015, Oracle and/or its affiliates. All rights reserved
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 Brie.h
12  *
13  * This header file contains the implementation for a generic, fixed
14  * length integer trie.
15  *
16  * Tries trie is utilized to store n-ary tuples of integers. Each level
17  * is implemented via a sparse array (also covered by this header file),
18  * referencing the following nested level. The leaf level is realized
19  * by a sparse bit-map to minimize the memory footprint.
20  *
21  * Multiple insert operations can be be conducted concurrently on trie
22  * structures. So can read-only operations. However, inserts and read
23  * operations may not be conducted at the same time.
24  *
25  ***********************************************************************/
26 
27 #pragma once
28 
29 #include "souffle/RamTypes.h"
34 #include "souffle/utility/span.h"
35 #include <algorithm>
36 #include <atomic>
37 #include <bitset>
38 #include <cassert>
39 #include <climits>
40 #include <cstdint>
41 #include <cstring>
42 #include <iostream>
43 #include <iterator>
44 #include <limits>
45 #include <type_traits>
46 #include <utility>
47 #include <vector>
48 
49 // TODO: replace intrinsics w/ std lib functions?
50 #ifdef _WIN32
51 /**
52  * When compiling for windows, redefine the gcc builtins which are used to
53  * their equivalents on the windows platform.
54  */
55 #define __sync_synchronize MemoryBarrier
56 #define __sync_bool_compare_and_swap(ptr, oldval, newval) \
57  (InterlockedCompareExchangePointer((void* volatile*)ptr, (void*)newval, (void*)oldval) == (void*)oldval)
58 #endif // _WIN32
59 
60 namespace souffle {
61 
62 template <unsigned Dim>
63 class Trie;
64 
65 namespace detail::brie {
66 
67 // FIXME: These data structs should be parameterised/made agnostic to `RamDomain` type.
69 
70 using tcb::make_span;
71 
72 template <typename A>
74  using value_type = A;
75  using difference_type = ptrdiff_t;
76  using iterator_category = std::forward_iterator_tag;
77  using pointer = const value_type*;
78  using reference = const value_type&;
79 };
80 
81 template <typename A, size_t arity>
82 auto copy(span<A, arity> s) {
83  std::array<std::decay_t<A>, arity> cpy;
84  std::copy_n(s.begin(), arity, cpy.begin());
85  return cpy;
86 }
87 
88 template <size_t offset, typename A, size_t arity>
89 auto drop(span<A, arity> s) -> std::enable_if_t<offset <= arity, span<A, arity - offset>> {
90  return {s.begin() + offset, s.end()};
91 }
92 
93 template <typename C>
94 auto tail(C& s) {
95  return drop<1>(make_span(s));
96 }
97 
98 /**
99  * A templated functor to obtain default values for
100  * unspecified elements of sparse array instances.
101  */
102 template <typename T>
103 struct default_factory {
104  T operator()() const {
105  return T(); // just use the default constructor
106  }
107 };
108 
109 /**
110  * A functor representing the identity function.
111  */
112 template <typename T>
113 struct identity {
114  T operator()(T v) const {
115  return v;
116  }
117 };
118 
119 /**
120  * A operation to be utilized by the sparse map when merging
121  * elements associated to different values.
122  */
123 template <typename T>
124 struct default_merge {
125  /**
126  * Merges two values a and b when merging spase maps.
127  */
128  T operator()(T a, T b) const {
130  // if a is the default => us b, else stick to a
131  return (a != def()) ? a : b;
132  }
133 };
134 
135 /**
136  * Iterator type for `souffle::SparseArray`.
137  */
138 template <typename SparseArray>
139 struct SparseArrayIter {
140  using Node = typename SparseArray::Node;
141  using index_type = typename SparseArray::index_type;
142  using array_value_type = typename SparseArray::value_type;
143 
144  using value_type = std::pair<index_type, array_value_type>;
145 
146  SparseArrayIter() = default; // default constructor -- creating an end-iterator
147  SparseArrayIter(const SparseArrayIter&) = default;
148  SparseArrayIter& operator=(const SparseArrayIter&) = default;
149 
150  SparseArrayIter(const Node* node, value_type value) : node(node), value(std::move(value)) {}
151 
152  SparseArrayIter(const Node* first, index_type firstOffset) : node(first), value(firstOffset, 0) {
153  // if the start is the end => we are done
154  if (!first) return;
155 
156  // load the value
157  if (first->cell[0].value == array_value_type()) {
158  ++(*this); // walk to first element
159  } else {
160  value.second = first->cell[0].value;
161  }
162  }
163 
164  // the equality operator as required by the iterator concept
165  bool operator==(const SparseArrayIter& other) const {
166  // only equivalent if pointing to the end
167  return (node == nullptr && other.node == nullptr) ||
168  (node == other.node && value.first == other.value.first);
169  }
170 
171  // the not-equality operator as required by the iterator concept
172  bool operator!=(const SparseArrayIter& other) const {
173  return !(*this == other);
174  }
175 
176  // the deref operator as required by the iterator concept
177  const value_type& operator*() const {
178  return value;
179  }
180 
181  // support for the pointer operator
182  const value_type* operator->() const {
183  return &value;
184  }
185 
186  // the increment operator as required by the iterator concept
188  assert(!isEnd());
189  // get current offset
191 
192  // go to next non-empty value in current node
193  do {
194  x++;
195  } while (x < SparseArray::NUM_CELLS && node->cell[x].value == array_value_type());
196 
197  // check whether one has been found
199  // update value and be done
200  value.first = (value.first & ~SparseArray::INDEX_MASK) | x;
201  value.second = node->cell[x].value;
202  return *this; // done
203  }
204 
205  // go to parent
206  node = node->parent;
207  int level = 1;
208 
209  // get current index on this level
210  x = SparseArray::getIndex(brie_element_type(value.first), level);
211  x++;
212 
213  while (level > 0 && node) {
214  // search for next child
215  while (x < SparseArray::NUM_CELLS) {
216  if (node->cell[x].ptr != nullptr) {
217  break;
218  }
219  x++;
220  }
221 
222  // pick next step
223  if (x < SparseArray::NUM_CELLS) {
224  // going down
225  node = node->cell[x].ptr;
226  value.first &= SparseArray::getLevelMask(level + 1);
227  value.first |= x << (SparseArray::BIT_PER_STEP * level);
228  level--;
229  x = 0;
230  } else {
231  // going up
232  node = node->parent;
233  level++;
234 
235  // get current index on this level
236  x = SparseArray::getIndex(brie_element_type(value.first), level);
237  x++; // go one step further
238  }
239  }
240 
241  // check whether it is the end of range
242  if (node == nullptr) {
243  return *this;
244  }
245 
246  // search the first value in this node
247  x = 0;
248  while (node->cell[x].value == array_value_type()) {
249  x++;
250  }
251 
252  // update value
253  value.first |= x;
254  value.second = node->cell[x].value;
255 
256  // done
257  return *this;
258  }
259 
261  auto cpy = *this;
262  ++(*this);
263  return cpy;
264  }
265 
266  // True if this iterator is passed the last element.
267  bool isEnd() const {
268  return node == nullptr;
269  }
270 
271  // enables this iterator core to be printed (for debugging)
272  void print(std::ostream& out) const {
273  // `StreamUtil.h` defines an overload for `pair`, but we can't rely on it b/c
274  // it's disabled if `__EMBEDDED__` is defined.
275  out << "SparseArrayIter(" << node << " @ (" << value.first << ", " << value.second << "))";
276  }
277 
278  friend std::ostream& operator<<(std::ostream& out, const SparseArrayIter& iter) {
279  iter.print(out);
280  return out;
281  }
282 
283 private:
284  // a pointer to the leaf node currently processed or null (end)
285  const Node* node{};
286 
287  // the value currently pointed to
289 };
290 
291 } // namespace detail::brie
292 
293 using namespace detail::brie;
294 
295 /**
296  * A sparse array simulates an array associating to every element
297  * of uint32_t an element of a generic type T. Any non-defined element
298  * will be default-initialized utilizing the detail::brie::default_factory
299  * functor.
300  *
301  * Internally the array is organized as a balanced tree. The leaf
302  * level of the tree corresponds to the elements of the represented
303  * array. Inner nodes utilize individual bits of the indices to reference
304  * sub-trees. For efficiency reasons, only the minimal sub-tree required
305  * to cover all non-null / non-default values stored in the array is
306  * maintained. Furthermore, several levels of nodes are aggreated in a
307  * B-tree like fashion to inprove cache utilization and reduce the number
308  * of steps required for lookup and insert operations.
309  *
310  * @tparam T the type of the stored elements
311  * @tparam BITS the number of bits consumed per node-level
312  * e.g. if it is set to 3, the resulting tree will be of a degree of
313  * 2^3=8, and thus 8 child-pointers will be stored in each inner node
314  * and as many values will be stored in each leaf node.
315  * @tparam merge_op the functor to be utilized when merging the content of two
316  * instances of this type.
317  * @tparam copy_op a functor to be applied to each stored value when copying an
318  * instance of this array. For instance, this is utilized by the
319  * trie implementation to create a clone of each sub-tree instead
320  * of preserving the original pointer.
321  */
322 template <typename T, unsigned BITS = 6, typename merge_op = default_merge<T>, typename copy_op = identity<T>>
323 class SparseArray {
324  template <typename A>
325  friend struct detail::brie::SparseArrayIter;
326 
328  using key_type = uint64_t;
329 
330  // some internal constants
331  static constexpr int BIT_PER_STEP = BITS;
332  static constexpr int NUM_CELLS = 1 << BIT_PER_STEP;
333  static constexpr key_type INDEX_MASK = NUM_CELLS - 1;
334 
335 public:
336  // the type utilized for indexing contained elements
337  using index_type = key_type;
338 
339  // the type of value stored in this array
340  using value_type = T;
341 
342  // the atomic view on stored values
343  using atomic_value_type = std::atomic<value_type>;
344 
345 private:
346  struct Node;
347 
348  /**
349  * The value stored in a single cell of a inner
350  * or leaf node.
351  */
352  union Cell {
353  // an atomic view on the pointer referencing a nested level
354  std::atomic<Node*> aptr;
355 
356  // a pointer to the nested level (unsynchronized operations)
357  Node* ptr{nullptr};
358 
359  // an atomic view on the value stored in this cell (leaf node)
360  atomic_value_type avalue;
361 
362  // the value stored in this cell (unsynchronized access, leaf node)
363  value_type value;
364  };
365 
366  /**
367  * The node type of the internally maintained tree.
368  */
369  struct Node {
370  // a pointer to the parent node (for efficient iteration)
371  const Node* parent;
372  // the pointers to the child nodes (inner nodes) or the stored values (leaf nodes)
373  Cell cell[NUM_CELLS];
374  };
375 
376  /**
377  * A struct describing all the information required by the container
378  * class to manage the wrapped up tree.
379  */
380  struct RootInfo {
381  // the root node of the tree
382  Node* root;
383  // the number of levels of the tree
384  uint32_t levels;
385  // the absolute offset of the theoretical first element in the tree
386  index_type offset;
387 
388  // the first leaf node in the tree
389  Node* first;
390  // the absolute offset of the first element in the first leaf node
391  index_type firstOffset;
392  };
393 
394  union {
395  RootInfo unsynced; // for sequential operations
396  volatile RootInfo synced; // for synchronized operations
397  };
398 
399 public:
400  /**
401  * A default constructor creating an empty sparse array.
402  */
403  SparseArray() : unsynced(RootInfo{nullptr, 0, 0, nullptr, std::numeric_limits<index_type>::max()}) {}
404 
405  /**
406  * A copy constructor for sparse arrays. It creates a deep
407  * copy of the data structure maintained by the handed in
408  * array instance.
409  */
410  SparseArray(const SparseArray& other)
411  : unsynced(RootInfo{clone(other.unsynced.root, other.unsynced.levels), other.unsynced.levels,
412  other.unsynced.offset, nullptr, other.unsynced.firstOffset}) {
413  if (unsynced.root) {
414  unsynced.root->parent = nullptr;
415  unsynced.first = findFirst(unsynced.root, unsynced.levels);
416  }
417  }
418 
419  /**
420  * A r-value based copy constructor for sparse arrays. It
421  * takes over ownership of the structure maintained by the
422  * handed in array.
423  */
424  SparseArray(SparseArray&& other)
425  : unsynced(RootInfo{other.unsynced.root, other.unsynced.levels, other.unsynced.offset,
426  other.unsynced.first, other.unsynced.firstOffset}) {
427  other.unsynced.root = nullptr;
428  other.unsynced.levels = 0;
429  other.unsynced.first = nullptr;
430  }
431 
432  /**
433  * A destructor for sparse arrays clearing up the internally
434  * maintained data structure.
435  */
436  ~SparseArray() {
437  clean();
438  }
439 
440  /**
441  * An assignment creating a deep copy of the handed in
442  * array structure (utilizing the copy functor provided
443  * as a template parameter).
444  */
445  SparseArray& operator=(const SparseArray& other) {
446  if (this == &other) return *this;
447 
448  // clean this one
449  clean();
450 
451  // copy content
452  unsynced.levels = other.unsynced.levels;
453  unsynced.root = clone(other.unsynced.root, unsynced.levels);
454  if (unsynced.root) {
455  unsynced.root->parent = nullptr;
456  }
457  unsynced.offset = other.unsynced.offset;
458  unsynced.first = (unsynced.root) ? findFirst(unsynced.root, unsynced.levels) : nullptr;
459  unsynced.firstOffset = other.unsynced.firstOffset;
460 
461  // done
462  return *this;
463  }
464 
465  /**
466  * An assignment operation taking over ownership
467  * from a r-value reference to a sparse array.
468  */
469  SparseArray& operator=(SparseArray&& other) {
470  // clean this one
471  clean();
472 
473  // harvest content
474  unsynced.root = other.unsynced.root;
475  unsynced.levels = other.unsynced.levels;
476  unsynced.offset = other.unsynced.offset;
477  unsynced.first = other.unsynced.first;
478  unsynced.firstOffset = other.unsynced.firstOffset;
479 
480  // reset other
481  other.unsynced.root = nullptr;
482  other.unsynced.levels = 0;
483  other.unsynced.first = nullptr;
484 
485  // done
486  return *this;
487  }
488 
489  /**
490  * Tests whether this sparse array is empty, thus it only
491  * contains default-values, or not.
492  */
493  bool empty() const {
494  return unsynced.root == nullptr;
495  }
496 
497  /**
498  * Computes the number of non-empty elements within this
499  * sparse array.
500  */
501  std::size_t size() const {
502  // quick one for the empty map
503  if (empty()) return 0;
504 
505  // count elements -- since maintaining is making inserts more expensive
506  std::size_t res = 0;
507  for (auto it = begin(); it != end(); ++it) {
508  ++res;
509  }
510  return res;
511  }
512 
513 private:
514  /**
515  * Computes the memory usage of the given sub-tree.
516  */
517  static std::size_t getMemoryUsage(const Node* node, int level) {
518  // support null-nodes
519  if (!node) return 0;
520 
521  // add size of current node
522  std::size_t res = sizeof(Node);
523 
524  // sum up memory usage of child nodes
525  if (level > 0) {
526  for (int i = 0; i < NUM_CELLS; i++) {
527  res += getMemoryUsage(node->cell[i].ptr, level - 1);
528  }
529  }
530 
531  // done
532  return res;
533  }
534 
535 public:
536  /**
537  * Computes the total memory usage of this data structure.
538  */
539  std::size_t getMemoryUsage() const {
540  // the memory of the wrapper class
541  std::size_t res = sizeof(*this);
542 
543  // add nodes
544  if (unsynced.root) {
545  res += getMemoryUsage(unsynced.root, unsynced.levels);
546  }
547 
548  // done
549  return res;
550  }
551 
552  /**
553  * Resets the content of this array to default values for each contained
554  * element.
555  */
556  void clear() {
557  clean();
558  unsynced.root = nullptr;
559  unsynced.levels = 0;
560  unsynced.first = nullptr;
561  unsynced.firstOffset = std::numeric_limits<index_type>::max();
562  }
563 
564  /**
565  * A struct to be utilized as a local, temporal context by client code
566  * to speed up the execution of various operations (optional parameter).
567  */
568  struct op_context {
569  index_type lastIndex{0};
570  Node* lastNode{nullptr};
571  op_context() = default;
572  };
573 
574 private:
575  // ---------------------------------------------------------------------
576  // Optimistic Locking of Root-Level Infos
577  // ---------------------------------------------------------------------
578 
579  /**
580  * A struct to cover a snapshot of the root node state.
581  */
582  struct RootInfoSnapshot {
583  // the current pointer to a root node
584  Node* root;
585  // the current number of levels
586  uint32_t levels;
587  // the current offset of the first theoretical element
588  index_type offset;
589  // a version number for the optimistic locking
590  uintptr_t version;
591  };
592 
593  /**
594  * Obtains the current version of the root.
595  */
596  uint64_t getRootVersion() const {
597  // here it is assumed that the load of a 64-bit word is atomic
598  return (uint64_t)synced.root;
599  }
600 
601  /**
602  * Obtains a snapshot of the current root information.
603  */
604  RootInfoSnapshot getRootInfo() const {
605  RootInfoSnapshot res{};
606  do {
607  // first take the mod counter
608  do {
609  // if res.mod % 2 == 1 .. there is an update in progress
610  res.version = getRootVersion();
611  } while (res.version % 2);
612 
613  // then the rest
614  res.root = synced.root;
615  res.levels = synced.levels;
616  res.offset = synced.offset;
617 
618  // check consistency of obtained data (optimistic locking)
619  } while (res.version != getRootVersion());
620 
621  // got a consistent snapshot
622  return res;
623  }
624 
625  /**
626  * Updates the current root information based on the handed in modified
627  * snapshot instance if the version number of the snapshot still corresponds
628  * to the current version. Otherwise a concurrent update took place and the
629  * operation is aborted.
630  *
631  * @param info the updated information to be assigned to the active root-info data
632  * @return true if successfully updated, false if aborted
633  */
634  bool tryUpdateRootInfo(const RootInfoSnapshot& info) {
635  // check mod counter
636  uintptr_t version = info.version;
637 
638  // update root to invalid pointer (ending with 1)
639  if (!__sync_bool_compare_and_swap(&synced.root, (Node*)version, (Node*)(version + 1))) {
640  return false;
641  }
642 
643  // conduct update
644  synced.levels = info.levels;
645  synced.offset = info.offset;
646 
647  // update root (and thus the version to enable future retrievals)
648  __sync_synchronize();
649  synced.root = info.root;
650 
651  // done
652  return true;
653  }
654 
655  /**
656  * A struct summarizing the state of the first node reference.
657  */
658  struct FirstInfoSnapshot {
659  // the pointer to the first node
660  Node* node;
661  // the offset of the first node
662  index_type offset;
663  // the version number of the first node (for the optimistic locking)
664  uintptr_t version;
665  };
666 
667  /**
668  * Obtains the current version number of the first node information.
669  */
670  uint64_t getFirstVersion() const {
671  // here it is assumed that the load of a 64-bit word is atomic
672  return (uint64_t)synced.first;
673  }
674 
675  /**
676  * Obtains a snapshot of the current first-node information.
677  */
678  FirstInfoSnapshot getFirstInfo() const {
679  FirstInfoSnapshot res{};
680  do {
681  // first take the version
682  do {
683  res.version = getFirstVersion();
684  } while (res.version % 2);
685 
686  // collect the values
687  res.node = synced.first;
688  res.offset = synced.firstOffset;
689 
690  } while (res.version != getFirstVersion());
691 
692  // we got a consistent snapshot
693  return res;
694  }
695 
696  /**
697  * Updates the information stored regarding the first node in a
698  * concurrent setting utilizing a optimistic locking approach.
699  * This is identical to the approach utilized for the root info.
700  */
701  bool tryUpdateFirstInfo(const FirstInfoSnapshot& info) {
702  // check mod counter
703  uintptr_t version = info.version;
704 
705  // temporary update first pointer to point to uneven value (lock-out)
706  if (!__sync_bool_compare_and_swap(&synced.first, (Node*)version, (Node*)(version + 1))) {
707  return false;
708  }
709 
710  // conduct update
711  synced.firstOffset = info.offset;
712 
713  // update node pointer (and thus the version number)
714  __sync_synchronize();
715  synced.first = info.node; // must be last (and atomic)
716 
717  // done
718  return true;
719  }
720 
721 public:
722  /**
723  * Obtains a mutable reference to the value addressed by the given index.
724  *
725  * @param i the index of the element to be addressed
726  * @return a mutable reference to the corresponding element
727  */
728  value_type& get(index_type i) {
729  op_context ctxt;
730  return get(i, ctxt);
731  }
732 
733  /**
734  * Obtains a mutable reference to the value addressed by the given index.
735  *
736  * @param i the index of the element to be addressed
737  * @param ctxt a operation context to exploit state-less temporal locality
738  * @return a mutable reference to the corresponding element
739  */
740  value_type& get(index_type i, op_context& ctxt) {
741  return getLeaf(i, ctxt).value;
742  }
743 
744  /**
745  * Obtains a mutable reference to the atomic value addressed by the given index.
746  *
747  * @param i the index of the element to be addressed
748  * @return a mutable reference to the corresponding element
749  */
750  atomic_value_type& getAtomic(index_type i) {
751  op_context ctxt;
752  return getAtomic(i, ctxt);
753  }
754 
755  /**
756  * Obtains a mutable reference to the atomic value addressed by the given index.
757  *
758  * @param i the index of the element to be addressed
759  * @param ctxt a operation context to exploit state-less temporal locality
760  * @return a mutable reference to the corresponding element
761  */
762  atomic_value_type& getAtomic(index_type i, op_context& ctxt) {
763  return getLeaf(i, ctxt).avalue;
764  }
765 
766 private:
767  /**
768  * An internal function capable of navigating to a given leaf node entry.
769  * If the cell does not exist yet it will be created as a side-effect.
770  *
771  * @param i the index of the requested cell
772  * @param ctxt a operation context to exploit state-less temporal locality
773  * @return a reference to the requested cell
774  */
775  inline Cell& getLeaf(index_type i, op_context& ctxt) {
776  // check context
777  if (ctxt.lastNode && (ctxt.lastIndex == (i & ~INDEX_MASK))) {
778  // return reference to referenced
779  return ctxt.lastNode->cell[i & INDEX_MASK];
780  }
781 
782  // get snapshot of root
783  auto info = getRootInfo();
784 
785  // check for emptiness
786  if (info.root == nullptr) {
787  // build new root node
788  info.root = newNode();
789 
790  // initialize the new node
791  info.root->parent = nullptr;
792  info.offset = i & ~(INDEX_MASK);
793 
794  // try updating root information atomically
795  if (tryUpdateRootInfo(info)) {
796  // success -- finish get call
797 
798  // update first
799  auto firstInfo = getFirstInfo();
800  while (info.offset < firstInfo.offset) {
801  firstInfo.node = info.root;
802  firstInfo.offset = info.offset;
803  if (!tryUpdateFirstInfo(firstInfo)) {
804  // there was some concurrent update => check again
805  firstInfo = getFirstInfo();
806  }
807  }
808 
809  // return reference to proper cell
810  return info.root->cell[i & INDEX_MASK];
811  }
812 
813  // somebody else was faster => use standard insertion procedure
814  delete info.root;
815 
816  // retrieve new root info
817  info = getRootInfo();
818 
819  // make sure there is a root
820  assert(info.root);
821  }
822 
823  // for all other inserts
824  // - check boundary
825  // - navigate to node
826  // - insert value
827 
828  // check boundaries
829  while (!inBoundaries(i, info.levels, info.offset)) {
830  // boundaries need to be expanded by growing upwards
831  raiseLevel(info); // try raising level unless someone else did already
832  // update root info
833  info = getRootInfo();
834  }
835 
836  // navigate to node
837  Node* node = info.root;
838  unsigned level = info.levels;
839  while (level != 0) {
840  // get X coordinate
841  auto x = getIndex(brie_element_type(i), level);
842 
843  // decrease level counter
844  --level;
845 
846  // check next node
847  std::atomic<Node*>& aNext = node->cell[x].aptr;
848  Node* next = aNext;
849  if (!next) {
850  // create new sub-tree
851  Node* newNext = newNode();
852  newNext->parent = node;
853 
854  // try to update next
855  if (!aNext.compare_exchange_strong(next, newNext)) {
856  // some other thread was faster => use updated next
857  delete newNext;
858  } else {
859  // the locally created next is the new next
860  next = newNext;
861 
862  // update first
863  if (level == 0) {
864  // compute offset of this node
865  auto off = i & ~INDEX_MASK;
866 
867  // fast over-approximation of whether a update is necessary
868  if (off < unsynced.firstOffset) {
869  // update first reference if this one is the smallest
870  auto first_info = getFirstInfo();
871  while (off < first_info.offset) {
872  first_info.node = next;
873  first_info.offset = off;
874  if (!tryUpdateFirstInfo(first_info)) {
875  // there was some concurrent update => check again
876  first_info = getFirstInfo();
877  }
878  }
879  }
880  }
881  }
882 
883  // now next should be defined
884  assert(next);
885  }
886 
887  // continue one level below
888  node = next;
889  }
890 
891  // update context
892  ctxt.lastIndex = (i & ~INDEX_MASK);
893  ctxt.lastNode = node;
894 
895  // return reference to cell
896  return node->cell[i & INDEX_MASK];
897  }
898 
899 public:
900  /**
901  * Updates the value stored in cell i by the given value.
902  */
903  void update(index_type i, const value_type& val) {
904  op_context ctxt;
905  update(i, val, ctxt);
906  }
907 
908  /**
909  * Updates the value stored in cell i by the given value. A operation
910  * context can be provided for exploiting temporal locality.
911  */
912  void update(index_type i, const value_type& val, op_context& ctxt) {
913  get(i, ctxt) = val;
914  }
915 
916  /**
917  * Obtains the value associated to index i -- which might be
918  * the default value of the covered type if the value hasn't been
919  * defined previously.
920  */
921  value_type operator[](index_type i) const {
922  return lookup(i);
923  }
924 
925  /**
926  * Obtains the value associated to index i -- which might be
927  * the default value of the covered type if the value hasn't been
928  * defined previously.
929  */
930  value_type lookup(index_type i) const {
931  op_context ctxt;
932  return lookup(i, ctxt);
933  }
934 
935  /**
936  * Obtains the value associated to index i -- which might be
937  * the default value of the covered type if the value hasn't been
938  * defined previously. A operation context can be provided for
939  * exploiting temporal locality.
940  */
941  value_type lookup(index_type i, op_context& ctxt) const {
942  // check whether it is empty
943  if (!unsynced.root) return default_factory<value_type>()();
944 
945  // check boundaries
946  if (!inBoundaries(i)) return default_factory<value_type>()();
947 
948  // check context
949  if (ctxt.lastNode && ctxt.lastIndex == (i & ~INDEX_MASK)) {
950  return ctxt.lastNode->cell[i & INDEX_MASK].value;
951  }
952 
953  // navigate to value
954  Node* node = unsynced.root;
955  unsigned level = unsynced.levels;
956  while (level != 0) {
957  // get X coordinate
958  auto x = getIndex(brie_element_type(i), level);
959 
960  // decrease level counter
961  --level;
962 
963  // check next node
964  Node* next = node->cell[x].ptr;
965 
966  // check next step
967  if (!next) return default_factory<value_type>()();
968 
969  // continue one level below
970  node = next;
971  }
972 
973  // remember context
974  ctxt.lastIndex = (i & ~INDEX_MASK);
975  ctxt.lastNode = node;
976 
977  // return reference to cell
978  return node->cell[i & INDEX_MASK].value;
979  }
980 
981 private:
982  /**
983  * A static operation utilized internally for merging sub-trees recursively.
984  *
985  * @param parent the parent node of the current merge operation
986  * @param trg a reference to the pointer the cloned node should be stored to
987  * @param src the node to be cloned
988  * @param levels the height of the cloned node
989  */
990  static void merge(const Node* parent, Node*& trg, const Node* src, int levels) {
991  // if other side is null => done
992  if (src == nullptr) {
993  return;
994  }
995 
996  // if the trg sub-tree is empty, clone the corresponding branch
997  if (trg == nullptr) {
998  trg = clone(src, levels);
999  if (trg != nullptr) {
1000  trg->parent = parent;
1001  }
1002  return; // done
1003  }
1004 
1005  // otherwise merge recursively
1007  // the leaf-node step
1008  if (levels == 0) {
1009  merge_op merg;
1010  for (int i = 0; i < NUM_CELLS; ++i) {
1011  trg->cell[i].value = merg(trg->cell[i].value, src->cell[i].value);
1012  }
1013  return;
1014  }
1015 
1016  // the recursive step
1017  for (int i = 0; i < NUM_CELLS; ++i) {
1018  merge(trg, trg->cell[i].ptr, src->cell[i].ptr, levels - 1);
1019  }
1020  }
1021 
1022 public:
1023  /**
1024  * Adds all the values stored in the given array to this array.
1025  */
1026  void addAll(const SparseArray& other) {
1027  // skip if other is empty
1028  if (other.empty()) {
1029  return;
1030  }
1031 
1032  // special case: emptiness
1033  if (empty()) {
1034  // use assignment operator
1035  *this = other;
1036  return;
1037  }
1038 
1039  // adjust levels
1040  while (unsynced.levels < other.unsynced.levels || !inBoundaries(other.unsynced.offset)) {
1041  raiseLevel();
1042  }
1043 
1044  // navigate to root node equivalent of the other node in this tree
1045  auto level = unsynced.levels;
1046  Node** node = &unsynced.root;
1047  while (level > other.unsynced.levels) {
1048  // get X coordinate
1049  auto x = getIndex(brie_element_type(other.unsynced.offset), level);
1050 
1051  // decrease level counter
1052  --level;
1053 
1054  // check next node
1055  Node*& next = (*node)->cell[x].ptr;
1056  if (!next) {
1057  // create new sub-tree
1058  next = newNode();
1059  next->parent = *node;
1060  }
1061 
1062  // continue one level below
1063  node = &next;
1064  }
1065 
1066  // merge sub-branches from here
1067  merge((*node)->parent, *node, other.unsynced.root, level);
1068 
1069  // update first
1070  if (unsynced.firstOffset > other.unsynced.firstOffset) {
1071  unsynced.first = findFirst(*node, level);
1072  unsynced.firstOffset = other.unsynced.firstOffset;
1073  }
1074  }
1075 
1076  // ---------------------------------------------------------------------
1077  // Iterator
1078  // ---------------------------------------------------------------------
1079 
1080  using iterator = SparseArrayIter<this_t>;
1081 
1082  /**
1083  * Obtains an iterator referencing the first non-default element or end in
1084  * case there are no such elements.
1085  */
1086  iterator begin() const {
1087  return iterator(unsynced.first, unsynced.firstOffset);
1088  }
1089 
1090  /**
1091  * An iterator referencing the position after the last non-default element.
1092  */
1093  iterator end() const {
1094  return iterator();
1095  }
1097  /**
1098  * An operation to obtain an iterator referencing an element addressed by the
1099  * given index. If the corresponding element is a non-default value, a corresponding
1100  * iterator will be returned. Otherwise end() will be returned.
1101  */
1102  iterator find(index_type i) const {
1103  op_context ctxt;
1104  return find(i, ctxt);
1105  }
1106 
1107  /**
1108  * An operation to obtain an iterator referencing an element addressed by the
1109  * given index. If the corresponding element is a non-default value, a corresponding
1110  * iterator will be returned. Otherwise end() will be returned. A operation context
1111  * can be provided for exploiting temporal locality.
1112  */
1113  iterator find(index_type i, op_context& ctxt) const {
1114  // check whether it is empty
1115  if (!unsynced.root) return end();
1116 
1117  // check boundaries
1118  if (!inBoundaries(i)) return end();
1119 
1120  // check context
1121  if (ctxt.lastNode && ctxt.lastIndex == (i & ~INDEX_MASK)) {
1122  Node* node = ctxt.lastNode;
1123 
1124  // check whether there is a proper entry
1125  value_type value = node->cell[i & INDEX_MASK].value;
1126  if (value == value_type{}) {
1127  return end();
1128  }
1129  // return iterator pointing to value
1130  return iterator(node, std::make_pair(i, value));
1131  }
1132 
1133  // navigate to value
1134  Node* node = unsynced.root;
1135  unsigned level = unsynced.levels;
1136  while (level != 0) {
1137  // get X coordinate
1138  auto x = getIndex(i, level);
1139 
1140  // decrease level counter
1141  --level;
1142 
1143  // check next node
1144  Node* next = node->cell[x].ptr;
1145 
1146  // check next step
1147  if (!next) return end();
1148 
1149  // continue one level below
1150  node = next;
1151  }
1152 
1153  // register in context
1154  ctxt.lastNode = node;
1155  ctxt.lastIndex = (i & ~INDEX_MASK);
1156 
1157  // check whether there is a proper entry
1158  value_type value = node->cell[i & INDEX_MASK].value;
1159  if (value == value_type{}) {
1160  return end();
1161  }
1162 
1163  // return iterator pointing to cell
1164  return iterator(node, std::make_pair(i, value));
1165  }
1166 
1167  /**
1168  * An operation obtaining the smallest non-default element such that it's index is >=
1169  * the given index.
1170  */
1171  iterator lowerBound(index_type i) const {
1172  op_context ctxt;
1173  return lowerBound(i, ctxt);
1174  }
1175 
1176  /**
1177  * An operation obtaining the smallest non-default element such that it's index is >=
1178  * the given index. A operation context can be provided for exploiting temporal locality.
1179  */
1180  iterator lowerBound(index_type i, op_context&) const {
1181  // check whether it is empty
1182  if (!unsynced.root) return end();
1183 
1184  // check boundaries
1185  if (!inBoundaries(i)) {
1186  // if it is on the lower end, return minimum result
1187  if (i < unsynced.offset) {
1188  const auto& value = unsynced.first->cell[0].value;
1189  auto res = iterator(unsynced.first, std::make_pair(unsynced.offset, value));
1190  if (value == value_type()) {
1191  ++res;
1192  }
1193  return res;
1194  }
1195  // otherwise it is on the high end, return end iterator
1196  return end();
1197  }
1198 
1199  // navigate to value
1200  Node* node = unsynced.root;
1201  unsigned level = unsynced.levels;
1202  while (true) {
1203  // get X coordinate
1204  auto x = getIndex(brie_element_type(i), level);
1205 
1206  // check next node
1207  Node* next = node->cell[x].ptr;
1208 
1209  // check next step
1210  if (!next) {
1211  if (x == NUM_CELLS - 1) {
1212  ++level;
1213  node = const_cast<Node*>(node->parent);
1214  if (!node) return end();
1215  }
1216 
1217  // continue search
1218  i = i & getLevelMask(level);
1219 
1220  // find next higher value
1221  i += 1ull << (BITS * level);
1222 
1223  } else {
1224  if (level == 0) {
1225  // found boundary
1226  return iterator(node, std::make_pair(i, node->cell[x].value));
1227  }
1228 
1229  // decrease level counter
1230  --level;
1231 
1232  // continue one level below
1233  node = next;
1234  }
1235  }
1236  }
1237 
1238  /**
1239  * An operation obtaining the smallest non-default element such that it's index is greater
1240  * the given index.
1241  */
1242  iterator upperBound(index_type i) const {
1243  op_context ctxt;
1244  return upperBound(i, ctxt);
1245  }
1246 
1247  /**
1248  * An operation obtaining the smallest non-default element such that it's index is greater
1249  * the given index. A operation context can be provided for exploiting temporal locality.
1250  */
1251  iterator upperBound(index_type i, op_context& ctxt) const {
1252  if (i == std::numeric_limits<index_type>::max()) {
1253  return end();
1254  }
1255  return lowerBound(i + 1, ctxt);
1256  }
1257 
1258 private:
1259  /**
1260  * An internal debug utility printing the internal structure of this sparse array to the given output
1261  * stream.
1262  */
1263  void dump(bool detailed, std::ostream& out, const Node& node, int level, index_type offset,
1264  int indent = 0) const {
1265  auto x = getIndex(offset, level + 1);
1266  out << times("\t", indent) << x << ": Node " << &node << " on level " << level
1267  << " parent: " << node.parent << " -- range: " << offset << " - "
1268  << (offset + ~getLevelMask(level + 1)) << "\n";
1269 
1270  if (level == 0) {
1271  for (int i = 0; i < NUM_CELLS; i++) {
1272  if (detailed || node.cell[i].value != value_type()) {
1273  out << times("\t", indent + 1) << i << ": [" << (offset + i) << "] " << node.cell[i].value
1274  << "\n";
1275  }
1276  }
1277  } else {
1278  for (int i = 0; i < NUM_CELLS; i++) {
1279  if (node.cell[i].ptr) {
1280  dump(detailed, out, *node.cell[i].ptr, level - 1,
1281  offset + (i * (index_type(1) << (level * BIT_PER_STEP))), indent + 1);
1282  } else if (detailed) {
1283  auto low = offset + (i * (1 << (level * BIT_PER_STEP)));
1284  auto hig = low + ~getLevelMask(level);
1285  out << times("\t", indent + 1) << i << ": empty range " << low << " - " << hig << "\n";
1286  }
1287  }
1288  }
1289  out << "\n";
1290  }
1291 
1292 public:
1293  /**
1294  * A debug utility printing the internal structure of this sparse array to the given output stream.
1295  */
1296  void dump(bool detail = false, std::ostream& out = std::cout) const {
1297  if (!unsynced.root) {
1298  out << " - empty - \n";
1299  return;
1300  }
1301  out << "root: " << unsynced.root << "\n";
1302  out << "offset: " << unsynced.offset << "\n";
1303  out << "first: " << unsynced.first << "\n";
1304  out << "fist offset: " << unsynced.firstOffset << "\n";
1305  dump(detail, out, *unsynced.root, unsynced.levels, unsynced.offset);
1306  }
1307 
1308 private:
1309  // --------------------------------------------------------------------------
1310  // Utilities
1311  // --------------------------------------------------------------------------
1313  /**
1314  * Creates new nodes and initializes them with 0.
1315  */
1316  static Node* newNode() {
1317  return new Node();
1318  }
1319 
1320  /**
1321  * Destroys a node and all its sub-nodes recursively.
1322  */
1323  static void freeNodes(Node* node, int level) {
1324  if (!node) return;
1325  if (level != 0) {
1326  for (int i = 0; i < NUM_CELLS; i++) {
1327  freeNodes(node->cell[i].ptr, level - 1);
1328  }
1329  }
1330  delete node;
1331  }
1333  /**
1334  * Conducts a cleanup of the internal tree structure.
1335  */
1336  void clean() {
1337  freeNodes(unsynced.root, unsynced.levels);
1338  unsynced.root = nullptr;
1339  unsynced.levels = 0;
1340  }
1341 
1342  /**
1343  * Clones the given node and all its sub-nodes.
1344  */
1345  static Node* clone(const Node* node, int level) {
1346  // support null-pointers
1347  if (node == nullptr) {
1348  return nullptr;
1349  }
1350 
1351  // create a clone
1352  auto* res = new Node();
1353 
1354  // handle leaf level
1355  if (level == 0) {
1356  copy_op copy;
1357  for (int i = 0; i < NUM_CELLS; i++) {
1358  res->cell[i].value = copy(node->cell[i].value);
1359  }
1360  return res;
1361  }
1362 
1363  // for inner nodes clone each child
1364  for (int i = 0; i < NUM_CELLS; i++) {
1365  auto cur = clone(node->cell[i].ptr, level - 1);
1366  if (cur != nullptr) {
1367  cur->parent = res;
1368  }
1369  res->cell[i].ptr = cur;
1370  }
1371 
1372  // done
1373  return res;
1374  }
1375 
1376  /**
1377  * Obtains the left-most leaf-node of the tree rooted by the given node
1378  * with the given level.
1379  */
1380  static Node* findFirst(Node* node, int level) {
1381  while (level > 0) {
1382  bool found = false;
1383  for (int i = 0; i < NUM_CELLS; i++) {
1384  Node* cur = node->cell[i].ptr;
1385  if (cur) {
1386  node = cur;
1387  --level;
1388  found = true;
1389  break;
1390  }
1391  }
1392  assert(found && "No first node!");
1393  }
1394 
1395  return node;
1396  }
1397 
1398  /**
1399  * Raises the level of this tree by one level. It does so by introducing
1400  * a new root node and inserting the current root node as a child node.
1401  */
1402  void raiseLevel() {
1403  // something went wrong when we pass that line
1404  assert(unsynced.levels < (sizeof(index_type) * 8 / BITS) + 1);
1405 
1406  // create new root
1407  Node* node = newNode();
1408  node->parent = nullptr;
1409 
1410  // insert existing root as child
1411  auto x = getIndex(brie_element_type(unsynced.offset), unsynced.levels + 1);
1412  node->cell[x].ptr = unsynced.root;
1413 
1414  // swap the root
1415  unsynced.root->parent = node;
1416 
1417  // update root
1418  unsynced.root = node;
1419  ++unsynced.levels;
1420 
1421  // update offset be removing additional bits
1422  unsynced.offset &= getLevelMask(unsynced.levels + 1);
1423  }
1424 
1425  /**
1426  * Attempts to raise the height of this tree based on the given root node
1427  * information and updates the root-info snapshot correspondingly.
1428  */
1429  void raiseLevel(RootInfoSnapshot& info) {
1430  // something went wrong when we pass that line
1431  assert(info.levels < (sizeof(index_type) * 8 / BITS) + 1);
1432 
1433  // create new root
1434  Node* newRoot = newNode();
1435  newRoot->parent = nullptr;
1436 
1437  // insert existing root as child
1438  auto x = getIndex(brie_element_type(info.offset), info.levels + 1);
1439  newRoot->cell[x].ptr = info.root;
1440 
1441  // exchange the root in the info struct
1442  auto oldRoot = info.root;
1443  info.root = newRoot;
1444 
1445  // update level counter
1446  ++info.levels;
1447 
1448  // update offset
1449  info.offset &= getLevelMask(info.levels + 1);
1450 
1451  // try exchanging root info
1452  if (tryUpdateRootInfo(info)) {
1453  // success => final step, update parent of old root
1454  oldRoot->parent = info.root;
1455  } else {
1456  // throw away temporary new node
1457  delete newRoot;
1458  }
1459  }
1460 
1461  /**
1462  * Tests whether the given index is covered by the boundaries defined
1463  * by the hight and offset of the internally maintained tree.
1464  */
1465  bool inBoundaries(index_type a) const {
1466  return inBoundaries(a, unsynced.levels, unsynced.offset);
1467  }
1468 
1469  /**
1470  * Tests whether the given index is within the boundaries defined by the
1471  * given tree hight and offset.
1472  */
1473  static bool inBoundaries(index_type a, uint32_t levels, index_type offset) {
1474  auto mask = getLevelMask(levels + 1);
1475  return (a & mask) == offset;
1476  }
1477 
1478  /**
1479  * Obtains the index within the arrays of cells of a given index on a given
1480  * level of the internally maintained tree.
1481  */
1482  static index_type getIndex(brie_element_type a, unsigned level) {
1483  return (a & (INDEX_MASK << (level * BIT_PER_STEP))) >> (level * BIT_PER_STEP);
1484  }
1485 
1486  /**
1487  * Computes the bit-mask to be applicable to obtain the offset of a node on a
1488  * given tree level.
1489  */
1490  static index_type getLevelMask(unsigned level) {
1491  if (level > (sizeof(index_type) * 8 / BITS)) return 0;
1492  return (~(index_type(0)) << (level * BIT_PER_STEP));
1493  }
1494 };
1495 
1496 namespace detail::brie {
1497 
1498 /**
1499  * Iterator type for `souffle::SparseArray`. It enumerates the indices set to 1.
1500  */
1501 template <typename SparseBitMap>
1502 class SparseBitMapIter {
1503  using value_t = typename SparseBitMap::value_t;
1504  using value_type = typename SparseBitMap::index_type;
1505  using data_store_t = typename SparseBitMap::data_store_t;
1506  using nested_iterator = typename data_store_t::iterator;
1507 
1508  // the iterator through the underlying sparse data structure
1509  nested_iterator iter;
1510 
1511  // the currently consumed mask
1512  uint64_t mask = 0;
1513 
1514  // the value currently pointed to
1515  value_type value{};
1516 
1517 public:
1518  SparseBitMapIter() = default; // default constructor -- creating an end-iterator
1520  SparseBitMapIter& operator=(const SparseBitMapIter&) = default;
1523  : iter(iter), mask(SparseBitMap::toMask(iter->second)),
1524  value(iter->first << SparseBitMap::LEAF_INDEX_WIDTH) {
1525  moveToNextInMask();
1526  }
1527 
1528  SparseBitMapIter(const nested_iterator& iter, uint64_t m, value_type value)
1529  : iter(iter), mask(m), value(value) {}
1530 
1531  // the equality operator as required by the iterator concept
1532  bool operator==(const SparseBitMapIter& other) const {
1533  // only equivalent if pointing to the end
1534  return iter == other.iter && mask == other.mask;
1535  }
1536 
1537  // the not-equality operator as required by the iterator concept
1538  bool operator!=(const SparseBitMapIter& other) const {
1539  return !(*this == other);
1540  }
1541 
1542  // the deref operator as required by the iterator concept
1543  const value_type& operator*() const {
1544  return value;
1545  }
1546 
1547  // support for the pointer operator
1548  const value_type* operator->() const {
1549  return &value;
1550  }
1551 
1552  // the increment operator as required by the iterator concept
1553  SparseBitMapIter& operator++() {
1554  // progress in current mask
1555  if (moveToNextInMask()) return *this;
1556 
1557  // go to next entry
1558  ++iter;
1560  // update value
1561  if (!iter.isEnd()) {
1562  value = iter->first << SparseBitMap::LEAF_INDEX_WIDTH;
1563  mask = SparseBitMap::toMask(iter->second);
1564  moveToNextInMask();
1565  }
1566 
1567  // done
1568  return *this;
1569  }
1570 
1571  SparseBitMapIter operator++(int) {
1572  auto cpy = *this;
1573  ++(*this);
1574  return cpy;
1575  }
1576 
1577  bool isEnd() const {
1578  return iter.isEnd();
1579  }
1580 
1581  void print(std::ostream& out) const {
1582  out << "SparseBitMapIter(" << iter << " -> " << std::bitset<64>(mask) << " @ " << value << ")";
1583  }
1584 
1585  // enables this iterator core to be printed (for debugging)
1586  friend std::ostream& operator<<(std::ostream& out, const SparseBitMapIter& iter) {
1587  iter.print(out);
1588  return out;
1589  }
1590 
1591 private:
1592  bool moveToNextInMask() {
1593  // check if there is something left
1594  if (mask == 0) return false;
1595 
1596  // get position of leading 1
1597  auto pos = __builtin_ctzll(mask);
1598 
1599  // consume this bit
1600  mask &= ~(1llu << pos);
1601 
1602  // update value
1603  value &= ~SparseBitMap::LEAF_INDEX_MASK;
1604  value |= pos;
1605 
1606  // done
1607  return true;
1608  }
1609 };
1610 
1611 } // namespace detail::brie
1612 
1613 /**
1614  * A sparse bit-map is a bit map virtually assigning a bit value to every value if the
1615  * uint32_t domain. However, only 1-bits are stored utilizing a nested sparse array
1616  * structure.
1617  *
1618  * @tparam BITS similar to the BITS parameter of the sparse array type
1619  */
1620 template <unsigned BITS = 4>
1621 class SparseBitMap {
1622  template <typename A>
1623  friend class detail::brie::SparseBitMapIter;
1624 
1625  using this_t = SparseBitMap<BITS>;
1626 
1627  // the element type stored in the nested sparse array
1628  using value_t = uint64_t;
1629 
1630  // define the bit-level merge operation
1631  struct merge_op {
1632  value_t operator()(value_t a, value_t b) const {
1633  return a | b; // merging bit masks => bitwise or operation
1634  }
1635  };
1636 
1637  // the type of the internal data store
1640 
1641  // some constants for manipulating stored values
1642  static constexpr size_t BITS_PER_ENTRY = sizeof(value_t) * CHAR_BIT;
1643  static constexpr size_t LEAF_INDEX_WIDTH = __builtin_ctz(BITS_PER_ENTRY);
1644  static constexpr uint64_t LEAF_INDEX_MASK = BITS_PER_ENTRY - 1;
1645 
1646  static uint64_t toMask(const value_t& value) {
1647  static_assert(sizeof(value_t) == sizeof(uint64_t), "Fixed for 64-bit compiler.");
1648  return reinterpret_cast<const uint64_t&>(value);
1649  }
1650 
1651 public:
1652  // the type to address individual entries
1653  using index_type = typename data_store_t::index_type;
1655 private:
1656  // it utilizes a sparse map to store its data
1657  data_store_t store;
1659 public:
1660  // a simple default constructor
1661  SparseBitMap() = default;
1663  // a default copy constructor
1664  SparseBitMap(const SparseBitMap&) = default;
1665 
1666  // a default r-value copy constructor
1667  SparseBitMap(SparseBitMap&&) = default;
1668 
1669  // a default assignment operator
1670  SparseBitMap& operator=(const SparseBitMap&) = default;
1671 
1672  // a default r-value assignment operator
1673  SparseBitMap& operator=(SparseBitMap&&) = default;
1674 
1675  // checks whether this bit-map is empty -- thus it does not have any 1-entries
1676  bool empty() const {
1677  return store.empty();
1678  }
1679 
1680  // the type utilized for recording context information for exploiting temporal locality
1681  using op_context = typename data_store_t::op_context;
1682 
1683  /**
1684  * Sets the bit addressed by i to 1.
1685  */
1686  bool set(index_type i) {
1687  op_context ctxt;
1688  return set(i, ctxt);
1689  }
1690 
1691  /**
1692  * Sets the bit addressed by i to 1. A context for exploiting temporal locality
1693  * can be provided.
1694  */
1695  bool set(index_type i, op_context& ctxt) {
1696  atomic_value_t& val = store.getAtomic(i >> LEAF_INDEX_WIDTH, ctxt);
1697  value_t bit = (1ull << (i & LEAF_INDEX_MASK));
1698 
1699 #ifdef __GNUC__
1700 #if __GNUC__ >= 7
1701  // In GCC >= 7 the usage of fetch_or causes a bug that needs further investigation
1702  // For now, this two-instruction based implementation provides a fix that does
1703  // not sacrifice too much performance.
1704 
1705  while (true) {
1706  auto order = std::memory_order::memory_order_relaxed;
1707 
1708  // load current value
1709  value_t old = val.load(order);
1710 
1711  // if bit is already set => we are done
1712  if (old & bit) return false;
1713 
1714  // set the bit, if failed, repeat
1715  if (!val.compare_exchange_strong(old, old | bit, order, order)) continue;
1716 
1717  // it worked, new bit added
1718  return true;
1719  }
1720 
1721 #endif
1722 #endif
1723 
1724  value_t old = val.fetch_or(bit, std::memory_order::memory_order_relaxed);
1725  return (old & bit) == 0u;
1726  }
1727 
1728  /**
1729  * Determines the whether the bit addressed by i is set or not.
1730  */
1731  bool test(index_type i) const {
1732  op_context ctxt;
1733  return test(i, ctxt);
1734  }
1735 
1736  /**
1737  * Determines the whether the bit addressed by i is set or not. A context for
1738  * exploiting temporal locality can be provided.
1739  */
1740  bool test(index_type i, op_context& ctxt) const {
1741  value_t bit = (1ull << (i & LEAF_INDEX_MASK));
1742  return store.lookup(i >> LEAF_INDEX_WIDTH, ctxt) & bit;
1743  }
1744 
1745  /**
1746  * Determines the whether the bit addressed by i is set or not.
1747  */
1748  bool operator[](index_type i) const {
1749  return test(i);
1750  }
1751 
1752  /**
1753  * Resets all contained bits to 0.
1754  */
1755  void clear() {
1756  store.clear();
1757  }
1758 
1759  /**
1760  * Determines the number of bits set.
1761  */
1762  std::size_t size() const {
1763  // this is computed on demand to keep the set operation simple.
1764  std::size_t res = 0;
1765  for (const auto& cur : store) {
1766  res += __builtin_popcountll(cur.second);
1767  }
1768  return res;
1769  }
1770 
1771  /**
1772  * Computes the total memory usage of this data structure.
1773  */
1774  std::size_t getMemoryUsage() const {
1775  // compute the total memory usage
1776  return sizeof(*this) - sizeof(data_store_t) + store.getMemoryUsage();
1777  }
1779  /**
1780  * Sets all bits set in other to 1 within this bit map.
1781  */
1782  void addAll(const SparseBitMap& other) {
1783  // nothing to do if it is a self-assignment
1784  if (this == &other) return;
1785 
1786  // merge the sparse store
1787  store.addAll(other.store);
1788  }
1789 
1790  // ---------------------------------------------------------------------
1791  // Iterator
1792  // ---------------------------------------------------------------------
1793 
1795 
1796  /**
1797  * Obtains an iterator pointing to the first index set to 1. If there
1798  * is no such bit, end() will be returned.
1799  */
1800  iterator begin() const {
1801  auto it = store.begin();
1802  if (it.isEnd()) return end();
1803  return iterator(it);
1804  }
1805 
1806  /**
1807  * Returns an iterator referencing the position after the last set bit.
1808  */
1809  iterator end() const {
1810  return iterator();
1811  }
1812 
1813  /**
1814  * Obtains an iterator referencing the position i if the corresponding
1815  * bit is set, end() otherwise.
1816  */
1817  iterator find(index_type i) const {
1818  op_context ctxt;
1819  return find(i, ctxt);
1820  }
1821 
1822  /**
1823  * Obtains an iterator referencing the position i if the corresponding
1824  * bit is set, end() otherwise. An operation context can be provided
1825  * to exploit temporal locality.
1826  */
1827  iterator find(index_type i, op_context& ctxt) const {
1828  // check prefix part
1829  auto it = store.find(i >> LEAF_INDEX_WIDTH, ctxt);
1830  if (it.isEnd()) return end();
1831 
1832  // check bit-set part
1833  uint64_t mask = toMask(it->second);
1834  if (!(mask & (1llu << (i & LEAF_INDEX_MASK)))) return end();
1835 
1836  // OK, it is there => create iterator
1837  mask &= ((1ull << (i & LEAF_INDEX_MASK)) - 1); // remove all bits before pos i
1838  return iterator(it, mask, i);
1839  }
1840 
1841  /**
1842  * Locates an iterator to the first element in this sparse bit map not less
1843  * than the given index.
1844  */
1845  iterator lower_bound(index_type i) const {
1846  auto it = store.lowerBound(i >> LEAF_INDEX_WIDTH);
1847  if (it.isEnd()) return end();
1848 
1849  // check bit-set part
1850  uint64_t mask = toMask(it->second);
1851 
1852  // if there is no bit remaining in this mask, check next mask.
1853  if (!(mask & ((~uint64_t(0)) << (i & LEAF_INDEX_MASK)))) {
1854  index_type next = ((i >> LEAF_INDEX_WIDTH) + 1) << LEAF_INDEX_WIDTH;
1855  if (next < i) return end();
1856  return lower_bound(next);
1857  }
1858 
1859  // there are bits left, use least significant bit of those
1860  if (it->first == i >> LEAF_INDEX_WIDTH) {
1861  mask &= ((~uint64_t(0)) << (i & LEAF_INDEX_MASK)); // remove all bits before pos i
1862  }
1863 
1864  // compute value represented by least significant bit
1865  index_type pos = __builtin_ctzll(mask);
1866 
1867  // remove this bit as well
1868  mask = mask & ~(1ull << pos);
1869 
1870  // construct value of this located bit
1871  index_type val = (it->first << LEAF_INDEX_WIDTH) | pos;
1872  return iterator(it, mask, val);
1873  }
1874 
1875  /**
1876  * Locates an iterator to the first element in this sparse bit map than is greater
1877  * than the given index.
1878  */
1879  iterator upper_bound(index_type i) const {
1880  if (i == std::numeric_limits<index_type>::max()) {
1881  return end();
1882  }
1883  return lower_bound(i + 1);
1884  }
1885 
1886  /**
1887  * A debugging utility printing the internal structure of this map to the
1888  * given output stream.
1889  */
1890  void dump(bool detail = false, std::ostream& out = std::cout) const {
1891  store.dump(detail, out);
1892  }
1893 
1894  /**
1895  * Provides write-protected access to the internal store for running
1896  * analysis on the data structure.
1897  */
1898  const data_store_t& getStore() const {
1899  return store;
1900  }
1901 };
1902 
1903 // ---------------------------------------------------------------------
1904 // TRIE
1905 // ---------------------------------------------------------------------
1907 namespace detail::brie {
1908 
1909 /**
1910  * An iterator over the stored entries.
1911  *
1912  * Iterators for tries consist of a top-level iterator maintaining the
1913  * master copy of a materialized tuple and a recursively nested iterator
1914  * core -- one for each nested trie level.
1915  */
1916 template <typename Value, typename IterCore>
1917 class TrieIterator {
1918  template <unsigned Len, unsigned Pos, unsigned Dimensions>
1919  friend struct fix_binding;
1920 
1921  template <unsigned Dimensions>
1922  friend struct fix_lower_bound;
1923 
1924  template <unsigned Dimensions>
1925  friend struct fix_upper_bound;
1926 
1927  template <unsigned Pos, unsigned Dimensions>
1928  friend struct fix_first;
1929 
1930  template <unsigned Dimensions>
1931  friend struct fix_first_nested;
1932 
1933  template <typename A, typename B>
1934  friend class TrieIterator;
1936  // remove ref-qual (if any); this can happen if we're a iterator-view
1937  using iter_core_arg_type = typename std::remove_reference_t<IterCore>::store_iter;
1939  Value value; // the value currently pointed to
1940  IterCore iter_core; // the wrapped iterator
1942  // return an ephemeral nested iterator-view (view -> mutating us mutates our parent)
1943  // NB: be careful that the lifetime of this iterator-view doesn't exceed that of its parent.
1944  auto getNestedView() {
1945  auto& nested_iter_ref = iter_core.getNested(); // by ref (this is critical, we're a view, not a copy)
1946  auto nested_val = tail(value);
1948  std::move(nested_val), nested_iter_ref);
1949  }
1951  // special constructor for iterator-views (see `getNestedView`)
1952  explicit TrieIterator(Value value, IterCore iter_core) : value(std::move(value)), iter_core(iter_core) {}
1954 public:
1955  TrieIterator() = default; // default constructor -- creating an end-iterator
1956  TrieIterator(const TrieIterator&) = default;
1957  TrieIterator(TrieIterator&&) = default;
1958  TrieIterator& operator=(const TrieIterator&) = default;
1959  TrieIterator& operator=(TrieIterator&&) = default;
1961  explicit TrieIterator(iter_core_arg_type param) : iter_core(std::move(param), value) {}
1962 
1963  // the equality operator as required by the iterator concept
1964  bool operator==(const TrieIterator& other) const {
1965  // equivalent if pointing to the same value
1966  return iter_core == other.iter_core;
1967  }
1969  // the not-equality operator as required by the iterator concept
1970  bool operator!=(const TrieIterator& other) const {
1971  return !(*this == other);
1972  }
1973 
1974  const Value& operator*() const {
1975  return value;
1976  }
1978  const Value* operator->() const {
1979  return &value;
1980  }
1981 
1982  TrieIterator& operator++() {
1983  iter_core.inc(value);
1984  return *this;
1985  }
1987  TrieIterator operator++(int) {
1988  auto cpy = *this;
1989  ++(*this);
1990  return cpy;
1991  }
1992 
1993  // enables this iterator to be printed (for debugging)
1994  void print(std::ostream& out) const {
1995  out << "iter(" << iter_core << " -> " << value << ")";
1996  }
1997 
1998  friend std::ostream& operator<<(std::ostream& out, const TrieIterator& iter) {
1999  iter.print(out);
2000  return out;
2001  }
2002 };
2004 template <unsigned Dim>
2005 struct TrieTypes;
2006 
2007 /**
2008  * A base class for the Trie implementation allowing various
2009  * specializations of the Trie template to inherit common functionality.
2010  *
2011  * @tparam Dim the number of dimensions / arity of the stored tuples
2012  * @tparam Derived the type derived from this base class
2013  */
2014 template <unsigned Dim, typename Derived>
2015 class TrieBase {
2016  Derived& impl() {
2017  return static_cast<Derived&>(*this);
2018  }
2019 
2020  const Derived& impl() const {
2021  return static_cast<const Derived&>(*this);
2022  }
2023 
2024 protected:
2025  using types = TrieTypes<Dim>;
2026  using store_type = typename types::store_type;
2027 
2028  store_type store;
2029 
2030 public:
2033  using entry_type = typename types::entry_type;
2034  using iterator = typename types::iterator;
2035  using iterator_core = typename types::iterator_core;
2036  using op_context = typename types::op_context;
2037 
2038  /**
2039  * Inserts all tuples stored within the given trie into this trie.
2040  * This operation is considerably more efficient than the consecutive
2041  * insertion of the elements in other into this trie.
2042  *
2043  * @param other the elements to be inserted into this trie
2044  */
2045  void insertAll(const TrieBase& other) {
2046  store.addAll(other.store);
2047  }
2049  /**
2050  * Provides protected access to the internally maintained store.
2051  */
2052  const store_type& getStore() const {
2053  return store;
2054  }
2055 
2056  /**
2057  * Determines whether this trie is empty or not.
2058  */
2059  bool empty() const {
2060  return store.empty();
2061  }
2062 
2063  /**
2064  * Obtains an iterator referencing the first element stored within this trie.
2065  */
2066  iterator begin() const {
2067  return empty() ? end() : iterator(store.begin());
2068  }
2069 
2070  /**
2071  * Obtains an iterator referencing the position after the last element stored
2072  * within this trie.
2073  */
2074  iterator end() const {
2075  return iterator();
2076  }
2077 
2078  iterator find(const_entry_span_type entry, op_context& ctxt) const {
2079  auto range = impl().template getBoundaries<Dim>(entry, ctxt);
2080  return range.empty() ? range.end() : range.begin();
2081  }
2083  // implemented by `Derived`:
2084  // bool insert(const entry_type& tuple, op_context& ctxt);
2085  // bool contains(const_entry_span_type tuple, op_context& ctxt) const;
2086  // bool lower_bound(const_entry_span_type tuple, op_context& ctxt) const;
2087  // bool upper_bound(const_entry_span_type tuple, op_context& ctxt) const;
2088  // template <unsigned levels>
2089  // range<iterator> getBoundaries(const_entry_span_type, op_context&) const;
2091  // -- operation wrappers --
2092 
2093  template <unsigned levels>
2094  range<iterator> getBoundaries(const_entry_span_type entry) const {
2095  op_context ctxt;
2096  return impl().template getBoundaries<levels>(entry, ctxt);
2097  }
2098 
2099  template <unsigned levels>
2100  range<iterator> getBoundaries(const entry_type& entry, op_context& ctxt) const {
2101  return impl().template getBoundaries<levels>(const_entry_span_type(entry), ctxt);
2102  }
2103 
2104  template <unsigned levels>
2105  range<iterator> getBoundaries(const entry_type& entry) const {
2106  return impl().template getBoundaries<levels>(const_entry_span_type(entry));
2107  }
2108 
2109  template <unsigned levels, typename... Values, typename = std::enable_if_t<(isRamType<Values> && ...)>>
2110  range<iterator> getBoundaries(Values... values) const {
2111  return impl().template getBoundaries<levels>(entry_type{ramBitCast(values)...});
2112  }
2113 
2114 // declare a initialiser-list compatible overload for a given function
2115 #define BRIE_OVERLOAD_INIT_LIST(fn, constness) \
2116  auto fn(const_entry_span_type entry) constness { \
2117  op_context ctxt; \
2118  return impl().fn(entry, ctxt); \
2119  } \
2120  auto fn(const entry_type& entry, op_context& ctxt) constness { \
2121  return impl().fn(const_entry_span_type(entry), ctxt); \
2122  } \
2123  auto fn(const entry_type& entry) constness { \
2124  return impl().fn(const_entry_span_type(entry)); \
2125  }
2127  BRIE_OVERLOAD_INIT_LIST(insert, )
2128  BRIE_OVERLOAD_INIT_LIST(find, const)
2130  BRIE_OVERLOAD_INIT_LIST(lower_bound, const)
2131  BRIE_OVERLOAD_INIT_LIST(upper_bound, const)
2132 
2133 #undef BRIE_OVERLOAD_INIT_LIST
2134 
2135  /* -------------- operator hint statistics ----------------- */
2136 
2137  // an aggregation of statistical values of the hint utilization
2138  struct hint_statistics {
2139  // the counter for insertion operations
2140  CacheAccessCounter inserts;
2141 
2142  // the counter for contains operations
2143  CacheAccessCounter contains;
2144 
2145  // the counter for get_boundaries operations
2146  CacheAccessCounter get_boundaries;
2147  };
2148 
2149 protected:
2150  // the hint statistic of this b-tree instance
2151  mutable hint_statistics hint_stats;
2152 
2153 public:
2154  void printStats(std::ostream& out) const {
2155  out << "---------------------------------\n";
2156  out << " insert-hint (hits/misses/total): " << hint_stats.inserts.getHits() << "/"
2157  << hint_stats.inserts.getMisses() << "/" << hint_stats.inserts.getAccesses() << "\n";
2158  out << " contains-hint (hits/misses/total):" << hint_stats.contains.getHits() << "/"
2159  << hint_stats.contains.getMisses() << "/" << hint_stats.contains.getAccesses() << "\n";
2160  out << " get-boundaries-hint (hits/misses/total):" << hint_stats.get_boundaries.getHits() << "/"
2161  << hint_stats.get_boundaries.getMisses() << "/" << hint_stats.get_boundaries.getAccesses()
2162  << "\n";
2163  out << "---------------------------------\n";
2164  }
2165 };
2166 
2167 template <unsigned Dim>
2168 struct TrieTypes;
2169 
2170 // FIXME: THIS KILLS COMPILE PERF - O(n^2)
2171 /**
2172  * A functor extracting a reference to a nested iterator core from an enclosing
2173  * iterator core.
2174  */
2175 template <unsigned Level>
2176 struct get_nested_iter_core {
2177  template <typename IterCore>
2178  auto operator()(IterCore& core) -> decltype(get_nested_iter_core<Level - 1>()(core.getNested())) {
2179  return get_nested_iter_core<Level - 1>()(core.getNested());
2180  }
2181 };
2182 
2183 template <>
2184 struct get_nested_iter_core<0> {
2185  template <typename IterCore>
2186  IterCore& operator()(IterCore& core) {
2187  return core;
2188  }
2189 };
2190 
2191 // FIXME: THIS KILLS COMPILE PERF - O(n^2)
2192 /**
2193  * A functor initializing an iterator upon creation to reference the first
2194  * element in the associated Trie.
2195  */
2196 template <unsigned Pos, unsigned Dim>
2197 struct fix_first {
2198  template <unsigned bits, typename iterator>
2199  void operator()(const SparseBitMap<bits>& store, iterator& iter) const {
2200  // set iterator to first in store
2201  auto first = store.begin();
2202  get_nested_iter_core<Pos>()(iter.iter_core).setIterator(first);
2203  iter.value[Pos] = *first;
2204  }
2205 
2206  template <typename Store, typename iterator>
2207  void operator()(const Store& store, iterator& iter) const {
2208  // set iterator to first in store
2209  auto first = store.begin();
2210  get_nested_iter_core<Pos>()(iter.iter_core).setIterator(first);
2211  iter.value[Pos] = first->first;
2212  // and continue recursively
2213  fix_first<Pos + 1, Dim>()(first->second->getStore(), iter);
2214  }
2215 };
2216 
2217 template <unsigned Dim>
2218 struct fix_first<Dim, Dim> {
2219  template <typename Store, typename iterator>
2220  void operator()(const Store&, iterator&) const {
2221  // terminal case => nothing to do
2222  }
2223 };
2224 
2225 template <unsigned Dim>
2226 struct fix_first_nested {
2227  template <unsigned bits, typename iterator>
2228  void operator()(const SparseBitMap<bits>& store, iterator&& iter) const {
2229  // set iterator to first in store
2230  auto first = store.begin();
2231  iter.value[0] = *first;
2232  iter.iter_core.setIterator(std::move(first));
2233  }
2235  template <typename Store, typename iterator>
2236  void operator()(const Store& store, iterator&& iter) const {
2237  // set iterator to first in store
2238  auto first = store.begin();
2239  iter.value[0] = first->first;
2240  iter.iter_core.setIterator(std::move(first));
2241  // and continue recursively
2242  fix_first_nested<Dim - 1>()(first->second->getStore(), iter.getNestedView());
2243  }
2244 };
2245 
2246 // TODO: rewrite to erase `Pos` and `Len` arguments. this can cause a template instance explosion
2247 /**
2248  * A functor initializing an iterator upon creation to reference the first element
2249  * exhibiting a given prefix within a given Trie.
2250  */
2251 template <unsigned Len, unsigned Pos, unsigned Dim>
2252 struct fix_binding {
2253  template <unsigned bits, typename iterator, typename entry_type>
2254  bool operator()(
2255  const SparseBitMap<bits>& store, iterator& begin, iterator& end, const entry_type& entry) const {
2256  // search in current level
2257  auto cur = store.find(entry[Pos]);
2258 
2259  // if not present => fail
2260  if (cur == store.end()) return false;
2261 
2262  // take current value
2263  get_nested_iter_core<Pos>()(begin.iter_core).setIterator(cur);
2264  ++cur;
2265  get_nested_iter_core<Pos>()(end.iter_core).setIterator(cur);
2266 
2267  // update iterator value
2268  begin.value[Pos] = entry[Pos];
2269 
2270  // no more remaining levels to fix
2271  return true;
2272  }
2273 
2274  template <typename Store, typename iterator, typename entry_type>
2275  bool operator()(const Store& store, iterator& begin, iterator& end, const entry_type& entry) const {
2276  // search in current level
2277  auto cur = store.find(entry[Pos]);
2278 
2279  // if not present => fail
2280  if (cur == store.end()) return false;
2281 
2282  // take current value as start
2283  get_nested_iter_core<Pos>()(begin.iter_core).setIterator(cur);
2284 
2285  // update iterator value
2286  begin.value[Pos] = entry[Pos];
2287 
2288  // fix remaining nested iterators
2289  auto res = fix_binding<Len - 1, Pos + 1, Dim>()(cur->second->getStore(), begin, end, entry);
2290 
2291  // update end of iterator
2292  if (get_nested_iter_core<Pos + 1>()(end.iter_core).getIterator() == cur->second->getStore().end()) {
2293  ++cur;
2294  if (cur != store.end()) {
2295  fix_first<Pos + 1, Dim>()(cur->second->getStore(), end);
2296  }
2297  }
2298  get_nested_iter_core<Pos>()(end.iter_core).setIterator(cur);
2299 
2300  // done
2301  return res;
2302  }
2303 };
2304 
2305 template <unsigned Pos, unsigned Dim>
2306 struct fix_binding<0, Pos, Dim> {
2307  template <unsigned bits, typename iterator, typename entry_type>
2308  bool operator()(const SparseBitMap<bits>& store, iterator& begin, iterator& /* end */,
2309  const entry_type& /* entry */) const {
2310  // move begin to begin of store
2311  auto a = store.begin();
2312  get_nested_iter_core<Pos>()(begin.iter_core).setIterator(a);
2313  begin.value[Pos] = *a;
2314 
2315  return true;
2316  }
2317 
2318  template <typename Store, typename iterator, typename entry_type>
2319  bool operator()(const Store& store, iterator& begin, iterator& end, const entry_type& entry) const {
2320  // move begin to begin of store
2321  auto a = store.begin();
2322  get_nested_iter_core<Pos>()(begin.iter_core).setIterator(a);
2323  begin.value[Pos] = a->first;
2325  // continue recursively
2326  fix_binding<0, Pos + 1, Dim>()(a->second->getStore(), begin, end, entry);
2327  return true;
2328  }
2329 };
2330 
2331 template <unsigned Dim>
2332 struct fix_binding<0, Dim, Dim> {
2333  template <typename Store, typename iterator, typename entry_type>
2334  bool operator()(const Store& /* store */, iterator& /* begin */, iterator& /* end */,
2335  const entry_type& /* entry */) const {
2336  // nothing more to do
2337  return true;
2338  }
2339 };
2340 
2341 /**
2342  * A functor initializing an iterator upon creation to reference the first element
2343  * within a given Trie being not less than a given value .
2344  */
2345 template <unsigned Dim>
2346 struct fix_lower_bound {
2347  using types = TrieTypes<Dim>;
2348  using const_entry_span_type = typename types::const_entry_span_type;
2349 
2350  template <unsigned bits, typename iterator>
2351  bool operator()(const SparseBitMap<bits>& store, iterator&& iter, const_entry_span_type entry) const {
2352  auto cur = store.lower_bound(entry[0]);
2353  if (cur == store.end()) return false;
2354  assert(entry[0] <= brie_element_type(*cur));
2355 
2356  iter.iter_core.setIterator(cur);
2357  iter.value[0] = *cur;
2358  return true;
2359  }
2360 
2361  template <typename Store, typename iterator>
2362  bool operator()(const Store& store, iterator&& iter, const_entry_span_type entry) const {
2363  auto cur = store.lowerBound(entry[0]); // search in current level
2364  if (cur == store.end()) return false; // if no lower boundary is found, be done
2365  assert(brie_element_type(cur->first) >= entry[0]);
2366 
2367  // if the lower bound is higher than the requested value, go to first in subtree
2368  if (brie_element_type(cur->first) > entry[0]) {
2369  iter.iter_core.setIterator(cur);
2370  iter.value[0] = cur->first;
2371  fix_first_nested<Dim - 1>()(cur->second->getStore(), iter.getNestedView());
2372  return true;
2373  }
2374 
2375  // attempt to fix the rest
2376  if (!fix_lower_bound<Dim - 1>()(cur->second->getStore(), iter.getNestedView(), tail(entry))) {
2377  // if it does not work, since there are no matching elements in this branch, go to next
2378  auto sub = copy(entry);
2379  sub[0] += 1;
2380  for (size_t i = 1; i < Dim; ++i)
2381  sub[i] = 0;
2382 
2383  return (*this)(store, iter, sub);
2384  }
2385 
2386  iter.iter_core.setIterator(cur); // remember result
2387  iter.value[0] = cur->first; // update iterator value
2388  return true;
2389  }
2390 };
2391 
2392 /**
2393  * A functor initializing an iterator upon creation to reference the first element
2394  * within a given Trie being greater than a given value .
2395  */
2396 template <unsigned Dim>
2397 struct fix_upper_bound {
2398  using types = TrieTypes<Dim>;
2399  using const_entry_span_type = typename types::const_entry_span_type;
2400 
2401  template <unsigned bits, typename iterator>
2402  bool operator()(const SparseBitMap<bits>& store, iterator&& iter, const_entry_span_type entry) const {
2403  auto cur = store.upper_bound(entry[0]);
2404  if (cur == store.end()) return false;
2405  assert(entry[0] <= brie_element_type(*cur));
2406 
2407  iter.iter_core.setIterator(cur);
2408  iter.value[0] = *cur;
2409  return true; // no more remaining levels to fix
2410  }
2411 
2412  template <typename Store, typename iterator>
2413  bool operator()(const Store& store, iterator&& iter, const_entry_span_type entry) const {
2414  auto cur = store.lowerBound(entry[0]); // search in current level
2415  if (cur == store.end()) return false; // if no upper boundary is found, be done
2416  assert(brie_element_type(cur->first) >= entry[0]);
2417 
2418  // if the lower bound is higher than the requested value, go to first in subtree
2419  if (brie_element_type(cur->first) > entry[0]) {
2420  iter.iter_core.setIterator(cur);
2421  iter.value[0] = cur->first;
2422  fix_first_nested<Dim - 1>()(cur->second->getStore(), iter.getNestedView());
2423  return true;
2424  }
2425 
2426  // attempt to fix the rest
2427  if (!fix_upper_bound<Dim - 1>()(cur->second->getStore(), iter.getNestedView(), tail(entry))) {
2428  // if it does not work, since there are no matching elements in this branch, go to next
2429  auto sub = copy(entry);
2430  sub[0] += 1;
2431  for (size_t i = 1; i < Dim; ++i)
2432  sub[i] = 0;
2433 
2434  return (*this)(store, iter, sub);
2435  }
2436 
2437  iter.iter_core.setIterator(cur); // remember result
2438  iter.value[0] = cur->first; // update iterator value
2439  return true;
2440  }
2441 };
2442 
2443 template <unsigned Dim>
2444 struct TrieTypes {
2445  using entry_type = std::array<brie_element_type, Dim>;
2446  using entry_span_type = span<brie_element_type, Dim>;
2447  using const_entry_span_type = span<const brie_element_type, Dim>;
2448 
2449  // the type of the nested tries (1 dimension less)
2450  using nested_trie_type = Trie<Dim - 1>;
2451 
2452  // the merge operation capable of merging two nested tries
2453  struct nested_trie_merger {
2454  nested_trie_type* operator()(nested_trie_type* a, const nested_trie_type* b) const {
2455  if (!b) return a;
2456  if (!a) return new nested_trie_type(*b);
2457  a->insertAll(*b);
2458  return a;
2459  }
2460  };
2462  // the operation capable of cloning a nested trie
2464  nested_trie_type* operator()(nested_trie_type* a) const {
2465  if (!a) return a;
2466  return new nested_trie_type(*a);
2467  }
2468  };
2470  // the data structure utilized for indexing nested tries
2472  6, // = 2^6 entries per block
2474 
2475  // The iterator core for trie iterators involving this level.
2476  struct iterator_core {
2477  using store_iter = typename store_type::iterator; // the iterator for the current level
2478  using nested_core_iter = typename nested_trie_type::iterator_core; // the type of the nested iterator
2480  private:
2481  store_iter iter;
2482  nested_core_iter nested;
2483 
2484  public:
2485  iterator_core() = default; // default -> end iterator
2486 
2487  iterator_core(store_iter store_iter, entry_span_type entry) : iter(std::move(store_iter)) {
2488  entry[0] = iter->first;
2489  nested = {iter->second->getStore().begin(), tail(entry)};
2490  }
2491 
2492  void setIterator(store_iter store_iter) {
2493  iter = std::move(store_iter);
2494  }
2495 
2496  store_iter& getIterator() {
2497  return iter;
2498  }
2499 
2500  nested_core_iter& getNested() {
2501  return nested;
2502  }
2504  bool inc(entry_span_type entry) {
2505  // increment nested iterator
2506  auto nested_entry = tail(entry);
2507  if (nested.inc(nested_entry)) return true;
2509  // increment the iterator on this level
2510  ++iter;
2511 
2512  // check whether the end has been reached
2513  if (iter.isEnd()) return false;
2514 
2515  // otherwise update entry value
2516  entry[0] = iter->first;
2517 
2518  // and restart nested
2519  nested = {iter->second->getStore().begin(), nested_entry};
2520  return true;
2521  }
2522 
2523  bool operator==(const iterator_core& other) const {
2524  return nested == other.nested && iter == other.iter;
2525  }
2526 
2527  bool operator!=(const iterator_core& other) const {
2528  return !(*this == other);
2529  }
2530 
2531  // enables this iterator core to be printed (for debugging)
2532  void print(std::ostream& out) const {
2533  out << iter << " | " << nested;
2534  }
2535 
2536  friend std::ostream& operator<<(std::ostream& out, const iterator_core& iter) {
2537  iter.print(out);
2538  return out;
2539  }
2540  };
2541 
2542  using iterator = TrieIterator<entry_type, iterator_core>;
2544  // the operation context aggregating all operation contexts of nested structures
2545  struct op_context {
2546  using local_ctxt = typename store_type::op_context;
2547  using nested_ctxt = typename nested_trie_type::op_context;
2549  // for insert and contain
2550  local_ctxt local{};
2551  brie_element_type lastQuery{};
2552  nested_trie_type* lastNested{nullptr};
2553  nested_ctxt nestedCtxt{};
2554 
2555  // for boundaries
2556  unsigned lastBoundaryLevels{Dim + 1};
2557  entry_type lastBoundaryRequest{};
2558  range<iterator> lastBoundaries{iterator(), iterator()};
2559  };
2560 };
2562 template <>
2563 struct TrieTypes<1u> {
2564  using entry_type = std::array<brie_element_type, 1>;
2568  // the map type utilized internally
2571 
2572  /**
2573  * The iterator core of this level contributing to the construction of
2574  * a composed trie iterator.
2575  */
2576  struct iterator_core {
2577  using store_iter = typename store_type::iterator;
2578 
2579  private:
2580  store_iter iter;
2582  public:
2583  iterator_core() = default; // default end-iterator constructor
2584 
2585  iterator_core(store_iter store_iter, entry_span_type entry)
2586  : iter(std::move(store_iter)) // NOLINT : mistaken warning -`store_iter` is not const-qual
2587  {
2588  entry[0] = brie_element_type(*iter);
2589  }
2590 
2591  void setIterator(store_iter store_iter) {
2592  iter = std::move(store_iter); // NOLINT : mistaken warning - `store_iter` is not const-qual
2593  }
2594 
2595  store_iter& getIterator() {
2596  return iter;
2597  }
2598 
2599  bool inc(entry_span_type entry) {
2600  // increment the iterator on this level
2601  ++iter;
2602 
2603  // check whether the end has been reached
2604  if (iter.isEnd()) return false;
2605 
2606  // otherwise update entry value
2607  entry[0] = brie_element_type(*iter);
2608  return true;
2609  }
2610 
2611  bool operator==(const iterator_core& other) const {
2612  return iter == other.iter;
2613  }
2614 
2615  bool operator!=(const iterator_core& other) const {
2616  return !(*this == other);
2617  }
2618 
2619  // enables this iterator core to be printed (for debugging)
2620  void print(std::ostream& out) const {
2621  out << iter;
2622  }
2623 
2624  friend std::ostream& operator<<(std::ostream& out, const iterator_core& iter) {
2625  iter.print(out);
2626  return out;
2627  }
2628  };
2629 
2630  using iterator = TrieIterator<entry_type, iterator_core>;
2631 };
2632 
2633 } // namespace detail::brie
2634 
2635 // use an inner class so `TrieN` is fully defined before the recursion, allowing us to use
2636 // `op_context` in `TrieBase`
2637 template <unsigned Dim>
2638 class Trie : public TrieBase<Dim, Trie<Dim>> {
2639  template <unsigned N>
2640  friend class Trie;
2641 
2642  // a shortcut for the common base class type
2643  using base = TrieBase<Dim, Trie<Dim>>;
2644  using types = TrieTypes<Dim>;
2645  using nested_trie_type = typename types::nested_trie_type;
2646  using store_type = typename types::store_type;
2647 
2648  using base::store;
2649 
2650 public:
2651  using const_entry_span_type = typename types::const_entry_span_type;
2652  using entry_span_type = typename types::entry_span_type;
2653  using entry_type = typename types::entry_type;
2654  using iterator = typename types::iterator;
2655  using iterator_core = typename types::iterator_core;
2656  using op_context = typename types::op_context;
2657  // type aliases for compatibility with `BTree` and others
2658  using operation_hints = op_context;
2661  using base::begin;
2663  using base::empty;
2664  using base::end;
2665  using base::find;
2666  using base::getBoundaries;
2667  using base::insert;
2668  using base::lower_bound;
2669  using base::upper_bound;
2671  ~Trie() {
2672  clear();
2673  }
2675  /**
2676  * Determines the number of entries in this trie.
2677  */
2678  std::size_t size() const {
2679  // the number of elements is lazy-evaluated
2680  std::size_t res = 0;
2681  for (auto&& [_, v] : store)
2682  res += v->size();
2683 
2684  return res;
2685  }
2686 
2687  /**
2688  * Computes the total memory usage of this data structure.
2689  */
2690  std::size_t getMemoryUsage() const {
2691  // compute the total memory usage of this level
2692  auto res = sizeof(*this) - sizeof(store) + store.getMemoryUsage();
2693  for (auto&& [_, v] : store)
2694  res += v->getMemoryUsage(); // add the memory usage of sub-levels
2695 
2696  return res;
2697  }
2698 
2699  /**
2700  * Removes all entries within this trie.
2701  */
2702  void clear() {
2703  // delete lower levels manually
2704  // (can't use `Own` b/c we need `atomic` instances and those require trivial assignment)
2705  for (auto& cur : store)
2706  delete cur.second;
2707 
2708  // clear store
2709  store.clear();
2710  }
2711 
2712  /**
2713  * Inserts a new entry. A operation context may be provided to exploit temporal
2714  * locality.
2715  *
2716  * @param tuple the entry to be added
2717  * @param ctxt the operation context to be utilized
2718  * @return true if the same tuple hasn't been present before, false otherwise
2719  */
2720  bool insert(const_entry_span_type tuple, op_context& ctxt) {
2721  using value_t = typename store_type::value_type;
2722  using atomic_value_t = typename store_type::atomic_value_type;
2723 
2724  // check context
2725  if (ctxt.lastNested && ctxt.lastQuery == tuple[0]) {
2726  base::hint_stats.inserts.addHit();
2727  return ctxt.lastNested->insert(tail(tuple), ctxt.nestedCtxt);
2728  }
2729 
2730  base::hint_stats.inserts.addMiss();
2731 
2732  // lookup nested
2733  atomic_value_t& next = store.getAtomic(tuple[0], ctxt.local);
2734 
2735  // get pure pointer to next level
2736  value_t nextPtr = next;
2737 
2738  // conduct a lock-free lazy-creation of nested trees
2739  if (!nextPtr) {
2740  // create a sub-tree && register it atomically
2741  auto newNested = std::make_unique<nested_trie_type>();
2742  if (next.compare_exchange_weak(nextPtr, newNested.get())) {
2743  nextPtr = newNested.release(); // worked, ownership is acquired by `store`
2744  }
2745  // otherwise some other thread was faster => use its version
2746  }
2747 
2748  // make sure a next has been established
2749  assert(nextPtr);
2750 
2751  // clear context if necessary
2752  if (nextPtr != ctxt.lastNested) {
2753  ctxt.lastQuery = tuple[0];
2754  ctxt.lastNested = nextPtr;
2755  ctxt.nestedCtxt = {};
2756  }
2757 
2758  // conduct recursive step
2759  return nextPtr->insert(tail(tuple), ctxt.nestedCtxt);
2760  }
2761 
2762  bool contains(const_entry_span_type tuple, op_context& ctxt) const {
2763  // check context
2764  if (ctxt.lastNested && ctxt.lastQuery == tuple[0]) {
2765  base::hint_stats.contains.addHit();
2766  return ctxt.lastNested->contains(tail(tuple), ctxt.nestedCtxt);
2767  }
2768 
2769  base::hint_stats.contains.addMiss();
2770 
2771  // lookup next step
2772  auto next = store.lookup(tuple[0], ctxt.local);
2773 
2774  // clear context if necessary
2775  if (next != ctxt.lastNested) {
2776  ctxt.lastQuery = tuple[0];
2777  ctxt.lastNested = next;
2778  ctxt.nestedCtxt = {};
2779  }
2780 
2781  // conduct recursive step
2782  return next && next->contains(tail(tuple), ctxt.nestedCtxt);
2783  }
2784 
2785  /**
2786  * Obtains a range of elements matching the prefix of the given entry up to
2787  * levels elements. A operation context may be provided to exploit temporal
2788  * locality.
2789  *
2790  * @tparam levels the length of the requested matching prefix
2791  * @param entry the entry to be looking for
2792  * @param ctxt the operation context to be utilized
2793  * @return the corresponding range of matching elements
2794  */
2795  template <unsigned levels>
2796  range<iterator> getBoundaries(const_entry_span_type entry, op_context& ctxt) const {
2797  // if nothing is bound => just use begin and end
2798  if constexpr (levels == 0) return make_range(begin(), end());
2799 
2800  // check context
2801  if (ctxt.lastBoundaryLevels == levels) {
2802  bool fit = true;
2803  for (unsigned i = 0; i < levels; ++i) {
2804  fit = fit && (entry[i] == ctxt.lastBoundaryRequest[i]);
2805  }
2806 
2807  // if it fits => take it
2808  if (fit) {
2809  base::hint_stats.get_boundaries.addHit();
2810  return ctxt.lastBoundaries;
2811  }
2812  }
2813 
2814  // the hint has not been a hit
2815  base::hint_stats.get_boundaries.addMiss();
2816 
2817  // start with two end iterators
2818  iterator begin{};
2819  iterator end{};
2820 
2821  // adapt them level by level
2822  auto found = fix_binding<levels, 0, Dim>()(store, begin, end, entry);
2823  if (!found) return make_range(iterator(), iterator());
2824 
2825  // update context
2826  static_assert(std::tuple_size_v<decltype(ctxt.lastBoundaryRequest)> == Dim);
2827  static_assert(std::tuple_size_v<decltype(entry)> == Dim);
2828  ctxt.lastBoundaryLevels = levels;
2829  std::copy_n(entry.begin(), Dim, ctxt.lastBoundaryRequest.begin());
2830  ctxt.lastBoundaries = make_range(begin, end);
2831 
2832  // use the result
2833  return ctxt.lastBoundaries;
2834  }
2835 
2836  /**
2837  * Obtains an iterator to the first element not less than the given entry value.
2838  *
2839  * @param entry the lower bound for this search
2840  * @param ctxt the operation context to be utilized
2841  * @return an iterator addressing the first element in this structure not less than the given value
2842  */
2843  iterator lower_bound(const_entry_span_type entry, op_context& /* ctxt */) const {
2844  // start with a default-initialized iterator
2845  iterator res;
2846 
2847  // adapt it level by level
2848  bool found = fix_lower_bound<Dim>()(store, res, entry);
2849 
2850  // use the result
2851  return found ? res : end();
2852  }
2853 
2854  /**
2855  * Obtains an iterator to the first element greater than the given entry value, or end if there is no
2856  * such element.
2857  *
2858  * @param entry the upper bound for this search
2859  * @param ctxt the operation context to be utilized
2860  * @return an iterator addressing the first element in this structure greater than the given value
2861  */
2862  iterator upper_bound(const_entry_span_type entry, op_context& /* ctxt */) const {
2863  // start with a default-initialized iterator
2864  iterator res;
2865 
2866  // adapt it level by level
2867  bool found = fix_upper_bound<Dim>()(store, res, entry);
2868 
2869  // use the result
2870  return found ? res : end();
2871  }
2872 
2873  /**
2874  * Computes a partition of an approximate number of chunks of the content
2875  * of this trie. Thus, the union of the resulting set of disjoint ranges is
2876  * equivalent to the content of this trie.
2877  *
2878  * @param chunks the number of chunks requested
2879  * @return a list of sub-ranges forming a partition of the content of this trie
2880  */
2881  std::vector<range<iterator>> partition(unsigned chunks = 500) const {
2882  std::vector<range<iterator>> res;
2883 
2884  // shortcut for empty trie
2885  if (this->empty()) return res;
2886 
2887  // use top-level elements for partitioning
2888  int step = std::max(store.size() / chunks, size_t(1));
2889 
2890  int c = 1;
2891  auto priv = begin();
2892  for (auto it = store.begin(); it != store.end(); ++it, c++) {
2893  if (c % step != 0 || c == 1) {
2894  continue;
2895  }
2896  auto cur = iterator(it);
2897  res.push_back(make_range(priv, cur));
2898  priv = cur;
2899  }
2900  // add final chunk
2901  res.push_back(make_range(priv, end()));
2902  return res;
2903  }
2904 };
2905 
2906 /**
2907  * A template specialization for tries representing a set.
2908  * For improved memory efficiency, this level is the leaf-node level
2909  * of all tries exhibiting an arity >= 1. Internally, values are stored utilizing
2910  * sparse bit maps.
2911  */
2912 template <>
2913 class Trie<1u> : public TrieBase<1u, Trie<1u>> {
2914  using base = TrieBase<1u, Trie<1u>>;
2915  using types = TrieTypes<1u>;
2916  using store_type = typename types::store_type;
2917 
2918  using base::store;
2919 
2920 public:
2921  using const_entry_span_type = typename types::const_entry_span_type;
2922  using entry_span_type = typename types::entry_span_type;
2923  using entry_type = typename types::entry_type;
2924  using iterator = typename types::iterator;
2925  using iterator_core = typename types::iterator_core;
2926  using op_context = typename types::op_context;
2927  // type aliases for compatibility with `BTree` and others
2928  using operation_hints = op_context;
2931  using base::begin;
2933  using base::empty;
2934  using base::end;
2935  using base::find;
2936  using base::getBoundaries;
2937  using base::insert;
2938  using base::lower_bound;
2939  using base::upper_bound;
2941  /**
2942  * Determines the number of entries in this trie.
2943  */
2944  std::size_t size() const {
2945  return store.size();
2946  }
2947 
2948  /**
2949  * Computes the total memory usage of this data structure.
2950  */
2951  std::size_t getMemoryUsage() const {
2952  // compute the total memory usage
2953  return sizeof(*this) - sizeof(store) + store.getMemoryUsage();
2954  }
2955 
2956  /**
2957  * Removes all elements form this trie.
2958  */
2959  void clear() {
2960  store.clear();
2961  }
2962 
2963  /**
2964  * Inserts the given tuple into this trie.
2965  * An operation context can be provided to exploit temporal locality.
2966  *
2967  * @param tuple the tuple to be inserted
2968  * @param ctxt an operation context for exploiting temporal locality
2969  * @return true if the tuple has not been present before, false otherwise
2970  */
2971  bool insert(const_entry_span_type tuple, op_context& ctxt) {
2972  return store.set(tuple[0], ctxt);
2973  }
2974 
2975  /**
2976  * Determines whether the given tuple is present in this trie or not.
2977  * An operation context can be provided to exploit temporal locality.
2978  *
2979  * @param tuple the tuple to be tested
2980  * @param ctxt an operation context for exploiting temporal locality
2981  * @return true if present, false otherwise
2982  */
2983  bool contains(const_entry_span_type tuple, op_context& ctxt) const {
2984  return store.test(tuple[0], ctxt);
2985  }
2986 
2987  // ---------------------------------------------------------------------
2988  // Iterator
2989  // ---------------------------------------------------------------------
2990 
2991  /**
2992  * Obtains a partition of this tire such that the resulting list of ranges
2993  * cover disjoint subsets of the elements stored in this trie. Their union
2994  * is equivalent to the content of this trie.
2995  */
2996  std::vector<range<iterator>> partition(unsigned chunks = 500) const {
2997  std::vector<range<iterator>> res;
2998 
2999  // shortcut for empty trie
3000  if (this->empty()) return res;
3001 
3002  // use top-level elements for partitioning
3003  int step = static_cast<int>(std::max(store.size() / chunks, size_t(1)));
3004 
3005  int c = 1;
3006  auto priv = begin();
3007  for (auto it = store.begin(); it != store.end(); ++it, c++) {
3008  if (c % step != 0 || c == 1) {
3009  continue;
3010  }
3011  auto cur = iterator(it);
3012  res.push_back(make_range(priv, cur));
3013  priv = cur;
3014  }
3015  // add final chunk
3016  res.push_back(make_range(priv, end()));
3017  return res;
3018  }
3019 
3020  /**
3021  * Obtains a range of elements matching the prefix of the given entry up to
3022  * levels elements. A operation context may be provided to exploit temporal
3023  * locality.
3024  *
3025  * @tparam levels the length of the requested matching prefix
3026  * @param entry the entry to be looking for
3027  * @param ctxt the operation context to be utilized
3028  * @return the corresponding range of matching elements
3029  */
3030  template <unsigned levels>
3031  range<iterator> getBoundaries(const_entry_span_type entry, op_context& ctxt) const {
3032  // for levels = 0
3033  if (levels == 0) return make_range(begin(), end());
3034  // for levels = 1
3035  auto pos = store.find(entry[0], ctxt);
3036  if (pos == store.end()) return make_range(end(), end());
3037  auto next = pos;
3038  ++next;
3039  return make_range(iterator(pos), iterator(next));
3040  }
3041 
3042  iterator lower_bound(const_entry_span_type entry, op_context&) const {
3043  return iterator(store.lower_bound(entry[0]));
3044  }
3045 
3046  iterator upper_bound(const_entry_span_type entry, op_context&) const {
3047  return iterator(store.upper_bound(entry[0]));
3048  }
3049 };
3050 
3051 } // end namespace souffle
3052 
3053 namespace std {
3054 
3055 using namespace ::souffle::detail::brie;
3056 
3057 template <typename A>
3058 struct iterator_traits<SparseArrayIter<A>>
3059  : forward_non_output_iterator_traits<typename SparseArrayIter<A>::value_type> {};
3060 
3061 template <typename A>
3062 struct iterator_traits<SparseBitMapIter<A>>
3063  : forward_non_output_iterator_traits<typename SparseBitMapIter<A>::value_type> {};
3064 
3065 template <typename A, typename IterCore>
3066 struct iterator_traits<TrieIterator<A, IterCore>> : forward_non_output_iterator_traits<A> {};
3067 
3068 } // namespace std
3069 
3070 #ifdef _WIN32
3071 #undef __sync_synchronize
3072 #undef __sync_bool_compare_and_swap
3073 #endif
souffle::detail::brie::TrieTypes::op_context
Definition: Brie.h:2561
souffle::detail::brie::TrieBase< 1u, Trie< 1u > >::store_type
typename types::store_type store_type
Definition: Brie.h:2042
souffle::SparseArray::unsynced
RootInfo unsynced
Definition: Brie.h:411
souffle::detail::brie::identity::operator()
T operator()(T v) const
Definition: Brie.h:130
souffle::Trie::operation_hints
op_context operation_hints
Definition: Brie.h:2674
souffle::SparseArray::clear
void clear()
Resets the content of this array to default values for each contained element.
Definition: Brie.h:572
TCB_SPAN_NAMESPACE_NAME::detail::size
constexpr auto size(const C &c) -> decltype(c.size())
Definition: span.h:198
souffle::detail::brie::TrieTypes::const_entry_span_type
span< const brie_element_type, Dim > const_entry_span_type
Definition: Brie.h:2463
souffle::detail::brie::TrieBase< 1u, Trie< 1u > >::iterator
typename types::iterator iterator
Definition: Brie.h:2050
souffle::SparseArray::find
iterator find(index_type i) const
An operation to obtain an iterator referencing an element addressed by the given index.
Definition: Brie.h:1118
souffle::range
A utility class enabling representation of ranges by pairing two iterator instances marking lower and...
Definition: ContainerUtil.h:313
souffle::Trie< 1u >::element_type
entry_type element_type
Definition: Brie.h:2945
m
var m
Definition: htmlJsChartistMin.h:15
souffle::detail::brie
Definition: Brie.h:81
souffle::detail::brie::SparseBitMapIter
Iterator type for souffle::SparseArray.
Definition: Brie.h:1518
souffle::detail::brie::TrieTypes::nested_trie_type
Trie< Dim - 1 > nested_trie_type
Definition: Brie.h:2466
souffle::RamDomain
int32_t RamDomain
Definition: RamTypes.h:56
souffle::SparseArray::getMemoryUsage
static std::size_t getMemoryUsage(const Node *node, int level)
Computes the memory usage of the given sub-tree.
Definition: Brie.h:533
souffle::range::end
Iter & end()
Definition: ContainerUtil.h:334
souffle::span
tcb::span< A, E > span
Definition: span.h:659
souffle::ast::analysis::sub
std::shared_ptr< Constraint< Var > > sub(const Var &a, const Var &b, const std::string &symbol="⊑")
A generic factory for constraints of the form.
Definition: ConstraintSystem.h:228
souffle::Trie::iterator_core
typename types::iterator_core iterator_core
Definition: Brie.h:2671
souffle::detail::print
A generic element printer.
Definition: StreamUtil.h:120
souffle::detail::brie::SparseArrayIter::operator!=
bool operator!=(const SparseArrayIter &other) const
Definition: Brie.h:188
souffle::Trie< 1u >::entry_type
typename types::entry_type entry_type
Definition: Brie.h:2939
souffle::SparseArray::FirstInfoSnapshot
A struct summarizing the state of the first node reference.
Definition: Brie.h:674
souffle::SparseBitMap::iterator
SparseBitMapIter< this_t > iterator
Definition: Brie.h:1810
souffle::detail::brie::default_merge::operator()
T operator()(T a, T b) const
Merges two values a and b when merging spase maps.
Definition: Brie.h:144
low
d d low
Definition: htmlJsChartistMin.h:15
souffle::detail::brie::forward_non_output_iterator_traits::pointer
const value_type * pointer
Definition: Brie.h:93
souffle::detail::brie::SparseBitMapIter::value_type
typename SparseBitMap::index_type value_type
Definition: Brie.h:1520
souffle::detail::brie::SparseArrayIter::index_type
typename SparseArray::index_type index_type
Definition: Brie.h:157
souffle::contains
bool contains(const C &container, const typename C::value_type &element)
A utility to check generically whether a given element is contained in a given container.
Definition: ContainerUtil.h:75
souffle::SparseArray::getIndex
static index_type getIndex(brie_element_type a, unsigned level)
Obtains the index within the arrays of cells of a given index on a given level of the internally main...
Definition: Brie.h:1498
souffle::detail::brie::identity
A functor representing the identity function.
Definition: Brie.h:129
souffle::Trie::op_context
typename types::op_context op_context
Definition: Brie.h:2672
souffle::detail::brie::SparseArrayIter::operator->
const value_type * operator->() const
Definition: Brie.h:198
types
std::vector< Own< ast::Type > > types
Definition: ComponentInstantiation.cpp:64
MiscUtil.h
souffle::SparseArray
A sparse array simulates an array associating to every element of uint32_t an element of a generic ty...
Definition: Brie.h:339
souffle::SparseBitMap::lower_bound
iterator lower_bound(index_type i) const
Locates an iterator to the first element in this sparse bit map not less than the given index.
Definition: Brie.h:1861
souffle::SparseArray::getAtomic
atomic_value_type & getAtomic(index_type i)
Obtains a mutable reference to the atomic value addressed by the given index.
Definition: Brie.h:766
souffle::detail::brie::SparseArrayIter::operator=
SparseArrayIter & operator=(const SparseArrayIter &)=default
souffle::detail::brie::SparseArrayIter
Iterator type for souffle::SparseArray.
Definition: Brie.h:155
souffle::SparseArray::lowerBound
iterator lowerBound(index_type i) const
An operation obtaining the smallest non-default element such that it's index is >= the given index.
Definition: Brie.h:1187
base
T & base
Definition: Reader.h:60
souffle::SparseBitMap::end
iterator end() const
Returns an iterator referencing the position after the last set bit.
Definition: Brie.h:1825
souffle::detail::brie::TrieIterator::print
void print(std::ostream &out) const
Definition: Brie.h:2010
souffle::detail::brie::forward_non_output_iterator_traits::difference_type
ptrdiff_t difference_type
Definition: Brie.h:91
souffle::detail::brie::TrieTypes::nested_trie_cloner
Definition: Brie.h:2479
souffle::Trie
Definition: Brie.h:79
souffle::SparseBitMap::store
data_store_t store
Definition: Brie.h:1673
souffle::SparseArray::RootInfo::levels
uint32_t levels
Definition: Brie.h:400
souffle::SparseArray::lookup
value_type lookup(index_type i) const
Obtains the value associated to index i – which might be the default value of the covered type if the...
Definition: Brie.h:946
souffle::Trie::element_type
entry_type element_type
Definition: Brie.h:2675
souffle::detail::brie::forward_non_output_iterator_traits
Definition: Brie.h:89
souffle::detail::brie::drop
auto drop(span< A, arity > s) -> std::enable_if_t< offset<=arity, span< A, arity - offset >>
Definition: Brie.h:105
souffle::detail::brie::TrieTypes< 1u >::const_entry_span_type
span< const brie_element_type, 1 > const_entry_span_type
Definition: Brie.h:2582
souffle::detail::brie::TrieTypes::nested_trie_merger
Definition: Brie.h:2469
souffle::detail::brie::forward_non_output_iterator_traits::iterator_category
std::forward_iterator_tag iterator_category
Definition: Brie.h:92
souffle::Trie::const_entry_span_type
typename types::const_entry_span_type const_entry_span_type
Definition: Brie.h:2667
souffle::SparseBitMap
A sparse bit-map is a bit map virtually assigning a bit value to every value if the uint32_t domain.
Definition: Brie.h:1637
souffle::SparseArray::RootInfo::first
Node * first
Definition: Brie.h:405
souffle::detail::brie::TrieTypes< 1u >::entry_type
std::array< brie_element_type, 1 > entry_type
Definition: Brie.h:2580
forward_non_output_iterator_traits
souffle::detail::brie::SparseBitMapIter::nested_iterator
typename data_store_t::iterator nested_iterator
Definition: Brie.h:1522
BRIE_OVERLOAD_INIT_LIST
#define BRIE_OVERLOAD_INIT_LIST(fn, constness)
Definition: Brie.h:2131
souffle::detail::brie::fix_binding
A functor initializing an iterator upon creation to reference the first element exhibiting a given pr...
Definition: Brie.h:2268
souffle::detail::brie::forward_non_output_iterator_traits::value_type
A value_type
Definition: Brie.h:90
souffle::SparseArray::RootInfo::offset
index_type offset
Definition: Brie.h:402
souffle::detail::brie::TrieIterator
An iterator over the stored entries.
Definition: Brie.h:1933
souffle::Trie::iterator
typename types::iterator iterator
Definition: Brie.h:2670
souffle::SparseBitMap::atomic_value_t
typename data_store_t::atomic_value_type atomic_value_t
Definition: Brie.h:1655
souffle::SparseArray::FirstInfoSnapshot::version
uintptr_t version
Definition: Brie.h:680
souffle::detail::brie::TrieBase::store
store_type store
Definition: Brie.h:2044
souffle::clone
auto clone(const std::vector< A * > &xs)
Definition: ContainerUtil.h:172
souffle::detail::brie::tail
auto tail(C &s)
Definition: Brie.h:110
souffle::SparseBitMap::begin
iterator begin() const
Obtains an iterator pointing to the first index set to 1.
Definition: Brie.h:1816
souffle::SparseArray::op_context
A struct to be utilized as a local, temporal context by client code to speed up the execution of vari...
Definition: Brie.h:584
souffle::times
detail::multiplying_printer< T > times(const T &value, unsigned num)
A utility printing a given value multiple times.
Definition: StreamUtil.h:322
i
size_t i
Definition: json11.h:663
ContainerUtil.h
souffle::SparseArray::getLevelMask
static index_type getLevelMask(unsigned level)
Computes the bit-mask to be applicable to obtain the offset of a node on a given tree level.
Definition: Brie.h:1506
souffle::detail::brie::fix_lower_bound::const_entry_span_type
typename types::const_entry_span_type const_entry_span_type
Definition: Brie.h:2364
souffle::SparseArray::addAll
void addAll(const SparseArray &other)
Adds all the values stored in the given array to this array.
Definition: Brie.h:1042
span.h
souffle::SparseBitMap::index_type
typename data_store_t::index_type index_type
Definition: Brie.h:1669
souffle::SparseBitMap::op_context
typename data_store_t::op_context op_context
Definition: Brie.h:1697
souffle::detail::brie::SparseBitMapIter::mask
uint64_t mask
Definition: Brie.h:1528
souffle::detail::brie::SparseArrayIter::isEnd
bool isEnd() const
Definition: Brie.h:283
souffle::detail::brie::TrieBase< 1u, Trie< 1u > >::entry_type
typename types::entry_type entry_type
Definition: Brie.h:2049
souffle::detail::brie::SparseBitMapIter::iter
nested_iterator iter
Definition: Brie.h:1525
souffle::detail::brie::TrieBase< 1u, Trie< 1u > >::entry_span_type
typename types::entry_span_type entry_span_type
Definition: Brie.h:2048
souffle::SparseBitMap::value_t
uint64_t value_t
Definition: Brie.h:1644
souffle::SparseArray::RootInfo::root
Node * root
Definition: Brie.h:398
souffle::make_range
range< Iter > make_range(const Iter &a, const Iter &b)
A utility function enabling the construction of ranges without explicitly specifying the iterator typ...
Definition: ContainerUtil.h:389
souffle::detail::brie::TrieTypes::iterator_core::iter
store_iter iter
Definition: Brie.h:2497
souffle::detail::brie::fix_first_nested
Definition: Brie.h:2242
souffle::detail::brie::SparseArrayIter::SparseArrayIter
SparseArrayIter()=default
souffle::detail::brie::SparseArrayIter::value
value_type value
Definition: Brie.h:304
souffle::detail::brie::default_merge
A operation to be utilized by the sparse map when merging elements associated to different values.
Definition: Brie.h:140
TCB_SPAN_NAMESPACE_NAME::get
constexpr auto get(span< E, S > s) -> decltype(s[N])
Definition: span.h:599
souffle::SparseBitMap::toMask
static uint64_t toMask(const value_t &value)
Definition: Brie.h:1662
souffle::SparseBitMap::find
iterator find(index_type i) const
Obtains an iterator referencing the position i if the corresponding bit is set, end() otherwise.
Definition: Brie.h:1833
souffle::detail::brie::TrieTypes::iterator_core::store_iter
typename store_type::iterator store_iter
Definition: Brie.h:2493
souffle::detail::brie::fix_lower_bound
A functor initializing an iterator upon creation to reference the first element within a given Trie b...
Definition: Brie.h:2362
souffle::detail::brie::fix_first< Dim, Dim >::operator()
void operator()(const Store &, iterator &) const
Definition: Brie.h:2236
souffle::detail::brie::brie_element_type
RamDomain brie_element_type
Definition: Brie.h:84
souffle::detail::brie::fix_upper_bound
A functor initializing an iterator upon creation to reference the first element within a given Trie b...
Definition: Brie.h:2413
souffle::detail::brie::TrieTypes::entry_type
std::array< brie_element_type, Dim > entry_type
Definition: Brie.h:2461
souffle::SparseArray::value_type
T value_type
Definition: Brie.h:356
souffle::detail::brie::TrieBase::getBoundaries
range< iterator > getBoundaries(const_entry_span_type entry) const
Definition: Brie.h:2110
souffle::detail::brie::SparseArrayIter::Node
typename SparseArray::Node Node
Definition: Brie.h:156
souffle::Trie< 1u >::op_context
typename types::op_context op_context
Definition: Brie.h:2942
souffle::operator<<
std::ostream & operator<<(std::ostream &os, AggregateOp op)
Definition: AggregateOp.h:51
souffle::detail::brie::TrieTypes::iterator_core
Definition: Brie.h:2492
souffle::SparseArray::INDEX_MASK
static constexpr key_type INDEX_MASK
Definition: Brie.h:349
souffle::detail::brie::TrieBase::const_entry_span_type
typename types::const_entry_span_type const_entry_span_type
Definition: Brie.h:2047
souffle::detail::brie::copy
auto copy(span< A, arity > s)
Definition: Brie.h:98
souffle::detail::brie::TrieIterator::iter_core_arg_type
typename std::remove_reference_t< IterCore >::store_iter iter_core_arg_type
Definition: Brie.h:1953
souffle::detail::brie::TrieBase
A base class for the Trie implementation allowing various specializations of the Trie template to inh...
Definition: Brie.h:2031
souffle::range::begin
Iter & begin()
Definition: ContainerUtil.h:326
souffle::detail::brie::SparseArrayIter::array_value_type
typename SparseArray::value_type array_value_type
Definition: Brie.h:158
souffle::detail::brie::TrieTypes::iterator_core::nested
nested_core_iter nested
Definition: Brie.h:2498
souffle::detail::brie::TrieIterator::iter_core
IterCore iter_core
Definition: Brie.h:1956
souffle::detail::brie::SparseArrayIter::value_type
std::pair< index_type, array_value_type > value_type
Definition: Brie.h:160
souffle::detail::brie::TrieTypes
Definition: Brie.h:2021
b
l j a showGridBackground &&c b raw series this eventEmitter b
Definition: htmlJsChartistMin.h:15
souffle::SparseArray::Node
The node type of the internally maintained tree.
Definition: Brie.h:385
std
Definition: Brie.h:3053
souffle::detail::brie::forward_non_output_iterator_traits::reference
const value_type & reference
Definition: Brie.h:94
souffle::detail::brie::TrieTypes::iterator
TrieIterator< entry_type, iterator_core > iterator
Definition: Brie.h:2558
souffle::detail::brie::fix_upper_bound::const_entry_span_type
typename types::const_entry_span_type const_entry_span_type
Definition: Brie.h:2415
RamTypes.h
souffle::SparseArray::BIT_PER_STEP
static constexpr int BIT_PER_STEP
Definition: Brie.h:347
souffle::SparseBitMap::data_store_t
SparseArray< value_t, BITS, merge_op > data_store_t
Definition: Brie.h:1654
souffle::detail::brie::SparseBitMapIter::isEnd
bool isEnd() const
Definition: Brie.h:1593
souffle::SparseArray::RootInfoSnapshot::version
uintptr_t version
Definition: Brie.h:606
StreamUtil.h
souffle::SparseArray::begin
iterator begin() const
Obtains an iterator referencing the first non-default element or end in case there are no such elemen...
Definition: Brie.h:1102
souffle::detail::brie::TrieBase< 1u, Trie< 1u > >::iterator_core
typename types::iterator_core iterator_core
Definition: Brie.h:2051
souffle::detail::brie::default_factory
A templated functor to obtain default values for unspecified elements of sparse array instances.
Definition: Brie.h:119
TCB_SPAN_NAMESPACE_NAME::make_span
constexpr span< ElementType, Extent > make_span(span< ElementType, Extent > s) noexcept
Definition: span.h:543
souffle
Definition: AggregateOp.h:25
souffle::SparseBitMap::LEAF_INDEX_WIDTH
static constexpr size_t LEAF_INDEX_WIDTH
Definition: Brie.h:1659
souffle::detail::brie::SparseArrayIter::operator==
bool operator==(const SparseArrayIter &other) const
Definition: Brie.h:181
souffle::SparseArray< value_t, 4, merge_op >::atomic_value_type
std::atomic< value_type > atomic_value_type
Definition: Brie.h:359
souffle::SparseArray::NUM_CELLS
static constexpr int NUM_CELLS
Definition: Brie.h:348
souffle::detail::brie::default_factory::operator()
T operator()() const
Definition: Brie.h:120
souffle::ramBitCast
To ramBitCast(From source)
In C++20 there will be a new way to cast between types by reinterpreting bits (std::bit_cast),...
Definition: RamTypes.h:87
souffle::detail::brie::TrieBase< 1u, Trie< 1u > >::op_context
typename types::op_context op_context
Definition: Brie.h:2052
souffle::SparseArray::RootInfo::firstOffset
index_type firstOffset
Definition: Brie.h:407
souffle::SparseArray::RootInfoSnapshot
A struct to cover a snapshot of the root node state.
Definition: Brie.h:598
souffle::Trie< 1u >::iterator
typename types::iterator iterator
Definition: Brie.h:2940
souffle::detail::brie::TrieTypes< 1u >::iterator_core::iter
store_iter iter
Definition: Brie.h:2596
souffle::detail::brie::SparseArrayIter::operator++
SparseArrayIter & operator++()
Definition: Brie.h:203
souffle::SparseBitMap::upper_bound
iterator upper_bound(index_type i) const
Locates an iterator to the first element in this sparse bit map than is greater than the given index.
Definition: Brie.h:1895
CacheUtil.h
souffle::SparseArray::RootInfo
A struct describing all the information required by the container class to manage the wrapped up tree...
Definition: Brie.h:396
souffle::tuple
Defines a tuple for the OO interface such that relations with varying columns can be accessed.
Definition: SouffleInterface.h:443
souffle::detail::brie::SparseArrayIter::print
void print(std::ostream &out) const
Definition: Brie.h:288
souffle::detail::brie::get_nested_iter_core
A functor extracting a reference to a nested iterator core from an enclosing iterator core.
Definition: Brie.h:2192
souffle::detail::brie::TrieTypes::entry_span_type
span< brie_element_type, Dim > entry_span_type
Definition: Brie.h:2462
souffle::Trie< 1u >::const_entry_span_type
typename types::const_entry_span_type const_entry_span_type
Definition: Brie.h:2937
souffle::SparseArray::empty
bool empty() const
Tests whether this sparse array is empty, thus it only contains default-values, or not.
Definition: Brie.h:509
souffle::detail::brie::TrieTypes< 1u >::iterator_core::store_iter
typename store_type::iterator store_iter
Definition: Brie.h:2593
souffle::detail::brie::SparseArrayIter::operator*
const value_type & operator*() const
Definition: Brie.h:193
souffle::detail::brie::SparseArrayIter::node
const Node * node
Definition: Brie.h:301
souffle::SparseArray::dump
void dump(bool detailed, std::ostream &out, const Node &node, int level, index_type offset, int indent=0) const
An internal debug utility printing the internal structure of this sparse array to the given output st...
Definition: Brie.h:1279
souffle::Trie::entry_type
typename types::entry_type entry_type
Definition: Brie.h:2669
souffle::detail::brie::TrieTypes< 1u >::entry_span_type
span< brie_element_type, 1 > entry_span_type
Definition: Brie.h:2581
json11::dump
static void dump(NullStruct, std::string &out)
Definition: json11.h:285
souffle::range::empty
bool empty() const
Definition: ContainerUtil.h:342
souffle::detail::brie::SparseArrayIter::operator<<
friend std::ostream & operator<<(std::ostream &out, const SparseArrayIter &iter)
Definition: Brie.h:294
souffle::SparseArray::index_type
key_type index_type
Definition: Brie.h:353
souffle::SparseArray::Cell
The value stored in a single cell of a inner or leaf node.
Definition: Brie.h:368
souffle::detail::brie::fix_first
A functor initializing an iterator upon creation to reference the first element in the associated Tri...
Definition: Brie.h:2213