Files
XCEngine/engine/include/XCEngine/Core/Containers/Array.h

293 lines
7.2 KiB
C
Raw Normal View History

#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