Files
basalt/include/containers/basalt_darray.h

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);
}
}