378 lines
9.3 KiB
C++
378 lines
9.3 KiB
C++
// Strongly influenced by Array.jai in Basic module.
|
|
#pragma once
|
|
|
|
// For Arena-Backed arrays use ArenaArray
|
|
|
|
MSVC_RUNTIME_CHECKS_OFF
|
|
|
|
template <typename T>
|
|
struct Array { // downcasts to an ArrayView.
|
|
using ValueType = T;
|
|
s64 count;
|
|
T* data;
|
|
s64 allocated;
|
|
Allocator allocator;
|
|
|
|
Array() {
|
|
memset(this, 0, sizeof(*this));
|
|
}
|
|
|
|
Array(s64 new_count, bool initialize=false) { // old: NewArray ::, array_new :
|
|
count = new_count;
|
|
allocator = get_context_allocator();
|
|
data = NewArray<T>(new_count, initialize);
|
|
allocated = new_count;
|
|
}
|
|
|
|
// Used by array_zero, array_copy, etc.
|
|
Array(s64 new_count, void* new_data, s64 _allocated) {
|
|
count = new_count;
|
|
data = (T*)new_data;
|
|
allocated = _allocated;
|
|
allocator = get_context_allocator();
|
|
}
|
|
|
|
Array(s64 new_count, void* new_data, s64 _allocated, Allocator _allocator) {
|
|
count = new_count;
|
|
data = (T*)new_data;
|
|
allocated = _allocated;
|
|
allocator = _allocator;
|
|
}
|
|
|
|
T& operator[](s64 index) {
|
|
#if ARRAY_ENABLE_BOUNDS_CHECKING
|
|
if (index < 0 || index >= count) { debug_break(); } // index out of bounds
|
|
#endif
|
|
return static_cast<T*>(data)[index];
|
|
}
|
|
};
|
|
|
|
template <typename T> bool is_resizable (Array<T>& src) {
|
|
// If we have a valid allocator, we assume this is resizeable.
|
|
return src.allocator.proc != nullptr;
|
|
}
|
|
|
|
template <typename T>
|
|
bool is_valid (Array<T> src) {
|
|
if (src.count == 0) return true;
|
|
if (src.count < 0) return false;
|
|
if (src.data == nullptr) return false;
|
|
if (src.allocated < src.count) return false;
|
|
return true;
|
|
}
|
|
|
|
template <typename T>
|
|
void array_zero (const Array<T>& src) {
|
|
memset(src.data, 0, src.count * sizeof(T));
|
|
}
|
|
|
|
template <typename T>
|
|
Array<T> array_copy_zero (const Array<T>& src) {
|
|
if (!src.data || src.count == 0) {
|
|
return Array<T>(); // Return an empty array
|
|
}
|
|
|
|
T* new_data = NewArray<T>(src.count, false);
|
|
memset(new_data, 0, src.count * sizeof(T));
|
|
|
|
return Array<T>(src.count, new_data, src.allocated);
|
|
}
|
|
|
|
template <typename T>
|
|
Array<T> array_copy (const Array<T>& src) {
|
|
if (!src.data || src.count == 0) {
|
|
return Array<T>(); // Return an empty array
|
|
}
|
|
|
|
T* new_data = NewArray<T>(src.count, false);
|
|
memcpy(new_data, src.data, src.count * sizeof(T));
|
|
|
|
return Array<T>(src.count, new_data, src.allocated);
|
|
}
|
|
|
|
template <typename T>
|
|
void array_reset_keeping_memory (Array<T>& src) {
|
|
src.count = 0;
|
|
}
|
|
|
|
template <typename T>
|
|
void array_free (Array<T>& src) {
|
|
if (!src.data) return;
|
|
if (src.allocated == 0) return;
|
|
if (src.allocator.proc != nullptr) {
|
|
src.allocator.proc(Allocator_Mode::DEALLOCATE, 0, 0, src.data, src.allocator.data);
|
|
} else {
|
|
internal_free(src.data);
|
|
}
|
|
src.count = 0;
|
|
src.data = nullptr;
|
|
src.allocated = 0;
|
|
}
|
|
|
|
template <typename T>
|
|
void array_initialize (Array<T>& src, s64 start, s64 end) {
|
|
for (s64 i = start; i < end; i += 1) {
|
|
// Really this can be one ini followed by a bunch of memcpy.
|
|
// For long arrays we could power-of-two double the copy out, etc.
|
|
src[i] = T(); // `new (&src[i]) T();` also works.
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
void array_reserve (Array<T>& src, s64 desired_items) {
|
|
if (desired_items <= src.allocated) return;
|
|
|
|
src.data = nullptr;
|
|
if (src.allocator.proc == nullptr) {
|
|
src.allocator = get_context_allocator();
|
|
}
|
|
|
|
Assert(src.allocator.proc != nullptr);
|
|
|
|
src.data = (T*)src.allocator.proc(Allocator_Mode::RESIZE, desired_items * sizeof(T), src.allocated * sizeof(T), src.data, src.allocator.data);
|
|
|
|
Assert(src.data != nullptr);
|
|
|
|
src.allocated = desired_items;
|
|
}
|
|
|
|
template <typename T>
|
|
void array_resize (Array<T>& src, s64 new_count, bool initialize=true) {
|
|
if (src.count == new_count) return;
|
|
|
|
s64 old_count = src.count;
|
|
|
|
array_reserve(src, new_count);
|
|
src.count = new_count;
|
|
|
|
if (initialize) { array_initialize(src, old_count, new_count); }
|
|
}
|
|
|
|
template <typename T>
|
|
force_inline void array_maybe_grow (Array<T>& src) {
|
|
if (src.count >= src.allocated) {
|
|
// Replace with Basic.max(8, 2 * src.count).
|
|
s64 reserve = 8;
|
|
if (src.count * 2 > reserve) { reserve = src.count * 2; }
|
|
array_reserve(src, reserve);
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
T pop (Array<T>& src) {
|
|
auto result = src[src.count-1];
|
|
src.count -= 1;
|
|
return result;
|
|
}
|
|
|
|
// template <typename T, typename U>
|
|
// void array_add (Array<T>& src, U new_item) {
|
|
// static_assert(sizeof(U) <= sizeof(T));
|
|
// auto new_count = src.count + 1;
|
|
// array_maybe_grow(src);
|
|
|
|
// T new_item_casted = (T)new_item;
|
|
|
|
// src.count += 1;
|
|
// memcpy(&src[src.count-1], &new_item_casted, sizeof(T));
|
|
// }
|
|
|
|
template <typename T>
|
|
void array_add (Array<T>& src, T new_item) {
|
|
array_maybe_grow(src);
|
|
|
|
src.data[src.count] = new_item;
|
|
|
|
src.count += 1;
|
|
// auto dst_ptr = &src.data[src.count-1];
|
|
// memcpy(dst_ptr, &new_item, sizeof(T));
|
|
}
|
|
|
|
template <typename T>
|
|
s64 array_find (Array<T>& src, T item) {
|
|
ForArray(i, src) {
|
|
if (src[i] == item) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
template <typename T>
|
|
void array_ordered_remove_by_index (Array<T>& src, s64 index) {
|
|
Assert(index >= 0); Assert(index < src.count);
|
|
|
|
for (s64 i = index; i < src.count-1; i += 1) {
|
|
src[i] = src[i + 1];
|
|
}
|
|
|
|
src.count -= 1;
|
|
}
|
|
|
|
template <typename T>
|
|
void array_ordered_remove_by_value (Array<T>& src, T item) {
|
|
auto index = array_find(src, item);
|
|
if (index != -1) { array_ordered_remove_by_index(src, index); }
|
|
}
|
|
|
|
template <typename T>
|
|
void array_unordered_remove_by_index (Array<T>& src, s64 index) {
|
|
Assert(index >= 0); Assert(index < src.count);
|
|
|
|
auto last_index = src.count - 1;
|
|
|
|
if (index != last_index) { // Copy back item:
|
|
memcpy(&src[index], &src[last_index], sizeof(T));
|
|
}
|
|
|
|
src.count -= 1;
|
|
}
|
|
|
|
template <typename T>
|
|
s64 array_unordered_remove_by_value (Array<T>& src, T item, s64 max_count_to_remove) {
|
|
s64 removed_count = 0;
|
|
|
|
for (s64 i = 0; i < src.count; i += 1) {
|
|
if (src[i] == item) {
|
|
removed_count += 1;
|
|
array_unordered_remove_by_index(src, i);
|
|
|
|
i -= 1; // check this element index again
|
|
if (max_count_to_remove == removed_count) { break; }
|
|
}
|
|
}
|
|
|
|
return removed_count;
|
|
}
|
|
|
|
template <typename T>
|
|
struct ArrayView {
|
|
using ValueType = T;
|
|
s64 count;
|
|
T* data;
|
|
|
|
ArrayView(Array<T> array) {
|
|
count = array.count;
|
|
data = array.data;
|
|
}
|
|
|
|
ArrayView() { count = 0; data = nullptr; }
|
|
|
|
ArrayView(s64 new_count, bool initialize=true) {
|
|
count = new_count;
|
|
data = NewArray<T>(new_count, initialize);
|
|
}
|
|
|
|
// #Note: use array_view to create slices or to downcast to ArrayView!
|
|
ArrayView(s64 _count, T* _data) {
|
|
count = _count;
|
|
data = _data;
|
|
}
|
|
|
|
T& operator[](s64 index) {
|
|
#if ARRAY_ENABLE_BOUNDS_CHECKING
|
|
if (index < 0 || index >= count) { debug_break(); } // index out of bounds
|
|
#endif
|
|
return static_cast<T*>(data)[index];
|
|
}
|
|
};
|
|
|
|
template <typename T>
|
|
bool is_empty (ArrayView<T> src) {
|
|
if (src.count == 0) return true;
|
|
return false;
|
|
}
|
|
|
|
// #NOTE: procedures should be robust to arrays with count of zero!
|
|
// Whether or not this is an error is procedure specific, but for most
|
|
// things, there is a default behavior that is expected.
|
|
template <typename T>
|
|
bool is_valid (ArrayView<T> src) {
|
|
if (src.count < 0) return false;
|
|
if (src.count == 0) return true;
|
|
if (src.data == nullptr) return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
// can also use ArrayView<T>(count, data) for initialization!
|
|
template <typename T>
|
|
ArrayView<T> array_view (Array<T> array) {
|
|
ArrayView<T> av;
|
|
av.count = array.count;
|
|
av.data = array.data;
|
|
return av;
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayView<T> array_view (ArrayView<T> array, s64 start_index, s64 view_count) {
|
|
ArrayView<T> av;
|
|
av.count = view_count; // check if count exceeds
|
|
Assert(start_index + view_count <= array.count);
|
|
av.data = &array[start_index];
|
|
return av;
|
|
}
|
|
|
|
template <typename T>
|
|
ArrayView<T> array_view (Array<T> array, s64 start_index, s64 view_count) {
|
|
ArrayView<T> av;
|
|
av.count = view_count; // check if count exceeds
|
|
Assert(start_index + view_count <= array.count);
|
|
av.data = &array[start_index];
|
|
return av;
|
|
}
|
|
|
|
template <typename T>
|
|
void array_reset_keeping_memory (ArrayView<T>& src) {
|
|
src.count = 0;
|
|
}
|
|
|
|
|
|
template <typename T>
|
|
ArrayView<T> array_copy (const ArrayView<T>& src) {
|
|
if (!src.data || src.count == 0) {
|
|
return ArrayView<T>(); // Return an empty array
|
|
}
|
|
|
|
T* new_data = NewArray<T>(src.count);
|
|
memcpy(new_data, src.data, src.count * sizeof(T));
|
|
|
|
return ArrayView<T>(src.count, (T*)new_data);
|
|
}
|
|
|
|
template <typename T>
|
|
void array_free (ArrayView<T>& src) {
|
|
if (!src.data || src.count == 0) { return; }
|
|
// Use with caution!
|
|
internal_free(src.data); // we just have to trust that the context.allocator is correct for this guy!
|
|
|
|
src.count = 0;
|
|
src.data = nullptr;
|
|
}
|
|
|
|
// Usage: `auto array = array_from_values<s32>(6,7,8,9,10,51);`
|
|
template <typename T, typename... ArgValues>
|
|
Array<T> array_from_values (ArgValues... args) {
|
|
constexpr s64 N = sizeof...(ArgValues);
|
|
auto array = Array<T>(N, /*initialize:*/false);
|
|
T values[] = {args...};
|
|
for (s64 i = 0; i < N; i += 1) {
|
|
array[i] = values[i];
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
|
|
// Usage `auto view = array_view_from_values<s32>(1,2,3,4,5);`
|
|
template <typename T, typename... ArgValues>
|
|
ArrayView<T> array_view_from_values (ArgValues... args) {
|
|
constexpr s64 N = sizeof...(ArgValues);
|
|
auto array = ArrayView<T>(N, /*initialize:*/false);
|
|
T values[] = {args...};
|
|
for (s64 i = 0; i < N; i += 1) {
|
|
array[i] = values[i];
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
MSVC_RUNTIME_CHECKS_RESTORE |