diff --git a/CMakeLists.txt b/CMakeLists.txt index a6ce057..7df0e9c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ add_library(fennec STATIC include/fennec/containers/containers.h include/fennec/containers/array.h + include/fennec/containers/bintree.h include/fennec/containers/deque.h include/fennec/containers/dynarray.h include/fennec/containers/graph.h @@ -114,6 +115,7 @@ add_library(fennec STATIC include/fennec/containers/optional.h include/fennec/containers/pair.h include/fennec/containers/rdtree.h + include/fennec/containers/sequence.h include/fennec/containers/set.h include/fennec/containers/traversal.h include/fennec/containers/tuple.h @@ -235,7 +237,7 @@ add_library(fennec STATIC # EXTRA SOURCES ======================================================================================================== ${FENNEC_EXTRA_SOURCES} - include/fennec/containers/bintree.h + include/fennec/renderers/interface/gfxresourcepool.h ) add_dependencies(fennec metaprogramming fennec-dependencies) diff --git a/include/fennec/containers/bintree.h b/include/fennec/containers/bintree.h index 029365b..0ee28bc 100644 --- a/include/fennec/containers/bintree.h +++ b/include/fennec/containers/bintree.h @@ -34,30 +34,38 @@ #include #include #include +#include #include #include namespace fennec { -template +/// +/// \brief Structure defining a binary tree +/// \tparam TypeT The data type +/// \tparam AllocT An allocator class +template> struct bintree { - // Definitions ========================================================================================================= +// Definitions ========================================================================================================= protected: struct node; public: using value_t = TypeT; using alloc_t = allocator_traits::template rebind; - static constexpr size_t root = 0; static constexpr size_t npos = -1; + inline static size_t sink = npos; + + friend class iterator; + friend class const_iterator; protected: struct node { - optional value; - size_t parent, left, right; - size_t depth; + value_t value; + size_t parent, left, right; + size_t depth; constexpr node() : value(nullopt) @@ -83,18 +91,103 @@ protected: using table_t = allocation; using freed_t = list; +// Constructors & Destructor =========================================================================================== +public: + + /// \name Constructors & Destructor + /// @{ + + /// + /// \brief Default Constructor, initializes an empty tree + constexpr bintree() + : _table() + , _freed() + , _root(npos) + , _size(0) { + } + + /// + /// \brief Move Constructor, takes ownership of a tree + /// \param tree The tree to take ownership of + constexpr bintree(bintree&& tree) noexcept + : _table(fennec::move(tree._table)) + , _freed(fennec::move(tree._freed)) + , _root(tree._root) + , _size(tree._size) { + } + + /// + /// \brief Copy Constructor, copies a tree + /// \param tree The tree to copy + constexpr bintree(const bintree& tree) + : _table(tree._table) + , _freed(tree._freed) + , _root(tree._root) + , _size(tree._size) { + } + + /// + /// \brief Destructor, clears the tree + constexpr ~bintree() { + clear(); + } + + /// @} + + +// Properties ========================================================================================================== +public: + + /// + /// \returns The number of elements in the tree + constexpr size_t size() const { + return _size; + } + + /// + /// \returns The capacity of the underlying allocation + constexpr size_t capacity() const { + return _table.capacity(); + } + + /// + /// \returns The next id to be returned by `insert` or `emplace`. + constexpr size_t next_id() const { + size_t i = _size; + if (not _freed.empty()) { + i = _freed.front(); + } + return i; + } + + /// + /// \returns The next id to be returned by `insert` or `emplace`. + constexpr size_t root() const { + return _root; + } + + + // Navigation ========================================================================================================== public: + /// \name Navigation + /// @{ + /// /// \details \f$O(1)\f$ /// \param i The node id /// \returns The parent of node `i` constexpr size_t parent(size_t i) const { - if (i >= _table.size()) { - return false; - } - return _table[i].parent; + return i >= _table.size() ? npos : _table[i].parent; + } + + /// + /// \details \f$O(1)\f$ + /// \param i The node id + /// \returns The grandparent of node `i` + constexpr size_t grandparent(size_t i) const { + return parent(parent(i)); } /// @@ -102,10 +195,7 @@ public: /// \param i The node id /// \returns The left child of node `i` constexpr size_t left(size_t i) const { - if (i >= _table.size()) { - return false; - } - return _table[i].left; + return i >= _table.size() ? npos : _table[i].left; } /// @@ -113,10 +203,26 @@ public: /// \param i The node id /// \returns The right child of node `i` constexpr size_t right(size_t i) const { - if (i >= _table.size()) { - return false; - } - return _table[i].right; + return i >= _table.size() ? npos : _table[i].right; + } + + /// + /// \brief \f$O(1)\f$ + /// \param i The id of the node + /// \returns The id of the sibling of `i` + constexpr size_t sibling(size_t i) const { + size_t p = parent(i); + size_t l = left(p); + size_t r = right(p); + return i == l ? l : r; + } + + /// + /// \brief Short for "Parent Sibling," \f$O(1)\f$ + /// \param i The id of the node + /// \returns The id of the parents' sibling of `i` + constexpr size_t parsib(size_t i) const { + return sibling(parent(i)); } /// @@ -124,14 +230,45 @@ public: /// \param i The node id /// \returns The depth of node `i` constexpr size_t depth(size_t i) const { + return i >= _table.size() ? npos : _table[i].depth; + } + + /// + /// \brief \f$O(\log n)\f$ + /// \param i The node id + /// \returns The id of the left-most node of `i` + constexpr size_t left_most(size_t i) const { if (i >= _table.size()) { return npos; } - return _table[i].depth; + while (_table[i].left != npos) { + i = _table[i].left; + } + return i; } + /// + /// \brief \f$O(\log n)\f$ + /// \param i The node id + /// \returns The id of the right-most node of `i` + constexpr size_t right_most(size_t i) const { + if (i >= _table.size()) { + return npos; + } + while (_table[i].right != npos) { + i = _table[i].right; + } + return i; + } + + /// @} + // Access ============================================================================================================== +public: + + /// \name Access + /// @{ /// /// \details \f$O(1)\f$ @@ -155,8 +292,13 @@ public: return _table[i] ? &*_table[i] : nullptr; } + /// @} + // Modifiers =========================================================================================================== + /// \name Modifiers + /// @{ + /// /// \brief Move Left Insertion, constructs a new node as the left child of `p` /// \details If the left node of `p` already exists, the move assignment operator is used instead @@ -174,11 +316,11 @@ public: /// \param val The object to copy to the new node /// \returns The id of the new node constexpr size_t insert_left(size_t p, const value_t& val) { - return this->_insert_left(p,, val); + return this->_insert_left(p, val); } /// - /// \brief Move Left Insertion, constructs a new node as the left child of `p` + /// \brief Emplace Left Insertion, constructs a new node as the left child of `p` /// \details If the left node of `p` already exists, the move assignment operator is used instead /// \param p The parent node /// \param args The arguments to construct the new node with @@ -188,6 +330,37 @@ public: return this->_insert_left(p, fennec::forward(args)...); } + /// + /// \brief Move Right Insertion, constructs a new node as the right child of `p` + /// \details If the right node of `p` already exists, the move assignment operator is used instead + /// \param p The parent node + /// \param val The object to move into the new node + /// \returns The id of the new node + constexpr size_t insert_right(size_t p, value_t&& val) { + return this->_insert_right(p, fennec::forward(val)); + } + + /// + /// \brief Copy Right Insertion, constructs a new node as the right child of `p` + /// \details If the right node of `p` already exists, the copy assignment operator is used instead + /// \param p The parent node + /// \param val The object to copy to the new node + /// \returns The id of the new node + constexpr size_t insert_right(size_t p, const value_t& val) { + return this->_insert_right(p, val); + } + + /// + /// \brief Emplace Right Insertion, constructs a new node as the right child of `p` + /// \details If the right node of `p` already exists, the move assignment operator is used instead + /// \param p The parent node + /// \param args The arguments to construct the new node with + /// \returns The id of the new node + template + constexpr size_t emplace_right(size_t p, ArgsT&&...args) { + return this->_insert_right(p, fennec::forward(args)...); + } + /// /// \brief Perform a Left Tree Rotation at `i` /// \param i The root node for the rotation @@ -242,55 +415,345 @@ public: _table[l].right = r; } + /// + /// \brief Clears the tree, destroying all elements + constexpr void clear() { + list queue; + + if (_root != npos) { + queue.push_back(_root); + } + + while (not queue.empty()) { + size_t i = queue.front(); + queue.pop_front(); + + if (_table[i].left != npos) { + queue.push_front(_table[i].left); + } + if (_table[i].right != npos) { + queue.push_front(_table[i].right); + } + + fennec::destruct(&_table[i]); + } + + _size = 0; + _root = npos; + } + + /// @} + + +// Traversal =========================================================================================================== + + /// + /// \brief Traverse the tree using a specified order and visiting functor + /// + /// \details + /// The visitor should accept a reference to a value of type `TypeT` and a `size_t` which contains the node's id. + /// The visitor should return one of the following values in the `fennec::traversal_control_` enum + /// + /// \tparam OrderT The order with which to traverse the tree. + /// \tparam VisitorT The visitor, should fulfill the signature `uint8_t visit(TypeT&, size_t)` + /// \param visit The visiting object + /// \param i The node to start at + template + constexpr void traverse(VisitorT&& visit, size_t i = root) { + OrderT order; + i = order(*this, i); + while (i != npos) { + uint8_t mode = traversal_control_continue; + if (_table[i].value) { + mode = visit(*_table[i].value, i); + } + if (mode == traversal_control_break) { + break; + } + i = order[*this, i, mode]; + } + } + + struct breadth_first { + list visit; + size_t head; + + size_t operator()(const bintree&, size_t start) { + return head = start; + } + + size_t operator[](const bintree& tree, size_t node, uint8_t mode) { + if (node == npos) { + return npos; + } + + size_t lft = tree.left(tree.parent(node)); + size_t nxt = lft == node ? tree.right(tree.parent(node)) : npos; + size_t chd = tree.left(node); + + if (nxt != npos && node != head) { + visit.push_front(nxt); + } + + if (chd != npos && mode != traversal_control_jump_over) { + visit.push_back(chd); + } + + if (not visit.empty()) { + node = visit.front(); + visit.pop_front(); + } else { + node = npos; + } + + return node; + } + }; + + struct pre_order { + list visit; + size_t head; + + constexpr size_t operator()(const bintree&, size_t start) { + head = start; + return start; + } + + constexpr size_t operator[](const bintree& tree, size_t node, uint8_t mode) { + if (node == npos) { + return npos; + } + + size_t nxt = tree.sibling(node); + size_t chd = tree.left(node); + nxt = node == nxt ? npos : nxt; + + if (nxt != npos && node != head) { + visit.push_front(nxt); + } + + if (chd != npos && mode != traversal_control_jump_over) { + visit.push_front(chd); + } + + if (not visit.empty()) { + node = visit.front(); + visit.pop_front(); + } else { + node = npos; + } + + return node; + } + }; + + struct in_order { + list visit; + size_t head; + + constexpr size_t operator()(const bintree& tree, size_t start) { + head = start; + return tree.left_most(start); + } + + constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) { + if (node == npos) { + return npos; + } + + size_t prnt = tree.parent(node); + size_t next = tree.sibling(node); + next = node == next ? npos : next; + + if (node != head) { + if (tree.left(prnt) == node) { + visit.push_back(prnt); + if (next != npos) { + visit.push_back(tree.left_most(next)); + } + } else if (next != npos) { + visit.push_front(tree.left_most(next)); + } + } + + if (not visit.empty()) { + node = visit.front(); + visit.pop_front(); + } else { + node = npos; + } + + return node; + } + }; + + struct post_order { + list visit; + size_t head; + + constexpr size_t operator()(const bintree& tree, size_t start) { + head = start; + return tree.left_most(start); + } + + constexpr size_t operator[](const bintree& tree, size_t node, uint8_t) { + if (node == npos) { + return npos; + } + + size_t prnt = tree.parent(node); + size_t next = tree.sibling(node); + next = node == next ? npos : next; + + if (node != head) { + if (next != npos) { + visit.push_front(tree.left_most(next)); + } else { + visit.push_front(prnt); + } + } + + if (not visit.empty()) { + node = visit.front(); + visit.pop_front(); + } else { + node = npos; + } + + return node; + } + }; + +// Iterator ============================================================================================================ + + class iterator { + protected: + bintree* _tree; + in_order _order; + size_t _n; + + public: + constexpr iterator(bintree* tree, size_t root, size_t node) + : _tree(tree) + , _order() + , _n(node) { + _order(*tree, root); + } + + iterator& operator++() { + return _n = _order[*_tree, _n, traversal_control_continue], *this; + } + + value_t& operator*() { + return _tree[_n]; + } + + value_t* operator->() { + return &_tree[_n]; + } + + const value_t& operator*() const { + return _tree[_n]; + } + + const value_t* operator->() const { + return &_tree[_n]; + } + + constexpr bool operator==(const iterator& it) { + return _tree == it._tree and _n == it._n; + } + + constexpr bool operator!=(const iterator& it) { + return _tree != it._tree or _n != it._n; + } + + }; + +// Fields ============================================================================================================== protected: table_t _table; freed_t _freed; size_t _root, _size; - constexpr void _next_free() { +// Helpers ============================================================================================================= + + constexpr size_t _next_free() { size_t i = _size; if (not _freed.empty()) { i = _freed.front(); _freed.pop_front(); } - if (i >= _table.size()) { - _table.reallocate(2 * fennec::max(_table.size(), 4)); + if (i >= _table.capacity()) { + _table.creallocate(2 * fennec::max(_table.capacity(), size_t(4))); } + ++_size; + return i; } template constexpr size_t _insert_left(size_t p, ArgsT&&...args) { - size_t i = left(p); - if (i == npos) { - i = _next_free(); - _table[i].value.emplace(fennec::forward(args)...); + size_t i = p == npos ? _root : left(p); + if (i != npos) { + _table[i].value = value_t(fennec::forward(args)...); } else { size_t d = 1; + i = _next_free(); if (p != npos) { d = depth(p) + 1; _table[p].left = i; } - fennec::construct(&_table[i], p, npos, npos, d); + fennec::construct(&_table[i], p, npos, npos, d, fennec::forward(args)...); } + return i; } template constexpr size_t _insert_right(size_t p, ArgsT&&...args) { - size_t i = right(p); - if (i == npos) { - i = _next_free(); - _table[i].value.emplace(fennec::forward(args)...); + size_t i = p == npos ? _root : right(p); + if (i != npos) { + _table[i].value = value_t(fennec::forward(args)...); if (p == npos || _root == npos) { _root = i; } } else { size_t d = 1; + i = _next_free(); if (p != npos) { d = depth(p) + 1; _table[p].right = i; } - fennec::construct(&_table[i], p, npos, npos, d); + fennec::construct(&_table[i], p, npos, npos, d, fennec::forward(args)...); } + return i; + } + + + constexpr size_t& parent(size_t i) { + return i >= _table.size() ? sink : _table[i].parent; + } + + constexpr size_t& grandparent(size_t i) { + return parent(parent(i)); + } + + constexpr size_t& left(size_t i) { + return i >= _table.size() ? sink : _table[i].left; + } + + constexpr size_t& right(size_t i) { + return i >= _table.size() ? sink : _table[i].right; + } + + constexpr size_t& sibling(size_t i) { + size_t p = parent(i); + size_t& l = left(p); + size_t& r = right(p); + return i == l ? l : r; + } + + constexpr size_t& parsib(size_t i) { + return sibling(parent(i)); } }; diff --git a/include/fennec/containers/detail/_tuple.h b/include/fennec/containers/detail/_tuple.h index 0380dac..96af8f1 100644 --- a/include/fennec/containers/detail/_tuple.h +++ b/include/fennec/containers/detail/_tuple.h @@ -25,7 +25,7 @@ namespace fennec::detail { -template +template struct _tuple_leaf { template diff --git a/include/fennec/containers/list.h b/include/fennec/containers/list.h index fc5bc47..7009f3d 100644 --- a/include/fennec/containers/list.h +++ b/include/fennec/containers/list.h @@ -140,6 +140,7 @@ public: for (const value_t& it : l) { this->push_back(it); } + return *this; } /// @@ -152,6 +153,7 @@ public: _freed = fennec::move(l._freed); _root = l._root; _last = l._last; _size = l._size; + return *this; } /// @} @@ -164,15 +166,21 @@ public: /// /// \returns The size of the list in elements. - constexpr size_t size() const { return _size; } + constexpr size_t size() const { + return _size; + } /// /// \returns The capacity of the list in elements. - constexpr size_t capacity() const { return _table.capacity(); } + constexpr size_t capacity() const { + return _table.capacity(); + } /// /// \returns `true` when the list is empty, `false` otherwise. - constexpr bool empty() const { return _root == npos; } + constexpr bool empty() const { + return _root == npos; + } /// @} diff --git a/include/fennec/containers/pair.h b/include/fennec/containers/pair.h index 357fcc8..e66b49e 100644 --- a/include/fennec/containers/pair.h +++ b/include/fennec/containers/pair.h @@ -95,19 +95,33 @@ struct pair { /// /// \brief Copy Constructor, copies both elements - constexpr pair(const pair&) = default; + constexpr pair(const pair& pair) + : first(fennec::copy(pair.first)) + , second(fennec::copy(pair.second)) { + } /// /// \brief Move Constructor, moves both elements - constexpr pair(pair&&) noexcept = default; + constexpr pair(pair&& pair) noexcept + : first(fennec::move(pair.first)) + , second(fennec::move(pair.second)) { + } /// /// \brief Copy Assignment, copies both elements - constexpr pair& operator=(const pair&) = default; + constexpr pair& operator=(const pair& pair) { + first = fennec::copy(pair.first); + second = fennec::copy(pair.second); + return *this; + } /// /// \brief Move Assignment, moves both elements - constexpr pair& operator=(pair&&) noexcept = default; + constexpr pair& operator=(pair&& pair) { + first = fennec::move(pair.first); + second = fennec::move(pair.second); + return *this; + } /// @} diff --git a/include/fennec/containers/rdtree.h b/include/fennec/containers/rdtree.h index 80c87e0..527088d 100644 --- a/include/fennec/containers/rdtree.h +++ b/include/fennec/containers/rdtree.h @@ -414,7 +414,7 @@ public: /// \param visit The visiting object /// \param i The node to start at template - void traverse(VisitorT&& visit, size_t i = root) { + constexpr void traverse(VisitorT&& visit, size_t i = root) { OrderT order; i = order(*this, i); while (i != npos) { @@ -429,16 +429,52 @@ public: } } - struct pre_order { + struct breadth_first { list visit; - size_t head; + size_t head; - size_t operator()(const rdtree&, size_t start) { + constexpr size_t operator()(const rdtree&, size_t start) { head = start; return start; } - size_t operator[](const rdtree& tree, size_t node, uint8_t mode) { + constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) { + if (node == npos) { + return npos; + } + + size_t nxt = tree.next(node); + size_t chd = tree.next(node); + + if (nxt != npos && node != head) { + visit.push_front(nxt); + } + + if (chd != npos && mode != traversal_control_jump_over) { + visit.push_back(chd); + } + + if (not visit.empty()) { + node = visit.front(); + visit.pop_front(); + } else { + node = npos; + } + + return node; + } + }; + + struct pre_order { + list visit; + size_t head; + + constexpr size_t operator()(const rdtree&, size_t start) { + head = start; + return start; + } + + constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t mode) { if (node == npos) { return npos; } @@ -469,12 +505,12 @@ public: list visit; size_t head; - size_t operator()(const rdtree& tree, size_t start) { + constexpr size_t operator()(const rdtree& tree, size_t start) { head = start; return tree.left_most(start); } - size_t operator[](const rdtree& tree, size_t node, uint8_t) { + constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) { if (node == npos) { return npos; } @@ -507,12 +543,12 @@ public: list visit; size_t head; - size_t operator()(const rdtree& tree, size_t start) { + constexpr size_t operator()(const rdtree& tree, size_t start) { head = start; return tree.left_most(start); } - size_t operator[](const rdtree& tree, size_t node, uint8_t) { + constexpr size_t operator[](const rdtree& tree, size_t node, uint8_t) { if (node == npos) { return npos; } diff --git a/include/fennec/containers/sequence.h b/include/fennec/containers/sequence.h new file mode 100644 index 0000000..9c270fc --- /dev/null +++ b/include/fennec/containers/sequence.h @@ -0,0 +1,359 @@ +// ===================================================================================================================== +// 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 . +// ===================================================================================================================== + +/// +/// \file sequence.h +/// \brief +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_CONTAINERS_SEQUENCE_H +#define FENNEC_CONTAINERS_SEQUENCE_H +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fennec +{ + +/// +/// +/// \brief wrapper for ordered sets of elements, called sequences in mathematics +/// \details +/// This data-structure behaves like an ordered-set, but does not use pointers, instead storing the table in-array +/// +/// | Property | Value | +/// |:----------:|:---------------:| +/// | stable | ⛔ | +/// | dynamic | ✅ | +/// | homogenous | ✅ | +/// | distinct | ✅ | +/// | ordered | ✅ | +/// | space | \f$O(N)\f$ | +/// | linear | ✅ | +/// | access | \f$O(\log N)\f$ | +/// | find | \f$O(\log N)\f$ | +/// | insertion | \f$O(\log N)\f$ | +/// | deletion | \f$O(\log N)\f$ | +/// +/// \tparam TypeT The type to contain +/// \tparam CompareT Function for comparing two values +/// \tparam AllocT An allocator class +template, class AllocT = allocator>> +struct sequence : protected bintree, AllocT> { + +// Definitions ========================================================================================================= +protected: + struct node; + +public: + using value_t = TypeT; + using node_t = pair; + using alloc_t = allocator_traits::template rebind; + using base_t = bintree, AllocT>; + using compare_t = CompareT; + static constexpr size_t npos = -1; + + enum color_ : bool { + black = false, + red = true, + }; + + class iterator; + +protected: + using typename base_t::in_order; + + using base_t::left; + using base_t::right; + using base_t::parent; + using base_t::grandparent; + using base_t::sibling; + using base_t::parsib; + + using base_t::insert_left; + using base_t::insert_right; + + using base_t::rotate_left; + using base_t::rotate_right; + + using base_t::_table; + using base_t::_root; + using base_t::_size; + +// Constructors & Destructors ========================================================================================== +public: + /// \name Constructors & Destructor + /// @{ + + /// + /// \brief Default Constructor, initializes an empty sequence + constexpr sequence() = default; + + /// + /// \brief Move Constructor, takes ownership of a sequence + constexpr sequence(sequence&&) noexcept = default; + + /// + /// \brief Copy Constructor, copies a sequence + constexpr sequence(const sequence&) = default; + + /// + /// \brief Default Destructor, destructs elements *in-order* + constexpr ~sequence() { + this->clear(); + } + + /// @} + +// Search ============================================================================================================== +public: + /// \name Search + /// @{ + + /// + /// \brief Value Find Function, finds the iterator position for `val`, otherwise returns `end()` + /// \param val The value to find + /// \returns An iterator at the value + constexpr iterator find(const value_t& val) { + size_t node = _root; + while (node != npos) { + if (_compare(val, _value(node))) { + node = left(node); + } else if (_compare(_value(node), val)) { + node = right(node); + } else { + return sequence::iterator(this, _root, node); + } + } + return sequence::iterator(this, _root, node); + } + + /// + /// \brief Value Contains Function, checks if the sequence contains a value + /// \param val The value to find + /// \returns `true` if `val` is in the sequence, `false` otherwise + bool contains(const value_t& val) { + return find(val) != end(); + } + + /// @} + + +// Properties ========================================================================================================== +public: + /// \name Properties + /// @{ + + /// + /// \returns The number of elements in the sequence + using base_t::size; + + /// + /// \returns The capacity of the underlying allocation + using base_t::capacity; + + /// @} + +// Modifiers =========================================================================================================== +public: + /// \name Modifiers + /// @{ + + /// + /// \brief Move Insertion, moves `val` into the sequence + /// \param val The value to insert + constexpr void insert(value_t&& val) { + size_t i = _insert_bst(fennec::forward(val)); + _fix_insert(i); + } + + /// + /// \brief Copy Insertion, inserts a copy of `val` into the sequence + /// \param val The value to insert + constexpr void insert(const value_t& val) { + size_t i = _insert_bst(val); + _fix_insert(i); + } + + /// + /// \brief Emplacement, constructs and adds a value into the sequence + /// \tparam ArgsT The argument types + /// \param args The arguments to construct with + template + constexpr void emplace(ArgsT&&...args) { + size_t i = _insert_bst(fennec::forward(args)...); + _fix_insert(i); + } + + constexpr void erase(const value_t& val) { + size_t i = find(val)._n; + + } + + /// + /// \brief Destructs all elements, *in-order*, contained in the sequence + constexpr void clear() { + in_order order; + size_t node = order(*this, _root); + while (node != npos) { + size_t erase = node; + node = order[*this, node, traversal_control_continue]; + fennec::destruct(&_table[erase]); + } + _size = 0; + } + + /// @} + +// Iterator ============================================================================================================ + + /// + /// \returns An iterator at the smallest element in the sequence + constexpr sequence::iterator begin() { + return sequence::iterator(this, _root, _root); + } + + /// + /// \returns An iterator after the largest element in the sequence + constexpr sequence::iterator end() { + return sequence::iterator(this, _root, npos); + } + + class iterator : public base_t::iterator { + protected: + using base_t::iterator::_n; + + + public: + using base_t::iterator::iterator; + + value_t& operator*() { + return _table[_n].value.second; + } + + const value_t& operator*() const { + return _table[_n].value.second; + } + + value_t* operator->() { + return &_table[_n].value.second; + } + + const value_t* operator->() const { + return &_table[_n].value.second; + } + }; + + +// Fields ============================================================================================================== +protected: + compare_t _compare; + inline static bool color_sink = red; + inline static value_t value_sink; + +// Helpers ============================================================================================================= +protected: + constexpr value_t& _value(size_t i) { + return i >= _table.capacity() ? value_sink : _table[i].value.first; + } + + constexpr const value_t& _value(size_t i) const { + return i >= _table.capacity() ? value_sink : _table[i].value.first; + } + + constexpr bool& _color(size_t i) { + return i >= _table.capacity() ? color_sink : _table[i].value.second; + } + + constexpr bool _color(size_t i) const { + return i >= _table.capacity() ? color_sink : _table[i].value.second; + } + + template + constexpr size_t _insert_bst(ArgsT&&...args) { + value_t val(fennec::forward(args)...); + + size_t i = _root; + size_t p = npos; + while (i != npos) { + p = i; + + if (_compare(val, _value(i))) { + i = left(i); + } else if (_compare(_value(i), val)) { + i = right(i); + } else { + return i; + } + } + + if (_root == npos) { + return _root = insert_left(npos, node_t(fennec::move(val), red)); + } + + if (_compare(val, _value(p))) { + return insert_left(p, node_t(fennec::move(val), red)); + } else { + return insert_right(p, node_t(fennec::move(val), red)); + } + } + + constexpr void _fix_insert(size_t x) { + while (x != _root && _color(parent(x)) == red) { + if (_color(parsib(x)) == red) { + _color(parent(x)) = black; + _color(parsib(x)) = black; + _color(grandparent(x)) = red; + x = grandparent(x); + } else if (parent(x) == left(grandparent(x))) { + if (x == right(parent(x))) { + x = parent(x); + rotate_left(x); + } + _color(parent(x)) = black; + _color(grandparent(x)) = red; + rotate_right(grandparent(x)); + } else { + if (x == left(parent(x))) { + x = parent(x); + rotate_right(x); + } + _color(parent(x)) = black; + _color(grandparent(x)) = red; + rotate_left(grandparent(x)); + } + } + _color(_root) = black; + } +}; + +} + +#endif // FENNEC_CONTAINERS_SEQUENCE_H \ No newline at end of file diff --git a/include/fennec/platform/interface/platform.h b/include/fennec/platform/interface/platform.h index 79d2e45..f291e79 100644 --- a/include/fennec/platform/interface/platform.h +++ b/include/fennec/platform/interface/platform.h @@ -20,6 +20,7 @@ #define FENNEC_PLATFORM_INTERFACE_PLATFORM_H #include +#include #include #include #include @@ -69,11 +70,48 @@ public: struct driver { int priority; ctor constructor; + + driver() + : priority(0) + , constructor(nullptr) { + } + + driver(int priority, ctor constructor) + : priority(priority) + , constructor(constructor) { + } + + driver(const driver& d) + : priority(d.priority) + , constructor(d.constructor) { + } + + driver(driver&& d) noexcept + : priority(d.priority) + , constructor(d.constructor) { + } + + driver& operator=(const driver& d) { + priority = d.priority; + constructor = d.constructor; + return *this; + } + + driver& operator=(driver&& d) noexcept { + priority = fennec::move(d.priority); + constructor = fennec::move(d.constructor); + return *this; + } + + bool operator<(const driver& d) const { + return priority > d.priority; + } }; const string name; virtual ~platform() = default; + platform(const platform&) = delete; // Dynamically linked objects virtual shared_object* load_object(const cstring& file) = 0; @@ -96,22 +134,27 @@ protected: explicit platform(const cstring& name, PlatformT* type) : typed(type) , name(name) { - assertf(singleton == nullptr, "Conflicting platform implementations!"); - singleton = this; + assertf(globals.singleton == nullptr, "Conflicting platform implementations!"); + globals.singleton = this; } -private: - platform(const platform&) = delete; - // Static Stuff ======================================================================================================== public: static platform* instance() { - return singleton; + return globals.singleton; } private: - static platform* singleton; + inline static struct global { + platform* singleton; + sequence windows; + + global() + : singleton(nullptr) + , windows() { + } + } globals = global(); }; } diff --git a/include/fennec/platform/unix/platform.h b/include/fennec/platform/unix/platform.h index bada8cc..c3acea4 100644 --- a/include/fennec/platform/unix/platform.h +++ b/include/fennec/platform/unix/platform.h @@ -27,8 +27,8 @@ namespace fennec class unix_platform : public platform { public: template - explicit unix_platform(const cstring& name, PlatformT*) - : platform(name, (PlatformT*)(nullptr)) { + explicit unix_platform(const cstring& name, PlatformT* type) + : platform(name, type) { } shared_object* load_object(const cstring& file) override; diff --git a/include/fennec/renderers/interface/forward.h b/include/fennec/renderers/interface/forward.h index 121bbfe..e8367af 100644 --- a/include/fennec/renderers/interface/forward.h +++ b/include/fennec/renderers/interface/forward.h @@ -34,7 +34,7 @@ namespace fennec { -class renderer; // Overarching rendering scheme +class gfxpass; // Overarching rendering scheme class gfxcontext; // Globals for an API's context class gfxresourcepool; // Handles resource creation, allocation, and mapping class gfxsubpass; // A subpass of the renderer, implements, for example, the steps of composing and lighting a deferred renderer diff --git a/include/fennec/renderers/interface/gfxcontext.h b/include/fennec/renderers/interface/gfxcontext.h index 5573e3a..3b3f13a 100644 --- a/include/fennec/renderers/interface/gfxcontext.h +++ b/include/fennec/renderers/interface/gfxcontext.h @@ -30,25 +30,45 @@ #ifndef FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H #define FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H -#include + +#include +#include +#include +#include namespace fennec { class gfxcontext { public: + enum texture_ : uint8_t { + texture_1d = 0, + texture_1d_array, + texture_2d, + texture_2d_array, + texture_multisample, + texture_multisample_array, + texture_cubemap, + texture_cubemap_array, + texture_3d, + }; + + using handle_t = uint32_t; + + struct version_t { + uint8_t major, minor, patch; + string str; + }; + + const version_t version; + gfxresourcepool resources; gfxcontext& operator=(const gfxcontext&) = delete; gfxcontext& operator=(gfxcontext&&) = delete; - renderer* get_renderer() { - return _renderer; - } + virtual gfxpass* create_pass() = 0; protected: - renderer* _renderer; - - gfxcontext(renderer* renderer); private: }; diff --git a/include/fennec/renderers/interface/renderer.h b/include/fennec/renderers/interface/gfxpass.h similarity index 80% rename from include/fennec/renderers/interface/renderer.h rename to include/fennec/renderers/interface/gfxpass.h index ce1ee00..8f50ccd 100644 --- a/include/fennec/renderers/interface/renderer.h +++ b/include/fennec/renderers/interface/gfxpass.h @@ -43,33 +43,8 @@ namespace fennec /// \details This represents the overarching renderer, and not any platform specific behaviour. /// I.E. This acts as a proxy for creating API objects, but the behaviour of those objects /// is defined elsewhere. -class renderer { +class gfxpass { public: - enum texture_ : uint8_t { - texture_1d = 0, - texture_1d_array, - texture_2d, - texture_2d_array, - texture_multisample, - texture_multisample_array, - texture_cubemap, - texture_cubemap_array, - texture_3d, - }; - - using handle_t = uint32_t; - - struct version_t { - uint8_t major, minor, patch; - string str; - }; - - const version_t version; - - gfxcontext context; - gfxresourcepool resources; - - handle_t add_subpass(); private: diff --git a/include/fennec/renderers/interface/gfxresourcepool.h b/include/fennec/renderers/interface/gfxresourcepool.h new file mode 100644 index 0000000..7218665 --- /dev/null +++ b/include/fennec/renderers/interface/gfxresourcepool.h @@ -0,0 +1,43 @@ +// ===================================================================================================================== +// 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 . +// ===================================================================================================================== + +/// +/// \file gfxresourcepool.h +/// \brief +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_RENDERERS_INTERFACE_GFXRESOURCEPOOL_H +#define FENNEC_RENDERERS_INTERFACE_GFXRESOURCEPOOL_H + +namespace fennec +{ + +class gfxresourcepool { + +}; + +} + +#endif // FENNEC_RENDERERS_INTERFACE_GFXRESOURCEPOOL_H \ No newline at end of file diff --git a/source/platform/interface/platform.cpp b/source/platform/interface/platform.cpp index ce5d967..c472875 100644 --- a/source/platform/interface/platform.cpp +++ b/source/platform/interface/platform.cpp @@ -41,8 +41,12 @@ void platform::initialize() { void platform::shutdown() { } -window* platform::create_window(const window::config& config) { - +window* platform::create_window(const window::config&) { + return nullptr; } -} \ No newline at end of file +void platform::register_window_driver(window_driver&& driver) { + globals.windows.insert(fennec::forward(driver)); +} + +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1dd7f5a..ea13eb6 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,6 +7,7 @@ set(CMAKE_C_STANDARD 23) add_executable(fennec-test main.cpp tests/containers/performance/test_iterator_visitor.h + tests/containers/test_sequence.h ) target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}" diff --git a/test/tests/containers/test_sequence.h b/test/tests/containers/test_sequence.h new file mode 100644 index 0000000..b2d1008 --- /dev/null +++ b/test/tests/containers/test_sequence.h @@ -0,0 +1,61 @@ +// ===================================================================================================================== +// 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 . +// ===================================================================================================================== + +/// +/// \file test_sequence.h +/// \brief +/// +/// +/// \details +/// \author Medusa Slockbower +/// +/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html)) +/// +/// + +#ifndef FENNEC_TEST_CONTAINERS_SEQUENCE_H +#define FENNEC_TEST_CONTAINERS_SEQUENCE_H +#include + +namespace fennec +{ + +namespace test +{ + +inline void fennec_test_containers_sequence() { + dynarray ref; + sequence test; + const size_t n = 50; + + for (size_t i = 0; i < n; ++i) { + size_t v = rand(); + ref.push_back(v); + test.insert(v); + } + + for (size_t v : ref) { + assertf(test.contains(v), "Failed Sequence Test!"); + } +} + +} + +} + +#endif // FENNEC_TEST_CONTAINERS_SEQUENCE_H \ No newline at end of file diff --git a/test/tests/test_containers.h b/test/tests/test_containers.h index 45f0980..eb63a0d 100644 --- a/test/tests/test_containers.h +++ b/test/tests/test_containers.h @@ -28,6 +28,7 @@ #include "containers/test_object_pool.h" #include "containers/test_optional.h" #include "containers/test_rdtree.h" +#include "containers/test_sequence.h" #include "containers/test_set.h" #include "containers/test_tuple.h" @@ -71,6 +72,11 @@ namespace fennec::test fennec_test_containers_set(); fennec_test_spacer(3); + fennec_test_subheader("sequence tests"); + fennec_test_spacer(2); + fennec_test_containers_sequence(); + fennec_test_spacer(3); + fennec_test_subheader("map tests"); fennec_test_spacer(2); fennec_test_containers_map();