From 38b7221fa0e2776afe1351ebc2f477116aacd65d Mon Sep 17 00:00:00 2001 From: Medusa Slockbower Date: Sat, 16 Aug 2025 07:56:25 -0400 Subject: [PATCH] - deque, object_pool, and graph data structures + PrettyPrinters --- CMakeLists.txt | 3 + cmake/opengl.cmake | 2 + gdb/fennec/containers.py | 280 ++++++++++++++++------- include/fennec/containers/deque.h | 199 ++++++++++++++++ include/fennec/containers/dynarray.h | 4 +- include/fennec/containers/graph.h | 167 ++++++++++++++ include/fennec/containers/map.h | 12 + include/fennec/containers/object_pool.h | 126 ++++++++++ include/fennec/containers/rdtree.h | 9 +- include/fennec/containers/set.h | 30 +-- include/fennec/memory/allocator.h | 24 +- test/CMakeLists.txt | 33 +-- test/tests/containers/test_graph.h | 53 +++++ test/tests/containers/test_list.h | 6 +- test/tests/containers/test_object_pool.h | 53 +++++ test/tests/test_containers.h | 12 + 16 files changed, 867 insertions(+), 146 deletions(-) create mode 100644 include/fennec/containers/deque.h create mode 100644 include/fennec/containers/graph.h create mode 100644 include/fennec/containers/object_pool.h create mode 100644 test/tests/containers/test_graph.h create mode 100644 test/tests/containers/test_object_pool.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 750317e..ddfa32f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -201,6 +201,9 @@ add_library(fennec STATIC ${FENNEC_EXTRA_SOURCES} include/fennec/containers/traversal.h + include/fennec/containers/graph.h + include/fennec/containers/deque.h + include/fennec/containers/object_pool.h ) add_dependencies(fennec metaprogramming) diff --git a/cmake/opengl.cmake b/cmake/opengl.cmake index 2f66ffe..c7f7b42 100644 --- a/cmake/opengl.cmake +++ b/cmake/opengl.cmake @@ -25,6 +25,7 @@ else() endif() if(TARGET OpenGL::GL AND TARGET GLEW::GLEW) + message(STATUS "Found OpenGL: ${OPENGL_gl_LIBRARY}") list(APPEND FENNEC_LINK_LIBRARIES OpenGL::GL GLEW::GLEW) list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_OPENGL=1) else() @@ -36,6 +37,7 @@ if(FENNEC_GRAPHICS_WANT_EGL) message(FATAL_ERROR "EGL Library not found.") endif() + message(STATUS "Found EGL: ${OPENGL_egl_LIBRARY}") list(APPEND FENNEC_LINK_LIBRARIES OpenGL::EGL) list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_EGL=1) list(APPEND FENNEC_EXTRA_SOURCES diff --git a/gdb/fennec/containers.py b/gdb/fennec/containers.py index 17e2c1f..efcb87a 100644 --- a/gdb/fennec/containers.py +++ b/gdb/fennec/containers.py @@ -108,6 +108,7 @@ class ListPrinter: self.list = val self.node = self.list['_root'] self.index = 0 + self.table = self.list['_table']['_data'] def __iter__(self): return self @@ -118,8 +119,8 @@ class ListPrinter: i = self.index self.index = self.index + 1 - value = self.list['_table']['_data'][self.node]['value']['_val'] - self.node = self.list['_table']['_data'][self.node]['next'] + value = self.table[self.node]['value']['_val'] + self.node = self.table[self.node]['next'] return '[{}]'.format(i), value @@ -137,79 +138,6 @@ class ListPrinter: return 'array' -# RDTREE =============================================================================================================== - -class RDTreePrinter: - """Print a fennec::rdtree""" - - class Iterator: - def __init__(self, tree, node, capacity): - self.tree = tree - self.capacity = capacity - self.visit = deque() - - self.visit.append((node, 0, 0)) - - def __iter__(self): - return self - - def __next__(self): - if len(self.visit) == 0: - raise StopIteration - - node = self.visit[0][0] - i = self.visit[0][1] - depth = self.visit[0][2] - self.visit.popleft() - - opt = self.tree[node]['value'] - value = None - if opt['_set']: - value = opt['_val'] - - nnext = self.tree[node]['next'] - 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 - - if nnext < self.capacity: - self.visit.appendleft((nnext, i + 1, depth)) - - if child < self.capacity: - self.visit.appendleft((child, 0, depth + 1)) - - # ┌ ─ ├ └ - - if nnext != 18446744073709551615: - index += '├' - else: - index += '└' - - index += '─' - index += '[{}, {}, {}, {}]'.format(i, node, depth, n_chld) - print(index) - if value is None: - return index, '{ empty }' - return index, value - - - def __init__(self, val): - self.tree = val['_table']['_data'] - self.size = val['_size'] - self.capacity = val['_table']['_capacity'] - - def to_string(self): - if self.size == 0: - return "{ empty }" - return "{ size = " + str(self.size) + " }" - - def children(self): - return self.Iterator(self.tree, 0, self.capacity) - - # SET ================================================================================================================== class SetPrinter: @@ -310,20 +238,204 @@ class MapPrinter: return 'map' +# OBJECT_POOL ========================================================================================================== +class ObjectPoolPrinter: + """Print a fennec::object_pool""" + + class Iterator: + def __init__(self, val): + self.list = val + self.index = 0 + self.capacity = self.list['_table']['_alloc']['_capacity'] + self.table = self.list['_table']['_alloc']['_data'] + + def __iter__(self): + return self + + def __next__(self): + + i = self.index + while True: + i = self.index + self.index = self.index + 1 + + if self.index >= self.capacity: + raise StopIteration + + if bool(self.table[i]['_set']): + value = self.table[i]['_val'] + break + + return '[{}]'.format(i), value + + + + def __init__(self, val): + self.val = val + + def to_string(self): + return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_alloc']['_capacity']) + " }" + + def children(self): + return self.Iterator(self.val) + + def display_hint(self): + return 'array' + + +# RDTREE =============================================================================================================== + +class RDTreePrinter: + """Print a fennec::rdtree""" + + class Iterator: + def __init__(self, tree, node, capacity): + self.tree = tree + self.capacity = capacity + self.visit = deque() + + self.visit.append((node, 0, 0)) + + def __iter__(self): + return self + + def __next__(self): + if len(self.visit) == 0: + raise StopIteration + + node = self.visit[0][0] + i = self.visit[0][1] + depth = self.visit[0][2] + self.visit.popleft() + + value = self.tree[node]['value'] + + nnext = self.tree[node]['next'] + 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 + + if nnext < self.capacity: + self.visit.appendleft((nnext, i + 1, depth)) + + if child < self.capacity: + self.visit.appendleft((child, 0, depth + 1)) + + # ┌ ─ ├ └ + + if nnext != 18446744073709551615: + index += '├' + else: + index += '└' + + index += '─' + index += '[{}, {}]'.format(node, i) + return index, value + + + def __init__(self, val): + self.tree = val['_table']['_data'] + self.size = val['_size'] + self.capacity = val['_table']['_capacity'] + + def to_string(self): + if self.size == 0: + return "{ empty }" + return "{ size = " + str(self.size) + " }" + + def children(self): + return self.Iterator(self.tree, 0, self.capacity) + + +# Graph ================================================================================================================ + +class GraphPrinter: + """Print a fennec::graph""" + + class Iterator: + def __init__(self, val): + self.node_pool = val['_node_pool']['_table']['_alloc']['_data'] + self.max_nodes = val['_node_pool']['_table']['_alloc']['_capacity'] + self.conn_map = val['_conn_map']['_alloc']['_data'] + self.max_conn = val['_conn_map']['_size'] + self.index = 0 + + def __iter__(self): + return self + + def __next__(self): + if self.index >= self.max_nodes: + raise StopIteration + + i = self.index + self.index = self.index + 1 + + while not bool(self.node_pool[i]['_set']): + i = self.index + self.index = self.index + 1 + + conns = self.get_conns(i) + value = self.node_pool[i]['_val'] + + return '[{} -> {{{}}}]'.format(i, str(conns)), value + + + + def get_conns(self, index): + indices = [] + + if index >= self.max_conn: + return indices + + map = self.conn_map[index]['_set'] + max_conns = map['_alloc']['_capacity'] + conns = map['_alloc']['_data'] + + print(max_conns) + + if max_conns == 0: + return indices + + for i in range(0, max_conns): + if bool(conns[i]['value']['_set']): + conn = conns[i]['value']['_val'] + indices.append(str(int(conn['first']))) + + return indices + + def __init__(self, val): + self.val = val + self.size = val['_node_pool']['_size'] + + def to_string(self): + if self.size == 0: + return "{ empty }" + return "{ size = " + str(self.size) + " }" + + def children(self): + return self.Iterator(self.val) + + + # GDB Code ============================================================================================================= def register_printers(): print("registering containers") pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::containers") - pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter) - pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter) - pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter) - pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter) - pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter) - pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter) - pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter) - pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter) - pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter) + pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter) + pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter) + pp.add_printer('fennec::graph', '^fennec::graph<.*>$', GraphPrinter) + pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter) + pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter) + pp.add_printer('fennec::object_pool', '^fennec::object_pool<.*>$', ObjectPoolPrinter) + pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter) + pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter) + pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter) + pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter) + pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter) return pp printer = register_printers() diff --git a/include/fennec/containers/deque.h b/include/fennec/containers/deque.h new file mode 100644 index 0000000..83e6e28 --- /dev/null +++ b/include/fennec/containers/deque.h @@ -0,0 +1,199 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_CONTAINERS_DEQUE_H +#define FENNEC_CONTAINERS_DEQUE_H + +#include + +// TODO: Document + +namespace fennec +{ + +template> +struct deque { + +// Definitions ========================================================================================================= +public: + using elem_t = TypeT; + + struct node { + elem_t value; + node *prev, *next; + + template + node(node* prev, node* next, ArgsT&&...args) + : value(fennec::forward(args)...) + , prev(prev), next(next) { + } + + ~node() = default; + }; + + using alloc_t = allocator_traits::template rebind; + + +// Constructors ======================================================================================================== + + deque() + : _alloc() + , _first(nullptr) + , _last(nullptr) { + } + + deque(deque&& deque) noexcept + : _alloc(deque._alloc) + , _first(deque._first) + , _last(deque._last) { + deque._first = nullptr; + deque._last = nullptr; + } + + ~deque() { + clear(); + } + +// Properties ========================================================================================================== + + bool empty() const { + return _size == 0; + } + + size_t size() const { + return _size; + } + + +// Access ============================================================================================================== + + elem_t& front() { + assert(not empty(), "Attempted to access an empty deque."); + return _first->value; + } + + elem_t& back() { + assert(not empty(), "Attempted to access an empty deque."); + return _last->value; + } + + +// Insertion =========================================================================================================== + + void push_front(elem_t&& elem) { + this->_push_front(elem); + } + + void push_front(const elem_t& elem) { + this->_push_front(elem); + } + + template + void emplace_front(ArgsT&&...args) { + this->_push_front(fennec::forward(args)...); + } + + void push_back(elem_t&& elem) { + this->_push_back(elem); + } + + void push_back(const elem_t& elem) { + this->_push_back(elem); + } + + template + void emplace_back(ArgsT&&...args) { + this->_push_back(fennec::forward(args)...); + } + + +// Deletion ============================================================================================================ + + void clear() { + node* it = _first; + while (it) { + node* next = it->next; + fennec::destruct(it); + _alloc.deallocate(it); + it = next; + } + _first = nullptr; + _last = nullptr; + _size = 0; + } + + void pop_front() { + if (_first == nullptr) { + return; + } + node* next = _first->next; + fennec::destruct(_first); + _alloc.deallocate(_first); + _first = next; + _last = next ? _last : nullptr; + --_size; + } + + void pop_back() { + if (_last == nullptr) { + return; + } + node* prev = _last->prev; + fennec::destruct(_last); + _alloc.deallocate(_last); + _last = prev; + _first = prev ? _first : nullptr; + --_size; + } + +private: + alloc_t _alloc; + node *_first, *_last; + size_t _size; + + template + void _push_front(ArgsT&&...args) { + node* next = _first; + _first = _alloc.allocate(1); + fennec::construct(_first, nullptr, next, fennec::forward(args)...); + if (next) { + next->prev = _first; + } else { + _last = _first; + } + ++_size; + } + + template + void _push_back(ArgsT&&...args) { + node* prev = _last; + _last = _alloc.allocate(1); + fennec::construct(_last, prev, nullptr, fennec::forward(args)...); + if (prev) { + prev->next = _last; + } else { + _first = _last; + } + ++_size; + } +}; + + +} + +#endif // FENNEC_CONTAINERS_DEQUE_H \ No newline at end of file diff --git a/include/fennec/containers/dynarray.h b/include/fennec/containers/dynarray.h index 0a9cfdf..652e609 100644 --- a/include/fennec/containers/dynarray.h +++ b/include/fennec/containers/dynarray.h @@ -357,7 +357,7 @@ public: /// \brief Resize the dynarray, invoking the default constructor for all new elements /// \param n The new size in elements constexpr void resize(size_t n) { - _alloc.reallocate(n); + _alloc.creallocate(n); while (_size < n) { emplace_back(); @@ -366,7 +366,7 @@ public: private: constexpr void _grow() { - _alloc.reallocate(_alloc.capacity() * 2); + _alloc.creallocate(_alloc.capacity() * 2); } allocation _alloc; diff --git a/include/fennec/containers/graph.h b/include/fennec/containers/graph.h new file mode 100644 index 0000000..e083830 --- /dev/null +++ b/include/fennec/containers/graph.h @@ -0,0 +1,167 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_CONTAINERS_GRAPH_H +#define FENNEC_CONTAINERS_GRAPH_H + +#include +#include +#include +#include +#include + +/* + * With the directed tree we were able to cheat a little, the structure has more rules to it which allows + * tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign + * properties or rules to connections which can simply be an extension to this graph. + * + * The most effective way to do this is to have a dynarray of lists, however this results in double the + * memory being used. This can also result in two connection objects being created. + * + * There is no nice way to avoid the problem of mapping pins to connections + */ + +namespace fennec +{ + +template +struct graph { +public: + using weight_t = ConnectionT; + using node_t = NodeT; + using conn_map_t = dynarray>; + using node_pool_t = object_pool; + using conn_pool_t = object_pool; + + static constexpr size_t npos = -1; + + constexpr graph() = default; + constexpr ~graph() = default; + + constexpr graph& operator=(const graph& g) = default; + constexpr graph& operator=(graph&& g) = default; + + +// Properties ========================================================================================================== + + constexpr size_t num_nodes() const { + return _node_pool.size(); + } + + constexpr size_t num_connections() const { + return _conn_pool.size(); + } + + constexpr size_t capacity() const { + return _node_pool.capacity(); + } + + constexpr bool empty() const { + return num_nodes() == 0; + } + + +// Nodes =============================================================================================================== + + constexpr size_t insert(node_t&& node) { + return this->_insert(fennec::forward(node)); + } + + constexpr size_t insert(const node_t& node) { + return this->_insert(node); + } + + template + constexpr size_t emplace(ArgsT&&...args) { + return this->_insert(fennec::forward(args)...); + } + + constexpr void erase(size_t node) { + disconnect(node); + _node_pool.erase(node); + } + + constexpr node_t& operator[](size_t node) { + return _node_pool[node]; + } + + constexpr const node_t& operator[](size_t node) const { + return _node_pool[node]; + } + +// Connections ========================================================================================================= + + list connections(size_t n) { + list conns; + if (_conn_map.empty()) return conns; + + for (auto it : _conn_map[n]) { + conns.push_back(it.first); + } + return conns; + } + + template + constexpr void connect(size_t a, size_t b, ArgsT&&...args) { + if (_conn_map.size() < _node_pool.capacity()) { + _conn_map.resize(_node_pool.capacity()); + } + + size_t conn = _conn_pool.emplace(fennec::forward(args)...); + _conn_map[a].emplace(b, conn); + _conn_map[b].emplace(a, conn); + } + + constexpr void disconnect(size_t a, size_t b) { + size_t c = *_conn_map[a][b]; + _conn_pool.erase(c); + _conn_map[a].erase(b); + _conn_map[b].erase(a); + } + + void disconnect(size_t n) { + list conns = connections(n); + for (size_t conn : conns) { + disconnect(n, conn); + } + } + + constexpr weight_t& operator[](size_t a, size_t b) { + return _conn_pool[_conn_map[a][b]]; + } + + constexpr const weight_t& operator[](size_t a, size_t b) const { + return _conn_pool[_conn_map[a][b]]; + } + + +private: + node_pool_t _node_pool; + conn_pool_t _conn_pool; + conn_map_t _conn_map; + + template + size_t _insert(ArgsT&&...args) { + return _node_pool.emplace(fennec::forward(args)...); + } + +}; + +} + +#endif // FENNEC_CONTAINERS_GRAPH_H \ No newline at end of file diff --git a/include/fennec/containers/map.h b/include/fennec/containers/map.h index 5fb6205..be028ce 100644 --- a/include/fennec/containers/map.h +++ b/include/fennec/containers/map.h @@ -62,6 +62,7 @@ public: using alloc_t = typename allocator_traits::template rebind; using hash_t = Hash; using set_t = set; + using iterator = set_t::iterator; // We only want to hash the key struct key_hash : hash_t { @@ -181,6 +182,17 @@ public: } +// Iteration =========================================================================================================== + + constexpr iterator begin() { + return _set.begin(); + } + + constexpr iterator end() { + return _set.end(); + } + + private: set_t _set; diff --git a/include/fennec/containers/object_pool.h b/include/fennec/containers/object_pool.h new file mode 100644 index 0000000..2e49816 --- /dev/null +++ b/include/fennec/containers/object_pool.h @@ -0,0 +1,126 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_CONTAINERS_OBJECT_POOL_H +#define FENNEC_CONTAINERS_OBJECT_POOL_H +#include +#include +#include + +namespace fennec +{ + +/// +/// \brief Struct which holds a pool of objects associated with ids +/// \tparam TypeT The value type +/// \tparam AllocT The allocator type +template> +struct object_pool { +public: + using value_t = TypeT; + using elem_t = optional; + using table_t = dynarray; + + constexpr object_pool() + : _size(0) { + }; + + /// + /// \returns The number of active objects in the pool + constexpr size_t size() const { + return _size; + } + + /// + /// \returns The capacity of the underlying allocation + constexpr size_t capacity() const { + return _table.capacity(); + } + + constexpr bool empty() const { + return size() == 0; + } + + constexpr value_t& operator[](size_t i) { + assert(i < capacity(), "Index out of Bounds!"); + assert(_table[i], "Attempted to access Null Object."); + return *_table[i]; + } + + constexpr const value_t& operator[](size_t i) const { + assert(i < capacity(), "Index out of Bounds!"); + assert(_table[i], "Attempted to access Null Object."); + return *_table[i]; + } + + constexpr size_t insert(value_t&& x) { + return this->_insert(fennec::forward(x)); + } + + constexpr size_t insert(const value_t& x) { + return this->_insert(x); + } + + template + constexpr size_t emplace(ArgsT&&...args) { + return this->_insert(fennec::forward(args)...); + } + + constexpr void erase(size_t i) { + _table[i] = nullopt; + _freed.push_back(i); + --_size; + } + + constexpr size_t next_id() const { + size_t next = _size; + if (not _freed.empty()) { + next = _freed.front(); + } + return next; + } + +private: + dynarray _table; + list _freed; + size_t _size; + + size_t _next_free() { + size_t next = _size; + if (not _freed.empty()) { + next = _freed.front(); + _freed.pop_front(); + } + ++_size; + return next; + } + + template + size_t _insert(ArgsT&&...args) { + size_t i = _next_free(); + if (i >= _table.size()) { + _table.emplace_back(); + } + _table[i].emplace(fennec::forward(args)...); + return i; + } +}; + +} + +#endif // FENNEC_CONTAINERS_OBJECT_POOL_H \ No newline at end of file diff --git a/include/fennec/containers/rdtree.h b/include/fennec/containers/rdtree.h index fbfb047..a775b43 100644 --- a/include/fennec/containers/rdtree.h +++ b/include/fennec/containers/rdtree.h @@ -320,10 +320,17 @@ public: /// \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) { + assertf(i0 != root and i1 != root, "Cannot Swap With Root"); + size_t p0 = parent(i0); size_t p1 = parent(i1); - fennec::swap(_table[i0], _table[i1]); + fennec::swap(_table[i0].parent, _table[i1].parent); + fennec::swap(_table[i0].child, _table[i1].child); + fennec::swap(_table[i0].next, _table[i1].next); + fennec::swap(_table[i0].prev, _table[i1].prev); + fennec::swap(_table[i0].depth, _table[i1].depth); + fennec::swap(_table[i0].num_children, _table[i1].num_children); if (child(p0) == i0) _table[p0].child = i1; if (child(p1) == i1) _table[p1].child = i0; diff --git a/include/fennec/containers/set.h b/include/fennec/containers/set.h index 430945b..4c5a106 100644 --- a/include/fennec/containers/set.h +++ b/include/fennec/containers/set.h @@ -21,6 +21,7 @@ // https://programming.guide/robin-hood-hashing.html +#include #include #include #include @@ -295,15 +296,15 @@ public: /// /// \brief Move Insertion /// \param val Value to insert - constexpr void insert(elem_t&& val) { - this->_insert(fennec::forward(val)); + constexpr iterator insert(elem_t&& val) { + return fennec::move(this->_insert(fennec::forward(val))); } /// /// \brief Copy Insertion /// \param val Value to insert - constexpr void insert(const elem_t& val) { - this->_insert(val); + constexpr iterator insert(const elem_t& val) { + return fennec::move(this->_insert(val)); } /// @@ -311,8 +312,8 @@ public: /// \tparam ArgsT Argument types /// \param args Arguments to construct with template - constexpr void emplace(ArgsT&&...args) { - this->_insert(fennec::forward(args)...); + constexpr iterator emplace(ArgsT&&...args) { + return fennec::move(this->_insert(fennec::forward(args)...)); } /// @@ -354,6 +355,11 @@ public: /// \brief Class for Iterating the Set class iterator { public: + constexpr iterator(const set* set, size_t i) + : _set(set) + , _i(i) { + } + constexpr ~iterator() { _set = nullptr; } @@ -392,15 +398,12 @@ public: return _set != it._set or _i != it._i; } + constexpr size_t index() const { return _i; } + private: const set* _set; size_t _i; friend set; - - constexpr iterator(const set* set, size_t i) - : _set(set) - , _i(i) { - } }; constexpr iterator begin() const { @@ -437,7 +440,7 @@ private: } template - constexpr void _insert(ArgsT&&...args) { + constexpr iterator _insert(ArgsT&&...args) { if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full _expand(); } @@ -447,7 +450,7 @@ private: int psl = 0; while (_alloc[i].value) { // Search for empty cell if (_equal(*_alloc[i].value, value)) { // Check to see if this element is already inserted - return; + return iterator(this, i); } if (psl > _alloc[i].psl) { // When psl is higher, swap _sumpsl += psl - _alloc[i].psl; @@ -459,6 +462,7 @@ private: _alloc[i].value = fennec::move(value); _sumpsl += (_alloc[i].psl = psl); ++_size; + return iterator(this, npos); } allocation _alloc; diff --git a/include/fennec/memory/allocator.h b/include/fennec/memory/allocator.h index 6ec7676..b2160f2 100644 --- a/include/fennec/memory/allocator.h +++ b/include/fennec/memory/allocator.h @@ -310,7 +310,7 @@ public: /// \param n The number of elements of type `T` to allocate for explicit constexpr allocation(size_t n) noexcept : _data(nullptr), _capacity(0), _alignment(zero()) { - allocate(n); + callocate(n); } /// @@ -320,7 +320,7 @@ public: /// \param n the number of elements constexpr allocation(const T* data, size_t n) : allocation(n) { - fennec::memcpy(_data, data, n); + fennec::memmove(_data, data, n); } /// @@ -331,7 +331,7 @@ public: : _data(nullptr) , _capacity(0) , _alignment(align) { - allocate(n, align); + callocate(n, align); } /// @@ -342,7 +342,7 @@ public: /// \param align The alignment of the allocation constexpr allocation(const T* data, size_t n, align_t align) : allocation(n, align) { - fennec::memcpy(_data, data, n); + fennec::memmove(static_cast(_data), data, n); } /// @@ -367,7 +367,7 @@ public: : _alloc(alloc) , _data(nullptr) , _capacity(0) { - allocate(n); + callocate(n); } /// @@ -380,7 +380,7 @@ public: /// \details This constructor should be used when the type `AllocT` needs internal data. constexpr allocation(const T* data, size_t n, const alloc_t& alloc) : allocation(n, alloc) { - fennec::memcpy(_data, data, n); + fennec::memmove(static_cast(_data), data, n); } /// @@ -395,7 +395,7 @@ public: , _data(nullptr) , _capacity(0) , _alignment(zero()) { - allocate(n, align); + callocate(n, align); } /// @@ -409,7 +409,7 @@ public: /// \details This constructor should be used when the type `AllocT` needs internal data. constexpr allocation(const T* data, size_t n, align_t align, const alloc_t& alloc) : allocation(n, align, alloc) { - fennec::memcpy(_data, data, n); + fennec::memmove(_data, data, n); } /// @@ -420,7 +420,7 @@ public: , _data(_alloc.allocate(alloc._capacity)) , _capacity(alloc._capacity) , _alignment(alloc._alignment) { - fennec::memcpy(_data, alloc._data, alloc._capacity * sizeof(T)); + fennec::memmove(static_cast(_data), alloc._data, alloc._capacity * sizeof(T)); } /// @@ -451,7 +451,7 @@ public: /// \returns a reference to `this` constexpr allocation& operator=(const allocation& alloc) { allocation::allocate(alloc.capacity()); - fennec::memcpy(_data, alloc, size()); + fennec::memmove(_data, alloc, size()); return *this; } @@ -528,7 +528,7 @@ public: value_t* old = _data; _data = nullptr; allocate(n, align); - fennec::memcpy(static_cast(_data), old, min(_capacity, n) * sizeof(T)); + fennec::memmove(static_cast(_data), old, min(_capacity, n) * sizeof(T)); _alloc.deallocate(old); _capacity = n; } @@ -544,7 +544,7 @@ public: value_t* old = _data; _data = nullptr; callocate(n, align); - fennec::memcpy(static_cast(_data), old, min(_capacity, n) * sizeof(T)); + fennec::memmove(static_cast(_data), old, min(_capacity, n) * sizeof(T)); _alloc.deallocate(old); _capacity = n; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c935096..a2d3f27 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -4,37 +4,8 @@ project(fennec-test) set(CMAKE_CXX_STANDARD 23) set(CMAKE_C_STANDARD 23) -add_executable(fennec-test main.cpp - test.h - tests/math/test_vector.h - tests/math/test_matrix.h - tests/math/test_scalar.h - tests/math/test_geometric.h - tests/test_memory.h - tests/test_math.h - tests/test_lang.h - tests/lang/test_conditional_types.h - tests/lang/test_bits.h - tests/lang/test_sequences.h - tests/math/test_common.h - tests/math/test_exponential.h - tests/math/test_relational.h - tests/math/test_trigonometric.h - tests/langproc/test_strings.h - tests/langproc/strings/test_cstring.h - tests/test_langproc.h - tests/langproc/test_io.h - printing.h - tests/math/test_ext.h - tests/math/ext/test_quaternion.h - tests/test_platform.h - tests/lang/test_hashing.h - tests/containers/test_array.h - tests/containers/test_set.h - tests/containers/test_map.h - tests/containers/test_rdtree.h - tests/containers/test_list.h - tests/containers/test_tuple.h +add_executable(fennec-test + main.cpp ) target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}" diff --git a/test/tests/containers/test_graph.h b/test/tests/containers/test_graph.h new file mode 100644 index 0000000..f01aa82 --- /dev/null +++ b/test/tests/containers/test_graph.h @@ -0,0 +1,53 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_TEST_CONTAINERS_GRAPH_H +#define FENNEC_TEST_CONTAINERS_GRAPH_H + +#include "../../test.h" + +#include + +namespace fennec +{ + +namespace test +{ + +inline void fennec_test_containers_graph() { + + graph test; + + const size_t n = 50; + for (size_t i = 0; i < n; ++i) { + assertf(test.insert(i) == i, "List Construct Test Failed."); + } + + for (size_t i = 0; i < n; ++i) { + test.erase(i); + } + + fennec_test_run(test.empty(), true); + +} + +} + +} + +#endif // FENNEC_TEST_CONTAINERS_OBJECT_POOL_H \ No newline at end of file diff --git a/test/tests/containers/test_list.h b/test/tests/containers/test_list.h index fe7631a..8cc589b 100644 --- a/test/tests/containers/test_list.h +++ b/test/tests/containers/test_list.h @@ -16,8 +16,8 @@ // along with this program. If not, see . // ===================================================================================================================== -#ifndef TS_CONTAINERS_TEST_LIST_H -#define TS_CONTAINERS_TEST_LIST_H +#ifndef FENNEC_TEST_CONTAINERS_LIST_H +#define FENNEC_TEST_CONTAINERS_LIST_H #include "../../test.h" @@ -51,4 +51,4 @@ inline void fennec_test_containers_list() { } -#endif // TS_CONTAINERS_TEST_LIST_H \ No newline at end of file +#endif // FENNEC_TEST_CONTAINERS_LIST_H \ No newline at end of file diff --git a/test/tests/containers/test_object_pool.h b/test/tests/containers/test_object_pool.h new file mode 100644 index 0000000..c242429 --- /dev/null +++ b/test/tests/containers/test_object_pool.h @@ -0,0 +1,53 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_TEST_CONTAINERS_OBJECT_POOL_H +#define FENNEC_TEST_CONTAINERS_OBJECT_POOL_H + +#include "../../test.h" + +#include + +namespace fennec +{ + +namespace test +{ + +inline void fennec_test_containers_object_pool() { + + object_pool test; + + const size_t n = 50; + for (size_t i = 0; i < n; ++i) { + assertf(test.insert(i) == i, "List Construct Test Failed."); + } + + for (size_t i = 0; i < n; ++i) { + test.erase(i); + } + + fennec_test_run(test.empty(), true); + +} + +} + +} + +#endif // FENNEC_TEST_CONTAINERS_OBJECT_POOL_H \ No newline at end of file diff --git a/test/tests/test_containers.h b/test/tests/test_containers.h index 2ea99cb..eaaff0d 100644 --- a/test/tests/test_containers.h +++ b/test/tests/test_containers.h @@ -21,8 +21,10 @@ #include "containers/test_array.h" #include "containers/test_dynarray.h" +#include "containers/test_graph.h" #include "containers/test_list.h" #include "containers/test_map.h" +#include "containers/test_object_pool.h" #include "containers/test_optional.h" #include "containers/test_rdtree.h" #include "containers/test_set.h" @@ -68,9 +70,19 @@ namespace fennec::test fennec_test_containers_map(); fennec_test_spacer(3); + fennec_test_subheader("object_pool tests"); + fennec_test_spacer(2); + fennec_test_containers_object_pool(); + fennec_test_spacer(3); + fennec_test_subheader("rdtree tests"); fennec_test_spacer(2); fennec_test_containers_rdtree(); + fennec_test_spacer(3); + + fennec_test_subheader("graph tests"); + fennec_test_spacer(2); + fennec_test_containers_graph(); // TODO }