509 lines
13 KiB
C++
509 lines
13 KiB
C++
#pragma once
|
|
/*
|
|
// Iterating and removing elements
|
|
for (int i = 0; i < array.len; i += 1) {
|
|
auto &it = array[i];
|
|
bool remove_item = false;
|
|
defer {
|
|
if (remove_item) {
|
|
array.ordered_remove(it);
|
|
i -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Simple delete
|
|
IterRemove(arr) {
|
|
IterRemovePrepare(arr);
|
|
|
|
remove_item = true;
|
|
}
|
|
|
|
// Deleting backwards
|
|
For(arr.reverse_iter()) {
|
|
defer{ arr.unordered_remove(it); };
|
|
}
|
|
*/
|
|
#define IterRemove(a) for (int i = 0; i < (a).len; i += 1)
|
|
#define IterRemovePrepare(a) \
|
|
auto &it = (a)[i]; \
|
|
bool remove_item = false; \
|
|
defer { \
|
|
if (remove_item) { \
|
|
Remove(&(a), it); \
|
|
i -= 1; \
|
|
} \
|
|
}
|
|
#define ForItem(it, array) for (auto &it : (array))
|
|
#define For(array) ForItem(it, array)
|
|
|
|
template <class T>
|
|
struct Slice {
|
|
T *data;
|
|
int64_t len;
|
|
|
|
Slice() = default;
|
|
Slice(T *s, int64_t l) : data(s), len(l) {}
|
|
|
|
T &operator[](int64_t index) {
|
|
Assert(index < len);
|
|
return data[index];
|
|
}
|
|
T *begin() { return data; }
|
|
T *end() { return data + len; }
|
|
};
|
|
|
|
template <class T>
|
|
Slice<T> Copy(Allocator alo, Slice<T> array) {
|
|
Slice<T> result = {};
|
|
result.data = AllocArray(alo, T, array.len);
|
|
memcpy(result.data, array.data, sizeof(T) * array.len);
|
|
result.len = array.len;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
T Pop(Slice<T> *arr) {
|
|
Assert(arr->len > 0);
|
|
return arr->data[--arr->len];
|
|
}
|
|
|
|
template <class T>
|
|
bool Contains(Slice<T> &arr, T item) {
|
|
For(arr) if (it == item) return true;
|
|
return false;
|
|
}
|
|
|
|
template <class T>
|
|
int64_t GetIndex(Slice<T> &arr, const T &item) {
|
|
ptrdiff_t index = (ptrdiff_t)(&item - arr.data);
|
|
Assert(index >= 0 && index < arr.len);
|
|
return (int64_t)index;
|
|
}
|
|
|
|
template <class T>
|
|
T Get(Slice<T> &arr, int64_t i, T default_value = {}) {
|
|
T result = default_value;
|
|
if (i >= 0 && i < arr.len) result = arr[i];
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
bool IsLast(Slice<T> &arr, T &item) {
|
|
bool result = arr.last() == &item;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
bool IsFirst(Slice<T> &arr, T &item) {
|
|
bool result = arr.first() == &item;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
T *GetFirst(Slice<T> &arr) {
|
|
Assert(arr.len > 0);
|
|
return arr.data;
|
|
}
|
|
|
|
template <class T>
|
|
T *GetLast(Slice<T> &arr) {
|
|
Assert(arr.len > 0);
|
|
return arr.data + arr.len - 1;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> Chop(Slice<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
Slice<T> result = {arr.data, arr.len - len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> Skip(Slice<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
Slice<T> result = {arr.data + len, arr.len - len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> GetPostfix(Slice<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
int64_t remain_len = arr.len - len;
|
|
Slice<T> result = {arr.data + remain_len, len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> GetPrefix(Slice<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
Slice<T> result = {arr.data, len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> GetSlice(Slice<T> &arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST) {
|
|
// Negative indexes work in python style, they return you the index counting from end of list
|
|
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
|
|
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
|
|
|
|
if (first_index == SLICE_LAST) first_index = arr.len;
|
|
if (first_index < 0) first_index = arr.len + first_index;
|
|
|
|
Slice<T> result = {arr.data, arr.len};
|
|
if (arr.len > 0) {
|
|
if (one_past_last_index > first_index) {
|
|
first_index = ClampTop(first_index, arr.len - 1);
|
|
one_past_last_index = ClampTop(one_past_last_index, arr.len);
|
|
result.data += first_index;
|
|
result.len = one_past_last_index - first_index;
|
|
} else {
|
|
result.len = 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Make arrays resize on every item
|
|
#define ARRAY_DEBUG BUILD_SLOW
|
|
#if ARRAY_DEBUG
|
|
#define ARRAY_IF_DEBUG_ELSE(IF, ELSE) IF
|
|
#else
|
|
#define ARRAY_IF_DEBUG_ELSE(IF, ELSE) ELSE
|
|
#endif
|
|
|
|
template <class T>
|
|
struct Array {
|
|
Allocator allocator;
|
|
int64_t cap;
|
|
union {
|
|
Slice<T> slice;
|
|
struct {
|
|
T *data;
|
|
int64_t len;
|
|
};
|
|
};
|
|
|
|
T &operator[](int64_t index) {
|
|
Assert(index >= 0 && index < len);
|
|
return data[index];
|
|
}
|
|
T *begin() { return data; }
|
|
T *end() { return data + len; }
|
|
};
|
|
|
|
template <class T>
|
|
T *GetFirst(Array<T> &arr) {
|
|
Assert(arr.len > 0);
|
|
return arr.data;
|
|
}
|
|
|
|
template <class T>
|
|
T *GetLast(Array<T> &arr) {
|
|
Assert(arr.len > 0);
|
|
return arr.data + arr.len - 1;
|
|
}
|
|
|
|
template <class T>
|
|
void Reserve(Array<T> *arr, int64_t size) {
|
|
if (size > arr->cap) {
|
|
if (!arr->allocator.proc) arr->allocator = GetSystemAllocator();
|
|
|
|
T *new_data = AllocArray(arr->allocator, T, size);
|
|
Assert(new_data);
|
|
memcpy(new_data, arr->data, arr->len * sizeof(T));
|
|
Dealloc(arr->allocator, arr->data);
|
|
|
|
arr->data = new_data;
|
|
arr->cap = size;
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void TryGrowing(Array<T> *arr) {
|
|
if (arr->len + 1 > arr->cap) {
|
|
int64_t initial_size = (int64_t)ARRAY_IF_DEBUG_ELSE(1, 16);
|
|
int64_t new_size = ClampBottom(initial_size, arr->cap ARRAY_IF_DEBUG_ELSE(+1, *2));
|
|
Reserve(arr, new_size);
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void TryGrowing(Array<T> *arr, int64_t item_count) {
|
|
if (arr->len + item_count > arr->cap) {
|
|
int64_t initial_size = (int64_t)ARRAY_IF_DEBUG_ELSE(1, 16);
|
|
int64_t new_size = ClampBottom(initial_size, (arr->cap + item_count) ARRAY_IF_DEBUG_ELSE(+1, *2));
|
|
Reserve(arr, new_size);
|
|
}
|
|
}
|
|
|
|
template <class T>
|
|
void Add(Array<T> *arr, T item) {
|
|
TryGrowing(arr);
|
|
arr->data[arr->len++] = item;
|
|
}
|
|
|
|
template <class T>
|
|
void Add(Array<T> *arr, Array<T> &another) {
|
|
For(another) Add(arr, it);
|
|
}
|
|
|
|
template <class T>
|
|
void Add(Array<T> *arr, T *items, int64_t item_count) {
|
|
for (int64_t i = 0; i < item_count; i += 1) Add(arr, items[i]);
|
|
}
|
|
|
|
template <class T>
|
|
void BoundedAdd(Array<T> *arr, T item) {
|
|
if (arr->len + 1 <= arr->cap) arr->data[arr->len++] = item;
|
|
}
|
|
|
|
template <class T>
|
|
void BoundedAddError(Array<T> *arr, T item) {
|
|
Assert(arr->len + 1 <= arr->cap);
|
|
if (arr->len + 1 <= arr->cap) arr->data[arr->len++] = item;
|
|
}
|
|
|
|
template <class T>
|
|
void Insert(Array<T> *arr, T item, int64_t index) {
|
|
if (index == arr->len) {
|
|
Add(arr, item);
|
|
return;
|
|
}
|
|
|
|
Assert(index < arr->len);
|
|
Assert(index >= 0);
|
|
TryGrowing(arr);
|
|
int64_t right_len = arr->len - index;
|
|
memmove(arr->data + index + 1, arr->data + index, sizeof(T) * right_len);
|
|
arr->data[index] = item;
|
|
arr->len += 1;
|
|
}
|
|
|
|
template <class T>
|
|
Array<T> Copy(Allocator alo, Array<T> array) {
|
|
Array<T> result = {alo};
|
|
Reserve(&result, array.cap);
|
|
memcpy(result.data, array.data, sizeof(T) * array.len);
|
|
result.len = array.len;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Array<T> TightCopy(Allocator alo, Array<T> array) {
|
|
Array<T> result = {alo};
|
|
Reserve(&result, array.len);
|
|
memcpy(result.data, array.data, sizeof(T) * array.len);
|
|
result.len = array.len;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
T Pop(Array<T> *arr) {
|
|
Assert(arr->len > 0);
|
|
return arr->data[--arr->len];
|
|
}
|
|
|
|
template <class T>
|
|
bool Contains(Array<T> &arr, T item) {
|
|
For(arr) if (it == item) return true;
|
|
return false;
|
|
}
|
|
|
|
template<class T>
|
|
int64_t FindValueIndex(Array<T> &arr, T item) {
|
|
for (int64_t i = 0; i < arr.len; i += 1) {
|
|
if (arr.data[i] == item) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
template <class T>
|
|
int64_t GetIndex(Array<T> &arr, const T &item) {
|
|
ptrdiff_t index = (ptrdiff_t)(&item - arr.data);
|
|
Assert(index >= 0 && index < arr.len);
|
|
return (int64_t)index;
|
|
}
|
|
|
|
template <class T>
|
|
void RemoveByIndex(Array<T> *arr, int64_t index) {
|
|
Assert(index >= 0 && index < arr->len);
|
|
int64_t right_len = arr->len - index - 1;
|
|
memmove(arr->data + index, arr->data + index + 1, right_len * sizeof(T));
|
|
arr->len -= 1;
|
|
}
|
|
|
|
template <class T>
|
|
void RemoveManyByIndex(Array<T> *arr, int64_t index, int64_t count) {
|
|
if (count == 0) return;
|
|
Assert(index >= 0 && index < arr->len);
|
|
Assert((index + count) > 0 && (index + count) <= arr->len);
|
|
int64_t right_len = arr->len - index - count;
|
|
memmove(arr->data + index, arr->data + index + count, right_len * sizeof(T));
|
|
arr->len -= count;
|
|
}
|
|
|
|
template <class T>
|
|
void RemoveMany(Array<T> *arr, T &item, int64_t count) {
|
|
Assert(arr->len > 0);
|
|
Assert(&item >= arr->begin() && &item < arr->end());
|
|
int64_t index = GetIndex(*arr, item);
|
|
RemoveManyByIndex(arr, index, count);
|
|
}
|
|
|
|
template <class T>
|
|
void Remove(Array<T> *arr, T &item) {
|
|
Assert(arr->len > 0);
|
|
Assert(&item >= arr->begin() && &item < arr->end());
|
|
int64_t index = GetIndex(*arr, item);
|
|
RemoveByIndex(arr, index);
|
|
}
|
|
|
|
template <class T>
|
|
void UnorderedRemove(Array<T> *arr, T &item) {
|
|
Assert(arr->len > 0);
|
|
Assert((&item >= arr->begin()) && (&item < arr->end()));
|
|
item = arr->data[--arr->len];
|
|
}
|
|
|
|
template <class T>
|
|
void UnorderedRemoveByIndex(Array<T> *arr, int64_t index) {
|
|
Assert(arr->len > 0);
|
|
Assert(index >= 0 && index < arr->len);
|
|
arr->data[index] = arr->data[--arr->len];
|
|
}
|
|
|
|
template <class T>
|
|
void InsertArray(Array<T> *arr, T *items, int64_t count, int64_t index) {
|
|
if (index == arr->len) {
|
|
Add(arr, items, count);
|
|
return;
|
|
}
|
|
Assert(index < arr->len);
|
|
|
|
TryGrowing(arr, count);
|
|
T *gap_begin = arr->data + index;
|
|
T *gap_end = gap_begin + count;
|
|
int64_t item_count = arr->len - index;
|
|
memmove(gap_end, gap_begin, item_count * sizeof(T));
|
|
for (int64_t i = 0; i < count; i += 1) arr->data[index + i] = items[i];
|
|
arr->len += count;
|
|
}
|
|
|
|
template <class T>
|
|
T Get(Array<T> &arr, int64_t i, T default_value = {}) {
|
|
T result = default_value;
|
|
if (i >= 0 && i < arr.len) result = arr[i];
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
void Dealloc(Array<T> *arr) {
|
|
if (arr->data) {
|
|
Dealloc(arr->allocator, arr->data);
|
|
arr->data = NULL;
|
|
}
|
|
arr->len = arr->cap = 0;
|
|
}
|
|
|
|
template <class T>
|
|
bool IsLast(Array<T> &arr, T &item) {
|
|
bool result = GetLast(arr) == &item;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
bool IsFirst(Array<T> &arr, T &item) {
|
|
bool result = GetFirst(arr) == &item;
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> Chop(Array<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
Slice<T> result = {arr.data, arr.len - len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> Skip(Array<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
Slice<T> result = {arr.data + len, arr.len - len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> GetPostfix(Array<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
int64_t remain_len = arr.len - len;
|
|
Slice<T> result = {arr.data + remain_len, len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> GetPrefix(Array<T> &arr, int64_t len) {
|
|
len = ClampTop(len, arr.len);
|
|
Slice<T> result = {arr.data, len};
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
Slice<T> GetSlice(Array<T> &arr, int64_t first_index = 0, int64_t one_past_last_index = SLICE_LAST) {
|
|
// Negative indexes work in python style, they return you the index counting from end of list
|
|
if (one_past_last_index == SLICE_LAST) one_past_last_index = arr.len;
|
|
if (one_past_last_index < 0) one_past_last_index = arr.len + one_past_last_index;
|
|
|
|
if (first_index == SLICE_LAST) first_index = arr.len;
|
|
if (first_index < 0) first_index = arr.len + first_index;
|
|
|
|
Slice<T> result = {arr.data, arr.len};
|
|
if (arr.len > 0) {
|
|
if (one_past_last_index > first_index) {
|
|
first_index = ClampTop(first_index, arr.len - 1);
|
|
one_past_last_index = ClampTop(one_past_last_index, arr.len);
|
|
result.data += first_index;
|
|
result.len = one_past_last_index - first_index;
|
|
} else {
|
|
result.len = 0;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
template <class T>
|
|
struct ReverseIter {
|
|
T *data;
|
|
Slice<T> *arr;
|
|
|
|
ReverseIter operator++(int) {
|
|
ReverseIter ret = *this;
|
|
data -= 1;
|
|
return ret;
|
|
}
|
|
ReverseIter &operator++() {
|
|
data -= 1;
|
|
return *this;
|
|
}
|
|
|
|
T &operator*() { return data[0]; }
|
|
T *operator->() { return data; }
|
|
|
|
friend bool operator==(const ReverseIter &a, const ReverseIter &b) { return a.data == b.data; };
|
|
friend bool operator!=(const ReverseIter &a, const ReverseIter &b) { return a.data != b.data; };
|
|
|
|
ReverseIter begin() { return ReverseIter{arr->end() - 1, arr}; }
|
|
ReverseIter end() { return ReverseIter{arr->begin() - 1, arr}; }
|
|
};
|
|
|
|
template <class T>
|
|
ReverseIter<T> IterateInReverse(Array<T> *arr) {
|
|
return {arr->end() - 1, &arr->slice};
|
|
}
|
|
template <class T>
|
|
ReverseIter<T> IterateInReverse(Slice<T> *slice) {
|
|
return {slice->end() - 1, slice};
|
|
}
|
|
|