#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(const std::initializer_list& vals, MEMORY_TAG tag=MEMORY_TAG_CLASS_DYNARRAY | MEMORY_TAG_ZONE_UNKNOWN | MEMORY_TAG_ALIGN_ANY); 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); void changetag(MEMORY_TAG new_tag); const T* begin(void) const noexcept; const T* end(void) const noexcept; T* begin(void) noexcept; T* end(void) noexcept; 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; void push_back(T* beg, T* end); void push_back(T* base, size_t nelements); void push_back(const darray& src); 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; const bool contains(const T& val) 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); BTRACE("darray<%s> %p was copied to %p (%p)\n", typeid(T).name(), &src, this, this->m_pdata); } 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 *this; , "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); BTRACE("darray<%s> %p was copied to %p (%p)\n", typeid(T).name(), &src, this, this->m_pdata); return *this; } 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; BTRACE("darray<%s> %p was moved to %p\n", typeid(T).name(), &src, this); } 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; BTRACE("darray<%s> %p was moved to %p\n", typeid(T).name(), &src, this); return *this; } template inline darray::darray(const std::initializer_list& vals, MEMORY_TAG tag) { this->m_pdata = mem::allocT(vals.size(), tag); if (this->m_pdata == nullptr) return; this->m_ncapacity = vals.size(); this->m_nelements = vals.size(); this->m_tag = tag; if (std::is_trivially_copyable_v) memcpy(this->m_pdata, vals.begin(), vals.size() * sizeof(T)); else if (std::is_copy_assignable_v) for (size_t i = 0; i < vals.size(); ++i) this->m_pdata[i] = *(vals.begin() + i); else if (std::is_copy_constructible_v) for (size_t i = 0; i < vals.size(); ++i) new (this->m_pdata + i) T(*(vals.begin() + i)); else BASSERT_FATAL(false, "Assertion %s failed at %s:%d\n\tFailed to copy objects from initializer_list<%s> into darray<%s>\n\t\t%s was not copyable\n", typeid(T).name(), typeid(T).name(), typeid(T).name()); BTRACE("darray<%s> %p created: initalizer ctor %p\n", typeid(T).name(), this, this->m_pdata); } template inline darray::darray(MEMORY_TAG tag) { this->m_pdata = nullptr; this->m_ncapacity = 0; this->m_nelements = 0; this->m_tag = tag; BTRACE("darray<%s> %p created: null ctor %p\n", typeid(T).name(), this, this->m_pdata); } template inline darray::darray(size_t n_capacity, MEMORY_TAG tag) { this->m_pdata = mem::allocT(n_capacity, tag); if (this->m_pdata == nullptr) return; this->m_ncapacity = n_capacity; this->m_nelements = 0; this->m_tag = tag; BTRACE("darray<%s> %p created: capacity ctor %p\n", typeid(T).name(), this, this->m_pdata); } template inline darray::darray(size_t n_elements, const T& fill_value, MEMORY_TAG tag) { this->m_pdata = mem::allocT(n_elements, tag); if (this->m_pdata == nullptr) return; this->m_ncapacity = n_elements; this->m_nelements = n_elements; for (size_t i = 0; i < n_elements; ++i) this->m_pdata[i] = fill_value; this->m_tag = tag; BTRACE("darray<%s> %p created: fill ctor %p\n", typeid(T).name(), this, this->m_pdata); } template inline darray::~darray() { if (this->m_pdata != nullptr) { if (!std::is_trivially_destructible_v) for (size_t i = 0; i < m_nelements; ++i) m_pdata[i].~T(); mem::dealloc(this->m_pdata, this->m_ncapacity * sizeof(T), this->m_tag); BTRACE("darray<%s> %p destroyed %p\n", typeid(T).name(), this, this->m_pdata); } } 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* tmp3 = other.m_pdata; other.m_pdata = this->m_pdata; this->m_pdata = tmp3; BTRACE("darray<%s> %p (%p) swapped with darray<%s> %p (%p)\n", typeid(T).name(), this, this->m_pdata, typeid(T).name(), &other, this->m_pdata); } template inline void darray::changetag(MEMORY_TAG new_tag) { basalt::mem::changetag(sizeof(T) * this->m_ncapacity, this->m_tag, new_tag); this->m_tag = new_tag; } 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 (new_size < m_nelements) for (size_t i = new_size; i < this->m_nelements; ++i) 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 constexpr (std::is_trivially_copy_assignable_v) { memcpy(tmp, this->m_pdata, this->m_nelements * sizeof(T)); } else if constexpr (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 constexpr (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 constexpr (std::is_copy_constructible_v) { for (size_t i = 0; i < this->m_nelements; ++i) new (tmp + i) T(this->m_pdata[i]); } else if constexpr (std::is_copy_assignable_v) { for (size_t i = 0; i < this->m_nelements; ++i) tmp[i] = 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, new_capacity*sizeof(T), this->m_tag); return; } for (size_t i = 0; i < this->m_nelements; ++i) this->m_pdata[i].~T(); mem::dealloc(this->m_pdata, this->m_ncapacity*sizeof(T), this->m_tag); BTRACE("darray<%s> %p Resized from %llu (%p) to %llu (%p)\n", typeid(T).name(), this, this->m_ncapacity, this->m_pdata, new_capacity, tmp); 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 constexpr (std::is_trivially_copy_assignable_v) memcpy(tmp, this->m_pdata, this->m_nelements * sizeof(T)); else if constexpr(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 constexpr (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 constexpr (std::is_copy_assignable_v) for (size_t i = 0; i < this->m_nelements; ++i) tmp[i] = this->m_pdata[i]; else if constexpr (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_nelements*sizeof(T), this->m_tag); return; } for (size_t i = 0; i < this->m_nelements; ++i) this->m_pdata[i].~T(); mem::dealloc(this->m_pdata, this->m_ncapacity * sizeof(T), this->m_tag); BTRACE("darray<%s> %p (%p) Shrink fit to %llu (%p)\n", typeid(T).name(), this, this->m_pdata, this->m_nelements, tmp); 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 constexpr (std::is_trivially_copyable_v) memcpy(this->m_pdata + this->m_nelements, &val, sizeof(T)); else if constexpr (std::is_copy_constructible_v) new (this->m_pdata + this->m_nelements) T(val); else if constexpr (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 constexpr (std::is_trivially_move_assignable_v) memcpy(this->m_pdata + this->m_nelements, &val, sizeof(T)); else if constexpr (std::is_move_constructible_v) new (this->m_pdata + this->m_nelements) T(std::move(val)); else if constexpr (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 void darray::push_back(T* beg, T* end) { ptrdiff_t nelements = end - beg; nelements = nelements < 0 ? -nelements : nelements; if ((this->m_nelements + nelements) > this->m_ncapacity) this->expand(nelements + this->m_nelements + 1); if constexpr (std::is_trivially_copyable_v) { memcpy(this->m_pdata + this->m_nelements, beg, sizeof(T) * nelements); } else if constexpr (std::is_copy_assignable_v) { for (size_t i = 0; i < nelements; ++i) this->m_pdata[this->m_nelements + i] = beg[i]; } else if constexpr (std::is_copy_constructible_v) { for (size_t i = 0; i < nelements; ++i) new (this->m_pdata + this->m_nelements + i) T(beg[i]); } 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 += nelements; } template inline void darray::push_back(T* base, const size_t nelements) { this->push_back(base, base+nelements); } template inline void darray::push_back(const darray& src) { this->push_back(src.m_pdata, src.m_pdata+src.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; this->m_pdata[this->m_nelements].~T(); return std::move(tmp); } template const T* darray::begin(void) const noexcept { return this->m_pdata; } template const T* darray::end(void) const noexcept { return this->m_pdata+this->m_nelements; } template T* darray::begin(void) noexcept { return this->m_pdata; } template T* darray::end(void) noexcept { return this->m_pdata+this->m_nelements; } }