Musa-Cpp-Lib-V2/lib/Base/Array.h

390 lines
9.8 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 = 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 = 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 = 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) {
for (s64 i = 0; i < src.count; i += 1) {
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;
// #TODO: Add initializers ArrayView<u8> from string, ArrayView<T> from ArenaArray<T>
ArrayView(Array<T> array) { // auto-downcast from Array<T>
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> ArrayView<u8> to_byte_view (ArrayView<T> src) {
ArrayView<u8> byte_view = { src.count * sizeof(T), src.data };
return byte_view;
}
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> to_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>
ArrayView<T> array_copy (Allocator allocator, const ArrayView<T>& src) {
push_allocator(allocator);
return array_copy(src);
}
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