#define ARRAY_GROW_FORMULA(x) (gb_max(((x)+1)*3 >> 1, 8)) GB_STATIC_ASSERT(ARRAY_GROW_FORMULA(0) > 0); template struct Array { gbAllocator allocator; T * data; isize count; isize capacity; T &operator[](isize index) { #if !defined(NO_ARRAY_BOUNDS_CHECK) GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count); #endif return data[index]; } T const &operator[](isize index) const { #if !defined(NO_ARRAY_BOUNDS_CHECK) GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count); #endif return data[index]; } }; template gb_internal void array_init (Array *array, gbAllocator const &a); template gb_internal void array_init (Array *array, gbAllocator const &a, isize count); template gb_internal void array_init (Array *array, gbAllocator const &a, isize count, isize capacity); template gb_internal Array array_make (gbAllocator const &a); template gb_internal Array array_make (gbAllocator const &a, isize count); template gb_internal Array array_make (gbAllocator const &a, isize count, isize capacity); template gb_internal Array array_make_from_ptr (T *data, isize count, isize capacity); template gb_internal void array_free (Array *array); template gb_internal void array_add (Array *array, T const &t); template gb_internal T * array_add_and_get (Array *array); template gb_internal void array_add_elems (Array *array, T const *elems, isize elem_count); template gb_internal T array_pop (Array *array); template gb_internal void array_clear (Array *array); template gb_internal void array_reserve (Array *array, isize capacity); template gb_internal void array_resize (Array *array, isize count); template gb_internal void array_set_capacity (Array *array, isize capacity); template gb_internal Array array_slice (Array const &array, isize lo, isize hi); template gb_internal Array array_clone (gbAllocator const &a, Array const &array); template gb_internal void array_ordered_remove (Array *array, isize index); template gb_internal void array_unordered_remove(Array *array, isize index); template gb_internal void array_copy(Array *array, Array const &data, isize offset); template gb_internal void array_copy(Array *array, Array const &data, isize offset, isize count); template gb_internal T *array_end_ptr(Array *array); template gb_internal void array_sort(Array &array, gbCompareProc compare_proc) { gb_sort_array(array.data, array.count, compare_proc); } template struct Slice { T *data; isize count; gb_inline T &operator[](isize index) { #if !defined(NO_ARRAY_BOUNDS_CHECK) GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count); #endif return data[index]; } gb_inline T const &operator[](isize index) const { #if !defined(NO_ARRAY_BOUNDS_CHECK) GB_ASSERT_MSG(cast(usize)index < cast(usize)count, "Index %td is out of bounds ranges 0..<%td", index, count); #endif return data[index]; } }; template gb_internal Slice slice_from_array(Array const &a); template gb_internal Slice slice_make(gbAllocator const &allocator, isize count) { GB_ASSERT(count >= 0); Slice s = {}; s.data = gb_alloc_array(allocator, T, count); if (count > 0) { GB_ASSERT(s.data != nullptr); } s.count = count; return s; } template gb_internal void slice_init(Slice *s, gbAllocator const &allocator, isize count) { GB_ASSERT(count >= 0); s->data = gb_alloc_array(allocator, T, count); if (count > 0) { GB_ASSERT(s->data != nullptr); } s->count = count; } template gb_internal void slice_free(Slice *s, gbAllocator const &allocator) { gb_free(allocator, s->data); } template gb_internal void slice_resize(Slice *s, gbAllocator const &allocator, isize new_count) { resize_array_raw(&s->data, allocator, s->count, new_count); s->count = new_count; } template gb_internal Slice slice_from_array(Array const &a) { return {a.data, a.count}; } template gb_internal Slice slice_array(Array const &array, isize lo, isize hi) { GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count); Slice out = {}; isize len = hi-lo; if (len > 0) { out.data = array.data+lo; out.count = len; } return out; } template gb_internal Slice slice_clone(gbAllocator const &allocator, Slice const &a) { T *data = cast(T *)gb_alloc_copy_align(allocator, a.data, a.count*gb_size_of(T), gb_align_of(T)); return {data, a.count}; } template gb_internal Slice slice_clone_from_array(gbAllocator const &allocator, Array const &a) { auto c = array_clone(allocator, a); return {c.data, c.count}; } template gb_internal void slice_copy(Slice *slice, Slice const &data) { isize n = gb_min(slice->count, data.count); gb_memmove(slice->data, data.data, gb_size_of(T)*n); } template gb_internal void slice_copy(Slice *slice, Slice const &data, isize offset) { isize n = gb_clamp(slice->count-offset, 0, data.count); gb_memmove(slice->data+offset, data.data, gb_size_of(T)*n); } template gb_internal void slice_copy(Slice *slice, Slice const &data, isize offset, isize count) { isize n = gb_clamp(slice->count-offset, 0, gb_min(data.count, count)); gb_memmove(slice->data+offset, data.data, gb_size_of(T)*n); } template gb_internal gb_inline Slice slice(Slice const &array, isize lo, isize hi) { GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count); Slice out = {}; isize len = hi-lo; if (len > 0) { out.data = array.data+lo; out.count = len; } return out; } template gb_internal gb_inline Slice slice(Array const &array, isize lo, isize hi) { GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count); Slice out = {}; isize len = hi-lo; if (len > 0) { out.data = array.data+lo; out.count = len; } return out; } template gb_internal void slice_ordered_remove(Slice *array, isize index) { GB_ASSERT(0 <= index && index < array->count); isize bytes = gb_size_of(T) * (array->count-(index+1)); gb_memmove(array->data+index, array->data+index+1, bytes); array->count -= 1; } template gb_internal void slice_unordered_remove(Slice *array, isize index) { GB_ASSERT(0 <= index && index < array->count); isize n = array->count-1; if (index != n) { gb_memmove(array->data+index, array->data+n, gb_size_of(T)); } array->count -= 1; } template gb_internal void array_copy(Array *array, Array const &data, isize offset) { gb_memmove(array->data+offset, data.data, gb_size_of(T)*data.count); } template gb_internal void array_copy(Array *array, Array const &data, isize offset, isize count) { gb_memmove(array->data+offset, data.data, gb_size_of(T)*gb_min(data.count, count)); } template gb_internal T *array_end_ptr(Array *array) { if (array->count > 0) { return &array->data[array->count-1]; } return nullptr; } template gb_internal gb_inline void array_init(Array *array, gbAllocator const &a) { isize cap = ARRAY_GROW_FORMULA(0); array_init(array, a, 0, cap); } template gb_internal gb_inline void array_init(Array *array, gbAllocator const &a, isize count) { array_init(array, a, count, count); } template gb_internal gb_inline void array_init(Array *array, gbAllocator const &a, isize count, isize capacity) { array->allocator = a; array->data = nullptr; if (capacity > 0) { array->data = gb_alloc_array(a, T, capacity); } array->count = count; array->capacity = capacity; } template gb_internal gb_inline Array array_make_from_ptr(T *data, isize count, isize capacity) { Array a = {0}; a.data = data; a.count = count; a.capacity = capacity; return a; } template gb_internal gb_inline Array array_make(gbAllocator const &a) { isize capacity = ARRAY_GROW_FORMULA(0); Array array = {}; array.allocator = a; array.data = gb_alloc_array(a, T, capacity); array.count = 0; array.capacity = capacity; return array; } template gb_internal gb_inline Array array_make(gbAllocator const &a, isize count) { Array array = {}; array.allocator = a; array.data = gb_alloc_array(a, T, count); array.count = count; array.capacity = count; return array; } template gb_internal gb_inline Array array_make(gbAllocator const &a, isize count, isize capacity) { Array array = {}; array.allocator = a; array.data = gb_alloc_array(a, T, capacity); array.count = count; array.capacity = capacity; return array; } template gb_internal gb_inline void array_free(Array *array) { if (array->allocator.proc != nullptr) { gb_free(array->allocator, array->data); } array->count = 0; array->capacity = 0; } template gb_internal void array__grow(Array *array, isize min_capacity) { isize new_capacity = ARRAY_GROW_FORMULA(array->capacity); if (new_capacity < min_capacity) { new_capacity = min_capacity; } array_set_capacity(array, new_capacity); } template gb_internal void array_add(Array *array, T const &t) { if (array->capacity < array->count+1) { array__grow(array, 0); } array->data[array->count] = t; array->count++; } gb_internal void array_add(Array *array, char const *t) { if (array->capacity < array->count+1) { array__grow(array, 0); } array->data[array->count] = t; array->count++; } template gb_internal T *array_add_and_get(Array *array) { if (array->count < array->capacity) { return &array->data[array->count++]; } if (array->capacity < array->count+1) { array__grow(array, 0); } return &array->data[array->count++]; } template gb_internal void array_add_elems(Array *array, T const *elems, isize elem_count) { GB_ASSERT(elem_count >= 0); if (array->capacity < array->count+elem_count) { array__grow(array, array->count+elem_count); } gb_memmove(array->data + array->count, elems, elem_count * gb_size_of(T)); array->count += elem_count; } template gb_internal gb_inline T array_pop(Array *array) { GB_ASSERT(array->count > 0); array->count--; return array->data[array->count]; } template gb_internal void array_clear(Array *array) { array->count = 0; } template gb_internal void array_reserve(Array *array, isize capacity) { if (array->capacity < capacity) { array_set_capacity(array, capacity); } } template gb_internal void array_resize(Array *array, isize count) { if (array->capacity < count) { array__grow(array, count); } array->count = count; } template gb_internal void array_set_capacity(Array *array, isize capacity) { if (capacity == array->capacity) { return; } if (capacity < array->count) { array_resize(array, capacity); } isize old_size = array->capacity * gb_size_of(T); isize new_size = capacity * gb_size_of(T); T *new_data = nullptr; // NOTE(bill): try gb_resize_align first, and then fallback to alloc+memmove+free new_data = cast(T *)gb_resize_align(array->allocator, array->data, old_size, new_size, gb_align_of(T)); if (new_data == nullptr) { if (capacity > 0) { new_data = gb_alloc_array(array->allocator, T, capacity); GB_ASSERT(new_data != nullptr); gb_memmove(new_data, array->data, old_size); } gb_free(array->allocator, array->data); } array->data = new_data; array->capacity = capacity; } template gb_internal gb_inline Array array_slice(Array const &array, isize lo, isize hi) { GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count); Array out = {}; isize len = hi-lo; if (len > 0) { out.data = array.data+lo; out.count = len; out.capacity = len; } return out; } template gb_internal Array array_clone(gbAllocator const &allocator, Array const &array) { auto clone = array_make(allocator, array.count, array.count); array_copy(&clone, array, 0); return clone; } template gb_internal void array_ordered_remove(Array *array, isize index) { GB_ASSERT(0 <= index && index < array->count); isize bytes = gb_size_of(T) * (array->count-(index+1)); gb_memmove(array->data+index, array->data+index+1, bytes); array->count -= 1; } template gb_internal void array_unordered_remove(Array *array, isize index) { GB_ASSERT(0 <= index && index < array->count); isize n = array->count-1; if (index != n) { gb_memmove(array->data+index, array->data+n, gb_size_of(T)); } array_pop(array); } template gb_internal T *begin(Array &array) { return array.data; } template gb_internal T const *begin(Array const &array) { return array.data; } template gb_internal T *end(Array &array) { return array.data + array.count; } template gb_internal T const *end(Array const &array) { return array.data + array.count; } template gb_internal T *begin(Slice &array) { return array.data; } template gb_internal T const *begin(Slice const &array) { return array.data; } template gb_internal T *end(Slice &array) { return array.data + array.count; } template gb_internal T const *end(Slice const &array) { return array.data + array.count; }