module; #include #include export module Core.List; import Core.AlignedData; import Core.Math; import Core.Meta; import Core.Utility; import Core.ToString; export namespace Core { template class List final { size_t length; size_t capacity; T* data; public: List() : length(0), capacity(0), data(nullptr) { } List(const List& other) : length(0), capacity(other.capacity), data(allocate(other.capacity)) { for(const T& t : other) { unsafeAdd(t); } } List(List&& other) noexcept : List() { swap(other); } ~List() { clear(); deleteWithSourceN>( reinterpret_cast*>(data)); } List& operator=(List other) noexcept { swap(other); return *this; } T* begin() { return data; } T* end() { return data + length; } const T* begin() const { return data; } const T* end() const { return data + length; } void reserve(size_t n) { if(n > capacity) { setSize(n); } } void shrink() { if(length != capacity) { setSize(length); } } void resize(size_t n, const T& t) { if(length < n) { reserve(n); for(size_t i = n - length; i != 0; i--) { unsafeAdd(t); } } else if(length > n) { for(size_t i = n; i < length; i++) { data[i].~T(); } length = n; } } void resize(size_t n) { if(length < n) { reserve(n); for(size_t i = n - length; i != 0; i--) { unsafeAdd(T()); } } else if(length > n) { for(size_t i = n; i < length; i++) { data[i].~T(); } length = n; } } template T& put(Args&&... args) { ensureCapacity(); return *unsafeAdd(Core::forward(args)...); } template List& add(Args&&... args) { put(Core::forward(args)...); return *this; } template T& putAt(size_t t, Args&&... args) { if(t >= length) { return put(Core::forward(args)...); } // put must not reallocate, to keep the moved element alive ensureCapacity(); put(Core::move(data[length - 1])); for(size_t i = length - 2; i > t; i--) { data[i] = Core::move(data[i - 1]); } data[t] = Core::move(T(Core::forward(args)...)); return data[t]; } template List& addAt(size_t index, Args&&... args) { putAt(index, Core::forward(args)...); return *this; } T& operator[](size_t index) { assert(index < length); return data[index]; } const T& operator[](size_t index) const { assert(index < length); return data[index]; } T& getLast() { assert(length > 0); return data[length - 1]; } const T& getLast() const { assert(length > 0); return data[length - 1]; } size_t getLength() const { return length; } size_t getCapacity() const { return capacity; } void clear() { for(T& t : *this) { t.~T(); } length = 0; } void removeBySwap(size_t index) { assert(index < length); length--; if(index != length) { data[index] = Core::move(data[length]); } data[length].~T(); } void remove(size_t index) { assert(index < length); length--; T* currentT = begin() + index; T* endT = end(); while(currentT != endT) { T* nextT = currentT + 1; *currentT = Core::move(*nextT); currentT = nextT; } endT->~T(); } void removeLast() { removeBySwap(length - 1); } void swap(List& other) noexcept { Core::swap(length, other.length); Core::swap(capacity, other.capacity); Core::swap(data, other.data); } private: static T* allocate(size_t n) { if(n <= 0) { return nullptr; } return reinterpret_cast(newWithSourceN>(n)); } void ensureCapacity() { if(length >= capacity) { reserve(capacity + Core::max(4lu, capacity / 4)); } } // does not check for capacity template T* unsafeAdd(Args&&... args) { return new(data + length++) T(Core::forward(args)...); } void setSize(size_t n) { List copy; copy.data = allocate(n); copy.capacity = n; for(size_t i = 0; i < length; i++) { copy.unsafeAdd(Core::move(data[i])); } swap(copy); } }; }