From 2535e1ac4b0e48fc7f8a4d26e300ede526c00708 Mon Sep 17 00:00:00 2001 From: Medusa Slockbower Date: Thu, 14 Aug 2025 17:07:48 -0400 Subject: [PATCH] Reworked RD-Tree to behave more consistently. The construction of the tree did not allow specifying what index to insert a child at under a parent. Traverser orders were also broken, which is now fixed. --- gdb/fennec/containers.py | 3 +- include/fennec/containers/rdtree.h | 199 ++++++++++++++++++++++------ test/tests/containers/test_rdtree.h | 20 +-- 3 files changed, 171 insertions(+), 51 deletions(-) diff --git a/gdb/fennec/containers.py b/gdb/fennec/containers.py index 1c8fd3b..17e2c1f 100644 --- a/gdb/fennec/containers.py +++ b/gdb/fennec/containers.py @@ -171,6 +171,7 @@ class RDTreePrinter: nprev = self.tree[node]['prev'] nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615 child = self.tree[node]['child'] + n_chld = self.tree[node]['num_children'] index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers @@ -188,7 +189,7 @@ class RDTreePrinter: index += '└' index += '─' - index += '[{}, {}]'.format(i, node) + index += '[{}, {}, {}, {}]'.format(i, node, depth, n_chld) print(index) if value is None: return index, '{ empty }' diff --git a/include/fennec/containers/rdtree.h b/include/fennec/containers/rdtree.h index 3116735..5f646fa 100644 --- a/include/fennec/containers/rdtree.h +++ b/include/fennec/containers/rdtree.h @@ -44,28 +44,26 @@ public: static constexpr size_t root = 0; static constexpr size_t npos = -1; - enum control_ : uint8_t { - control_continue = 0, - control_jump_over, - control_break - }; - protected: struct node { optional value; size_t parent, child, prev, next; + size_t depth, num_children; template - constexpr node(size_t p, size_t c, size_t v, size_t n, ArgsT&&...args) + constexpr node(size_t p, size_t c, size_t v, size_t n, size_t d, ArgsT&&...args) : value(fennec::forward(args)...) - , parent(p), child(c), prev(v), next(n) { + , parent(p), child(c), prev(v), next(n) + , depth(d), num_children(0) { } constexpr ~node() { - parent = npos; - child = npos; - prev = npos; - next = npos; + parent = npos; + child = npos; + prev = npos; + next = npos; + depth = npos; + num_children = 0; } }; @@ -77,7 +75,7 @@ public: explicit constexpr rdtree(ArgsT&&...args) : _table(), _freed(), _size(1) { _table.callocate(8); - fennec::construct(&_table[0], npos, npos, npos, npos, fennec::forward(args)...); + fennec::construct(&_table[0], npos, npos, npos, npos, 0, fennec::forward(args)...); } constexpr rdtree(const rdtree& tree) @@ -133,34 +131,74 @@ public: /// \param i The id of the node to check /// \returns The id of the parent node constexpr size_t parent(size_t i) const { + if (i >= _table.capacity()) return npos; return i == npos ? npos : _table[i].parent; } /// /// \param i The id of the node to check /// \returns The id of the child node - constexpr size_t child(size_t i) const { - return i == npos ? npos : _table[i].child; + constexpr size_t child(size_t i, size_t n = 0) const { + if (i >= _table.capacity()) return npos; + size_t c = i == npos ? npos : _table[i].child; + if (n != 0) + return next(c, n == npos ? npos : n - 1); + return c; } /// /// \param i The id of the node to check /// \returns The id of the next node - constexpr size_t next(size_t i) const { - return i == npos ? npos : _table[i].next; + constexpr size_t next(size_t i, size_t n = 0) const { + if (i >= _table.capacity()) return npos; + if (i == npos) { + return npos; + } + + size_t org = i; + size_t nxt = _table[i].next; + while (nxt != npos) { + i = nxt; + nxt = _table[i].next; + if (n != npos) { + if (n-- == 0) { + break; + } + } + } + + return i == org && n != npos ? npos : i; } /// /// \param i The id of the node to check /// \returns The id of the previous node - constexpr size_t prev(size_t i) const { - return i == npos ? npos : _table[i].prev; + constexpr size_t prev(size_t i, size_t n = 0) const { + if (i >= _table.capacity()) return npos; + if (i == npos) { + return npos; + } + + size_t org = i; + size_t prv = _table[i].prev; + while (prv != npos) { + i = prv; + prv = _table[i].prev; + if (n != npos) { + if (n-- == 0) { + break; + } + } + } + + return i == org && n != npos ? npos : i; } /// /// \param i the node to start at /// \returns the left-most child of node `i` constexpr size_t left_most(size_t i) const { + if (i >= _table.capacity()) return npos; size_t n = i; if ((n = child(n)) == npos) { return i; @@ -177,6 +215,7 @@ public: /// \param i the node to start at /// \returns the right-most child of node `i` constexpr size_t right_most(size_t i) const { + if (i >= _table.capacity()) return npos; if ((i = child(i)) == npos) { return npos; } @@ -192,18 +231,54 @@ public: } } + /// + /// \param i The id of the node to check + /// \returns The depth of the node + constexpr size_t depth(size_t i) const { + if (i >= _table.capacity()) return npos; + return i == npos ? npos : _table[i].depth; + } + + /// + /// \param i The id of the node to check + /// \returns The number of children the node has + constexpr size_t num_children(size_t i) const { + if (i >= _table.capacity()) return 0; + return i == npos ? 0 : _table[i].num_children; + } + + /// + /// \returns The next node id were `insert` or `emplace` to be called + constexpr size_t next_free() const { + size_t i = _size; + if (not _freed.empty()) { + i = _freed.front(); + } + return i; + } + /// /// \param i The id of the node to access /// \returns A reference to the value of the node wrapped in an optional - constexpr optional& operator[](size_t i) { - return _table[i].value; + constexpr value_t* operator[](size_t i) { + auto& it = _table[i].value; + if (it) { + return &*_table[i].value; + } else { + return nullptr; + } } /// /// \param i The id of the node to access /// \returns A const-qualified reference to the value of the node wrapped in an optional - constexpr const optional& operator[](size_t i) const { - return _table[i].value; + constexpr const value_t* operator[](size_t i) const { + const auto& it = _table[i].value; + if (it) { + return &*_table[i].value; + } else { + return nullptr; + } } @@ -212,21 +287,49 @@ public: /// /// \brief Insertion, creates a node in the tree with parent `parent` /// \param parent the parent node, if `npos` sets the value of the root node + /// \param next the next node, as an index relative to the parent /// \param val the value to insert /// \returns the index of the created node - constexpr size_t insert(size_t parent, const value_t& val) { - return this->_insert(parent, val); + constexpr size_t insert(size_t parent, size_t next, const value_t& val) { + return this->_insert(parent, next, val); } /// /// \brief Insertion, creates a node in the tree with parent `parent` /// \param parent the parent node, if `npos` sets the value of the root node + /// \param next the next node, as an index relative to the parent /// \param val the value to insert /// \returns the index of the created node - constexpr size_t insert(size_t parent, value_t&& val) { - return this->_insert(parent, fennec::forward(val)); + constexpr size_t insert(size_t parent, size_t next, value_t&& val) { + return this->_insert(parent, next, fennec::forward(val)); } + /// + /// \brief Insertion, creates a node in the tree with parent `parent` + /// \param parent the parent node, if `npos` sets the value of the root node + /// \param next the next node, as an index relative to the parent + /// \param args the args to construct the value to insert + /// \returns the index of the created node + template + constexpr size_t emplace(size_t parent, size_t next, ArgsT&&...args) { + return this->_insert(parent, next, fennec::forward(args)...); + } + + /// + /// \brief Swap two nodes + /// \param i0 The id of the first node + /// \param i1 The id of the second node + constexpr void swap(size_t i0, size_t i1) { + size_t p0 = parent(i0); + size_t p1 = parent(i1); + + fennec::swap(_table[i0], _table[i1]); + + if (child(p0) == i0) _table[p0].child = i1; + if (child(p1) == i1) _table[p1].child = i0; + } + + /// /// \brief Erase a node in the tree and all of it's children /// \param i the index of the node @@ -332,7 +435,7 @@ public: OrderT order; i = order(*this, i); while (i != npos) { - uint8_t mode = visit(*_table[i].value); + uint8_t mode = visit(*_table[i].value, i); if (mode == traversal_control_break) { break; } @@ -353,8 +456,8 @@ protected: size_t _next_free() { size_t next = _size; if (not _freed.empty()) { - next = _freed.back(); - _freed.pop_back(); + next = _freed.front(); + _freed.pop_front(); } if (_size >= capacity()) { _expand(); @@ -364,9 +467,9 @@ protected: } template - constexpr size_t _insert(size_t p, ArgsT&&...args) { + constexpr size_t _insert(size_t p, size_t n, ArgsT&&...args) { if (_size == 0) { - fennec::construct(&_table[root], npos, npos, npos, npos, fennec::forward(args)...); + fennec::construct(&_table[root], npos, npos, npos, npos, 0, fennec::forward(args)...); _size = 1; return root; } @@ -377,17 +480,34 @@ protected: return root; } - size_t i = _next_free(); - size_t n = child(p); - _table[p].child = i; - if (n != npos) _table[n].prev = i; - fennec::construct(&_table[i], p, npos, npos, n, fennec::forward(args)...); - return i; + size_t idx = _next_free(); + size_t nxt = child(p, n); + size_t prv = n == npos ? npos : prev(n); + + ++_table[p].num_children; + if ((nxt == child(p) && n != npos) || nxt == npos) { + _table[p].child = idx; + } + + if (n == npos) { + if (nxt != npos) { + _table[nxt].next = idx; + } + fennec::construct(&_table[idx], p, npos, nxt, npos, depth(p) + 1, fennec::forward(args)...); + } else { + if (nxt != npos) { + _table[nxt].prev = idx; + } + if (prv != npos) { + _table[prv].next = idx; + } + fennec::construct(&_table[idx], p, npos, prv, nxt, depth(p) + 1, fennec::forward(args)...); + } + return idx; } constexpr void _erase(size_t i) { list queue; - size_t j = 0; queue.push_back(child(i)); while (queue.empty() == false) { size_t n = queue.front(); queue.pop_front(); @@ -397,7 +517,6 @@ protected: fennec::destruct(&_table[n]); _freed.push_back(n); --_size; - ++j; } fennec::destruct(&_table[i]); diff --git a/test/tests/containers/test_rdtree.h b/test/tests/containers/test_rdtree.h index f880d89..45ed064 100644 --- a/test/tests/containers/test_rdtree.h +++ b/test/tests/containers/test_rdtree.h @@ -45,7 +45,7 @@ inline void fennec_test_containers_rdtree() { const size_t prev = rdtree::npos; const size_t next = test.child(parent); size_t l; - assertf((l = test.insert(parent, i)) == i + 1, "Tree Construct Test Failed."); + assertf((l = test.insert(parent, 0, i)) == i + 1, "Tree Construct Test Failed."); assertf(test.parent(l) == parent, "Tree Construct Test Failed."); assertf(test.child(l) == child, "Tree Construct Test Failed."); assertf(test.prev(l) == prev, "Tree Construct Test Failed."); @@ -55,12 +55,12 @@ inline void fennec_test_containers_rdtree() { test.erase(0); - size_t n1 = test.insert(npos, 1); - size_t n3 = test.insert(n1, 3); - size_t n2 = test.insert(n1, 2); - size_t n5 = test.insert(n2, 5); - size_t n4 = test.insert(n2, 4); - size_t n6 = test.insert(n3, 6); + size_t n1 = test.insert(npos, 0, 1); + size_t n2 = test.insert(n1, 0, 2); + size_t n3 = test.insert(n1, npos, 3); + size_t n4 = test.insert(n2, 0, 4); + size_t n5 = test.insert(n2, npos, 5); + size_t n6 = test.insert(n3, 0, 6); fennec_test_run(n1 != npos, true); fennec_test_run(n2 != npos, true); fennec_test_run(n3 != npos, true); @@ -73,7 +73,7 @@ inline void fennec_test_containers_rdtree() { size_t i; i = 0; - test.traverse([&](size_t x) -> uint8_t { + test.traverse([&](size_t x, size_t) -> uint8_t { fennec_test_run(x, pre_order[i++]); return traversal_control_continue; }); @@ -81,7 +81,7 @@ inline void fennec_test_containers_rdtree() { fennec_test_spacer(1); i = 0; - test.traverse([&](size_t x) -> uint8_t { + test.traverse([&](size_t x, size_t) -> uint8_t { fennec_test_run(x, in_order[i++]); return traversal_control_continue; }); @@ -89,7 +89,7 @@ inline void fennec_test_containers_rdtree() { fennec_test_spacer(1); i = 0; - test.traverse([&](size_t x) -> uint8_t { + test.traverse([&](size_t x, size_t) -> uint8_t { fennec_test_run(x, post_order[i++]); return traversal_control_continue; });