#pragma once #include #include #include #include "core/basalt_memory.h" #include "core/basalt_logger.h" namespace basalt { template class darray { public: darray(const darray& src); darray& operator =(const darray& src); darray(darray&& src); darray& operator =(darray&& 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& 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 inline darray::darray(const darray& src) { this->m_pdata = mem::allocT(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 inline basalt::darray& darray::operator=(const darray& src) { if (&src == this) return *this; this->~darray(); this->m_pdata = mem::allocT(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 inline darray::darray(darray&& 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 inline darray& darray::operator=(darray&& 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 inline darray::darray(MEMORY_TAG tag) { this->m_pdata = nullptr; this->m_ncapacity = 0; this->m_nelements = 0; this->m_tag = tag; } template inline darray::darray(size_t n_capacity, MEMORY_TAG tag) { this->m_pdata = mem::allocT(n_capacity, tag); this->m_ncapacity = n_capacity; this->m_nelements = 0; this->m_tag = tag; } template inline darray::darray(size_t n_elements, const T& fill_value, MEMORY_TAG tag) { this->m_pdata = mem::allocT(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 inline darray::~darray() { if (this->m_pdata != nullptr) mem::dealloc(this->m_pdata, this->m_nelements * sizeof(T), this->m_tag); } template inline void darray::swap(darray& 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 const size_t darray::size(void) const noexcept { return m_nelements; } template inline T* darray::operator+(const size_t offset) noexcept { return this->m_pdata+offset; } template inline T* darray::operator-(const size_t offset) noexcept { return this->m_pdata - offset; } template inline const T* darray::operator+(const size_t offset) const noexcept { return this->m_pdata + offset; } template inline const T* darray::operator-(const size_t offset) const noexcept { return this->m_pdata - offset; } template inline darray::operator T* () noexcept { return this->m_pdata; } template inline T& darray::operator [](const size_t idx) noexcept { return this->m_pdata[idx]; } template inline const T& darray::operator [](const size_t idx) const noexcept { return this->m_pdata[idx]; } template inline void darray::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) 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 inline void darray::expand(size_t extra_capacity) noexcept { this->reserve(this->m_ncapacity + extra_capacity); } template inline void darray::reserve(size_t new_capacity) noexcept { if (new_capacity < this->m_ncapacity) return; T* tmp = mem::allocT(new_capacity, this->m_tag); if (std::is_trivially_copy_assignable_v) memcpy(tmp, this->m_pdata, this->m_nelements * sizeof(T)); else if (std::is_move_assignable_v) 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) 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) for (size_t i = 0; i < this->m_nelements; ++i) tmp[i] = this->m_pdata[i]; else if (std::is_copy_constructible_v) 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) 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 inline void darray::shrink_fit(void) { if (this->m_nelements == this->m_ncapacity) return; T* tmp = mem::allocT(this->m_nelements, this->m_tag); if (std::is_trivially_copy_assignable_v) memcpy(tmp, this->m_pdata, this->m_nelements * sizeof(T)); else if (std::is_move_assignable_v) 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) 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) for (size_t i = 0; i < this->m_nelements; ++i) tmp[i] = this->m_pdata[i]; else if (std::is_copy_constructible_v) 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) 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 inline void darray::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) memcpy(this->m_pdata + this->m_nelements, &val, sizeof(T)); else if (std::is_copy_constructible_v) new (this->m_pdata + this->m_nelements) T(val); else if (std::is_copy_assignable_v) 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 inline void darray::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) memcpy(this->m_pdata + this->m_nelements, &val, sizeof(T)); else if (std::is_move_constructible_v) new (this->m_pdata + this->m_nelements) T(std::move(val)); else if (std::is_move_assignable_v) 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 inline const T& darray::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 inline T& darray::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 const T& darray::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 inline T& basalt::darray::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 T&& darray::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 tmp = std::move(this->m_pdata[idx]); --this->m_nelements; if (!std::is_trivially_destructible_v) this->m_pdata[this->m_nelements].~T(); return std::move(tmp); } }