// vim:fileencoding=utf-8:foldmethod=marker #include "./array.h" #include "../../common/assert/assert.h" #include "../mem/allocator/mem_allocator.h" #include "../../common/misc/misc_utils.h" #include "../../common/aliases/aliases.h" #include #define _array_header(ARRAY) (ArrayHeader *)(wapp_pointer_offset(ARRAY, (i64)sizeof(ArrayHeader) * -1)) wapp_persist inline void _array_validate(const GenericArray array, u64 item_size); u64 _array_count(GenericArray array) { wapp_debug_assert(array != NULL, "`array` should not be NULL"); ArrayHeader *header = _array_header(array); wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); return header->count; } u64 _array_capacity(GenericArray array) { wapp_debug_assert(array != NULL, "`array` should not be NULL"); ArrayHeader *header = _array_header(array); wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); return header->capacity; } u64 _array_item_size(GenericArray array) { wapp_debug_assert(array != NULL, "`array` should not be NULL"); ArrayHeader *header = _array_header(array); wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); return header->item_size; } void _array_set_count(GenericArray array, u64 count) { wapp_debug_assert(array != NULL, "`array` should not be NULL"); ArrayHeader *header = _array_header(array); wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); header->count = count; } void *_array_get(GenericArray array, u64 index, u64 item_size) { wapp_runtime_assert(array != NULL, "`array` should not be NULL"); _array_validate(array, item_size); ArrayHeader *header = _array_header(array); wapp_runtime_assert(index < header->count, "`index` is out of bounds"); return wapp_pointer_offset(array, header->item_size * index); } void _array_set(GenericArray array, u64 index, void *value, u64 item_size) { void *item = _array_get(array, index, item_size); ArrayHeader *header = _array_header(array); memcpy(item, value, header->item_size); } void _array_append_capped(GenericArray array, void *value, u64 item_size) { wapp_runtime_assert(array != NULL, "`array` should not be NULL"); _array_validate(array, item_size); ArrayHeader *header = _array_header(array); if (header->count >= header->capacity) { return; } u64 index = (header->count)++; _array_set(array, index, value, item_size); } void _array_extend_capped(GenericArray dst, const GenericArray src, u64 item_size) { wapp_runtime_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); _array_validate(dst, item_size); _array_validate(src, item_size); ArrayHeader *src_header = _array_header(src); ArrayHeader *dst_header = _array_header(dst); u64 remaining_capacity = dst_header->capacity - dst_header->count; u64 copy_count = src_header->count < remaining_capacity ? src_header->count : remaining_capacity; void *dst_ptr = wapp_pointer_offset(dst, dst_header->count * dst_header->item_size); memcpy(dst_ptr, src, copy_count * src_header->item_size); dst_header->count += copy_count; } void _array_copy_capped(GenericArray dst, const GenericArray src, u64 item_size) { wapp_runtime_assert(dst != NULL && src != NULL, "`dst` and `src` should not be NULL"); _array_validate(dst, item_size); _array_validate(src, item_size); _array_clear(dst, item_size); ArrayHeader *src_header = _array_header(src); ArrayHeader *dst_header = _array_header(dst); u64 copy_count = src_header->count < dst_header->capacity ? src_header->count : dst_header->capacity; memcpy((void *)dst, (void *)src, copy_count * src_header->item_size); dst_header->count = copy_count; } GenericArray _array_append_alloc(const Allocator *allocator, GenericArray array, void *value, ArrayInitFlags flags, u64 item_size) { wapp_runtime_assert(allocator != NULL && array != NULL, "`allocator` and `array` should not be NULL"); _array_validate(array, item_size); GenericArray output = array; ArrayHeader *header = _array_header(array); if (header->count >= header->capacity) { u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(header->capacity * 2); output = (GenericArray )_array_alloc_capacity(allocator, new_capacity, flags, header->item_size); if (!output) { output = array; goto RETURN_ARRAY_APPEND_ALLOC; } _array_copy_capped(output, array, item_size); } _array_append_capped(output, value, item_size); if ((flags & ARRAY_INIT_FILLED) == ARRAY_INIT_FILLED) { _array_set_count(output, _array_capacity(output)); } RETURN_ARRAY_APPEND_ALLOC: return output; } GenericArray _array_extend_alloc(const Allocator *allocator, GenericArray dst, const GenericArray src, ArrayInitFlags flags, u64 item_size) { wapp_runtime_assert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL"); _array_validate(dst, item_size); _array_validate(src, item_size); GenericArray output = dst; ArrayHeader *src_header = _array_header(src); ArrayHeader *dst_header = _array_header(dst); u64 remaining_capacity = dst_header->capacity - dst_header->count; if (src_header->count >= remaining_capacity) { u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(dst_header->capacity * 2); output = (GenericArray )_array_alloc_capacity(allocator, new_capacity, flags, dst_header->item_size); if (!output) { output = dst; goto RETURN_ARRAY_EXTEND_ALLOC; } _array_copy_capped(output, dst, item_size); } _array_extend_capped(output, src, item_size); if ((flags & ARRAY_INIT_FILLED) == ARRAY_INIT_FILLED) { _array_set_count(output, _array_capacity(output)); } RETURN_ARRAY_EXTEND_ALLOC: return output; } GenericArray _array_copy_alloc(const Allocator *allocator, GenericArray dst, const GenericArray src, ArrayInitFlags flags, u64 item_size) { wapp_runtime_assert(allocator != NULL && dst != NULL && src != NULL, "`allocator`, `dst` and `src` should not be NULL"); _array_validate(dst, item_size); _array_validate(src, item_size); GenericArray output = dst; ArrayHeader *src_header = _array_header(src); ArrayHeader *dst_header = _array_header(dst); if (src_header->count >= dst_header->capacity) { u64 new_capacity = wapp_misc_utils_u64_round_up_pow2(dst_header->capacity * 2); output = (GenericArray )_array_alloc_capacity(allocator, new_capacity, flags, src_header->item_size); if (!output) { output = dst; goto RETURN_ARRAY_COPY_ALLOC; } } _array_copy_capped(output, src, item_size); if ((flags & ARRAY_INIT_FILLED) == ARRAY_INIT_FILLED) { _array_set_count(output, _array_capacity(output)); } RETURN_ARRAY_COPY_ALLOC: return output; } void *_array_pop(GenericArray array, u64 item_size) { wapp_runtime_assert(array != NULL, "`array` should not be NULL"); _array_validate(array, item_size); ArrayHeader *header = _array_header(array); if (header->count == 0) { return NULL; } u64 index = header->count - 1; void *out = _array_get(array, index, item_size); --(header->count); return out; } void _array_clear(GenericArray array, u64 item_size) { wapp_runtime_assert(array != NULL, "`array` should not be NULL"); _array_validate(array, item_size); ArrayHeader *header = _array_header(array); header->count = 0; } u64 _array_calc_alloc_size(u64 capacity, u64 item_size) { return sizeof(ArrayHeader) + item_size * capacity; } GenericArray _array_alloc_capacity(const Allocator *allocator, u64 capacity, ArrayInitFlags flags, u64 item_size) { wapp_runtime_assert(allocator != NULL, "`allocator` should not be NULL"); GenericArray output = NULL; u64 allocation_size = _array_calc_alloc_size(capacity, item_size); void *buffer = wapp_mem_allocator_alloc(allocator, allocation_size); if (!buffer) { goto RETURN_ARRAY_ALLOC; } output = _array_from_preallocated_buffer(buffer, allocation_size, flags, item_size); RETURN_ARRAY_ALLOC: return output; } GenericArray _array_from_preallocated_buffer(void *buffer, u64 buffer_size, ArrayInitFlags flags, u64 item_size) { wapp_runtime_assert(buffer != NULL, "`buffer` should not be NULL"); i64 data_buffer_size = (i64)buffer_size - (i64)(sizeof(ArrayHeader)); if (data_buffer_size <= 0) { return NULL; } u64 item_capacity = (u64)data_buffer_size / item_size; ArrayHeader *header = (ArrayHeader *)buffer; GenericArray output = (u8 *)(header + 1); header->magic = WAPP_ARRAY_MAGIC; header->count = flags & ARRAY_INIT_FILLED ? item_capacity : 0; header->capacity = item_capacity; header->item_size = item_size; return output; } wapp_persist inline void _array_validate(const GenericArray array, u64 item_size) { ArrayHeader *header = _array_header(array); wapp_runtime_assert(WAPP_ARRAY_MAGIC == header->magic, "`array` is not a valid wapp array"); wapp_runtime_assert(item_size == header->item_size, "Invalid item type provided"); }