#ifndef CORE_LIST_HPP #define CORE_LIST_HPP #include "core/utils/AlignedData.hpp" #include "core/utils/ArrayString.hpp" #include "core/utils/New.hpp" namespace Core { template class List final { i64 length; i64 capacity; T* data; public: List() : length(0), capacity(0), data(nullptr) { } List(const List& other) = delete; List(List&& other) : List() { swap(other); } ~List() { clear(); delete[] reinterpret_cast*>(data); } List& operator=(const List& other) = delete; List& operator=(List&& other) { swap(other); return *this; } check_return Error copyFrom(const List& other) { List copy; CORE_RETURN_ERROR(allocate(copy.data, other.capacity)); copy.capacity = other.capacity; for(i64 i = 0; i < other.length; i++) { copy.unsafeAdd(other[i]); } swap(copy); return Error::NONE; } T* begin() { return data; } T* end() { return data + length; } const T* begin() const { return data; } const T* end() const { return data + length; } check_return Error reserve(i64 n) { if(n <= capacity) { return Error::NONE; } return setSize(n); } check_return Error shrink() { if(length == capacity) { return Error::NONE; } return setSize(length); } check_return Error resize(i64 n, const T& t) { if(length < n) { CORE_RETURN_ERROR(reserve(n)); for(i64 i = n - length; i != 0; i--) { unsafeAdd(t); } } else if(length > n) { for(i64 i = n; i < length; i++) { data[i].~T(); } length = n; } return Error::NONE; } check_return Error resize(i64 n) { if(length < n) { CORE_RETURN_ERROR(reserve(n)); for(i64 i = n - length; i != 0; i--) { unsafeAdd(T()); } } else if(length > n) { for(i64 i = n; i < length; i++) { data[i].~T(); } length = n; } return Error::NONE; } template check_return Error put(T*& t, Args&&... args) { CORE_RETURN_ERROR(ensureCapacity()); t = unsafeAdd(Core::forward(args)...); return Error::NONE; } template check_return Error add(Args&&... args) { T* t = nullptr; return put(t, Core::forward(args)...); } T& operator[](i64 index) { return data[index]; } const T& operator[](i64 index) const { return data[index]; } i64 getLength() const { return length; } i64 getCapacity() const { return capacity; } void clear() { for(i64 i = 0; i < length; i++) { data[i].~T(); } length = 0; } check_return Error removeBySwap(i64 index) { if(index < 0 || index >= length) { return Error::INVALID_INDEX; } length--; if(index != length) { data[index] = Core::move(data[length]); } data[length].~T(); return Error::NONE; } check_return Error remove(i64 index) { if(index < 0 || index >= length) { return Error::INVALID_INDEX; } length--; T* currentT = begin() + index; T* endT = end(); while(currentT != endT) { T* nextT = currentT + 1; *currentT = Core::move(*nextT); currentT = nextT; } endT->~T(); return Error::NONE; } check_return Error removeLast() { return removeBySwap(length - 1); } template check_return Error toString(String& s) const { return Core::toString(s, *this); } void swap(List& other) { Core::swap(length, other.length); Core::swap(capacity, other.capacity); Core::swap(data, other.data); } private: static Error allocate(T*& t, i64 n) { if(n <= 0) { return Error::NONE; } t = reinterpret_cast(new AlignedType[static_cast(n)]); return t == nullptr ? Error::OUT_OF_MEMORY : Error::NONE; } check_return Error ensureCapacity() { return length < capacity ? Error::NONE : reserve(capacity + Core::Math::max(4l, capacity / 4)); } // does not check for capacity template T* unsafeAdd(Args&&... args) { return new(data + length++) T(Core::forward(args)...); } check_return Error setSize(i64 n) { List copy; CORE_RETURN_ERROR(allocate(copy.data, n)); copy.capacity = n; for(i64 i = 0; i < length; i++) { copy.unsafeAdd(Core::move(data[i])); } swap(copy); return Error::NONE; } }; } #endif