diff --git a/javascript/packages/node/binding.gyp b/javascript/packages/node/binding.gyp index 650762632..ea0cd5a1b 100644 --- a/javascript/packages/node/binding.gyp +++ b/javascript/packages/node/binding.gyp @@ -40,6 +40,7 @@ "./extension/libherb/util.c", "./extension/libherb/util/hb_arena.c", "./extension/libherb/util/hb_array.c", + "./extension/libherb/util/hb_narray.c", "./extension/libherb/util/hb_buffer.c", "./extension/libherb/util/hb_string.c", "./extension/libherb/util/hb_system.c", diff --git a/src/include/util/hb_narray.h b/src/include/util/hb_narray.h new file mode 100644 index 000000000..e04b99c4c --- /dev/null +++ b/src/include/util/hb_narray.h @@ -0,0 +1,29 @@ +#ifndef HERB_NARRAY_H +#define HERB_NARRAY_H + +#include +#include +#include + +typedef struct HB_NARRAY_STRUCT { + uint8_t* items; + size_t item_size; + size_t size; + size_t capacity; +} hb_narray_T; + +void hb_narray_init(hb_narray_T* array, size_t item_size, size_t initial_capacity); +#define hb_narray_pointer_init(array, initial_capacity) (hb_narray_init(array, sizeof(void*), initial_capacity)) + +void* hb_narray_get(const hb_narray_T* array, size_t index); +void* hb_narray_first(hb_narray_T* array); +void* hb_narray_last(hb_narray_T* array); + +void hb_narray_append(hb_narray_T* array, void* item); +void hb_narray_remove(hb_narray_T* array, size_t index); +void hb_narray_deinit(hb_narray_T* array); + +#define hb_narray_push(array, item) (hb_narray_append(array, item)) +bool hb_narray_pop(hb_narray_T* array, void* item); + +#endif diff --git a/src/util/hb_narray.c b/src/util/hb_narray.c new file mode 100644 index 000000000..f8ee500b2 --- /dev/null +++ b/src/util/hb_narray.c @@ -0,0 +1,75 @@ +#include "../include/util/hb_narray.h" + +#include +#include +#include + +void hb_narray_init(hb_narray_T* array, size_t item_size, size_t initial_capacity) { + assert(initial_capacity != 0); + + array->item_size = item_size; + array->capacity = initial_capacity; + array->size = 0; + array->items = malloc(array->capacity * array->item_size); +} + +void hb_narray_append(hb_narray_T* array, void* item) { + if (array->size + 1 > array->capacity) { + array->capacity *= 2; + void* new_buffer = realloc(array->items, array->capacity * array->item_size); + assert(new_buffer != NULL); + array->items = new_buffer; + } + + memcpy(array->items + (array->size * array->item_size), item, array->item_size); + array->size += 1; +} + +static inline uint8_t* hb_narray_memory_position(const hb_narray_T* array, size_t index) { + return array->items + (array->item_size * index); +} + +void hb_narray_remove(hb_narray_T* array, size_t index) { + assert(index < array->size); + + if (array->size - 1 > index) { + size_t elements_to_shift = (array->size - 1) - index; + size_t bytes_to_shift = array->item_size * elements_to_shift; + + memcpy(hb_narray_memory_position(array, index), hb_narray_memory_position(array, index + 1), bytes_to_shift); + } + + array->size -= 1; +} + +void* hb_narray_get(const hb_narray_T* array, size_t index) { + assert(index < array->size); + + return hb_narray_memory_position(array, index); +} + +void* hb_narray_first(hb_narray_T* array) { + if (array->size == 0) { return NULL; } + + return hb_narray_get(array, 0); +} + +void* hb_narray_last(hb_narray_T* array) { + if (array->size == 0) { return NULL; } + return hb_narray_get(array, array->size - 1); +} + +bool hb_narray_pop(hb_narray_T* array, void* item) { + if (array->size == 0) { return false; } + memcpy(item, hb_narray_last(array), array->item_size); + array->size -= 1; + + return true; +} + +void hb_narray_deinit(hb_narray_T* array) { + array->item_size = 0; + array->capacity = 0; + array->size = 0; + free(array->items); +} diff --git a/test/c/main.c b/test/c/main.c index 9ec6e06bf..cfc931acd 100644 --- a/test/c/main.c +++ b/test/c/main.c @@ -3,6 +3,7 @@ TCase *hb_arena_tests(void); TCase *hb_array_tests(void); +TCase *hb_narray_tests(void); TCase *hb_buffer_tests(void); TCase *hb_string_tests(void); TCase *herb_tests(void); @@ -18,6 +19,7 @@ Suite *herb_suite(void) { suite_add_tcase(suite, hb_arena_tests()); suite_add_tcase(suite, hb_array_tests()); + suite_add_tcase(suite, hb_narray_tests()); suite_add_tcase(suite, hb_buffer_tests()); suite_add_tcase(suite, hb_string_tests()); suite_add_tcase(suite, herb_tests()); diff --git a/test/c/test_hb_narray.c b/test/c/test_hb_narray.c new file mode 100644 index 000000000..68b47f8de --- /dev/null +++ b/test/c/test_hb_narray.c @@ -0,0 +1,152 @@ +#include "include/test.h" +#include "../../src/include/util/hb_narray.h" + +TEST(test_hb_narray_init) + hb_narray_T array; + + hb_narray_init(&array, sizeof(uint64_t), 1024); + + ck_assert_int_eq(array.item_size, sizeof(uint64_t)); + ck_assert_int_eq(array.capacity, 1024); + ck_assert_int_eq(array.size, 0); + ck_assert_ptr_nonnull(array.items); + + hb_narray_deinit(&array); +END + +TEST(test_hb_narray_pointer_init) + hb_narray_T array; + + hb_narray_pointer_init(&array, 1024); + + ck_assert_int_eq(array.item_size, sizeof(void *)); + ck_assert_int_eq(array.capacity, 1024); + ck_assert_int_eq(array.size, 0); + ck_assert_ptr_nonnull(array.items); + + hb_narray_deinit(&array); +END + +TEST(test_hb_narray_append) + hb_narray_T array; + + hb_narray_init(&array, sizeof(uint64_t), 2); + + uint64_t number = 1; + hb_narray_append(&array, &number); + ck_assert_int_eq(array.capacity, 2); + + number = 2; + hb_narray_append(&array, &number); + ck_assert_int_eq(array.capacity, 2); + + number = 3; + hb_narray_append(&array, &number); + ck_assert_int_eq(array.capacity, 4); + + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 1), 2); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 2), 3); + + ck_assert_int_eq(array.size, 3); + + hb_narray_deinit(&array); +END + +TEST(test_hb_narray_first_last) + hb_narray_T array; + + hb_narray_init(&array, sizeof(uint64_t), 2); + + ck_assert_ptr_null(hb_narray_first(&array)); + ck_assert_ptr_null(hb_narray_last(&array)); + + uint64_t number = 1; + hb_narray_append(&array, &number); + + ck_assert_int_eq(*(uint64_t *)hb_narray_first(&array), 1); + ck_assert_int_eq(*(uint64_t *)hb_narray_last(&array), 1); + + number = 2; + hb_narray_append(&array, &number); + + ck_assert_int_eq(*(uint64_t *)hb_narray_first(&array), 1); + ck_assert_int_eq(*(uint64_t *)hb_narray_last(&array), 2); + + hb_narray_deinit(&array); +END + +TEST(test_hb_narray_stack_behavior) + hb_narray_T array; + + hb_narray_init(&array, sizeof(uint64_t), 2); + + for(uint64_t i = 0; i < 4; i++) { + hb_narray_push(&array, &i); + } + + uint64_t number; + + ck_assert(hb_narray_pop(&array, &number)); + ck_assert_int_eq(number, 3); + ck_assert_int_eq(array.size, 3); + + ck_assert(hb_narray_pop(&array, &number)); + ck_assert_int_eq(number, 2); + ck_assert_int_eq(array.size, 2); + + ck_assert(hb_narray_pop(&array, &number)); + ck_assert_int_eq(number, 1); + ck_assert_int_eq(array.size, 1); + + ck_assert(hb_narray_pop(&array, &number)); + ck_assert_int_eq(number, 0); + ck_assert_int_eq(array.size, 0); + + ck_assert(!hb_narray_pop(&array, &number)); + + hb_narray_deinit(&array); +END + +TEST(test_hb_narray_remove) + hb_narray_T array; + + hb_narray_init(&array, sizeof(uint64_t), 2); + + for(uint64_t i = 0; i < 4; i++) { + hb_narray_push(&array, &i); + } + + hb_narray_remove(&array, 0); + ck_assert_int_eq(array.size, 3); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 1), 2); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 2), 3); + + hb_narray_remove(&array, 1); + ck_assert_int_eq(array.size, 2); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 1), 3); + + hb_narray_remove(&array, 1); + ck_assert_int_eq(array.size, 1); + ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1); + + hb_narray_remove(&array, 0); + ck_assert_int_eq(array.size, 0); + + hb_narray_deinit(&array); +END + +TCase *hb_narray_tests(void) { + TCase *buffer = tcase_create("Herb (New) Array"); + + tcase_add_test(buffer, test_hb_narray_init); + tcase_add_test(buffer, test_hb_narray_pointer_init); + tcase_add_test(buffer, test_hb_narray_append); + tcase_add_test(buffer, test_hb_narray_first_last); + tcase_add_test(buffer, test_hb_narray_stack_behavior); + tcase_add_test(buffer, test_hb_narray_remove); + + return buffer; +}