diff --git a/CMakeLists.txt b/CMakeLists.txt
index 750317e..ddfa32f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -201,6 +201,9 @@ add_library(fennec STATIC
${FENNEC_EXTRA_SOURCES}
include/fennec/containers/traversal.h
+ include/fennec/containers/graph.h
+ include/fennec/containers/deque.h
+ include/fennec/containers/object_pool.h
)
add_dependencies(fennec metaprogramming)
diff --git a/cmake/opengl.cmake b/cmake/opengl.cmake
index 2f66ffe..c7f7b42 100644
--- a/cmake/opengl.cmake
+++ b/cmake/opengl.cmake
@@ -25,6 +25,7 @@ else()
endif()
if(TARGET OpenGL::GL AND TARGET GLEW::GLEW)
+ message(STATUS "Found OpenGL: ${OPENGL_gl_LIBRARY}")
list(APPEND FENNEC_LINK_LIBRARIES OpenGL::GL GLEW::GLEW)
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_OPENGL=1)
else()
@@ -36,6 +37,7 @@ if(FENNEC_GRAPHICS_WANT_EGL)
message(FATAL_ERROR "EGL Library not found.")
endif()
+ message(STATUS "Found EGL: ${OPENGL_egl_LIBRARY}")
list(APPEND FENNEC_LINK_LIBRARIES OpenGL::EGL)
list(APPEND FENNEC_COMPILE_DEFINITIONS FENNEC_GRAPHICS_EGL=1)
list(APPEND FENNEC_EXTRA_SOURCES
diff --git a/gdb/fennec/containers.py b/gdb/fennec/containers.py
index 17e2c1f..efcb87a 100644
--- a/gdb/fennec/containers.py
+++ b/gdb/fennec/containers.py
@@ -108,6 +108,7 @@ class ListPrinter:
self.list = val
self.node = self.list['_root']
self.index = 0
+ self.table = self.list['_table']['_data']
def __iter__(self):
return self
@@ -118,8 +119,8 @@ class ListPrinter:
i = self.index
self.index = self.index + 1
- value = self.list['_table']['_data'][self.node]['value']['_val']
- self.node = self.list['_table']['_data'][self.node]['next']
+ value = self.table[self.node]['value']['_val']
+ self.node = self.table[self.node]['next']
return '[{}]'.format(i), value
@@ -137,79 +138,6 @@ class ListPrinter:
return 'array'
-# RDTREE ===============================================================================================================
-
-class RDTreePrinter:
- """Print a fennec::rdtree"""
-
- class Iterator:
- def __init__(self, tree, node, capacity):
- self.tree = tree
- self.capacity = capacity
- self.visit = deque()
-
- self.visit.append((node, 0, 0))
-
- def __iter__(self):
- return self
-
- def __next__(self):
- if len(self.visit) == 0:
- raise StopIteration
-
- node = self.visit[0][0]
- i = self.visit[0][1]
- depth = self.visit[0][2]
- self.visit.popleft()
-
- opt = self.tree[node]['value']
- value = None
- if opt['_set']:
- value = opt['_val']
-
- nnext = self.tree[node]['next']
- nprev = self.tree[node]['prev']
- nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615
- child = self.tree[node]['child']
- n_chld = self.tree[node]['num_children']
-
- index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
-
- if nnext < self.capacity:
- self.visit.appendleft((nnext, i + 1, depth))
-
- if child < self.capacity:
- self.visit.appendleft((child, 0, depth + 1))
-
- # ┌ ─ ├ └
-
- if nnext != 18446744073709551615:
- index += '├'
- else:
- index += '└'
-
- index += '─'
- index += '[{}, {}, {}, {}]'.format(i, node, depth, n_chld)
- print(index)
- if value is None:
- return index, '{ empty }'
- return index, value
-
-
- def __init__(self, val):
- self.tree = val['_table']['_data']
- self.size = val['_size']
- self.capacity = val['_table']['_capacity']
-
- def to_string(self):
- if self.size == 0:
- return "{ empty }"
- return "{ size = " + str(self.size) + " }"
-
- def children(self):
- return self.Iterator(self.tree, 0, self.capacity)
-
-
# SET ==================================================================================================================
class SetPrinter:
@@ -310,20 +238,204 @@ class MapPrinter:
return 'map'
+# OBJECT_POOL ==========================================================================================================
+class ObjectPoolPrinter:
+ """Print a fennec::object_pool"""
+
+ class Iterator:
+ def __init__(self, val):
+ self.list = val
+ self.index = 0
+ self.capacity = self.list['_table']['_alloc']['_capacity']
+ self.table = self.list['_table']['_alloc']['_data']
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+
+ i = self.index
+ while True:
+ i = self.index
+ self.index = self.index + 1
+
+ if self.index >= self.capacity:
+ raise StopIteration
+
+ if bool(self.table[i]['_set']):
+ value = self.table[i]['_val']
+ break
+
+ return '[{}]'.format(i), value
+
+
+
+ def __init__(self, val):
+ self.val = val
+
+ def to_string(self):
+ return "{ length " + str(self.val['_size']) + ", capacity " + str(self.val['_table']['_alloc']['_capacity']) + " }"
+
+ def children(self):
+ return self.Iterator(self.val)
+
+ def display_hint(self):
+ return 'array'
+
+
+# RDTREE ===============================================================================================================
+
+class RDTreePrinter:
+ """Print a fennec::rdtree"""
+
+ class Iterator:
+ def __init__(self, tree, node, capacity):
+ self.tree = tree
+ self.capacity = capacity
+ self.visit = deque()
+
+ self.visit.append((node, 0, 0))
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if len(self.visit) == 0:
+ raise StopIteration
+
+ node = self.visit[0][0]
+ i = self.visit[0][1]
+ depth = self.visit[0][2]
+ self.visit.popleft()
+
+ value = self.tree[node]['value']
+
+ nnext = self.tree[node]['next']
+ nprev = self.tree[node]['prev']
+ nprevc = self.tree[nprev]['child'] if nprev != 18446744073709551615 else 18446744073709551615
+ child = self.tree[node]['child']
+ n_chld = self.tree[node]['num_children']
+
+ index = '⠀' * depth * 2 # Uses Braille Space, otherwise it would get eaten as whitespace by parsers
+
+ if nnext < self.capacity:
+ self.visit.appendleft((nnext, i + 1, depth))
+
+ if child < self.capacity:
+ self.visit.appendleft((child, 0, depth + 1))
+
+ # ┌ ─ ├ └
+
+ if nnext != 18446744073709551615:
+ index += '├'
+ else:
+ index += '└'
+
+ index += '─'
+ index += '[{}, {}]'.format(node, i)
+ return index, value
+
+
+ def __init__(self, val):
+ self.tree = val['_table']['_data']
+ self.size = val['_size']
+ self.capacity = val['_table']['_capacity']
+
+ def to_string(self):
+ if self.size == 0:
+ return "{ empty }"
+ return "{ size = " + str(self.size) + " }"
+
+ def children(self):
+ return self.Iterator(self.tree, 0, self.capacity)
+
+
+# Graph ================================================================================================================
+
+class GraphPrinter:
+ """Print a fennec::graph"""
+
+ class Iterator:
+ def __init__(self, val):
+ self.node_pool = val['_node_pool']['_table']['_alloc']['_data']
+ self.max_nodes = val['_node_pool']['_table']['_alloc']['_capacity']
+ self.conn_map = val['_conn_map']['_alloc']['_data']
+ self.max_conn = val['_conn_map']['_size']
+ self.index = 0
+
+ def __iter__(self):
+ return self
+
+ def __next__(self):
+ if self.index >= self.max_nodes:
+ raise StopIteration
+
+ i = self.index
+ self.index = self.index + 1
+
+ while not bool(self.node_pool[i]['_set']):
+ i = self.index
+ self.index = self.index + 1
+
+ conns = self.get_conns(i)
+ value = self.node_pool[i]['_val']
+
+ return '[{} -> {{{}}}]'.format(i, str(conns)), value
+
+
+
+ def get_conns(self, index):
+ indices = []
+
+ if index >= self.max_conn:
+ return indices
+
+ map = self.conn_map[index]['_set']
+ max_conns = map['_alloc']['_capacity']
+ conns = map['_alloc']['_data']
+
+ print(max_conns)
+
+ if max_conns == 0:
+ return indices
+
+ for i in range(0, max_conns):
+ if bool(conns[i]['value']['_set']):
+ conn = conns[i]['value']['_val']
+ indices.append(str(int(conn['first'])))
+
+ return indices
+
+ def __init__(self, val):
+ self.val = val
+ self.size = val['_node_pool']['_size']
+
+ def to_string(self):
+ if self.size == 0:
+ return "{ empty }"
+ return "{ size = " + str(self.size) + " }"
+
+ def children(self):
+ return self.Iterator(self.val)
+
+
+
# GDB Code =============================================================================================================
def register_printers():
print("registering containers")
pp = gdb.printing.RegexpCollectionPrettyPrinter("fennec::containers")
- pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter)
- pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
- pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
- pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter)
- pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter)
- pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter)
- pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
- pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
- pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter)
+ pp.add_printer('fennec::array', '^fennec::array<.*>$', ArrayPrinter)
+ pp.add_printer('fennec::dynarray', '^fennec::dynarray<.*>$', DynArrayPrinter)
+ pp.add_printer('fennec::graph', '^fennec::graph<.*>$', GraphPrinter)
+ pp.add_printer('fennec::list', '^fennec::list<.*>$', ListPrinter)
+ pp.add_printer('fennec::map', '^fennec::map<.*>$', MapPrinter)
+ pp.add_printer('fennec::object_pool', '^fennec::object_pool<.*>$', ObjectPoolPrinter)
+ pp.add_printer('fennec::optional', '^fennec::optional<.*>$', OptionalPrinter)
+ pp.add_printer('fennec::pair', '^fennec::pair<.*>$', PairPrinter)
+ pp.add_printer('fennec::set', '^fennec::set<.*>$', SetPrinter)
+ pp.add_printer('fennec::rdtree', '^fennec::rdtree<.*>$', RDTreePrinter)
+ pp.add_printer('fennec::tuple', '^fennec::tuple<.*>$', TuplePrinter)
return pp
printer = register_printers()
diff --git a/include/fennec/containers/deque.h b/include/fennec/containers/deque.h
new file mode 100644
index 0000000..83e6e28
--- /dev/null
+++ b/include/fennec/containers/deque.h
@@ -0,0 +1,199 @@
+// =====================================================================================================================
+// fennec, a free and open source game engine
+// Copyright © 2025 Medusa Slockbower
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+// =====================================================================================================================
+
+#ifndef FENNEC_CONTAINERS_DEQUE_H
+#define FENNEC_CONTAINERS_DEQUE_H
+
+#include
+
+// TODO: Document
+
+namespace fennec
+{
+
+template>
+struct deque {
+
+// Definitions =========================================================================================================
+public:
+ using elem_t = TypeT;
+
+ struct node {
+ elem_t value;
+ node *prev, *next;
+
+ template
+ node(node* prev, node* next, ArgsT&&...args)
+ : value(fennec::forward(args)...)
+ , prev(prev), next(next) {
+ }
+
+ ~node() = default;
+ };
+
+ using alloc_t = allocator_traits::template rebind;
+
+
+// Constructors ========================================================================================================
+
+ deque()
+ : _alloc()
+ , _first(nullptr)
+ , _last(nullptr) {
+ }
+
+ deque(deque&& deque) noexcept
+ : _alloc(deque._alloc)
+ , _first(deque._first)
+ , _last(deque._last) {
+ deque._first = nullptr;
+ deque._last = nullptr;
+ }
+
+ ~deque() {
+ clear();
+ }
+
+// Properties ==========================================================================================================
+
+ bool empty() const {
+ return _size == 0;
+ }
+
+ size_t size() const {
+ return _size;
+ }
+
+
+// Access ==============================================================================================================
+
+ elem_t& front() {
+ assert(not empty(), "Attempted to access an empty deque.");
+ return _first->value;
+ }
+
+ elem_t& back() {
+ assert(not empty(), "Attempted to access an empty deque.");
+ return _last->value;
+ }
+
+
+// Insertion ===========================================================================================================
+
+ void push_front(elem_t&& elem) {
+ this->_push_front(elem);
+ }
+
+ void push_front(const elem_t& elem) {
+ this->_push_front(elem);
+ }
+
+ template
+ void emplace_front(ArgsT&&...args) {
+ this->_push_front(fennec::forward(args)...);
+ }
+
+ void push_back(elem_t&& elem) {
+ this->_push_back(elem);
+ }
+
+ void push_back(const elem_t& elem) {
+ this->_push_back(elem);
+ }
+
+ template
+ void emplace_back(ArgsT&&...args) {
+ this->_push_back(fennec::forward(args)...);
+ }
+
+
+// Deletion ============================================================================================================
+
+ void clear() {
+ node* it = _first;
+ while (it) {
+ node* next = it->next;
+ fennec::destruct(it);
+ _alloc.deallocate(it);
+ it = next;
+ }
+ _first = nullptr;
+ _last = nullptr;
+ _size = 0;
+ }
+
+ void pop_front() {
+ if (_first == nullptr) {
+ return;
+ }
+ node* next = _first->next;
+ fennec::destruct(_first);
+ _alloc.deallocate(_first);
+ _first = next;
+ _last = next ? _last : nullptr;
+ --_size;
+ }
+
+ void pop_back() {
+ if (_last == nullptr) {
+ return;
+ }
+ node* prev = _last->prev;
+ fennec::destruct(_last);
+ _alloc.deallocate(_last);
+ _last = prev;
+ _first = prev ? _first : nullptr;
+ --_size;
+ }
+
+private:
+ alloc_t _alloc;
+ node *_first, *_last;
+ size_t _size;
+
+ template
+ void _push_front(ArgsT&&...args) {
+ node* next = _first;
+ _first = _alloc.allocate(1);
+ fennec::construct(_first, nullptr, next, fennec::forward(args)...);
+ if (next) {
+ next->prev = _first;
+ } else {
+ _last = _first;
+ }
+ ++_size;
+ }
+
+ template
+ void _push_back(ArgsT&&...args) {
+ node* prev = _last;
+ _last = _alloc.allocate(1);
+ fennec::construct(_last, prev, nullptr, fennec::forward(args)...);
+ if (prev) {
+ prev->next = _last;
+ } else {
+ _first = _last;
+ }
+ ++_size;
+ }
+};
+
+
+}
+
+#endif // FENNEC_CONTAINERS_DEQUE_H
\ No newline at end of file
diff --git a/include/fennec/containers/dynarray.h b/include/fennec/containers/dynarray.h
index 0a9cfdf..652e609 100644
--- a/include/fennec/containers/dynarray.h
+++ b/include/fennec/containers/dynarray.h
@@ -357,7 +357,7 @@ public:
/// \brief Resize the dynarray, invoking the default constructor for all new elements
/// \param n The new size in elements
constexpr void resize(size_t n) {
- _alloc.reallocate(n);
+ _alloc.creallocate(n);
while (_size < n) {
emplace_back();
@@ -366,7 +366,7 @@ public:
private:
constexpr void _grow() {
- _alloc.reallocate(_alloc.capacity() * 2);
+ _alloc.creallocate(_alloc.capacity() * 2);
}
allocation _alloc;
diff --git a/include/fennec/containers/graph.h b/include/fennec/containers/graph.h
new file mode 100644
index 0000000..e083830
--- /dev/null
+++ b/include/fennec/containers/graph.h
@@ -0,0 +1,167 @@
+// =====================================================================================================================
+// fennec, a free and open source game engine
+// Copyright © 2025 Medusa Slockbower
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+// =====================================================================================================================
+
+#ifndef FENNEC_CONTAINERS_GRAPH_H
+#define FENNEC_CONTAINERS_GRAPH_H
+
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * With the directed tree we were able to cheat a little, the structure has more rules to it which allows
+ * tighter constraints. A graph is basically no rules whatsoever. Some variants, such as weighted graphs, assign
+ * properties or rules to connections which can simply be an extension to this graph.
+ *
+ * The most effective way to do this is to have a dynarray of lists, however this results in double the
+ * memory being used. This can also result in two connection objects being created.
+ *
+ * There is no nice way to avoid the problem of mapping pins to connections
+ */
+
+namespace fennec
+{
+
+template
+struct graph {
+public:
+ using weight_t = ConnectionT;
+ using node_t = NodeT;
+ using conn_map_t = dynarray