// Strongly influenced by Array.jai in Basic module. #pragma once // For Arena-Backed arrays use ArenaArray MSVC_RUNTIME_CHECKS_OFF template 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(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(data)[index]; } }; template bool is_resizable (Array& src) { // If we have a valid allocator, we assume this is resizeable. return src.allocator.proc != nullptr; } template bool is_valid (Array 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 void array_zero (const Array& src) { memset(src.data, 0, src.count * sizeof(T)); } template Array array_copy_zero (const Array& src) { if (!src.data || src.count == 0) { return Array(); // Return an empty array } T* new_data = NewArray(src.count, false); memset(new_data, 0, src.count * sizeof(T)); return Array(src.count, new_data, src.allocated); } template Array array_copy (const Array& src) { if (!src.data || src.count == 0) { return Array(); // Return an empty array } T* new_data = NewArray(src.count, false); memcpy(new_data, src.data, src.count * sizeof(T)); return Array(src.count, new_data, src.allocated); } template void array_reset_keeping_memory (Array& src) { src.count = 0; } template void array_free (Array& 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 void array_initialize (Array& 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 void array_reserve (Array& 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 void array_resize (Array& 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 force_inline void array_maybe_grow (Array& 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 T pop (Array& src) { auto result = src[src.count-1]; src.count -= 1; return result; } // template // void array_add (Array& 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 void array_add (Array& 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 s64 array_find (Array& src, T item) { ForArray(i, src) { if (src[i] == item) return i; } return -1; } template void array_ordered_remove_by_index (Array& 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 void array_ordered_remove_by_value (Array& src, T item) { auto index = array_find(src, item); if (index != -1) { array_ordered_remove_by_index(src, index); } } template void array_unordered_remove_by_index (Array& 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 s64 array_unordered_remove_by_value (Array& 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 struct ArrayView { using ValueType = T; s64 count; T* data; ArrayView(Array array) { count = array.count; data = array.data; } ArrayView() { count = 0; data = nullptr; } ArrayView(s64 new_count, bool initialize=true) { count = new_count; data = NewArray(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(data)[index]; } }; template bool is_empty (ArrayView 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 bool is_valid (ArrayView 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(count, data) for initialization! template ArrayView array_view (Array array) { ArrayView av; av.count = array.count; av.data = array.data; return av; } template ArrayView array_view (ArrayView array, s64 start_index, s64 view_count) { ArrayView av; av.count = view_count; // check if count exceeds Assert(start_index + view_count <= array.count); av.data = &array[start_index]; return av; } template ArrayView array_view (Array array, s64 start_index, s64 view_count) { ArrayView av; av.count = view_count; // check if count exceeds Assert(start_index + view_count <= array.count); av.data = &array[start_index]; return av; } template void array_reset_keeping_memory (ArrayView& src) { src.count = 0; } template ArrayView array_copy (const ArrayView& src) { if (!src.data || src.count == 0) { return ArrayView(); // Return an empty array } T* new_data = NewArray(src.count); memcpy(new_data, src.data, src.count * sizeof(T)); return ArrayView(src.count, (T*)new_data); } template void array_free (ArrayView& 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(6,7,8,9,10,51);` template Array array_from_values (ArgValues... args) { constexpr s64 N = sizeof...(ArgValues); auto array = Array(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(1,2,3,4,5);` template ArrayView array_view_from_values (ArgValues... args) { constexpr s64 N = sizeof...(ArgValues); auto array = ArrayView(N, /*initialize:*/false); T values[] = {args...}; for (s64 i = 0; i < N; i += 1) { array[i] = values[i]; } return array; } MSVC_RUNTIME_CHECKS_RESTORE