- Binary Tree (Partial)

- Sequence (Partial)
This commit is contained in:
2025-08-30 22:11:41 -04:00
parent 992a02db3e
commit dbcb50349d
17 changed files with 1133 additions and 98 deletions

View File

@@ -105,6 +105,7 @@ add_library(fennec STATIC
include/fennec/containers/containers.h include/fennec/containers/containers.h
include/fennec/containers/array.h include/fennec/containers/array.h
include/fennec/containers/bintree.h
include/fennec/containers/deque.h include/fennec/containers/deque.h
include/fennec/containers/dynarray.h include/fennec/containers/dynarray.h
include/fennec/containers/graph.h include/fennec/containers/graph.h
@@ -114,6 +115,7 @@ add_library(fennec STATIC
include/fennec/containers/optional.h include/fennec/containers/optional.h
include/fennec/containers/pair.h include/fennec/containers/pair.h
include/fennec/containers/rdtree.h include/fennec/containers/rdtree.h
include/fennec/containers/sequence.h
include/fennec/containers/set.h include/fennec/containers/set.h
include/fennec/containers/traversal.h include/fennec/containers/traversal.h
include/fennec/containers/tuple.h include/fennec/containers/tuple.h
@@ -235,7 +237,7 @@ add_library(fennec STATIC
# EXTRA SOURCES ======================================================================================================== # EXTRA SOURCES ========================================================================================================
${FENNEC_EXTRA_SOURCES} ${FENNEC_EXTRA_SOURCES}
include/fennec/containers/bintree.h include/fennec/renderers/interface/gfxresourcepool.h
) )
add_dependencies(fennec metaprogramming fennec-dependencies) add_dependencies(fennec metaprogramming fennec-dependencies)

View File

@@ -34,28 +34,36 @@
#include <fennec/containers/dynarray.h> #include <fennec/containers/dynarray.h>
#include <fennec/containers/list.h> #include <fennec/containers/list.h>
#include <fennec/containers/optional.h> #include <fennec/containers/optional.h>
#include <fennec/containers/traversal.h>
#include <fennec/math/exponential.h> #include <fennec/math/exponential.h>
#include <fennec/memory/allocator.h> #include <fennec/memory/allocator.h>
namespace fennec namespace fennec
{ {
template<typename TypeT, class AllocT> ///
/// \brief Structure defining a binary tree
/// \tparam TypeT The data type
/// \tparam AllocT An allocator class
template<typename TypeT, class AllocT = allocator<TypeT>>
struct bintree { struct bintree {
// Definitions ========================================================================================================= // Definitions =========================================================================================================
protected: protected:
struct node; struct node;
public: public:
using value_t = TypeT; using value_t = TypeT;
using alloc_t = allocator_traits<AllocT>::template rebind<node>; using alloc_t = allocator_traits<AllocT>::template rebind<node>;
static constexpr size_t root = 0;
static constexpr size_t npos = -1; static constexpr size_t npos = -1;
inline static size_t sink = npos;
friend class iterator;
friend class const_iterator;
protected: protected:
struct node { struct node {
optional<value_t> value; value_t value;
size_t parent, left, right; size_t parent, left, right;
size_t depth; size_t depth;
@@ -83,18 +91,103 @@ protected:
using table_t = allocation<node, alloc_t>; using table_t = allocation<node, alloc_t>;
using freed_t = list<size_t, alloc_t>; using freed_t = list<size_t, alloc_t>;
// 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 ========================================================================================================== // Navigation ==========================================================================================================
public: public:
/// \name Navigation
/// @{
/// ///
/// \details \f$O(1)\f$ /// \details \f$O(1)\f$
/// \param i The node id /// \param i The node id
/// \returns The parent of node `i` /// \returns The parent of node `i`
constexpr size_t parent(size_t i) const { constexpr size_t parent(size_t i) const {
if (i >= _table.size()) { return i >= _table.size() ? npos : _table[i].parent;
return false;
} }
return _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 /// \param i The node id
/// \returns The left child of node `i` /// \returns The left child of node `i`
constexpr size_t left(size_t i) const { constexpr size_t left(size_t i) const {
if (i >= _table.size()) { return i >= _table.size() ? npos : _table[i].left;
return false;
}
return _table[i].left;
} }
/// ///
@@ -113,10 +203,26 @@ public:
/// \param i The node id /// \param i The node id
/// \returns The right child of node `i` /// \returns The right child of node `i`
constexpr size_t right(size_t i) const { constexpr size_t right(size_t i) const {
if (i >= _table.size()) { return i >= _table.size() ? npos : _table[i].right;
return false;
} }
return _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 /// \param i The node id
/// \returns The depth of node `i` /// \returns The depth of node `i`
constexpr size_t depth(size_t i) const { 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()) { if (i >= _table.size()) {
return npos; 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 ============================================================================================================== // Access ==============================================================================================================
public:
/// \name Access
/// @{
/// ///
/// \details \f$O(1)\f$ /// \details \f$O(1)\f$
@@ -155,8 +292,13 @@ public:
return _table[i] ? &*_table[i] : nullptr; return _table[i] ? &*_table[i] : nullptr;
} }
/// @}
// Modifiers =========================================================================================================== // Modifiers ===========================================================================================================
/// \name Modifiers
/// @{
/// ///
/// \brief Move Left Insertion, constructs a new node as the left child of `p` /// \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 /// \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 /// \param val The object to copy to the new node
/// \returns The id of the new node /// \returns The id of the new node
constexpr size_t insert_left(size_t p, const value_t& val) { 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 /// \details If the left node of `p` already exists, the move assignment operator is used instead
/// \param p The parent node /// \param p The parent node
/// \param args The arguments to construct the new node with /// \param args The arguments to construct the new node with
@@ -188,6 +330,37 @@ public:
return this->_insert_left(p, fennec::forward<ArgsT>(args)...); return this->_insert_left(p, fennec::forward<ArgsT>(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<value_t>(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<typename...ArgsT>
constexpr size_t emplace_right(size_t p, ArgsT&&...args) {
return this->_insert_right(p, fennec::forward<ArgsT>(args)...);
}
/// ///
/// \brief Perform a Left Tree Rotation at `i` /// \brief Perform a Left Tree Rotation at `i`
/// \param i The root node for the rotation /// \param i The root node for the rotation
@@ -242,55 +415,345 @@ public:
_table[l].right = r; _table[l].right = r;
} }
///
/// \brief Clears the tree, destroying all elements
constexpr void clear() {
list<size_t> 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<typename OrderT, typename VisitorT>
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<size_t> 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<size_t> 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<size_t> 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<size_t> 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: protected:
table_t _table; table_t _table;
freed_t _freed; freed_t _freed;
size_t _root, _size; size_t _root, _size;
constexpr void _next_free() { // Helpers =============================================================================================================
constexpr size_t _next_free() {
size_t i = _size; size_t i = _size;
if (not _freed.empty()) { if (not _freed.empty()) {
i = _freed.front(); i = _freed.front();
_freed.pop_front(); _freed.pop_front();
} }
if (i >= _table.size()) { if (i >= _table.capacity()) {
_table.reallocate(2 * fennec::max(_table.size(), 4)); _table.creallocate(2 * fennec::max(_table.capacity(), size_t(4)));
} }
++_size;
return i;
} }
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t _insert_left(size_t p, ArgsT&&...args) { constexpr size_t _insert_left(size_t p, ArgsT&&...args) {
size_t i = left(p); size_t i = p == npos ? _root : left(p);
if (i == npos) { if (i != npos) {
i = _next_free(); _table[i].value = value_t(fennec::forward<ArgsT>(args)...);
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
} else { } else {
size_t d = 1; size_t d = 1;
i = _next_free();
if (p != npos) { if (p != npos) {
d = depth(p) + 1; d = depth(p) + 1;
_table[p].left = i; _table[p].left = i;
} }
fennec::construct(&_table[i], p, npos, npos, d); fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(args)...);
} }
return i;
} }
template<typename...ArgsT> template<typename...ArgsT>
constexpr size_t _insert_right(size_t p, ArgsT&&...args) { constexpr size_t _insert_right(size_t p, ArgsT&&...args) {
size_t i = right(p); size_t i = p == npos ? _root : right(p);
if (i == npos) { if (i != npos) {
i = _next_free(); _table[i].value = value_t(fennec::forward<ArgsT>(args)...);
_table[i].value.emplace(fennec::forward<ArgsT>(args)...);
if (p == npos || _root == npos) { if (p == npos || _root == npos) {
_root = i; _root = i;
} }
} else { } else {
size_t d = 1; size_t d = 1;
i = _next_free();
if (p != npos) { if (p != npos) {
d = depth(p) + 1; d = depth(p) + 1;
_table[p].right = i; _table[p].right = i;
} }
fennec::construct(&_table[i], p, npos, npos, d); fennec::construct(&_table[i], p, npos, npos, d, fennec::forward<ArgsT>(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));
} }
}; };

View File

@@ -25,7 +25,7 @@ namespace fennec::detail
{ {
template <std::size_t I, typename T> template <size_t I, typename T>
struct _tuple_leaf struct _tuple_leaf
{ {
template <typename ArgT> template <typename ArgT>

View File

@@ -140,6 +140,7 @@ public:
for (const value_t& it : l) { for (const value_t& it : l) {
this->push_back(it); this->push_back(it);
} }
return *this;
} }
/// ///
@@ -152,6 +153,7 @@ public:
_freed = fennec::move(l._freed); _freed = fennec::move(l._freed);
_root = l._root; _last = l._last; _root = l._root; _last = l._last;
_size = l._size; _size = l._size;
return *this;
} }
/// @} /// @}
@@ -164,15 +166,21 @@ public:
/// ///
/// \returns The size of the list in elements. /// \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. /// \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. /// \returns `true` when the list is empty, `false` otherwise.
constexpr bool empty() const { return _root == npos; } constexpr bool empty() const {
return _root == npos;
}
/// @} /// @}

View File

@@ -95,19 +95,33 @@ struct pair {
/// ///
/// \brief Copy Constructor, copies both elements /// \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 /// \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 /// \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 /// \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;
}
/// @} /// @}

View File

@@ -414,7 +414,7 @@ public:
/// \param visit The visiting object /// \param visit The visiting object
/// \param i The node to start at /// \param i The node to start at
template<typename OrderT, typename VisitorT> template<typename OrderT, typename VisitorT>
void traverse(VisitorT&& visit, size_t i = root) { constexpr void traverse(VisitorT&& visit, size_t i = root) {
OrderT order; OrderT order;
i = order(*this, i); i = order(*this, i);
while (i != npos) { while (i != npos) {
@@ -429,16 +429,52 @@ public:
} }
} }
struct pre_order { struct breadth_first {
list<size_t> visit; list<size_t> 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; head = start;
return 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<size_t> 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) { if (node == npos) {
return npos; return npos;
} }
@@ -469,12 +505,12 @@ public:
list<size_t> visit; list<size_t> visit;
size_t head; size_t head;
size_t operator()(const rdtree& tree, size_t start) { constexpr size_t operator()(const rdtree& tree, size_t start) {
head = start; head = start;
return tree.left_most(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) { if (node == npos) {
return npos; return npos;
} }
@@ -507,12 +543,12 @@ public:
list<size_t> visit; list<size_t> visit;
size_t head; size_t head;
size_t operator()(const rdtree& tree, size_t start) { constexpr size_t operator()(const rdtree& tree, size_t start) {
head = start; head = start;
return tree.left_most(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) { if (node == npos) {
return npos; return npos;
} }

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \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 <fennec/containers/bintree.h>
#include <fennec/containers/bintree.h>
#include <fennec/containers/pair.h>
#include <fennec/containers/sequence.h>
#include <fennec/containers/sequence.h>
#include <fennec/containers/sequence.h>
#include <fennec/lang/compare.h>
#include <fennec/memory/allocator.h>
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<typename TypeT, typename CompareT = less<TypeT>, class AllocT = allocator<pair<TypeT, bool>>>
struct sequence : protected bintree<pair<TypeT, bool>, AllocT> {
// Definitions =========================================================================================================
protected:
struct node;
public:
using value_t = TypeT;
using node_t = pair<TypeT, bool>;
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
using base_t = bintree<pair<TypeT, bool>, 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<value_t>(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<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
size_t i = _insert_bst(fennec::forward<ArgsT>(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<typename...ArgsT>
constexpr size_t _insert_bst(ArgsT&&...args) {
value_t val(fennec::forward<ArgsT>(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

View File

@@ -20,6 +20,7 @@
#define FENNEC_PLATFORM_INTERFACE_PLATFORM_H #define FENNEC_PLATFORM_INTERFACE_PLATFORM_H
#include <fennec/containers/list.h> #include <fennec/containers/list.h>
#include <fennec/containers/sequence.h>
#include <fennec/langproc/strings/cstring.h> #include <fennec/langproc/strings/cstring.h>
#include <fennec/langproc/strings/string.h> #include <fennec/langproc/strings/string.h>
#include <fennec/lang/typed.h> #include <fennec/lang/typed.h>
@@ -69,11 +70,48 @@ public:
struct driver { struct driver {
int priority; int priority;
ctor constructor; 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; const string name;
virtual ~platform() = default; virtual ~platform() = default;
platform(const platform&) = delete;
// Dynamically linked objects // Dynamically linked objects
virtual shared_object* load_object(const cstring& file) = 0; virtual shared_object* load_object(const cstring& file) = 0;
@@ -96,22 +134,27 @@ protected:
explicit platform(const cstring& name, PlatformT* type) explicit platform(const cstring& name, PlatformT* type)
: typed(type) : typed(type)
, name(name) { , name(name) {
assertf(singleton == nullptr, "Conflicting platform implementations!"); assertf(globals.singleton == nullptr, "Conflicting platform implementations!");
singleton = this; globals.singleton = this;
} }
private:
platform(const platform&) = delete;
// Static Stuff ======================================================================================================== // Static Stuff ========================================================================================================
public: public:
static platform* instance() { static platform* instance() {
return singleton; return globals.singleton;
} }
private: private:
static platform* singleton; inline static struct global {
platform* singleton;
sequence<window_driver> windows;
global()
: singleton(nullptr)
, windows() {
}
} globals = global();
}; };
} }

View File

@@ -27,8 +27,8 @@ namespace fennec
class unix_platform : public platform { class unix_platform : public platform {
public: public:
template<typename PlatformT> template<typename PlatformT>
explicit unix_platform(const cstring& name, PlatformT*) explicit unix_platform(const cstring& name, PlatformT* type)
: platform(name, (PlatformT*)(nullptr)) { : platform(name, type) {
} }
shared_object* load_object(const cstring& file) override; shared_object* load_object(const cstring& file) override;

View File

@@ -34,7 +34,7 @@
namespace fennec namespace fennec
{ {
class renderer; // Overarching rendering scheme class gfxpass; // Overarching rendering scheme
class gfxcontext; // Globals for an API's context class gfxcontext; // Globals for an API's context
class gfxresourcepool; // Handles resource creation, allocation, and mapping 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 class gfxsubpass; // A subpass of the renderer, implements, for example, the steps of composing and lighting a deferred renderer

View File

@@ -30,25 +30,45 @@
#ifndef FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H #ifndef FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H
#define FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H #define FENNEC_RENDERERS_INTERFACE_GFXCONTEXT_H
#include <fennec/renderers/interface/renderer.h>
#include <fennec/lang/types.h>
#include <fennec/langproc/strings/string.h>
#include <fennec/renderers/interface/forward.h>
#include <fennec/renderers/interface/gfxresourcepool.h>
namespace fennec namespace fennec
{ {
class gfxcontext { class gfxcontext {
public: 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=(const gfxcontext&) = delete;
gfxcontext& operator=(gfxcontext&&) = delete; gfxcontext& operator=(gfxcontext&&) = delete;
renderer* get_renderer() { virtual gfxpass* create_pass() = 0;
return _renderer;
}
protected: protected:
renderer* _renderer;
gfxcontext(renderer* renderer);
private: private:
}; };

View File

@@ -43,33 +43,8 @@ namespace fennec
/// \details This represents the overarching renderer, and not any platform specific behaviour. /// \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 /// I.E. This acts as a proxy for creating API objects, but the behaviour of those objects
/// is defined elsewhere. /// is defined elsewhere.
class renderer { class gfxpass {
public: 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: private:

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \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

View File

@@ -41,8 +41,12 @@ void platform::initialize() {
void platform::shutdown() { void platform::shutdown() {
} }
window* platform::create_window(const window::config& config) { window* platform::create_window(const window::config&) {
return nullptr;
}
void platform::register_window_driver(window_driver&& driver) {
globals.windows.insert(fennec::forward<window_driver>(driver));
} }
} }

View File

@@ -7,6 +7,7 @@ set(CMAKE_C_STANDARD 23)
add_executable(fennec-test add_executable(fennec-test
main.cpp main.cpp
tests/containers/performance/test_iterator_visitor.h 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}" target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}"

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
///
/// \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 <fennec/containers/sequence.h>
namespace fennec
{
namespace test
{
inline void fennec_test_containers_sequence() {
dynarray<size_t> ref;
sequence<size_t> 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

View File

@@ -28,6 +28,7 @@
#include "containers/test_object_pool.h" #include "containers/test_object_pool.h"
#include "containers/test_optional.h" #include "containers/test_optional.h"
#include "containers/test_rdtree.h" #include "containers/test_rdtree.h"
#include "containers/test_sequence.h"
#include "containers/test_set.h" #include "containers/test_set.h"
#include "containers/test_tuple.h" #include "containers/test_tuple.h"
@@ -71,6 +72,11 @@ namespace fennec::test
fennec_test_containers_set(); fennec_test_containers_set();
fennec_test_spacer(3); 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_subheader("map tests");
fennec_test_spacer(2); fennec_test_spacer(2);
fennec_test_containers_map(); fennec_test_containers_map();