#pragma once #include "Allocator.h" #include "Arena.h" constexpr s64 ARRAY_ARENA_START_OFFSET = 64; template struct ArenaArray { // downcasts to an ArrayView. using ValueType = T; s64 count; T* data; s64 allocated; Arena* arena; // We can probably assume arena is &Array-32 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]; } }; // #NOTE: I am not defining arena_array_init (ArenaArray*), because I do not want to // encourage it's usage! // #TODO: array_free vs arena_array_destroy or arena_array_delete or template ArenaArray* arena_array_new (s64 preallocate_count, Arena_Reserve reserve_size) { Arena* arena = next_arena(reserve_size); Push_Arena push_arena(arena); Push_Alignment push_alignment(arena, 1); ArenaArray* array = New>(true); array->arena = arena; s64 commit_size_bytes = preallocate_count * sizeof(T); if (arena_commit_first_pages(array->arena, commit_size_bytes, ARRAY_ARENA_START_OFFSET)) { array->allocated = preallocate_count; } array.count = 0; array.arena = new_arena; array.data = array_start(array); return array; } template T* array_start (ArenaArray& array) { return (array->arena->memory_base + ARRAY_ARENA_START_OFFSET); } template bool is_empty (ArenaArray& array) { return ((array.count == 0) || !is_valid(array.arena)); } template s64 memory_usage (ArenaArray& array) { if (array == nullptr) return 0; return arena_usage_committed_bytes(array.arena); } template void array_free (ArenaArray& array) { array.count = 0; array.allocated = 0; release_arena(array.arena, delete_extra_pages=true); } template ArrayView array_view (Array array) { ArrayView av; av.count = array.count; av.data = array.data; return av; } template ArrayView to_view (ArenaArray& array) { ArrayView av; av.count = array.count; av.data = array.data; return av; } template ArrayView to_view (ArenaArray& array, s64 start_offset, s64 count) { Assert(start_offset >= 0); Assert(count >= 0); ArrayView av = { 0, nullptr }; if (start_offset >= array.count) { return av; // empty } av.count = count; av.data = array.data + start_offset; if (start_offset + count > array.count) { av.count = array.count - offset; } return av; } template void array_add (ArenaArray& array, T item) { maybe_grow(array); array.data[array.count] = item; array.count += 1; } template T* array_add (ArenaArray& array) { maybe_grow(array); T* result = &array.data[array.count]; (*result) = T(); array.count += 1; return result; } template force_inline void maybe_grow (ArenaArray& array) { if (array.count >= array.allocated) { s64 reserve = 2 * array.allocated; // if reserve < 8 reserve = 8; // no point doing this because we allocate by page, and we're never realloc'ing reserve_internal(array, reserve, sizeof(T)); } } template force_inline void array_reserve (ArenaArray& array, s64 desired_item_count) { reserve_internal((ArenaArray&)array, desired_item_count, sizeof(T)); } template void array_resize (ArenaArray& array, s64 desired_item_count, bool initialize) { s64 old_count = array.count; reserve_internal((ArenaArray&)array, desired_item_count, sizeof(T)); array.count = desired_item_count; if (initialize) { init_range(array.data, old_count, desired_item_count); } } void reserve_internal (ArenaArray& array, s64 desired_item_count, s64 element_size) { if (desired_item_count <= array.allocated) return; array_arena_realloc(array, desired_item_count * element_size, array.allocated * element_size); array.allocated = desired_item_count; } s64 max_array_size (ArenaArray& array) { return reserve_size(array.arena) - sizeof(Arena) - sizeof(ArenaArray); } void array_arena_realloc (ArenaArray& array, s64 new_size, s64 old_size) { Assert(new_size <= max_array_size(array)); void* array_begin = (void*)array.data; void* result_end = array_begin + new_size; // Check if we need more pages: if (result_end > array.arena.first_uncommitted_page) { // Critical error if we run out of address space! if (result_end > address_limit(array.arena)) { // #TODO Log error. Assert(false); // Failed to allocate because Arena is full and cannot expand return; } extend_committed_pages(array.arena, (u8*)result_end); } } template void init_range (T* ptr, s64 start_offset, s64 end_offset) { for (s64 i = start_offset; i < end_offset; i += 1) { T* current_item = ptr + i; (*current_item) = T(); // is this correct in-place init? } } template force_inline void array_reset (ArenaArray& array) { // reset backing array: arena_reset(array.arena); array.count = 0; array.allocated = 0; } template force_inline void reset_keeping_memory (ArenaArray& array) { array.count = 0; } template force_inline void ordered_remove_by_index (ArenaArray& array, s64 index) { Assert(index >= 0); Assert(index < array.count); for (s64 i = index; i < array.count-1; i += 1) { array.data[i] = array.data[i - 1]; } array.count -= 1; } template force_inline void unordered_remove_by_index (ArenaArray& array, s64 index) { Assert(index >= 0); Assert(index < array.count); s64 last_index = array.count - 1; if index != last_index { array.data[index] = array.data[last_index]; } array.count -= 1; } template void insert_at(ArenaArray& array, s64 offset, ArenaArray& src_array) { Assert(offset >= 0 && offset <= array.count); if (!is_valid(src_array)) return; s64 new_count = array.count + src_array.count; array_reserve(array, new_count); T* src = array.data + offset; T* dst = src + src_array.count; memcpy(dst, src, (array.count - offset) * sizeof(T)); memcpy(array.data + offset, src_array.data, src_array.count * sizeof(T)); } template T pop (ArenaArray& array) { T result = array.data[array.count-1]; array.count -= 1; return result; } template T peek (ArenaArray& array) { return array.data[array.count-1]; } template T* peek_pointer (ArenaArray& array) { return &(array.data[array.count-1]); } template void delete_range (ArenaArray& array, s64 start_offset, s64 count) { Assert(start_offset >= 0 && count >= 0 && start_offset + count <= array.count); memcpy(array.data + start_offset, array.data + start_offset + count, (array.count - start_offset - count) * sizeof(T)); array.count -= count; } template ArenaArray& array_copy (ArenaArray& array) { auto new_array = arena_array_new(array.arena.reserve_size); array_reserve(new_array, array.count); memcpy(new_array.data, array.data, array.count * sizeof(T)); new_array.count = array.count; return new_array; } template ArrayView array_copy_as_view (ArenaArray& array) { ArrayView view = { array.count, array.data }; return view; } // [x] initialize_range // [x] max_array_size // [x] maybe_grow // [x] resize // [x] reserve // [x] reserve_internal // [x] array_arena_realloc // [x] reset_keeping_memory // [x] ordered_remove_by_index // [x] unordered_remove_by_index // [x] insert_at // [x] pop // [x] peek // [x] peek_pointer // [x] delete_range // [x] array_copy