368 lines
11 KiB
C++
368 lines
11 KiB
C++
#pragma once
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <typeinfo>
|
|
#include "core/basalt_memory.h"
|
|
#include "core/basalt_logger.h"
|
|
|
|
namespace basalt
|
|
{
|
|
template <typename T>
|
|
class darray
|
|
{
|
|
public:
|
|
darray(const darray<T>& src);
|
|
darray<T>& operator =(const darray<T>& src);
|
|
darray(darray<T>&& src);
|
|
darray<T>& operator =(darray<T>&& src);
|
|
|
|
darray(MEMORY_TAG tag);
|
|
darray(size_t n_capacity, MEMORY_TAG tag);
|
|
darray(size_t n_elements, const T& fill_value, MEMORY_TAG tag);
|
|
~darray();
|
|
|
|
void swap(darray<T>& other);
|
|
|
|
T* operator +(const size_t offset) noexcept;
|
|
T* operator -(const size_t offset) noexcept;
|
|
const T* operator +(const size_t offset) const noexcept;
|
|
const T* operator -(const size_t offset) const noexcept;
|
|
|
|
operator T* () noexcept;
|
|
|
|
T& operator [](const size_t idx) noexcept;
|
|
const T& operator [](const size_t idx) const noexcept;
|
|
|
|
const size_t size(void) const noexcept;
|
|
|
|
void resize(size_t new_size) noexcept;
|
|
void expand(size_t extra_capacity) noexcept;
|
|
void reserve(size_t new_capacity) noexcept;
|
|
void shrink_fit(void);
|
|
|
|
void push_back(const T& val) noexcept;
|
|
void push_back(T&& val) noexcept;
|
|
|
|
T&& pop_back(void) noexcept;
|
|
T& peek_back(void) noexcept;
|
|
const T& peek_back(void) const noexcept;
|
|
T& peek_front(void) noexcept;
|
|
const T& peek_front(void) const noexcept;
|
|
|
|
T* m_pdata = nullptr;
|
|
size_t m_nelements = 0;
|
|
size_t m_ncapacity = 0;
|
|
MEMORY_TAG m_tag = 0;
|
|
};
|
|
template<typename T>
|
|
inline darray<T>::darray(const darray<T>& src)
|
|
{
|
|
this->m_pdata = mem::allocT<T>(src.m_nelements, src.m_tag);
|
|
BASSERT_ERROR(this->m_pdata != nullptr, return, "Assertion %s failed at %s:%d\n\tMemory allocation failed for allocation size %u\n\t%s | %s | %u");
|
|
this->m_tag = src.m_tag;
|
|
this->m_ncapacity = src.m_nelements;
|
|
this->m_nelements = src.m_nelements;
|
|
memcpy(this->m_pdata, src.m_pdata, sizeof(T) * this->m_nelements);
|
|
}
|
|
template<typename T>
|
|
inline basalt::darray<T>& darray<T>::operator=(const darray<T>& src)
|
|
{
|
|
if (&src == this)
|
|
return *this;
|
|
this->~darray();
|
|
this->m_pdata = mem::allocT<T>(src.m_nelements, src.m_tag);
|
|
BASSERT_ERROR(this->m_pdata != nullptr, return, "Assertion %s failed at %s:%d\n\tMemory allocation failed for allocation size %u\n\t%s | %s | %u");
|
|
this->m_tag = src.m_tag;
|
|
this->m_ncapacity = src.m_nelements;
|
|
this->m_nelements = src.m_nelements;
|
|
memcpy(this->m_pdata, src.m_pdata, sizeof(T)*this->m_nelements);
|
|
}
|
|
|
|
template<typename T>
|
|
inline darray<T>::darray(darray<T>&& src)
|
|
{
|
|
this->m_pdata = src.m_pdata;
|
|
this->m_ncapacity = src.m_ncapacity;
|
|
this->m_nelements = src.m_nelements;
|
|
this->m_tag = src.m_tag;
|
|
src.m_pdata = nullptr;
|
|
src.m_ncapacity = 0;
|
|
src.m_nelements = 0;
|
|
src.m_tag = 0;
|
|
}
|
|
|
|
template<typename T>
|
|
inline darray<T>& darray<T>::operator=(darray<T>&& src)
|
|
{
|
|
if (&src == this)
|
|
return *this;
|
|
this->~darray();
|
|
this->m_pdata = src.m_pdata;
|
|
this->m_ncapacity = src.m_ncapacity;
|
|
this->m_nelements = src.m_nelements;
|
|
this->m_tag = src.m_tag;
|
|
src.m_pdata = nullptr;
|
|
src.m_ncapacity = 0;
|
|
src.m_nelements = 0;
|
|
src.m_tag = 0;
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
inline darray<T>::darray(MEMORY_TAG tag)
|
|
{
|
|
this->m_pdata = nullptr;
|
|
this->m_ncapacity = 0;
|
|
this->m_nelements = 0;
|
|
this->m_tag = tag;
|
|
}
|
|
|
|
template<typename T>
|
|
inline darray<T>::darray(size_t n_capacity, MEMORY_TAG tag)
|
|
{
|
|
this->m_pdata = mem::allocT<T>(n_capacity, tag);
|
|
this->m_ncapacity = n_capacity;
|
|
this->m_nelements = 0;
|
|
this->m_tag = tag;
|
|
}
|
|
|
|
template<typename T>
|
|
inline darray<T>::darray(size_t n_elements, const T& fill_value, MEMORY_TAG tag)
|
|
{
|
|
this->m_pdata = mem::allocT<T>(n_capacity, tag);
|
|
this->m_ncapacity = n_capacity;
|
|
this->m_nelements = n_elements;
|
|
for (size_t i = 0; i < n_elements; ++i)
|
|
this->m_pdata[i] = fill_value;
|
|
this->m_tag = tag;
|
|
}
|
|
|
|
template<typename T>
|
|
inline darray<T>::~darray()
|
|
{
|
|
if (this->m_pdata != nullptr)
|
|
mem::dealloc(this->m_pdata, this->m_nelements * sizeof(T), this->m_tag);
|
|
}
|
|
template<typename T>
|
|
inline void darray<T>::swap(darray<T>& other)
|
|
{
|
|
size_t tmp = other.m_ncapacity;
|
|
other.m_ncapacity = this->m_ncapacity;
|
|
this->m_ncapacity = tmp;
|
|
|
|
tmp = other.m_nelements;
|
|
other.m_nelements = this->m_nelements;
|
|
this->m_nelements = tmp;
|
|
|
|
MEMORY_TAG tmp2 = other.m_tag;
|
|
other.m_tag = this->m_tag;
|
|
this->m_tag = tmp2;
|
|
|
|
T* tmp = other.m_pdata;
|
|
other.m_pdata = this->m_pdata;
|
|
this->m_pdata = tmp;
|
|
}
|
|
template <typename T>
|
|
const size_t darray<T>::size(void) const noexcept
|
|
{ return m_nelements; }
|
|
|
|
template<typename T>
|
|
inline T* darray<T>::operator+(const size_t offset) noexcept
|
|
{ return this->m_pdata+offset; }
|
|
template<typename T>
|
|
inline T* darray<T>::operator-(const size_t offset) noexcept
|
|
{ return this->m_pdata - offset; }
|
|
template<typename T>
|
|
inline const T* darray<T>::operator+(const size_t offset) const noexcept
|
|
{ return this->m_pdata + offset; }
|
|
template<typename T>
|
|
inline const T* darray<T>::operator-(const size_t offset) const noexcept
|
|
{ return this->m_pdata - offset; }
|
|
template<typename T>
|
|
inline darray<T>::operator T* () noexcept
|
|
{ return this->m_pdata; }
|
|
|
|
template<typename T>
|
|
inline T& darray<T>::operator [](const size_t idx) noexcept
|
|
{ return this->m_pdata[idx]; }
|
|
template<typename T>
|
|
inline const T& darray<T>::operator [](const size_t idx) const noexcept
|
|
{ return this->m_pdata[idx]; }
|
|
template<typename T>
|
|
inline void darray<T>::resize(size_t new_size) noexcept
|
|
{
|
|
if (new_size & (1 << 63))
|
|
{
|
|
BWARN("Attempted to resize object dynarray<%s> to a negative value of %ll\n", typeid(T).name(), new_size);
|
|
return;
|
|
}
|
|
if ((this->m_nelements + new_size) > this->m_ncapacity)
|
|
this->reserve(new_size * 2);
|
|
// Call destructor for objects outside of the new size
|
|
if (!std::is_trivially_destructible_v<T>)
|
|
if (new_size < m_nelements)
|
|
for (size_t i = new_size; i < this->m_nelements)
|
|
this->m_pdata[i].~T();
|
|
|
|
this->m_nelements = new_size;
|
|
}
|
|
template<typename T>
|
|
inline void darray<T>::expand(size_t extra_capacity) noexcept
|
|
{ this->reserve(this->m_ncapacity + extra_capacity); }
|
|
template<typename T>
|
|
inline void darray<T>::reserve(size_t new_capacity) noexcept
|
|
{
|
|
if (new_capacity < this->m_ncapacity)
|
|
return;
|
|
T* tmp = mem::allocT<T>(new_capacity, this->m_tag);
|
|
if (std::is_trivially_copy_assignable_v<T>)
|
|
memcpy(tmp, this->m_pdata, this->m_nelements * sizeof(T));
|
|
else if (std::is_move_assignable_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
tmp[i] = std::move(this->m_pdata[i]);
|
|
else if (std::is_move_constructible_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
new (tmp + i) T(std::move(this->m_pdata[i]));
|
|
else if (std::is_copy_assignable_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
tmp[i] = this->m_pdata[i];
|
|
else if (std::is_copy_constructible_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
new (tmp+i) T(this->m_pdata[i]);
|
|
else
|
|
{
|
|
BERROR("Can not resize object dynarray<%s>\n\tIt is not trivially copyable, copy assignable, copy constructible, move assignable or move constructable\n", typeid(T).name());
|
|
mem::dealloc(tmp, this->m_ncapacity*sizeof(T), this->m_tag);
|
|
return;
|
|
}
|
|
|
|
if (!std::is_trivially_destructible_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
this->m_pdata[i].~T();
|
|
mem::dealloc(this->m_pdata, this->m_nelements*sizeof(T), this->m_tag);
|
|
this->m_ncapacity = new_capacity;
|
|
this->m_pdata = tmp;
|
|
}
|
|
template<typename T>
|
|
inline void darray<T>::shrink_fit(void)
|
|
{
|
|
if (this->m_nelements == this->m_ncapacity)
|
|
return;
|
|
T* tmp = mem::allocT<T>(this->m_nelements, this->m_tag);
|
|
if (std::is_trivially_copy_assignable_v<T>)
|
|
memcpy(tmp, this->m_pdata, this->m_nelements * sizeof(T));
|
|
else if (std::is_move_assignable_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
tmp[i] = std::move(this->m_pdata[i]);
|
|
else if (std::is_move_constructible_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
new (tmp + i) T(std::move(this->m_pdata[i]));
|
|
else if (std::is_copy_assignable_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
tmp[i] = this->m_pdata[i];
|
|
else if (std::is_copy_constructible_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
new (tmp + i) T(this->m_pdata[i]);
|
|
else
|
|
{
|
|
BERROR("Can not resize object dynarray<%s>\n\tIt is not trivially copyable, copy assignable, copy constructible, move assignable or move constructable\n", typeid(T).name());
|
|
mem::dealloc(tmp);
|
|
return;
|
|
}
|
|
|
|
if (!std::is_trivially_destructible_v<T>)
|
|
for (size_t i = 0; i < this->m_nelements; ++i)
|
|
this->m_pdata[i].~T();
|
|
mem::dealloc(this->m_pdata, this->m_nelements * sizeof(T), this->m_tag);
|
|
this->m_ncapacity = this->m_nelements;
|
|
this->m_pdata = tmp;
|
|
}
|
|
|
|
template<typename T>
|
|
inline void darray<T>::push_back(const T& val) noexcept
|
|
{
|
|
if ((this->m_nelements + 1) > this->m_ncapacity)
|
|
this->expand(this->m_ncapacity < 1 ? 1 : this->m_ncapacity);
|
|
if (std::is_trivially_copyable_v<T>)
|
|
memcpy(this->m_pdata + this->m_nelements, &val, sizeof(T));
|
|
else if (std::is_copy_constructible_v<T>)
|
|
new (this->m_pdata + this->m_nelements) T(val);
|
|
else if (std::is_copy_assignable_v<T>)
|
|
this->m_pdata[this->m_nelements] = val;
|
|
else
|
|
{
|
|
BERROR("Can not use push_back(const %s&) on object that is not copy constructable or assignable\n", typeid(T).name());
|
|
return;
|
|
}
|
|
this->m_nelements++;
|
|
}
|
|
|
|
template<typename T>
|
|
inline void darray<T>::push_back(T&& val) noexcept
|
|
{
|
|
if ((this->m_nelements + 1) > this->m_ncapacity)
|
|
this->expand(this->m_ncapacity < 1 ? 1 : this->m_ncapacity);
|
|
if (std::is_trivially_move_assignable_v<T>)
|
|
memcpy(this->m_pdata + this->m_nelements, &val, sizeof(T));
|
|
else if (std::is_move_constructible_v<T>)
|
|
new (this->m_pdata + this->m_nelements) T(std::move(val));
|
|
else if (std::is_move_assignable_v<T>)
|
|
this->m_pdata[this->m_nelements] = std::move(val);
|
|
else
|
|
{
|
|
BERROR("Can not use push_back(%s&&) on object that is not move constructable or assignable\n", typeid(T).name());
|
|
return;
|
|
}
|
|
this->m_nelements++;
|
|
}
|
|
|
|
template<typename T>
|
|
inline const T& darray<T>::peek_back(void) const noexcept
|
|
{
|
|
size_t idx = this->m_nelements - 1;
|
|
BASSERT_FATAL(this->m_nelements > 0, "Assertion %s failed at %s:%d\n\tAttempted to pop an empty darray<%s>\n", typeid(T).name());
|
|
if (idx > this->m_nelements)
|
|
idx = 0;
|
|
return *this->m_pdata[idx];
|
|
}
|
|
|
|
template<typename T>
|
|
inline T& darray<T>::peek_front(void) noexcept
|
|
{
|
|
BASSERT_FATAL(this->m_nelements > 0, "Assertion %s failed at %s:%d\n\tAttempted to pop an empty darray<%s>\n", typeid(T).name());
|
|
return *this->m_pdata;
|
|
}
|
|
|
|
template <typename T>
|
|
const T& darray<T>::peek_front(void) const noexcept
|
|
{
|
|
BASSERT_FATAL(this->m_nelements > 0, "Assertion %s failed at %s:%d\n\tAttempted to pop an empty darray<%s>\n", typeid(T).name());
|
|
return *this->m_pdata;
|
|
}
|
|
|
|
template<typename T>
|
|
inline T& basalt::darray<T>::peek_back(void) noexcept
|
|
{
|
|
size_t idx = this->m_nelements - 1;
|
|
BASSERT_FATAL(this->m_nelements > 0, "Assertion %s failed at %s:%d\n\tAttempted to pop an empty darray<%s>\n", typeid(T).name());
|
|
if (idx > this->m_nelements)
|
|
idx = 0;
|
|
return this->m_pdata[idx];
|
|
}
|
|
|
|
template <typename T>
|
|
T&& darray<T>::pop_back(void) noexcept
|
|
{
|
|
size_t idx = this->m_nelements - 1;
|
|
BASSERT_FATAL(this->m_nelements > 0, "Assertion %s failed at %s:%d\n\tAttempted to pop an empty darray<%s>\n", typeid(T).name());
|
|
if (idx > this->m_nelements)
|
|
idx = 0;
|
|
darray<T> tmp = std::move(this->m_pdata[idx]);
|
|
--this->m_nelements;
|
|
if (!std::is_trivially_destructible_v<T>)
|
|
this->m_pdata[this->m_nelements].~T();
|
|
return std::move(tmp);
|
|
}
|
|
}
|
|
|