mdds
trie_map_itr.hpp
1 // SPDX-FileCopyrightText: 2016 - 2025 Kohei Yoshida
2 //
3 // SPDX-License-Identifier: MIT
4 
5 #pragma once
6 
7 #include <utility>
8 #include <cassert>
9 #include <iostream>
10 #ifdef MDDS_TRIE_MAP_DEBUG
11 #include <sstream>
12 #endif
13 
14 #include "mdds/global.hpp"
15 #include "mdds/ref_pair.hpp"
16 
17 namespace mdds { namespace trie { namespace detail {
18 
19 enum class iterator_type
20 {
25  normal,
31  end,
37  empty
38 };
39 
40 enum empty_iterator_type
41 {
42  empty_iterator
43 };
44 
45 template<typename _TrieType, typename _C>
47 
48 template<typename _TrieType>
49 struct get_node_stack_type<_TrieType, std::true_type>
50 {
51  using type = typename _TrieType::const_node_stack_type;
52 };
53 
54 template<typename _TrieType>
55 struct get_node_stack_type<_TrieType, std::false_type>
56 {
57  using type = typename _TrieType::node_stack_type;
58 };
59 
60 template<typename _TrieType>
62 
63 template<typename _TrieType, bool IsConst>
65 {
66 protected:
67  using trie_type = _TrieType;
68 
69  using _is_const = std::bool_constant<IsConst>;
70 
71  friend trie_type;
73 
74  using node_stack_type = typename get_node_stack_type<trie_type, _is_const>::type;
75  using trie_node_type = mdds::detail::const_t<typename trie_type::trie_node, IsConst>;
76  using trie_node_child_pos_type = typename mdds::detail::get_iterator_type<
77  typename std::remove_const<trie_node_type>::type::children_type, _is_const>::type;
78 
79  using key_type = typename trie_type::key_type;
81 
82 public:
83  // iterator traits
85  using pointer = value_type*;
86  using reference = value_type&;
87  using difference_type = std::ptrdiff_t;
88  using iterator_category = std::bidirectional_iterator_tag;
89 
90 protected:
91  node_stack_type m_node_stack;
92  key_type m_buffer;
93  key_type m_current_key;
94  trie_value_type* m_current_value_ptr;
95  iterator_type m_type;
96 
97  static trie_node_type* push_child_node_to_stack(
98  node_stack_type& node_stack, key_type& buf, trie_node_child_pos_type& child_pos)
99  {
100  trie_node_type* node = &child_pos->second;
101  buf.push_back(child_pos->first);
102  node_stack.emplace_back(node, node->children.begin());
103 
104  return node;
105  }
106 
111  static trie_node_type* descend_to_previus_leaf_node(node_stack_type& node_stack, key_type& buf)
112  {
113  trie_node_type* cur_node = nullptr;
114 
115  do
116  {
117  // Keep moving down on the right-most child nodes until the
118  // leaf node is reached.
119 
120  auto& si = node_stack.back();
121 
122  --si.child_pos;
123  buf.push_back(si.child_pos->first);
124  cur_node = &si.child_pos->second;
125  node_stack.emplace_back(cur_node, cur_node->children.end());
126  } while (!cur_node->children.empty());
127 
128  return cur_node;
129  }
130 
131  iterator_base(empty_iterator_type) : m_current_value_ptr(nullptr), m_type(iterator_type::empty)
132  {}
133 
134 public:
135  iterator_base() : m_current_value_ptr(nullptr), m_type(iterator_type::normal)
136  {}
137 
138  iterator_base(node_stack_type&& node_stack, key_type&& buf, iterator_type type)
139  : m_node_stack(std::move(node_stack)), m_buffer(std::move(buf)), m_current_key(m_buffer),
140  m_current_value_ptr(&m_node_stack.back().node->value), m_type(type)
141  {}
142 
143  bool operator==(const iterator_base& other) const
144  {
145  if (m_type != other.m_type)
146  return false;
147 
148  if (m_type == iterator_type::empty)
149  return true;
150 
151  return m_node_stack.back() == other.m_node_stack.back();
152  }
153 
154  bool operator!=(const iterator_base& other) const
155  {
156  return !operator==(other);
157  }
158 
159  value_type operator*()
160  {
161  return value_type(m_current_key, *m_current_value_ptr);
162  }
163 
164  value_type operator->()
165  {
166  return value_type(m_current_key, *m_current_value_ptr);
167  }
168 
169  iterator_base& operator++()
170  {
171  trie_node_type* cur_node = m_node_stack.back().node;
172 
173  do
174  {
175  if (cur_node->children.empty())
176  {
177  // Current node is a leaf node. Keep moving up the stack until we
178  // reach a parent node with unvisited children.
179 
180  while (true)
181  {
182  if (m_node_stack.size() == 1)
183  {
184 #ifdef MDDS_TRIE_MAP_DEBUG
185  if (m_type == iterator_type::end)
186  {
187  std::ostringstream os;
188  os << "iterator_base::operator++#" << __LINE__ << ": moving past the end position!";
189  throw general_error(os.str());
190  }
191 #endif
192  // We've reached the end position. Bail out.
193  m_type = iterator_type::end;
194  return *this;
195  }
196 
197  // Move up one parent and see if it has an unvisited child node.
198  m_buffer.pop_back();
199  m_node_stack.pop_back();
200  auto& si = m_node_stack.back();
201  ++si.child_pos;
202 
203  if (si.child_pos != si.node->children.end())
204  {
205  // Move down to this unvisited child node.
206  cur_node = push_child_node_to_stack(m_node_stack, m_buffer, si.child_pos);
207  break;
208  }
209  }
210  }
211  else
212  {
213  // Current node has child nodes. Follow the first child node.
214  auto child_pos = cur_node->children.begin();
215  cur_node = push_child_node_to_stack(m_node_stack, m_buffer, child_pos);
216  }
217  } while (!cur_node->has_value);
218 
219  m_current_key = m_buffer;
220  m_current_value_ptr = &cur_node->value;
221  return *this;
222  }
223 
224  iterator_base operator++(int)
225  {
226  iterator_base tmp(*this);
227  operator++();
228  return tmp;
229  }
230 
231  iterator_base& operator--()
232  {
233  trie_node_type* cur_node = m_node_stack.back().node;
234 
235  if (m_type == iterator_type::end && cur_node->has_value)
236  {
237  assert(m_node_stack.size() == 1);
238  m_type = iterator_type::normal;
239  }
240  else if (m_node_stack.size() == 1)
241  {
242  // This is the end position aka root node. Move down to the
243  // right-most leaf node.
244  auto& si = m_node_stack.back();
245  assert(si.child_pos == cur_node->children.end());
246  cur_node = descend_to_previus_leaf_node(m_node_stack, m_buffer);
247  m_type = iterator_type::normal;
248  }
249  else if (cur_node->children.empty())
250  {
251  // This is a leaf node. Keep going up until it finds a parent
252  // node with unvisited child nodes on the left side, then descend
253  // on that path all the way to its leaf.
254 
255  do
256  {
257  // Go up one node.
258 
259  m_buffer.pop_back();
260  m_node_stack.pop_back();
261  auto& si = m_node_stack.back();
262  cur_node = si.node;
263 
264  if (si.child_pos != cur_node->children.begin())
265  {
266  // Left and down.
267  cur_node = descend_to_previus_leaf_node(m_node_stack, m_buffer);
268  assert(cur_node->has_value);
269  }
270  } while (!cur_node->has_value);
271  }
272  else
273  {
274  // Non-leaf node with value. Keep going up until either the root
275  // node or another node with value is reached.
276 
277  assert(cur_node->has_value);
278  assert(m_node_stack.back().child_pos == cur_node->children.begin());
279 
280  do
281  {
282  // Go up.
283  m_buffer.pop_back();
284  m_node_stack.pop_back();
285  auto& si = m_node_stack.back();
286  cur_node = si.node;
287 
288  if (m_node_stack.size() == 1)
289  {
290  // Root node reached. Left and down.
291  cur_node = descend_to_previus_leaf_node(m_node_stack, m_buffer);
292  assert(cur_node->has_value);
293  }
294  } while (!cur_node->has_value);
295  }
296 
297  assert(cur_node->has_value);
298  m_current_key = m_buffer;
299  m_current_value_ptr = &cur_node->value;
300  return *this;
301  }
302 
303  iterator_base operator--(int)
304  {
305  iterator_base tmp(*this);
306  operator--();
307  return tmp;
308  }
309 };
310 
311 template<typename _TrieType>
313 
314 template<typename _TrieType>
315 class iterator : public iterator_base<_TrieType, false>
316 {
317  using trie_type = _TrieType;
318 
319  friend trie_type;
322 
324  using node_stack_type = typename base_type::node_stack_type;
325  using key_type = typename base_type::key_type;
326 
327  iterator(empty_iterator_type t) : base_type(t)
328  {}
329 
330 public:
331  iterator() : base_type()
332  {}
333 
334  iterator(node_stack_type&& node_stack, key_type&& buf, iterator_type type)
335  : base_type(std::move(node_stack), std::move(buf), type)
336  {}
337 };
338 
339 template<typename _TrieType>
340 class const_iterator : public iterator_base<_TrieType, true>
341 {
342  using trie_type = _TrieType;
343 
344  friend trie_type;
346 
347  using base_type = iterator_base<trie_type, true>;
348  using node_stack_type = typename base_type::node_stack_type;
349  using key_type = typename base_type::key_type;
350 
351  using base_type::m_buffer;
352  using base_type::m_current_key;
353  using base_type::m_current_value_ptr;
354  using base_type::m_node_stack;
355  using base_type::m_type;
356 
357  const_iterator(empty_iterator_type t) : base_type(t)
358  {}
359 
360 public:
361  const_iterator() : base_type()
362  {}
363 
364  const_iterator(node_stack_type&& node_stack, key_type&& buf, iterator_type type)
365  : base_type(std::move(node_stack), std::move(buf), type)
366  {}
367 
368  const_iterator(const iterator<_TrieType>& it) : base_type()
369  {
370  m_buffer = it.m_buffer;
371  m_current_key = it.m_current_key;
372  m_current_value_ptr = it.m_current_value_ptr;
373  m_type = it.m_type;
374 
375  for (const auto& e : it.m_node_stack)
376  m_node_stack.emplace_back(e.node, e.child_pos);
377  }
378 };
379 
380 template<typename _TrieType>
381 bool operator==(const iterator<_TrieType>& left, const const_iterator<_TrieType>& right)
382 {
383  return const_iterator<_TrieType>(left) == right;
384 }
385 
386 template<typename _TrieType>
387 bool operator!=(const iterator<_TrieType>& left, const const_iterator<_TrieType>& right)
388 {
389  return const_iterator<_TrieType>(left) != right;
390 }
391 
392 template<typename _TrieType>
393 bool operator==(const const_iterator<_TrieType>& left, const iterator<_TrieType>& right)
394 {
395  return left == const_iterator<_TrieType>(right);
396 }
397 
398 template<typename _TrieType>
399 bool operator!=(const const_iterator<_TrieType>& left, const iterator<_TrieType>& right)
400 {
401  return left != const_iterator<_TrieType>(right);
402 }
403 
404 template<typename _TrieType>
405 class search_results
406 {
407  using trie_type = _TrieType;
408  friend trie_type;
409  using node_stack_type = typename trie_type::const_node_stack_type;
410 
411  using trie_node = typename trie_type::trie_node;
412  using key_type = typename trie_type::key_type;
413  using key_unit_type = typename key_type::value_type;
414 
415  const trie_node* m_node;
416  key_type m_buffer;
417  node_stack_type m_node_stack;
418 
419  search_results(const trie_node* node, key_type&& buf) : m_node(node), m_buffer(buf)
420  {}
421 
422 public:
423  using const_iterator = typename trie_type::const_iterator;
424 
425  const_iterator begin() const
426  {
427  if (!m_node)
428  // empty results.
429  return const_iterator(empty_iterator);
430 
431  // Push the root node.
432  key_type buf(m_buffer);
433  node_stack_type node_stack;
434  node_stack.emplace_back(m_node, m_node->children.begin());
435 
436  while (!node_stack.back().node->has_value)
437  {
438  // There should always be at least one value node along the
439  // left-most branch.
440 
441  auto it = node_stack.back().child_pos;
442  const_iterator::push_child_node_to_stack(node_stack, buf, it);
443  }
444 
445  return const_iterator(std::move(node_stack), std::move(buf), iterator_type::normal);
446  }
447 
448  const_iterator end() const
449  {
450  if (!m_node)
451  // empty results.
452  return const_iterator(empty_iterator);
453 
454  node_stack_type node_stack;
455  node_stack.emplace_back(m_node, m_node->children.end());
456  return const_iterator(std::move(node_stack), key_type(m_buffer), iterator_type::end);
457  }
458 };
459 
460 template<typename _TrieType>
462 
463 template<typename TrieT>
465 {
466  using trie_type = TrieT;
467  friend trie_type;
469 
470  using stack_item = typename trie_type::stack_item;
471  using node_stack_type = typename trie_type::node_stack_type;
472 
473  using key_type = typename trie_type::key_type;
474  using size_type = typename trie_type::size_type;
475  using trie_value_type = typename trie_type::value_type;
476  using value_store_type = typename trie_type::value_store_type;
477  using pack_value_type = typename trie_type::pack_value_type;
478  using key_unit_type = typename key_type::value_type;
479 
480 public:
481  // iterator traits
482  using value_type = mdds::detail::ref_pair<std::add_const_t<key_type>, std::add_const_t<trie_value_type>>;
483  using pointer = value_type*;
484  using reference = value_type&;
485  using difference_type = std::ptrdiff_t;
486  using iterator_category = std::bidirectional_iterator_tag;
487 
488 private:
489  const value_store_type* m_value_store = nullptr;
490  node_stack_type m_node_stack;
491  key_type m_buffer;
492  pack_value_type m_current_value = trie_type::null_value;
493  iterator_type m_type;
494 
499  static void push_child_node_to_stack(
500  const value_store_type* value_store, node_stack_type& node_stack, key_type& buf,
501  const pack_value_type* child_pos)
502  {
503  assert(value_store);
504  const auto* node_pos = node_stack.back().node_pos;
505 
506  key_unit_type c = static_cast<key_unit_type>(*child_pos);
507  buf.push_back(c);
508  ++child_pos;
509  auto offset = static_cast<size_type>(*child_pos);
510  node_pos -= offset; // Jump to the head of the child node.
511  const auto* p = node_pos;
512  ++p;
513  size_type index_size = *p;
514  ++p;
515  child_pos = p;
516  const auto* child_end = child_pos + index_size;
517 
518  // Push it onto the stack.
519  node_stack.emplace_back(value_store, node_pos, child_pos, child_end);
520  }
521 
522  static void descend_to_previous_leaf_node(node_stack_type& node_stack, key_type& buf)
523  {
524  const pack_value_type* node_pos = nullptr;
525  size_t index_size = 0;
526 
527  do
528  {
529  // Keep moving down on the right-most child nodes until the
530  // leaf node is reached.
531 
532  stack_item* si = &node_stack.back();
533  node_pos = si->node_pos;
534  --si->child_pos;
535  size_t offset = *si->child_pos;
536  --si->child_pos;
537  key_unit_type c = *si->child_pos;
538  node_pos -= offset; // Jump to the head of the child node.
539  buf.push_back(c);
540 
541  const auto* p = node_pos;
542  ++p;
543  index_size = *p;
544  ++p;
545  const auto* child_pos = p;
546  const auto* child_end = child_pos + index_size;
547  node_stack.emplace_back(node_stack.back().value_store, node_pos, child_end, child_end);
548  } while (index_size);
549  }
550 
551  packed_iterator_base(empty_iterator_type) : m_type(iterator_type::empty)
552  {}
553 
554 public:
555  packed_iterator_base() : m_type(iterator_type::normal)
556  {}
557 
559  const value_store_type* value_store, node_stack_type&& node_stack, key_type&& buf, pack_value_type pos)
560  : m_value_store(value_store), m_node_stack(std::move(node_stack)), m_buffer(std::move(buf)),
561  m_current_value(pos), m_type(iterator_type::normal)
562  {}
563 
564  packed_iterator_base(const value_store_type* value_store, node_stack_type&& node_stack, key_type&& buf)
565  : m_value_store(value_store), m_node_stack(std::move(node_stack)), m_buffer(std::move(buf)),
566  m_type(iterator_type::end)
567  {}
568 
569  bool operator==(const packed_iterator_base& other) const
570  {
571  if (m_type != other.m_type)
572  return false;
573 
574  if (m_type == iterator_type::empty)
575  return true;
576 
577  return m_node_stack.back() == other.m_node_stack.back();
578  }
579 
580  bool operator!=(const packed_iterator_base& other) const
581  {
582  return !operator==(other);
583  }
584 
585  value_type operator*()
586  {
587  assert(m_value_store);
588  assert(m_current_value != trie_type::null_value);
589  return value_type(m_buffer, (*m_value_store)[m_current_value]);
590  }
591 
592  value_type operator->()
593  {
594  assert(m_value_store);
595  assert(m_current_value != trie_type::null_value);
596  return value_type(m_buffer, (*m_value_store)[m_current_value]);
597  }
598 
599  packed_iterator_base& operator++()
600  {
601  stack_item* si = &m_node_stack.back();
602  pack_value_type v = trie_type::null_value;
603  size_t index_size = *(si->node_pos + 1);
604 
605  do
606  {
607  if (!index_size)
608  {
609  // Current node is a leaf node. Keep moving up the stack until we
610  // reach a parent node with unvisited children.
611 
612  while (true)
613  {
614  if (m_node_stack.size() == 1)
615  {
616 #ifdef MDDS_TRIE_MAP_DEBUG
617  if (m_type == iterator_type::end)
618  {
619  std::ostringstream os;
620  os << "packed_iterator_base::operator++#" << __LINE__ << ": moving past the end position!";
621  throw general_error(os.str());
622  }
623 #endif
624  // We've reached the end position. Bail out.
625  m_type = iterator_type::end;
626  return *this;
627  }
628 
629  // Move up one parent and see if it has an unvisited child node.
630  m_buffer.pop_back();
631  m_node_stack.pop_back();
632  si = &m_node_stack.back();
633  std::advance(si->child_pos, 2);
634 
635  if (si->child_pos != si->child_end)
636  {
637  // Move down to this unvisited child node.
638  push_child_node_to_stack(m_value_store, m_node_stack, m_buffer, si->child_pos);
639  break;
640  }
641  }
642  }
643  else
644  {
645  // Current node has child nodes. Follow the first child node.
646  push_child_node_to_stack(m_value_store, m_node_stack, m_buffer, si->child_pos);
647  }
648 
649  si = &m_node_stack.back();
650  v = *si->node_pos;
651  index_size = *(si->node_pos + 1);
652  } while (v == trie_type::null_value);
653 
654  assert(v != trie_type::null_value);
655  m_current_value = v;
656 
657  return *this;
658  }
659 
660  packed_iterator_base operator++(int)
661  {
662  packed_iterator_base tmp(*this);
663  operator++();
664  return tmp;
665  }
666 
667  packed_iterator_base& operator--()
668  {
669  stack_item* si = &m_node_stack.back();
670  pack_value_type v = *si->node_pos;
671  size_t index_size = *(si->node_pos + 1); // index size for child nodes.
672 
673  if (m_type == iterator_type::end && v != trie_type::null_value)
674  {
675  assert(m_node_stack.size() == 1);
676  m_type = iterator_type::normal;
677  }
678  else if (m_node_stack.size() == 1)
679  {
680  // This is the end position aka root node. Move down to the
681  // right-most leaf node.
682  assert(si->child_pos == si->child_end);
683  descend_to_previous_leaf_node(m_node_stack, m_buffer);
684  si = &m_node_stack.back();
685  v = *si->node_pos;
686  m_type = iterator_type::normal;
687  }
688  else if (!index_size)
689  {
690  // This is a leaf node. Keep going up until it finds a parent
691  // node with unvisited child nodes on the left side, then descend
692  // on that path all the way to its leaf.
693 
694  do
695  {
696  // Go up one node.
697 
698  m_buffer.pop_back();
699  m_node_stack.pop_back();
700  si = &m_node_stack.back();
701  const auto* p = si->node_pos;
702  v = *p;
703  ++p;
704  index_size = *p;
705  ++p;
706  const auto* first_child = p;
707 
708  if (si->child_pos != first_child)
709  {
710  // Left and down.
711  descend_to_previous_leaf_node(m_node_stack, m_buffer);
712  si = &m_node_stack.back();
713  p = si->node_pos;
714  v = *p;
715  assert(v != trie_type::null_value);
716  }
717  } while (v == trie_type::null_value);
718  }
719  else
720  {
721  // Non-leaf node with value. Keep going up until either the root
722  // node or another node with value is reached.
723 
724  assert(*si->node_pos); // this node should have a value.
725  assert(si->child_pos == (si->node_pos + 2));
726 
727  do
728  {
729  // Go up.
730  m_buffer.pop_back();
731  m_node_stack.pop_back();
732  si = &m_node_stack.back();
733  v = *si->node_pos;
734 
735  if (m_node_stack.size() == 1)
736  {
737  // Root node reached.
738  descend_to_previous_leaf_node(m_node_stack, m_buffer);
739  si = &m_node_stack.back();
740  v = *si->node_pos;
741  assert(v != trie_type::null_value);
742  }
743  } while (v == trie_type::null_value);
744  }
745 
746  assert(v != trie_type::null_value);
747  m_current_value = v;
748 
749  return *this;
750  }
751 
752  packed_iterator_base operator--(int)
753  {
754  packed_iterator_base tmp(*this);
755  operator--();
756  return tmp;
757  }
758 };
759 
760 template<typename _TrieType>
762 {
763  using trie_type = _TrieType;
764  friend trie_type;
765  using node_stack_type = typename trie_type::node_stack_type;
766  using value_store_type = typename trie_type::value_store_type;
767  using pack_value_type = typename trie_type::pack_value_type;
768 
769  using key_type = typename trie_type::key_type;
770  using key_unit_type = typename key_type::value_type;
771 
772  const value_store_type* m_value_store = nullptr;
773  const pack_value_type* m_node = nullptr;
774  key_type m_buffer;
775 
776  packed_search_results(const value_store_type* value_store, const pack_value_type* node, key_type&& buf)
777  : m_value_store(value_store), m_node(node), m_buffer(std::move(buf))
778  {
779  assert(m_value_store);
780  }
781 
782  node_stack_type get_root_node() const
783  {
784  const auto* p = m_node;
785  ++p;
786  size_t index_size = *p;
787  ++p;
788  const auto* child_pos = p;
789  const auto* child_end = child_pos + index_size;
790 
791  // Push this child node onto the stack.
792  node_stack_type node_stack;
793  node_stack.emplace_back(m_value_store, m_node, child_pos, child_end);
794  return node_stack;
795  }
796 
797  void swap(packed_search_results& other)
798  {
799  std::swap(m_node, other.m_node);
800  std::swap(m_buffer, other.m_buffer);
801  }
802 
803 public:
804  using const_iterator = packed_iterator_base<trie_type>;
805 
806  packed_search_results() : m_node(nullptr)
807  {}
808 
809  packed_search_results(const packed_search_results& other) : m_node(other.m_node), m_buffer(other.m_buffer)
810  {}
811 
812  packed_search_results(packed_search_results&& other) : m_node(other.m_node), m_buffer(std::move(other.m_buffer))
813  {
814  other.m_node = nullptr;
815  }
816 
817  packed_search_results& operator=(packed_search_results other)
818  {
819  packed_search_results tmp(std::move(other));
820  swap(tmp);
821  return *this;
822  }
823 
824  const_iterator begin() const
825  {
826  if (!m_node)
827  // empty results.
828  return const_iterator(empty_iterator);
829 
830  // Push the root node.
831  key_type buf(m_buffer);
832  node_stack_type node_stack = get_root_node();
833 
834  while (!node_stack.back().has_value())
835  {
836  // There should always be at least one value node along the
837  // left-most branch.
838 
839  const_iterator::push_child_node_to_stack(m_value_store, node_stack, buf, node_stack.back().child_pos);
840  }
841 
842  return const_iterator(m_value_store, std::move(node_stack), std::move(buf), node_stack.back().get_value_pos());
843  }
844 
845  const_iterator end() const
846  {
847  if (!m_node)
848  // empty results.
849  return const_iterator(empty_iterator);
850 
851  node_stack_type node_stack = get_root_node();
852  auto& si = node_stack.back();
853  si.child_pos = si.child_end;
854  return const_iterator(m_value_store, std::move(node_stack), key_type(m_buffer));
855  }
856 };
857 
858 }}} // namespace mdds::trie::detail
Definition: trie_map_itr.hpp:464
static trie_node_type * descend_to_previus_leaf_node(node_stack_type &node_stack, key_type &buf)
Definition: trie_map_itr.hpp:111
Definition: trie_map_itr.hpp:315
Definition: ref_pair.hpp:14
Definition: trie_map_itr.hpp:64
Definition: trie_map_itr.hpp:312
Definition: trie_map_itr.hpp:61
Definition: global.hpp:158
Definition: global.hpp:59
Definition: global.hpp:122
Definition: cref_wrapper.hpp:12
Definition: trie_map_itr.hpp:46
Definition: trie_map_itr.hpp:461