#ifndef UTIL_H #define UTIL_H #include #include typedef struct vector_t { uint8_t *data; int count; int capacity; int stride; } vector_t; /** * Zero-initialize a vector with the given element stride. * * @param v vector to initialize * @param stride byte size of each element */ static void vec_init(vector_t *v, int stride) { memset(v, 0, sizeof(*v)); v->stride = stride; } /** * Grow the vector's backing array to at least min_capacity elements. * Doubles capacity (starting at 8) or uses min_capacity, whichever is larger. * * @param v vector to grow * @param min_capacity minimum element count required */ static void vec_grow(vector_t *v, int min_capacity) { int new_cap = v->capacity ? v->capacity * 2 : 8; if (new_cap < min_capacity) new_cap = min_capacity; uint8_t *new_data = (uint8_t*) ALLOC(new_cap * v->stride); if (!new_data) { EM_ASM({ console.error("vec_grow: ALLOC failed for %d elements of %d bytes", $0, $1); }, new_cap, v->stride); return; } if (v->data) { memcpy(new_data, v->data, v->count * v->stride); FREE(v->data); } v->data = new_data; v->capacity = new_cap; } /** * Append an uninitialized element to the end of the vector. Grows if needed. * * @param v vector to push into * @return pointer to the new (uninitialized) element */ static void *vec_push(vector_t *v) { if (v->count >= v->capacity) vec_grow(v, v->count + 1); return v->data + (v->count++) * v->stride; } /** * Remove the last element from the vector (decrements count, no free). * * @param v vector to pop from */ static void vec_pop(vector_t *v) { if (v->count > 0) v->count--; } static void vec_remove_ordered(vector_t *v, int index) { if (index < 0 || index >= v->count) return; if (index < v->count - 1) { memmove(v->data + index * v->stride, v->data + (index + 1) * v->stride, (v->count - index - 1) * v->stride); } v->count--; } // Remove `count` elements at given indices in a single compaction pass. // Indices must be sorted in ascending order and must be valid. static void vec_remove_ordered_bulk(vector_t *v, const int *indices, int count) { if (count <= 0) return; int write = indices[0]; for (int k = 0; k < count; k++) { int gap_start = indices[k]; int gap_end = (k + 1 < count) ? indices[k + 1] : v->count; int keep = gap_end - gap_start - 1; if (keep > 0) { memmove(v->data + write * v->stride, v->data + (gap_start + 1) * v->stride, (size_t)keep * (size_t)v->stride); write += keep; } } v->count -= count; } static void *vec_insert(vector_t *v, int index) { if (index < 0 || index > v->count) return NULL; if (v->count >= v->capacity) vec_grow(v, v->count + 1); if (index < v->count) { memmove(v->data + (index + 1) * v->stride, v->data + index * v->stride, (v->count - index) * v->stride); } v->count++; return v->data + index * v->stride; } /** * Remove the element at index by swapping in the last element (O(1)). * Order is not preserved. * * @param v vector to remove from * @param index index of the element to remove */ static void vec_remove(vector_t *v, int index) { if (index < 0 || index >= v->count) return; if (index < v->count - 1) { memcpy(v->data + index * v->stride, v->data + (v->count - 1) * v->stride, v->stride); } v->count--; } /** * Return a pointer to the element at index (no bounds check). * * @param v vector to access * @param index element index * @return pointer to the element */ static void *vec_get(vector_t *v, int index) { return v->data + index * v->stride; } /** * Free the backing array and reset the vector to empty. * * @param v vector to free */ static void vec_free(vector_t *v) { if (v->data) FREE(v->data); v->data = NULL; v->count = 0; v->capacity = 0; } #endif