- Framework for Vulkan context - Fixed a bug with dynarray where if `resize()` shrinks the array, destructors are not called. - Fixed grammar issues with the containers library and added property tables to existing data structures.
628 lines
17 KiB
C++
628 lines
17 KiB
C++
// =====================================================================================================================
|
|
// 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 dynarray.h
|
|
/// \brief A header containing the definition for a dynamically allocated array
|
|
///
|
|
///
|
|
/// \details
|
|
/// \author Medusa Slockbower
|
|
///
|
|
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
|
///
|
|
///
|
|
|
|
#ifndef FENNEC_CONTAINERS_DYNARRAY_H
|
|
#define FENNEC_CONTAINERS_DYNARRAY_H
|
|
|
|
#include <fennec/containers/initializer_list.h>
|
|
#include <fennec/lang/utility.h>
|
|
#include <fennec/memory/allocator.h>
|
|
#include <fennec/memory/new.h>
|
|
|
|
namespace fennec
|
|
{
|
|
|
|
///
|
|
///
|
|
/// \brief Wrapper for dynamically sized and allocated arrays
|
|
/// \details
|
|
/// | Property | Value |
|
|
/// |:-----------:|:----------:|
|
|
/// | stable | ⛔ |
|
|
/// | dynamic | ✅ |
|
|
/// | homogeneous | ✅ |
|
|
/// | distinct | ⛔ |
|
|
/// | ordered | ⛔ |
|
|
/// | space | \f$O(N)\f$ |
|
|
/// | linear | ✅ |
|
|
/// | access | \f$O(1)\f$ |
|
|
/// | find | \f$O(N)\f$ |
|
|
/// | insertion | \f$O(N)\f$ |
|
|
/// | deletion | \f$O(N)\f$ |
|
|
///
|
|
/// This structure prefers shallow moves and deep copies.
|
|
///
|
|
/// \tparam TypeT value type
|
|
template<class TypeT, class Alloc = allocator<TypeT>>
|
|
struct dynarray {
|
|
|
|
// Definitions =========================================================================================================
|
|
public:
|
|
using value_t = TypeT; ///< Alias for the value type
|
|
using alloc_t = Alloc; ///< Alias for the allocator type
|
|
|
|
|
|
// Constructors ========================================================================================================
|
|
public:
|
|
|
|
/// \name Constructors & Destructor
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Default Constructor, initializes an empty allocation.
|
|
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.
|
|
explicit constexpr dynarray(const alloc_t& alloc)
|
|
: _alloc(8, alloc)
|
|
, _size(0) {
|
|
}
|
|
|
|
///
|
|
/// \brief Alloc Move 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.
|
|
explicit constexpr dynarray(alloc_t&& alloc) noexcept
|
|
: _alloc(8, alloc)
|
|
, _size(0) {
|
|
}
|
|
|
|
///
|
|
/// \brief Sized Allocation, initializes a dynarray with \f$n\f$ elements using the default constructor.
|
|
/// \param n The number of elements.
|
|
explicit constexpr dynarray(size_t n)
|
|
: _alloc(n)
|
|
, _size(n)
|
|
{
|
|
value_t* addr = _alloc.data();
|
|
for(; n > 0; --n, ++addr) {
|
|
fennec::construct(addr);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Sized Allocation Alloc Constructor, initializes a dynarray with allocator \f$alloc\f$ and \f$n\f$ elements
|
|
/// using the default constructor.
|
|
/// \param n The number of elements
|
|
/// \param alloc The allocator object to copy
|
|
constexpr dynarray(size_t n, const alloc_t& alloc)
|
|
: _alloc(n, alloc)
|
|
, _size(n) {
|
|
value_t* addr = _alloc.data();
|
|
for(; n > 0; --n, ++addr) {
|
|
fennec::construct(addr);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Sized Allocation Alloc Move Constructor, initializes a dynarray with allocator \f$alloc\f$ and \f$n\f$ elements
|
|
/// using the default constructor.
|
|
/// \param n The number of elements
|
|
/// \param alloc The allocator object to copy
|
|
constexpr dynarray(size_t n, alloc_t&& alloc)
|
|
: _alloc(n, alloc)
|
|
, _size(n) {
|
|
value_t* addr = _alloc.data();
|
|
for(; n > 0; --n, ++addr) {
|
|
fennec::construct(addr);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Sized Allocation Copy Constructor, Create an allocation of size \f$n\f$ elements, with each element
|
|
/// constructed using the copy constructor
|
|
/// \param n the number of elements
|
|
/// \param val the value to copy
|
|
constexpr dynarray(size_t n, const TypeT& val)
|
|
: _alloc(n)
|
|
, _size(n) {
|
|
value_t* addr = _alloc.data();
|
|
for(; n > 0; --n, ++addr) {
|
|
fennec::construct(addr, val);
|
|
}
|
|
}
|
|
|
|
// This constructor should not be invokable since moving is a single object operation and will cause undefined
|
|
// behaviour when moving to multiple elements
|
|
constexpr dynarray(size_t n, TypeT&& val) = delete;
|
|
|
|
///
|
|
/// \brief Emplace Constructor
|
|
/// \tparam ArgsT A sequence of argument types
|
|
/// \param n The number of objects to create
|
|
/// \param args The arguments to create each object with
|
|
template<typename...ArgsT>
|
|
constexpr explicit dynarray(size_t n, ArgsT&&...args)
|
|
: _alloc(n)
|
|
, _size(n) {
|
|
for(; n > 0; --n) {
|
|
fennec::construct(&_alloc[n], fennec::forward<ArgsT>(args)...);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Array Copy Constructor
|
|
/// \tparam N The length of the array, automatically deduced
|
|
/// \param arr The array to copy
|
|
template<size_t N>
|
|
constexpr dynarray(const TypeT (&arr)[N])
|
|
: _alloc(N)
|
|
, _size(N) {
|
|
for (size_t i = 0; i < N; ++i) {
|
|
_alloc[i] = arr[i];
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Array Move Constructor
|
|
/// \tparam N The length of the array, automatically deduced
|
|
/// \param arr The array to move
|
|
template<size_t N>
|
|
constexpr dynarray(TypeT (&&arr)[N])
|
|
: _alloc(N)
|
|
, _size(N) {
|
|
for (size_t i = 0; i < N; ++i) {
|
|
_alloc[i] = fennec::move(arr[i]);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Conversion Constructor, copies elements of conv as this `value_t`
|
|
/// \tparam OTypeT The other value type
|
|
/// \tparam OAlloc The other allocator type
|
|
/// \param conv The dynarray to convert
|
|
template<typename OTypeT, class OAlloc>
|
|
constexpr dynarray(const dynarray<OTypeT, OAlloc>& conv)
|
|
: _alloc(conv.size())
|
|
, _size(conv.size()) {
|
|
size_t i = 0;
|
|
for (const auto& it : conv) {
|
|
fennec::construct(&_alloc[i++], it);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Initializer List Constructor
|
|
/// \param l List of elements to initialize with
|
|
/// \param alloc An allocator object to copy
|
|
constexpr dynarray(initializer_list<value_t> l, const alloc_t& alloc = alloc_t())
|
|
: _alloc(l.size(), alloc)
|
|
, _size(l.size()) {
|
|
size_t i = 0;
|
|
for (auto& it : l) {
|
|
fennec::construct(&_alloc[i++], fennec::move(it));
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Constructor, uses the copy constructor to copy each element
|
|
/// \param arr the dynarray to copy
|
|
constexpr dynarray(const dynarray& arr)
|
|
: _alloc(arr._size)
|
|
, _size(arr._size) {
|
|
for (size_t i = 0; i < _size; ++i) {
|
|
fennec::construct(&_alloc[i], arr[i]);
|
|
}
|
|
}
|
|
|
|
///
|
|
/// \brief Move Constructor, takes ownership of the allocation
|
|
/// \param arr the dynarray to move
|
|
constexpr dynarray(dynarray&& arr) noexcept
|
|
: _alloc(fennec::move(arr._alloc))
|
|
, _size(arr._size) {
|
|
arr._size = 0;
|
|
}
|
|
|
|
///
|
|
/// \brief Default Destructor, destructs all elements and frees the underlying allocation
|
|
constexpr ~dynarray() {
|
|
value_t* addr = _alloc.data();
|
|
if (addr == nullptr) return;
|
|
for(int n = _size; n > 0; --n, ++addr) {
|
|
fennec::destruct(addr);
|
|
}
|
|
}
|
|
|
|
/// @}
|
|
|
|
// Assignment ==========================================================================================================
|
|
public:
|
|
|
|
/// \name Properties
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Copy Assignment Operator
|
|
/// \param arr the array to copy
|
|
/// \returns A dynarray after having copied each element of \f$arr\f$
|
|
constexpr dynarray& operator=(const dynarray& arr) {
|
|
this->clear();
|
|
_alloc.creallocate(_size = arr._size);
|
|
for (size_t i = 0; i < _size; ++i) {
|
|
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief Move Assignment Operator
|
|
/// \param arr the array to move
|
|
/// \returns A dynarray after having taken ownership of the contents of \f$arr\f$
|
|
constexpr dynarray& operator=(dynarray&& arr) noexcept {
|
|
this->clear();
|
|
_alloc = fennec::move(arr._alloc);
|
|
_size = arr._size;
|
|
arr._size = 0;
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief Array Copy Assignment Operator
|
|
/// \tparam N the length of the array
|
|
/// \param arr the array to copy
|
|
/// \returns A dynarray after having copied each element of \f$arr\f$
|
|
template<size_t N>
|
|
constexpr dynarray& operator=(const TypeT (&arr)[N]) {
|
|
this->clear();
|
|
_alloc.creallocate(_size = N);
|
|
for (size_t i = 0; i < _size; ++i) {
|
|
fennec::construct(&_alloc[i], fennec::copy(arr[i]));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
///
|
|
/// \brief Array Copy Assignment Operator
|
|
/// \tparam N the length of the array
|
|
/// \param arr the array to copy
|
|
/// \returns A dynarray after having moved each element of \f$arr\f$
|
|
template<size_t N>
|
|
constexpr dynarray& operator=(TypeT (&&arr)[N]) {
|
|
this->clear();
|
|
_alloc.creallocate(_size = N);
|
|
for (size_t i = 0; i < _size; ++i) {
|
|
fennec::construct(&_alloc[i], fennec::move(arr[i]));
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
/// @}
|
|
|
|
// Properties ==========================================================================================================
|
|
public:
|
|
|
|
/// \name Properties
|
|
/// @{
|
|
|
|
///
|
|
/// \returns The size of the dynarray in elements
|
|
constexpr size_t size() const {
|
|
return _size;
|
|
}
|
|
|
|
///
|
|
/// \returns The current capacity, in elements, of the underlying allocation
|
|
constexpr size_t capacity() const {
|
|
return _alloc.capacity();
|
|
}
|
|
|
|
///
|
|
/// \returns True when there are no elements active, otherwise false
|
|
constexpr bool is_empty() const {
|
|
return _size == 0;
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Element Access ======================================================================================================
|
|
public:
|
|
|
|
/// \name Access
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Array Access Operator
|
|
/// \param i The index to access
|
|
/// \returns A reference to the element at index \f$i\f$
|
|
constexpr TypeT& operator[](size_t i) {
|
|
assertd(i < _size, "Array Out of Bounds");
|
|
return _alloc[i];
|
|
}
|
|
|
|
///
|
|
/// \brief Array Access Operator (const)
|
|
/// \param i The index to access
|
|
/// \returns A const qualified reference to the element at index \f$i\f$
|
|
constexpr const TypeT& operator[](size_t i) const {
|
|
assertd(i < _size, "Array Out of Bounds");
|
|
return _alloc[i];
|
|
}
|
|
|
|
///
|
|
/// \returns Reference to the first element in the dynarray
|
|
constexpr TypeT& front() {
|
|
return this->operator[](0);
|
|
}
|
|
|
|
///
|
|
/// \returns A const-qualified reference to the first element in the dynarray
|
|
constexpr const TypeT& front() const {
|
|
return this->operator[](0);
|
|
}
|
|
|
|
///
|
|
/// \returns A reference to the last element in the dynarray
|
|
constexpr TypeT& back() {
|
|
return this->operator[](size() - 1);
|
|
}
|
|
|
|
///
|
|
/// \returns A const-qualified reference to the last element in the dynarray
|
|
constexpr const TypeT& back() const {
|
|
return this->operator[](size() - 1);
|
|
}
|
|
|
|
///
|
|
/// \returns A pointer to the underlying allocation
|
|
constexpr TypeT* data() {
|
|
return _alloc.data();
|
|
}
|
|
|
|
///
|
|
/// \returns A pointer to the underlying allocation
|
|
constexpr const TypeT* data() const {
|
|
return _alloc.data();
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Modifiers ===========================================================================================================
|
|
public:
|
|
|
|
/// \name Modifiers
|
|
/// @{
|
|
|
|
///
|
|
/// \brief Move Insertion
|
|
/// \param i index to insert at
|
|
/// \param val the value to initialize with
|
|
constexpr void insert(size_t i, TypeT&& val) {
|
|
|
|
// Grow if the size has reached the capacity of the allocation
|
|
if(_size == capacity()) {
|
|
_grow();
|
|
}
|
|
|
|
// Move the data if we are not inserting at the end of the array
|
|
if((i = min(i, _size)) < _size) {
|
|
fennec::memmove(
|
|
(void*)(_alloc.data() + i + 1)
|
|
, (void*)(_alloc.data() + i)
|
|
, (_size - i) * sizeof(TypeT));
|
|
}
|
|
|
|
// Insert the element
|
|
fennec::construct(_alloc.data() + i, fennec::forward<TypeT>(val));
|
|
++_size;
|
|
}
|
|
|
|
///
|
|
/// \brief Copy Insertion
|
|
/// \param i index to insert at
|
|
/// \param val the value to initialize with
|
|
constexpr void insert(size_t i, const TypeT& val) {
|
|
|
|
// Grow if the size has reached the capacity of the allocation
|
|
if(_size == capacity()) {
|
|
_grow();
|
|
}
|
|
|
|
// Move the data if we are not inserting at the end of the array
|
|
if((i = min(i, _size)) < _size) {
|
|
fennec::memmove(
|
|
(void*)(_alloc.data() + i),
|
|
(void*)(_alloc.data() + i + 1),
|
|
(_size - i) * sizeof(TypeT)
|
|
);
|
|
}
|
|
|
|
// Insert the element
|
|
fennec::construct(_alloc.data() + i, val);
|
|
++_size;
|
|
}
|
|
|
|
///
|
|
/// \brief Emplace Insertion
|
|
/// \param i index to insert at
|
|
/// \param args Arguments to construct with
|
|
/// \tparam ArgsT Argument types
|
|
template<typename...ArgsT>
|
|
constexpr void emplace(size_t i, ArgsT&&...args) {
|
|
|
|
// Grow if the size has reached the capacity of the allocation
|
|
if(_size == capacity()) {
|
|
_grow();
|
|
}
|
|
|
|
// Move the data if we are not inserting at the end of the array
|
|
if((i = min(i, _size)) < _size) {
|
|
fennec::memmove(
|
|
(void*)(_alloc.data() + i)
|
|
, (void*)(_alloc.data() + i + 1)
|
|
, (_size - i) * sizeof(TypeT));
|
|
}
|
|
|
|
// Insert the element
|
|
fennec::construct(_alloc.data() + i, fennec::forward<ArgsT>(args)...);
|
|
++_size;
|
|
}
|
|
|
|
///
|
|
/// \brief Push Back Copy
|
|
/// \param val Value to initialize with
|
|
constexpr void push_back(const TypeT& val) {
|
|
dynarray::insert(_size, val);
|
|
}
|
|
|
|
///
|
|
/// \brief Push Back Move
|
|
/// \param val Value to initialize with
|
|
constexpr void push_back(TypeT&& val) {
|
|
dynarray::insert(_size, fennec::forward<TypeT>(val));
|
|
}
|
|
|
|
///
|
|
/// \brief Emplace Back
|
|
/// \tparam ArgsT Argument Types
|
|
/// \param args Arguments to construct with
|
|
template<typename...ArgsT>
|
|
constexpr void emplace_back(ArgsT...args) {
|
|
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
|
|
}
|
|
|
|
///
|
|
/// \brief Erase last element
|
|
constexpr void pop_back() {
|
|
fennec::destruct(&_alloc[--_size]);
|
|
}
|
|
|
|
///
|
|
/// \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) {
|
|
_reduce(n);
|
|
_alloc.creallocate(n);
|
|
|
|
for (size_t i = _size; i < n; ++i) {
|
|
fennec::construct(&_alloc[i]);
|
|
}
|
|
|
|
_size = n;
|
|
}
|
|
|
|
///
|
|
/// \brief Resize the dynarray, invoking the copy constructor for all new elements
|
|
/// \param n The new size in elements
|
|
/// \param val The value to fill with
|
|
///
|
|
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
|
constexpr void resize(size_t n, const TypeT& val) {
|
|
_reduce(n);
|
|
_alloc.creallocate(n);
|
|
|
|
for (size_t i = _size; i < n; ++i) {
|
|
fennec::construct(&_alloc[i], val);
|
|
}
|
|
|
|
_size = n;
|
|
}
|
|
|
|
///
|
|
/// \brief Reserve the array, allocating new space without initialization
|
|
/// \param n The new capacity in elements
|
|
///
|
|
/// \details if \f$n\f$ is less than the current size, any elements that would be removed are destructed
|
|
constexpr void reserve(size_t n) {
|
|
_reduce(n);
|
|
_alloc.creallocate(n);
|
|
}
|
|
|
|
///
|
|
/// \brief Clears the contents of the dynarray, destructing all elements and releasing the allocation.
|
|
constexpr void clear() {
|
|
_reduce(0);
|
|
_alloc.deallocate();
|
|
}
|
|
|
|
/// @}
|
|
|
|
|
|
// Iteration ===========================================================================================================
|
|
public:
|
|
|
|
/// \name Iteration
|
|
/// @{
|
|
|
|
///
|
|
/// \returns A pointer to the first element in the dynarray
|
|
constexpr TypeT* begin() { return _alloc.data(); }
|
|
|
|
///
|
|
/// \brief C++ Iterator Specification `begin()`
|
|
/// \returns A const qualified pointer to the first element in the dynarray
|
|
constexpr const TypeT* begin() const { return _alloc; }
|
|
|
|
|
|
///
|
|
/// \return A pointer to the address after the last element in the dynarray
|
|
constexpr TypeT* end() { return begin() + _size; }
|
|
|
|
///
|
|
/// \brief C++ Iterator Specification `end()`
|
|
/// \return A const qualified pointer to the address after the last element in the dynarray
|
|
constexpr const TypeT* end() const { return begin() + _size; }
|
|
|
|
/// @}
|
|
|
|
// Private Members =====================================================================================================
|
|
private:
|
|
allocation<value_t, alloc_t> _alloc;
|
|
size_t _size;
|
|
|
|
|
|
// Private Helpers =====================================================================================================
|
|
private:
|
|
|
|
// helper to double the capacity of the allocation
|
|
constexpr void _grow() {
|
|
_alloc.creallocate(_alloc.capacity() * 2);
|
|
}
|
|
|
|
// helper to destruct elements past n
|
|
constexpr void _reduce(size_t n) {
|
|
while (_size > n) {
|
|
fennec::destruct(&_alloc[--_size]);
|
|
}
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
#endif // FENNEC_CONTAINERS_DYNARRAY_H
|