// zlib open source license // // Copyright (c) 2018 to 2025 David Forsgren Piuva // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. #ifndef DFPSR_COLLECTION_ARRAY #define DFPSR_COLLECTION_ARRAY #include "collections.h" namespace dsr { // A fixed size collection of impl_elements initialized to the same default value. // Unlike Buffer, Array is a value type, so be careful not to pass it by value unless you intend to clone its content. template class Array { private: intptr_t impl_elementCount = 0; T *impl_elements = nullptr; void impl_free() { if (this->impl_elements != nullptr) { heap_decreaseUseCount(this->impl_elements); this->impl_elements = nullptr; } } // Pre-condition: this->impl_elements == nullptr void impl_allocate(intptr_t elementCount) { this->impl_elementCount = elementCount; UnsafeAllocation newAllocation = heap_allocate(elementCount * sizeof(T), false); #ifdef SAFE_POINTER_CHECKS heap_setAllocationName(newAllocation.data, "Array allocation"); #endif this->impl_elements = (T*)(newAllocation.data); heap_increaseUseCount(newAllocation.header); } void impl_reallocate(intptr_t elementCount) { // Check how much space is available in the target. uintptr_t allocationSize = heap_getAllocationSize(this->impl_elements); uintptr_t neededSize = elementCount * sizeof(T); if (neededSize > allocationSize) { // Need to replace the old allocation. this->impl_free(); this->impl_allocate(elementCount); } else { // Resize the allocation within the available space. heap_setUsedSize(this->impl_elements, neededSize); this->impl_elementCount = elementCount; } } void impl_destroy() { for (intptr_t index = 0; index < this->impl_elementCount; index++) { this->impl_elements[index].~T(); } } public: // Constructors. Array() : impl_elementCount(0), impl_elements(nullptr) {} Array(const intptr_t newLength, const T& defaultValue) { if (newLength > 0) { this->impl_allocate(newLength); for (intptr_t index = 0; index < newLength; index++) { new (this->impl_elements + index) T(defaultValue); } } else { this->impl_elementCount = 0; } } // Copy constructor. Array(const Array& source) { this->impl_allocate(source.impl_elementCount); for (intptr_t e = 0; e < this->impl_elementCount; e++) { new (this->impl_elements + e) T(source.impl_elements[e]); } }; // Move constructor. Array(Array &&source) noexcept : impl_elementCount(source.impl_elementCount), impl_elements(source.impl_elements) { source.impl_elementCount = 0; source.impl_elements = nullptr; } // Copy assignment. Array& operator = (const Array& source) { if (this != &source) { this->impl_destroy(); this->impl_reallocate(source.impl_elementCount); // Copy impl_elements from source. for (intptr_t e = 0; e < this->impl_elementCount; e++) { new (this->impl_elements + e) T(source.impl_elements[e]); } } return *this; }; // Move assignment. Array& operator = (Array &&source) { if (this != &source) { this->impl_destroy(); this->impl_free(); this->impl_elementCount = source.impl_elementCount; this->impl_elements = source.impl_elements; source.impl_elementCount = 0; source.impl_elements = nullptr; } return *this; } // Destructor ~Array() { this->impl_destroy(); this->impl_free(); } // Bound check inline bool inside(intptr_t index) const { return 0 <= index && index < this->impl_elementCount; } inline T& unsafe_writeAccess(intptr_t index) { assert(this->inside(index)); return this->impl_elements[index]; } inline const T& unsafe_readAccess(intptr_t index) const { assert(this->inside(index)); return this->impl_elements[index]; } // Element access T& operator[] (const intptr_t index) { impl_baseZeroBoundCheck(index, this->length(), "Array index"); return this->impl_elements[index]; } const T& operator[] (const intptr_t index) const { impl_baseZeroBoundCheck(index, this->length(), "Array index"); return this->impl_elements[index]; } inline intptr_t length() const { return this->impl_elementCount; } }; } #endif