Skip to content

Commit ba1d227

Browse files
authored
C: New hb_array implementation (#782)
This PR adds a new version of the `hb_array` called `hb_narray` that implements an array that can actually hold the elements it stores. The existing `hb_array` only allows to store pointers to heap allocated elements, the new `hb_narray` acts as a container that also provides the backing memory for the elements it holds. It's important to note that you can still make the elements pointers if you want to. ## Why? - Improves locality of the elements - Reduces the number of individual heap allocations for elements ## Next steps 1. Get this merged 2. Refactor existing `hb_array` usages 3. Remove `hb_array` and rename `hb_narray` to `hb_array` ## Memory layout ![herb_new_array](https://github.com/user-attachments/assets/8012ff75-38c0-418c-a188-6a8d0fa90baa)
1 parent 96329ce commit ba1d227

File tree

5 files changed

+259
-0
lines changed

5 files changed

+259
-0
lines changed

javascript/packages/node/binding.gyp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"./extension/libherb/util.c",
4141
"./extension/libherb/util/hb_arena.c",
4242
"./extension/libherb/util/hb_array.c",
43+
"./extension/libherb/util/hb_narray.c",
4344
"./extension/libherb/util/hb_buffer.c",
4445
"./extension/libherb/util/hb_string.c",
4546
"./extension/libherb/util/hb_system.c",

src/include/util/hb_narray.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
#ifndef HERB_NARRAY_H
2+
#define HERB_NARRAY_H
3+
4+
#include <stdint.h>
5+
#include <stdlib.h>
6+
#include <stdbool.h>
7+
8+
typedef struct HB_NARRAY_STRUCT {
9+
uint8_t* items;
10+
size_t item_size;
11+
size_t size;
12+
size_t capacity;
13+
} hb_narray_T;
14+
15+
void hb_narray_init(hb_narray_T* array, size_t item_size, size_t initial_capacity);
16+
#define hb_narray_pointer_init(array, initial_capacity) (hb_narray_init(array, sizeof(void*), initial_capacity))
17+
18+
void* hb_narray_get(const hb_narray_T* array, size_t index);
19+
void* hb_narray_first(hb_narray_T* array);
20+
void* hb_narray_last(hb_narray_T* array);
21+
22+
void hb_narray_append(hb_narray_T* array, void* item);
23+
void hb_narray_remove(hb_narray_T* array, size_t index);
24+
void hb_narray_deinit(hb_narray_T* array);
25+
26+
#define hb_narray_push(array, item) (hb_narray_append(array, item))
27+
bool hb_narray_pop(hb_narray_T* array, void* item);
28+
29+
#endif

src/util/hb_narray.c

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include "../include/util/hb_narray.h"
2+
3+
#include <assert.h>
4+
#include <stdbool.h>
5+
#include <string.h>
6+
7+
void hb_narray_init(hb_narray_T* array, size_t item_size, size_t initial_capacity) {
8+
assert(initial_capacity != 0);
9+
10+
array->item_size = item_size;
11+
array->capacity = initial_capacity;
12+
array->size = 0;
13+
array->items = malloc(array->capacity * array->item_size);
14+
}
15+
16+
void hb_narray_append(hb_narray_T* array, void* item) {
17+
if (array->size + 1 > array->capacity) {
18+
array->capacity *= 2;
19+
void* new_buffer = realloc(array->items, array->capacity * array->item_size);
20+
assert(new_buffer != NULL);
21+
array->items = new_buffer;
22+
}
23+
24+
memcpy(array->items + (array->size * array->item_size), item, array->item_size);
25+
array->size += 1;
26+
}
27+
28+
static inline uint8_t* hb_narray_memory_position(const hb_narray_T* array, size_t index) {
29+
return array->items + (array->item_size * index);
30+
}
31+
32+
void hb_narray_remove(hb_narray_T* array, size_t index) {
33+
assert(index < array->size);
34+
35+
if (array->size - 1 > index) {
36+
size_t elements_to_shift = (array->size - 1) - index;
37+
size_t bytes_to_shift = array->item_size * elements_to_shift;
38+
39+
memcpy(hb_narray_memory_position(array, index), hb_narray_memory_position(array, index + 1), bytes_to_shift);
40+
}
41+
42+
array->size -= 1;
43+
}
44+
45+
void* hb_narray_get(const hb_narray_T* array, size_t index) {
46+
assert(index < array->size);
47+
48+
return hb_narray_memory_position(array, index);
49+
}
50+
51+
void* hb_narray_first(hb_narray_T* array) {
52+
if (array->size == 0) { return NULL; }
53+
54+
return hb_narray_get(array, 0);
55+
}
56+
57+
void* hb_narray_last(hb_narray_T* array) {
58+
if (array->size == 0) { return NULL; }
59+
return hb_narray_get(array, array->size - 1);
60+
}
61+
62+
bool hb_narray_pop(hb_narray_T* array, void* item) {
63+
if (array->size == 0) { return false; }
64+
memcpy(item, hb_narray_last(array), array->item_size);
65+
array->size -= 1;
66+
67+
return true;
68+
}
69+
70+
void hb_narray_deinit(hb_narray_T* array) {
71+
array->item_size = 0;
72+
array->capacity = 0;
73+
array->size = 0;
74+
free(array->items);
75+
}

test/c/main.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
TCase *hb_arena_tests(void);
55
TCase *hb_array_tests(void);
6+
TCase *hb_narray_tests(void);
67
TCase *hb_buffer_tests(void);
78
TCase *hb_string_tests(void);
89
TCase *herb_tests(void);
@@ -18,6 +19,7 @@ Suite *herb_suite(void) {
1819

1920
suite_add_tcase(suite, hb_arena_tests());
2021
suite_add_tcase(suite, hb_array_tests());
22+
suite_add_tcase(suite, hb_narray_tests());
2123
suite_add_tcase(suite, hb_buffer_tests());
2224
suite_add_tcase(suite, hb_string_tests());
2325
suite_add_tcase(suite, herb_tests());

test/c/test_hb_narray.c

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
#include "include/test.h"
2+
#include "../../src/include/util/hb_narray.h"
3+
4+
TEST(test_hb_narray_init)
5+
hb_narray_T array;
6+
7+
hb_narray_init(&array, sizeof(uint64_t), 1024);
8+
9+
ck_assert_int_eq(array.item_size, sizeof(uint64_t));
10+
ck_assert_int_eq(array.capacity, 1024);
11+
ck_assert_int_eq(array.size, 0);
12+
ck_assert_ptr_nonnull(array.items);
13+
14+
hb_narray_deinit(&array);
15+
END
16+
17+
TEST(test_hb_narray_pointer_init)
18+
hb_narray_T array;
19+
20+
hb_narray_pointer_init(&array, 1024);
21+
22+
ck_assert_int_eq(array.item_size, sizeof(void *));
23+
ck_assert_int_eq(array.capacity, 1024);
24+
ck_assert_int_eq(array.size, 0);
25+
ck_assert_ptr_nonnull(array.items);
26+
27+
hb_narray_deinit(&array);
28+
END
29+
30+
TEST(test_hb_narray_append)
31+
hb_narray_T array;
32+
33+
hb_narray_init(&array, sizeof(uint64_t), 2);
34+
35+
uint64_t number = 1;
36+
hb_narray_append(&array, &number);
37+
ck_assert_int_eq(array.capacity, 2);
38+
39+
number = 2;
40+
hb_narray_append(&array, &number);
41+
ck_assert_int_eq(array.capacity, 2);
42+
43+
number = 3;
44+
hb_narray_append(&array, &number);
45+
ck_assert_int_eq(array.capacity, 4);
46+
47+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1);
48+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 1), 2);
49+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 2), 3);
50+
51+
ck_assert_int_eq(array.size, 3);
52+
53+
hb_narray_deinit(&array);
54+
END
55+
56+
TEST(test_hb_narray_first_last)
57+
hb_narray_T array;
58+
59+
hb_narray_init(&array, sizeof(uint64_t), 2);
60+
61+
ck_assert_ptr_null(hb_narray_first(&array));
62+
ck_assert_ptr_null(hb_narray_last(&array));
63+
64+
uint64_t number = 1;
65+
hb_narray_append(&array, &number);
66+
67+
ck_assert_int_eq(*(uint64_t *)hb_narray_first(&array), 1);
68+
ck_assert_int_eq(*(uint64_t *)hb_narray_last(&array), 1);
69+
70+
number = 2;
71+
hb_narray_append(&array, &number);
72+
73+
ck_assert_int_eq(*(uint64_t *)hb_narray_first(&array), 1);
74+
ck_assert_int_eq(*(uint64_t *)hb_narray_last(&array), 2);
75+
76+
hb_narray_deinit(&array);
77+
END
78+
79+
TEST(test_hb_narray_stack_behavior)
80+
hb_narray_T array;
81+
82+
hb_narray_init(&array, sizeof(uint64_t), 2);
83+
84+
for(uint64_t i = 0; i < 4; i++) {
85+
hb_narray_push(&array, &i);
86+
}
87+
88+
uint64_t number;
89+
90+
ck_assert(hb_narray_pop(&array, &number));
91+
ck_assert_int_eq(number, 3);
92+
ck_assert_int_eq(array.size, 3);
93+
94+
ck_assert(hb_narray_pop(&array, &number));
95+
ck_assert_int_eq(number, 2);
96+
ck_assert_int_eq(array.size, 2);
97+
98+
ck_assert(hb_narray_pop(&array, &number));
99+
ck_assert_int_eq(number, 1);
100+
ck_assert_int_eq(array.size, 1);
101+
102+
ck_assert(hb_narray_pop(&array, &number));
103+
ck_assert_int_eq(number, 0);
104+
ck_assert_int_eq(array.size, 0);
105+
106+
ck_assert(!hb_narray_pop(&array, &number));
107+
108+
hb_narray_deinit(&array);
109+
END
110+
111+
TEST(test_hb_narray_remove)
112+
hb_narray_T array;
113+
114+
hb_narray_init(&array, sizeof(uint64_t), 2);
115+
116+
for(uint64_t i = 0; i < 4; i++) {
117+
hb_narray_push(&array, &i);
118+
}
119+
120+
hb_narray_remove(&array, 0);
121+
ck_assert_int_eq(array.size, 3);
122+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1);
123+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 1), 2);
124+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 2), 3);
125+
126+
hb_narray_remove(&array, 1);
127+
ck_assert_int_eq(array.size, 2);
128+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1);
129+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 1), 3);
130+
131+
hb_narray_remove(&array, 1);
132+
ck_assert_int_eq(array.size, 1);
133+
ck_assert_int_eq(*(uint64_t *)hb_narray_get(&array, 0), 1);
134+
135+
hb_narray_remove(&array, 0);
136+
ck_assert_int_eq(array.size, 0);
137+
138+
hb_narray_deinit(&array);
139+
END
140+
141+
TCase *hb_narray_tests(void) {
142+
TCase *buffer = tcase_create("Herb (New) Array");
143+
144+
tcase_add_test(buffer, test_hb_narray_init);
145+
tcase_add_test(buffer, test_hb_narray_pointer_init);
146+
tcase_add_test(buffer, test_hb_narray_append);
147+
tcase_add_test(buffer, test_hb_narray_first_last);
148+
tcase_add_test(buffer, test_hb_narray_stack_behavior);
149+
tcase_add_test(buffer, test_hb_narray_remove);
150+
151+
return buffer;
152+
}

0 commit comments

Comments
 (0)