- TEST_SPEC.md: Updated test directory structure to reflect Core/Asset, Core/IO, and Resources/<Type> subdirectories - TEST_SPEC.md: Updated module names and test counts (852 total) - TEST_SPEC.md: Updated build commands for new Resources subdirectories - README.md: Updated engine structure with Core/Asset/ and Core/IO/ - README.md: Updated Resources section with layered architecture - README.md: Updated test coverage table with accurate counts
293 lines
7.2 KiB
C++
293 lines
7.2 KiB
C++
#pragma once
|
|
|
|
#include <cstddef>
|
|
#include <initializer_list>
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
#include <XCEngine/Memory/Allocator.h>
|
|
|
|
namespace XCEngine {
|
|
namespace Containers {
|
|
|
|
template<typename T>
|
|
class Array {
|
|
public:
|
|
using Iterator = T*;
|
|
using ConstIterator = const T*;
|
|
|
|
Array() = default;
|
|
explicit Array(size_t capacity);
|
|
Array(size_t count, const T& value);
|
|
Array(std::initializer_list<T> init);
|
|
~Array();
|
|
|
|
Array(const Array& other);
|
|
Array(Array&& other) noexcept;
|
|
Array& operator=(const Array& other);
|
|
Array& operator=(Array&& other) noexcept;
|
|
|
|
T& operator[](size_t index);
|
|
const T& operator[](size_t index) const;
|
|
|
|
T* Data() { return m_data; }
|
|
const T* Data() const { return m_data; }
|
|
|
|
size_t Size() const { return m_size; }
|
|
size_t Capacity() const { return m_capacity; }
|
|
|
|
bool Empty() const { return m_size == 0; }
|
|
|
|
void Clear();
|
|
void Reserve(size_t capacity);
|
|
void Resize(size_t newSize);
|
|
void Resize(size_t newSize, const T& value);
|
|
|
|
void PushBack(const T& value);
|
|
void PushBack(T&& value);
|
|
template<typename... Args>
|
|
T& EmplaceBack(Args&&... args);
|
|
void PopBack();
|
|
|
|
T& Front() { return m_data[0]; }
|
|
const T& Front() const { return m_data[0]; }
|
|
T& Back() { return m_data[m_size - 1]; }
|
|
const T& Back() const { return m_data[m_size - 1]; }
|
|
|
|
Iterator begin() { return m_data; }
|
|
Iterator end() { return m_data + m_size; }
|
|
ConstIterator begin() const { return m_data; }
|
|
ConstIterator end() const { return m_data + m_size; }
|
|
|
|
void SetAllocator(Memory::IAllocator* allocator) { m_allocator = allocator; }
|
|
|
|
private:
|
|
T* m_data = nullptr;
|
|
size_t m_size = 0;
|
|
size_t m_capacity = 0;
|
|
Memory::IAllocator* m_allocator = nullptr;
|
|
|
|
void Reallocate(size_t newCapacity);
|
|
void DestructRange(T* begin, T* end);
|
|
void CopyRange(const T* src, T* dst, size_t count);
|
|
};
|
|
|
|
template<typename T>
|
|
Array<T>::Array(size_t capacity) : m_capacity(capacity) {
|
|
if (capacity > 0) {
|
|
m_data = static_cast<T*>(::operator new(capacity * sizeof(T)));
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
Array<T>::Array(size_t count, const T& value) : m_size(count), m_capacity(count) {
|
|
if (count > 0) {
|
|
m_data = static_cast<T*>(::operator new(count * sizeof(T)));
|
|
for (size_t i = 0; i < count; ++i) {
|
|
new (&m_data[i]) T(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
Array<T>::Array(std::initializer_list<T> init) : m_size(init.size()), m_capacity(init.size()) {
|
|
if (m_size > 0) {
|
|
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
|
|
size_t i = 0;
|
|
for (const auto& item : init) {
|
|
new (&m_data[i++]) T(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
Array<T>::~Array() {
|
|
DestructRange(m_data, m_data + m_size);
|
|
::operator delete(m_data);
|
|
}
|
|
|
|
template<typename T>
|
|
Array<T>::Array(const Array& other) : m_size(other.m_size), m_capacity(other.m_size) {
|
|
if (m_size > 0) {
|
|
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
|
|
CopyRange(other.m_data, m_data, m_size);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
Array<T>::Array(Array&& other) noexcept
|
|
: m_data(other.m_data), m_size(other.m_size), m_capacity(other.m_capacity), m_allocator(other.m_allocator) {
|
|
other.m_data = nullptr;
|
|
other.m_size = 0;
|
|
other.m_capacity = 0;
|
|
}
|
|
|
|
template<typename T>
|
|
Array<T>& Array<T>::operator=(const Array& other) {
|
|
if (this != &other) {
|
|
DestructRange(m_data, m_data + m_size);
|
|
::operator delete(m_data);
|
|
|
|
m_size = other.m_size;
|
|
m_capacity = other.m_size;
|
|
m_allocator = other.m_allocator;
|
|
|
|
if (m_size > 0) {
|
|
m_data = static_cast<T*>(::operator new(m_size * sizeof(T)));
|
|
CopyRange(other.m_data, m_data, m_size);
|
|
} else {
|
|
m_data = nullptr;
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
Array<T>& Array<T>::operator=(Array&& other) noexcept {
|
|
if (this != &other) {
|
|
DestructRange(m_data, m_data + m_size);
|
|
::operator delete(m_data);
|
|
|
|
m_data = other.m_data;
|
|
m_size = other.m_size;
|
|
m_capacity = other.m_capacity;
|
|
m_allocator = other.m_allocator;
|
|
|
|
other.m_data = nullptr;
|
|
other.m_size = 0;
|
|
other.m_capacity = 0;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
template<typename T>
|
|
T& Array<T>::operator[](size_t index) {
|
|
return m_data[index];
|
|
}
|
|
|
|
template<typename T>
|
|
const T& Array<T>::operator[](size_t index) const {
|
|
return m_data[index];
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::Clear() {
|
|
DestructRange(m_data, m_data + m_size);
|
|
m_size = 0;
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::Reserve(size_t capacity) {
|
|
if (capacity > m_capacity) {
|
|
Reallocate(capacity);
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::Resize(size_t newSize) {
|
|
if (newSize > m_capacity) {
|
|
Reallocate(newSize);
|
|
}
|
|
|
|
if (newSize > m_size) {
|
|
for (size_t i = m_size; i < newSize; ++i) {
|
|
new (&m_data[i]) T();
|
|
}
|
|
} else if (newSize < m_size) {
|
|
DestructRange(m_data + newSize, m_data + m_size);
|
|
}
|
|
|
|
m_size = newSize;
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::Resize(size_t newSize, const T& value) {
|
|
if (newSize > m_capacity) {
|
|
Reallocate(newSize);
|
|
}
|
|
|
|
if (newSize > m_size) {
|
|
for (size_t i = m_size; i < newSize; ++i) {
|
|
new (&m_data[i]) T(value);
|
|
}
|
|
} else if (newSize < m_size) {
|
|
DestructRange(m_data + newSize, m_data + m_size);
|
|
}
|
|
|
|
m_size = newSize;
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::PushBack(const T& value) {
|
|
if (m_size >= m_capacity) {
|
|
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
|
|
Reallocate(newCapacity);
|
|
}
|
|
new (&m_data[m_size]) T(value);
|
|
++m_size;
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::PushBack(T&& value) {
|
|
if (m_size >= m_capacity) {
|
|
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
|
|
Reallocate(newCapacity);
|
|
}
|
|
new (&m_data[m_size]) T(std::move(value));
|
|
++m_size;
|
|
}
|
|
|
|
template<typename T>
|
|
template<typename... Args>
|
|
T& Array<T>::EmplaceBack(Args&&... args) {
|
|
if (m_size >= m_capacity) {
|
|
size_t newCapacity = m_capacity == 0 ? 4 : m_capacity * 2;
|
|
Reallocate(newCapacity);
|
|
}
|
|
new (&m_data[m_size]) T(std::forward<Args>(args)...);
|
|
++m_size;
|
|
return m_data[m_size - 1];
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::PopBack() {
|
|
if (m_size > 0) {
|
|
--m_size;
|
|
m_data[m_size].~T();
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::Reallocate(size_t newCapacity) {
|
|
T* newData = nullptr;
|
|
if (newCapacity > 0) {
|
|
newData = static_cast<T*>(::operator new(newCapacity * sizeof(T)));
|
|
size_t count = std::min(m_size, newCapacity);
|
|
if (count > 0) {
|
|
CopyRange(m_data, newData, count);
|
|
}
|
|
}
|
|
|
|
DestructRange(m_data, m_data + m_size);
|
|
::operator delete(m_data);
|
|
|
|
m_data = newData;
|
|
m_capacity = newCapacity;
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::DestructRange(T* begin, T* end) {
|
|
for (T* p = begin; p < end; ++p) {
|
|
p->~T();
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
void Array<T>::CopyRange(const T* src, T* dst, size_t count) {
|
|
for (size_t i = 0; i < count; ++i) {
|
|
new (&dst[i]) T(src[i]);
|
|
}
|
|
}
|
|
|
|
} // namespace Containers
|
|
} // namespace XCEngine
|