List Data Structure

This commit is contained in:
2025-07-23 13:26:50 -04:00
parent f9de242b87
commit 7ea2710ee0
8 changed files with 267 additions and 33 deletions

View File

@@ -175,6 +175,7 @@ add_library(fennec STATIC
include/fennec/containers/tuple.h
include/fennec/containers/detail/__tuple.h
include/fennec/platform/linux/wayland/window.h
include/fennec/containers/list.h
)
add_dependencies(fennec metaprogramming)

View File

@@ -48,7 +48,7 @@ PROJECT_NAME = fennec
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER =
PROJECT_NUMBER = 1.0.2
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a

View File

@@ -47,12 +47,12 @@ public:
///
/// \brief Default Constructor, initializes an empty allocation.
dynarray() : _alloc(8), _size(0) {}
constexpr dynarray() : _alloc(8), _size(0) {}
///
/// \brief Alloc Constructor, initialize empty allocation with allocator instance.
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some data.
dynarray(const alloc_t& alloc)
constexpr dynarray(const alloc_t& alloc)
: _alloc(8, alloc)
, _size(0) {
@@ -60,7 +60,7 @@ public:
///
/// \brief Sized Allocation, create an allocation with a size of `n` elements, initialized with the default constructor.
dynarray(size_t n)
constexpr dynarray(size_t n)
: _alloc(n)
, _size(n)
{
@@ -74,7 +74,7 @@ public:
/// \brief
/// \param n
/// \param alloc
dynarray(size_t n, const alloc_t& alloc)
constexpr dynarray(size_t n, const alloc_t& alloc)
: _alloc(n, alloc)
, _size(n) {
element_t* addr = _alloc.data();
@@ -86,7 +86,7 @@ public:
///
/// \brief Create an allocation of size `n`, with each element constructed using the copy constructor
/// \brief n the number of elements
dynarray(size_t n, const TypeT& val)
constexpr dynarray(size_t n, const TypeT& val)
: _alloc(n)
, _size(n) {
element_t* addr = _alloc.data();
@@ -96,37 +96,49 @@ public:
}
template<typename...ArgsT>
dynarray(size_t n, ArgsT&&...args) {
constexpr dynarray(size_t n, ArgsT&&...args) {
element_t* addr = _alloc.data();
for(; n > 0; --n, ++addr) {
fennec::construct(addr, fennec::forward<ArgsT>(args)...);
}
}
~dynarray() {
constexpr ~dynarray() {
element_t* addr = _alloc.data();
for(int n = _size; n > 0; --n, ++addr) {
fennec::destruct(addr);
}
}
size_t size() const {
constexpr size_t size() const {
return _size;
}
size_t capacity() const {
constexpr size_t capacity() const {
return _alloc.capacity();
}
TypeT& operator[](size_t i) {
constexpr bool empty() const {
return _size == 0;
}
constexpr TypeT& operator[](size_t i) {
assertd(i < _size, "Array Out of Bounds"); return _alloc.data()[i];
}
const TypeT& operator[](size_t i) const {
constexpr const TypeT& operator[](size_t i) const {
assertd(i < _size, "Array Out of Bounds"); return _alloc.data()[i];
}
void insert(size_t i, const TypeT& val) {
constexpr TypeT& back() {
return this->operator[](size() - 1);
}
constexpr const TypeT& back() const {
return this->operator[](size() - 1);
}
constexpr void insert(size_t i, const TypeT& val) {
// Grow if the size has reached the capacity of the allocation
if(_size == capacity()) {
@@ -146,7 +158,7 @@ public:
++_size;
}
void insert(size_t i, TypeT&& val) {
constexpr void insert(size_t i, TypeT&& val) {
// Grow if the size has reached the capacity of the allocation
if(_size == capacity()) {
@@ -167,7 +179,7 @@ public:
}
template<typename...ArgsT>
void emplace(size_t i, ArgsT&&...args) {
constexpr void emplace(size_t i, ArgsT&&...args) {
// Grow if the size has reached the capacity of the allocation
if(_size == capacity()) {
@@ -187,24 +199,29 @@ public:
++_size;
}
void push_back(const TypeT& val) {
constexpr void push_back(const TypeT& val) {
dynarray::insert(_size, val);
}
void push_back(TypeT&& val) {
constexpr void push_back(TypeT&& val) {
dynarray::insert(_size, fennec::forward<TypeT>(val));
}
template<typename...ArgsT> void emplace_back(ArgsT...args) {
constexpr void pop_back() {
_alloc[_size--].~TypeT();
}
template<typename...ArgsT>
constexpr void emplace_back(ArgsT...args) {
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
}
void resize(size_t n) {
constexpr void resize(size_t n) {
_alloc.reallocate(n);
}
private:
void _grow() {
constexpr void _grow() {
_alloc.reallocate(_alloc.capacity() * 2);
}

View File

@@ -0,0 +1,205 @@
// =====================================================================================================================
// 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_LIST_H
#define FENNEC_CONTAINERS_LIST_H
#include <fennec/containers/dynarray.h>
#include <fennec/containers/optional.h>
#include <fennec/memory/allocator.h>
namespace fennec
{
template<class TypeT, class Alloc = allocator<TypeT>>
struct list {
public:
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
using value_t = TypeT;
static constexpr size_t npos = -1;
private:
struct node {
optional<value_t> data;
size_t prev, next;
constexpr node()
: data()
, prev(npos)
, next(npos) {
}
constexpr node(size_t p, size_t n)
: data()
, prev(p)
, next(n) {
}
constexpr node(size_t p, size_t n, value_t&& val)
: data(fennec::forward<value_t>(val))
, prev(p)
, next(n) {
}
constexpr ~node() = default;
constexpr void clear() {
data = nullopt;
prev = npos;
next = npos;
}
};
size_t _next(size_t n) {
return _table[n].next;
}
size_t _prev(size_t n) {
return _table[n].prev;
}
size_t _walk(size_t i) {
size_t n = _root;
if (n == npos) return n;
while (i > 0 && n != npos) {
n = _next(n); --i;
}
return n;
}
size_t _next_free(size_t i) {
if (not _freed.empty()) {
size_t n = _freed.back();
_freed.pop_back();
return n;
}
_table[_size];
}
public:
using elem_t = node;
constexpr list()
: _table(), _root(npos), _last(npos), _freed(), _size(0) {
}
constexpr ~list() = default;
constexpr bool size() const { return _size; }
constexpr bool capacity() const { return _table.capacity(); }
constexpr bool empty() const { return _root == npos; }
constexpr value_t& operator[](int i) {
assertd(i >= 0, "Index out of Bounds");
size_t n = _walk(i);
assertd(n != npos, "Index out of Bounds");
return _table[n].data;
}
constexpr const value_t& operator[](int i) const {
assertd(i >= 0, "Index out of Bounds");
size_t n = _walk(i);
assertd(n != npos, "Index out of Bounds");
return _table[n].data;
}
void insert(size_t i, value_t&& x) {
assert(i <= size(), "Index out of Bounds");
if (size() == capacity()) {
_expand();
}
size_t n = _walk(min(i, size() - 1));
size_t p = _next_free(i);
if (n == npos) {
_root = p;
_table[p].data = fennec::forward<value_t>(x);
return;
}
_table[p].data = fennec::forward<value_t>(x);
_table[p].next = n;
_table[p].prev = _prev(p);
_table[n].prev = p;
++_size;
_last = (n == npos) ? p : _last;
}
void insert(size_t i, const value_t& x) {
this->insert(i, elem_t(x));
}
void push_front(const value_t& x) {
this->insert(0, x);
}
void push_front(value_t&& x) {
this->insert(0, fennec::forward<value_t>(x));
}
void push_back(const value_t& x) {
this->insert(_size, x);
}
void push_back(value_t&& x) {
this->insert(_size, fennec::forward<value_t>(x));
}
void erase(size_t i) {
size_t j = _walk(i);
if (j == npos) return;
if (not _table[j].data) return;
// Get the prev and next indices
size_t p = _prev(j);
size_t n = _next(j);
// clear the node
_table[j].data = nullopt;
_table[j].prev = npos;
_table[j].next = npos;
// Fix prev and next nodes
if (p != npos) _table[p].next = n;
if (n != npos) _table[n].prev = p;
else _last = j;
// Mark node as freed
_freed.push_back(j);
}
void pop_front() {
erase(0);
}
void pop_back() {
erase(_size - 1);
}
private:
allocation<elem_t, alloc_t> _table;
dynarray<size_t> _freed;
size_t _root, _last, _size;
constexpr void _expand() {
_table.reallocate(_table.capacity() * 2);
}
};
}
#endif // FENNEC_CONTAINERS_LIST_H

View File

@@ -52,30 +52,30 @@ struct pair {
constexpr pair(const pair&) = default;
constexpr pair(pair&&) noexcept = default;
pair& operator=(const pair&) = default;
pair& operator=(pair&&) noexcept = default;
constexpr pair& operator=(const pair&) = default;
constexpr pair& operator=(pair&&) noexcept = default;
bool operator==(const pair& p) const {
constexpr bool operator==(const pair& p) const {
return first == p.first and second == p.second;
}
bool operator!=(const pair& p) const {
constexpr bool operator!=(const pair& p) const {
return first != p.first or second != p.second;
}
bool operator<(const pair& p) const {
constexpr bool operator<(const pair& p) const {
return first < p.first or (first == p.first and second < p.second);
}
bool operator<=(const pair& p) const {
constexpr bool operator<=(const pair& p) const {
return first < p.first or (first == p.first and second <= p.second);
}
bool operator>(const pair& p) const {
constexpr bool operator>(const pair& p) const {
return first > p.first or (first == p.first and second > p.second);
}
bool operator>=(const pair& p) const {
constexpr bool operator>=(const pair& p) const {
return first > p.first or (first == p.first and second >= p.second);
}

View File

@@ -152,7 +152,7 @@ public:
if (_equal(*_alloc[i].value, val)) { // Check to see if this element is already inserted
return;
}
if (psl >= _alloc[i].psl) { // When psl is higher, swap
if (psl > _alloc[i].psl) { // When psl is higher, swap
fennec::swap(*_alloc[i].value, value);
fennec::swap(_alloc[i].psl, psl);
}

View File

@@ -31,14 +31,14 @@ template<typename...TypesT> struct tuple;
template<size_t i, typename...TypesT>
typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
constexpr typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
auto it = static_cast<detail::__tuple_leaf<i, elem_t>>(x);
auto& it = static_cast<detail::__tuple_leaf<i, elem_t>>(x);
return it;
}
template<size_t i, typename...TypesT>
const typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
constexpr const typename tuple<TypesT...>::template elem_t<i>& get(tuple<TypesT...>& x) {
using elem_t = typename tuple<TypesT...>::template elem_t<i>;
auto& it = static_cast<detail::__tuple_leaf<i, elem_t>>(x);
return it;
@@ -55,7 +55,7 @@ public:
using elem_t = nth_element<i, TypesT...>;
template<typename...ArgsT>
tuple(ArgsT&&...args) : base_t(args...) {
constexpr tuple(ArgsT&&...args) : base_t(args...) {
}
};

View File

@@ -19,4 +19,15 @@
#ifndef FENNEC_PLATFORM_LINUX_WAYLAND_WINDOW_H
#define FENNEC_PLATFORM_LINUX_WAYLAND_WINDOW_H
namespace fennec
{
class wayland_window {
public:
private:
};
}
#endif // FENNEC_PLATFORM_LINUX_WAYLAND_WINDOW_H