- deque, object_pool, and graph data structures + PrettyPrinters

This commit is contained in:
2025-08-16 07:56:25 -04:00
parent 8bfb59cd20
commit 38b7221fa0
16 changed files with 867 additions and 146 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_CONTAINERS_DEQUE_H
#define FENNEC_CONTAINERS_DEQUE_H
#include <fennec/memory/allocator.h>
// TODO: Document
namespace fennec
{
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct deque {
// Definitions =========================================================================================================
public:
using elem_t = TypeT;
struct node {
elem_t value;
node *prev, *next;
template<typename...ArgsT>
node(node* prev, node* next, ArgsT&&...args)
: value(fennec::forward<ArgsT>(args)...)
, prev(prev), next(next) {
}
~node() = default;
};
using alloc_t = allocator_traits<AllocT>::template rebind<node>;
// 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<typename...ArgsT>
void emplace_front(ArgsT&&...args) {
this->_push_front(fennec::forward<ArgsT>(args)...);
}
void push_back(elem_t&& elem) {
this->_push_back(elem);
}
void push_back(const elem_t& elem) {
this->_push_back(elem);
}
template<typename...ArgsT>
void emplace_back(ArgsT&&...args) {
this->_push_back(fennec::forward<ArgsT>(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<typename...ArgsT>
void _push_front(ArgsT&&...args) {
node* next = _first;
_first = _alloc.allocate(1);
fennec::construct(_first, nullptr, next, fennec::forward<ArgsT>(args)...);
if (next) {
next->prev = _first;
} else {
_last = _first;
}
++_size;
}
template<typename...ArgsT>
void _push_back(ArgsT&&...args) {
node* prev = _last;
_last = _alloc.allocate(1);
fennec::construct(_last, prev, nullptr, fennec::forward<ArgsT>(args)...);
if (prev) {
prev->next = _last;
} else {
_first = _last;
}
++_size;
}
};
}
#endif // FENNEC_CONTAINERS_DEQUE_H

View File

@@ -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<element_t, alloc_t> _alloc;

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_CONTAINERS_GRAPH_H
#define FENNEC_CONTAINERS_GRAPH_H
#include <fennec/containers/dynarray.h>
#include <fennec/containers/list.h>
#include <fennec/containers/map.h>
#include <fennec/containers/object_pool.h>
#include <fennec/containers/set.h>
/*
* 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<typename NodeT, typename ConnectionT = nullptr_t>
struct graph {
public:
using weight_t = ConnectionT;
using node_t = NodeT;
using conn_map_t = dynarray<map<size_t, size_t>>;
using node_pool_t = object_pool<node_t>;
using conn_pool_t = object_pool<weight_t>;
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_t>(node));
}
constexpr size_t insert(const node_t& node) {
return this->_insert(node);
}
template<typename...ArgsT>
constexpr size_t emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(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<size_t> connections(size_t n) {
list<size_t> conns;
if (_conn_map.empty()) return conns;
for (auto it : _conn_map[n]) {
conns.push_back(it.first);
}
return conns;
}
template<typename...ArgsT>
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<ArgsT>(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<size_t> 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<typename...ArgsT>
size_t _insert(ArgsT&&...args) {
return _node_pool.emplace(fennec::forward<ArgsT>(args)...);
}
};
}
#endif // FENNEC_CONTAINERS_GRAPH_H

View File

@@ -62,6 +62,7 @@ public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<elem_t>;
using hash_t = Hash;
using set_t = set<elem_t, key_hash, node_equals, alloc_t>;
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;

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_CONTAINERS_OBJECT_POOL_H
#define FENNEC_CONTAINERS_OBJECT_POOL_H
#include <fennec/containers/dynarray.h>
#include <fennec/containers/list.h>
#include <fennec/containers/optional.h>
namespace fennec
{
///
/// \brief Struct which holds a pool of objects associated with ids
/// \tparam TypeT The value type
/// \tparam AllocT The allocator type
template<typename TypeT, typename AllocT = allocator<TypeT>>
struct object_pool {
public:
using value_t = TypeT;
using elem_t = optional<TypeT>;
using table_t = dynarray<elem_t, AllocT>;
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<value_t>(x));
}
constexpr size_t insert(const value_t& x) {
return this->_insert(x);
}
template<typename...ArgsT>
constexpr size_t emplace(ArgsT&&...args) {
return this->_insert(fennec::forward<ArgsT>(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<elem_t, AllocT> _table;
list<size_t> _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<typename...ArgsT>
size_t _insert(ArgsT&&...args) {
size_t i = _next_free();
if (i >= _table.size()) {
_table.emplace_back();
}
_table[i].emplace(fennec::forward<ArgsT>(args)...);
return i;
}
};
}
#endif // FENNEC_CONTAINERS_OBJECT_POOL_H

View File

@@ -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;

View File

@@ -21,6 +21,7 @@
// https://programming.guide/robin-hood-hashing.html
#include <fennec/containers/multiset.h>
#include <fennec/containers/optional.h>
#include <fennec/containers/set.h>
#include <fennec/lang/compare.h>
@@ -295,15 +296,15 @@ public:
///
/// \brief Move Insertion
/// \param val Value to insert
constexpr void insert(elem_t&& val) {
this->_insert(fennec::forward<elem_t>(val));
constexpr iterator insert(elem_t&& val) {
return fennec::move(this->_insert(fennec::forward<elem_t>(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<typename...ArgsT>
constexpr void emplace(ArgsT&&...args) {
this->_insert(fennec::forward<ArgsT>(args)...);
constexpr iterator emplace(ArgsT&&...args) {
return fennec::move(this->_insert(fennec::forward<ArgsT>(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<typename...ArgsT>
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<node, alloc_t> _alloc;

View File

@@ -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<align_t>()) {
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<void*>(_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<void*>(_data), data, n);
}
///
@@ -395,7 +395,7 @@ public:
, _data(nullptr)
, _capacity(0)
, _alignment(zero<align_t>()) {
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<void*>(_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<void*>(_data), old, min(_capacity, n) * sizeof(T));
fennec::memmove(static_cast<void*>(_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<void*>(_data), old, min(_capacity, n) * sizeof(T));
fennec::memmove(static_cast<void*>(_data), old, min(_capacity, n) * sizeof(T));
_alloc.deallocate(old);
_capacity = n;
}

View File

@@ -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}"

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_TEST_CONTAINERS_GRAPH_H
#define FENNEC_TEST_CONTAINERS_GRAPH_H
#include "../../test.h"
#include <fennec/containers/graph.h>
namespace fennec
{
namespace test
{
inline void fennec_test_containers_graph() {
graph<size_t> 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

View File

@@ -16,8 +16,8 @@
// along with this program. If not, see <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#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
#endif // FENNEC_TEST_CONTAINERS_LIST_H

View File

@@ -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 <https://www.gnu.org/licenses/>.
// =====================================================================================================================
#ifndef FENNEC_TEST_CONTAINERS_OBJECT_POOL_H
#define FENNEC_TEST_CONTAINERS_OBJECT_POOL_H
#include "../../test.h"
#include <fennec/containers/object_pool.h>
namespace fennec
{
namespace test
{
inline void fennec_test_containers_object_pool() {
object_pool<size_t> 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

View File

@@ -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
}