|
@@ -1,131 +1,212 @@
|
|
|
-#ifndef CORE_LIST_H
|
|
|
-#define CORE_LIST_H
|
|
|
+#ifndef CORE_LIST_HPP
|
|
|
+#define CORE_LIST_HPP
|
|
|
|
|
|
-#include <assert.h>
|
|
|
+#include <cassert>
|
|
|
+#include <new>
|
|
|
|
|
|
-#include "core/ToString.hpp"
|
|
|
-#include "core/Types.hpp"
|
|
|
+#include "core/AlignedData.hpp"
|
|
|
+#include "core/Math.hpp"
|
|
|
+#include "core/Meta.hpp"
|
|
|
#include "core/Utility.hpp"
|
|
|
|
|
|
-#define LIST(T, N) \
|
|
|
- struct ListT##N { \
|
|
|
- size_t length; \
|
|
|
- size_t capacity; \
|
|
|
- T* data; \
|
|
|
- }; \
|
|
|
- typedef struct ListT##N List##N; \
|
|
|
- \
|
|
|
- void initList##N(List##N* l); \
|
|
|
- void destroyList##N(List##N* l); \
|
|
|
- void reserveListEntries##N(List##N* l, size_t n); \
|
|
|
- void addListData##N(List##N* l, T data); \
|
|
|
- void addLastListData##N(List##N* l); \
|
|
|
- T* addEmptyListData##N(List##N* l); \
|
|
|
- T* getListIndex##N(const List##N* l, size_t index); \
|
|
|
- T* getListLast##N(const List##N* l); \
|
|
|
- void clearList##N(List##N* l); \
|
|
|
- void removeListIndexBySwap##N(List##N* l, size_t index); \
|
|
|
- void removeListIndex##N(List##N* l, size_t index); \
|
|
|
- void removeListLast##N(List##N* l); \
|
|
|
- T* getListStart##N(const List##N* l); \
|
|
|
- T* getListEnd##N(const List##N* l); \
|
|
|
- typedef size_t (*ToString##N)(const T* data, char* buffer, size_t n); \
|
|
|
- size_t toStringList##N( \
|
|
|
- const List##N* l, char* buffer, size_t n, ToString##N c);
|
|
|
-
|
|
|
-#define LIST_SOURCE(T, N) \
|
|
|
- void initList##N(List##N* l) { \
|
|
|
- *l = (List##N){0, 0, nullptr}; \
|
|
|
- } \
|
|
|
- \
|
|
|
- void destroyList##N(List##N* l) { \
|
|
|
- coreFree(l->data); \
|
|
|
- *l = (List##N){0}; \
|
|
|
- } \
|
|
|
- \
|
|
|
- void reserveListEntries##N(List##N* l, size_t n) { \
|
|
|
- if(n > l->capacity) { \
|
|
|
- l->capacity = n; \
|
|
|
- l->data = coreReallocate(l->data, sizeof(T) * n); \
|
|
|
- } \
|
|
|
- } \
|
|
|
- \
|
|
|
- void addListData##N(List##N* l, T data) { \
|
|
|
- *addEmptyListData##N(l) = data; \
|
|
|
- } \
|
|
|
- \
|
|
|
- void addLastListData##N(List##N* l) { \
|
|
|
- addListData##N(l, *getListLast##N(l)); \
|
|
|
- } \
|
|
|
- \
|
|
|
- T* addEmptyListData##N(List##N* l) { \
|
|
|
- if(l->length >= l->capacity) { \
|
|
|
- reserveListEntries##N( \
|
|
|
- l, l->capacity + maxSize(4lu, l->capacity / 4)); \
|
|
|
- } \
|
|
|
- return l->data + l->length++; \
|
|
|
- } \
|
|
|
- \
|
|
|
- T* getListIndex##N(const List##N* l, size_t index) { \
|
|
|
- assert(index < l->length); \
|
|
|
- return l->data + index; \
|
|
|
- } \
|
|
|
- \
|
|
|
- T* getListLast##N(const List##N* l) { \
|
|
|
- return getListIndex##N(l, l->length - 1); \
|
|
|
- } \
|
|
|
- \
|
|
|
- void clearList##N(List##N* l) { \
|
|
|
- l->length = 0; \
|
|
|
- } \
|
|
|
- \
|
|
|
- void removeListIndexBySwap##N(List##N* l, size_t index) { \
|
|
|
- assert(index < l->length); \
|
|
|
- size_t length = l->length - 1; \
|
|
|
- if(index != length) { \
|
|
|
- l->data[index] = l->data[length]; \
|
|
|
- } \
|
|
|
- l->length = length; \
|
|
|
- } \
|
|
|
- \
|
|
|
- void removeListIndex##N(List##N* l, size_t index) { \
|
|
|
- assert(index < l->length); \
|
|
|
- l->length--; \
|
|
|
- T* p = l->data + index; \
|
|
|
- T* end = getListEnd##N(l); \
|
|
|
- while(p != end) { \
|
|
|
- *p = *(p + 1); \
|
|
|
- p++; \
|
|
|
- } \
|
|
|
- } \
|
|
|
- \
|
|
|
- void removeListLast##N(List##N* l) { \
|
|
|
- removeListIndexBySwap##N(l, l->length - 1); \
|
|
|
- } \
|
|
|
- \
|
|
|
- T* getListStart##N(const List##N* l) { \
|
|
|
- return l->data; \
|
|
|
- } \
|
|
|
- \
|
|
|
- T* getListEnd##N(const List##N* l) { \
|
|
|
- return l->data + l->length; \
|
|
|
- } \
|
|
|
- \
|
|
|
- size_t toStringList##N( \
|
|
|
- const List##N* l, char* buffer, size_t n, ToString##N c) { \
|
|
|
- size_t w = 0; \
|
|
|
- stringAdd(&w, &buffer, &n, toString(buffer, n, "[")); \
|
|
|
- T* end = getListEnd##N(l); \
|
|
|
- T* p = getListStart##N(l); \
|
|
|
- while(p != end) { \
|
|
|
- stringAdd(&w, &buffer, &n, c(p, buffer, n)); \
|
|
|
- p++; \
|
|
|
- if(p != end) { \
|
|
|
- stringAdd(&w, &buffer, &n, toString(buffer, n, ", ")); \
|
|
|
- } \
|
|
|
- } \
|
|
|
- stringAdd(&w, &buffer, &n, toString(buffer, n, "]")); \
|
|
|
- return w; \
|
|
|
- }
|
|
|
+namespace Core {
|
|
|
+ template<typename T>
|
|
|
+ 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) : List() {
|
|
|
+ swap(other);
|
|
|
+ }
|
|
|
+
|
|
|
+ ~List() {
|
|
|
+ clear();
|
|
|
+ delete[] reinterpret_cast<AlignedType<T>*>(data);
|
|
|
+ }
|
|
|
+
|
|
|
+ List& operator=(List other) {
|
|
|
+ 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<typename... Args>
|
|
|
+ T& put(Args&&... args) {
|
|
|
+ ensureCapacity();
|
|
|
+ return *unsafeAdd(Core::forward<Args>(args)...);
|
|
|
+ }
|
|
|
+
|
|
|
+ template<typename... Args>
|
|
|
+ List& add(Args&&... args) {
|
|
|
+ put(Core::forward<Args>(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) {
|
|
|
+ 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<T*>(coreNewN(AlignedType<T>, n));
|
|
|
+ }
|
|
|
+
|
|
|
+ void ensureCapacity() {
|
|
|
+ if(length >= capacity) {
|
|
|
+ reserve(capacity + Core::max(4lu, capacity / 4));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // does not check for capacity
|
|
|
+ template<typename... Args>
|
|
|
+ T* unsafeAdd(Args&&... args) {
|
|
|
+ return new(data + length++) T(Core::forward<Args>(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);
|
|
|
+ }
|
|
|
+ };
|
|
|
+}
|
|
|
|
|
|
#endif
|