mdds
Loading...
Searching...
No Matches
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
17namespace mdds { namespace trie { namespace detail {
18
19enum class iterator_type
20{
25 normal,
31 end,
37 empty
38};
39
40enum empty_iterator_type
41{
42 empty_iterator
43};
44
45template<typename _TrieType, typename _C>
47
48template<typename _TrieType>
49struct get_node_stack_type<_TrieType, std::true_type>
50{
51 using type = typename _TrieType::const_node_stack_type;
52};
53
54template<typename _TrieType>
55struct get_node_stack_type<_TrieType, std::false_type>
56{
57 using type = typename _TrieType::node_stack_type;
58};
59
60template<typename _TrieType>
61class search_results;
62
63template<typename _TrieType, bool IsConst>
64class iterator_base
65{
66protected:
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
82public:
83 // iterator traits
84 using value_type = mdds::detail::ref_pair<typename std::add_const<key_type>::type, trie_value_type>;
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
90protected:
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
134public:
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
311template<typename _TrieType>
312class const_iterator;
313
314template<typename _TrieType>
315class iterator : public iterator_base<_TrieType, false>
316{
317 using trie_type = _TrieType;
318
319 friend trie_type;
322
323 using base_type = iterator_base<trie_type, false>;
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
330public:
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
339template<typename _TrieType>
340class 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
360public:
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
380template<typename _TrieType>
381bool operator==(const iterator<_TrieType>& left, const const_iterator<_TrieType>& right)
382{
383 return const_iterator<_TrieType>(left) == right;
384}
385
386template<typename _TrieType>
387bool operator!=(const iterator<_TrieType>& left, const const_iterator<_TrieType>& right)
388{
389 return const_iterator<_TrieType>(left) != right;
390}
391
392template<typename _TrieType>
393bool operator==(const const_iterator<_TrieType>& left, const iterator<_TrieType>& right)
394{
395 return left == const_iterator<_TrieType>(right);
396}
397
398template<typename _TrieType>
399bool operator!=(const const_iterator<_TrieType>& left, const iterator<_TrieType>& right)
400{
401 return left != const_iterator<_TrieType>(right);
402}
403
404template<typename _TrieType>
405class 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
422public:
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
460template<typename _TrieType>
462
463template<typename TrieT>
464class packed_iterator_base
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
480public:
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
488private:
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
554public:
555 packed_iterator_base() : m_type(iterator_type::normal)
556 {}
557
558 packed_iterator_base(
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
760template<typename _TrieType>
761class packed_search_results
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
803public:
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 global.hpp:60
Definition trie_map_itr.hpp:341
Definition trie_map_itr.hpp:65
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:316
Definition trie_map_itr.hpp:465
Definition trie_map_itr.hpp:762
Definition trie_map_itr.hpp:406
Definition global.hpp:122
Definition global.hpp:158
Definition ref_pair.hpp:15
Definition trie_map_itr.hpp:46