42
42
//
43
43
// bool rb_darray_append(rb_darray(T) *ptr_to_ary, T element);
44
44
//
45
- #define rb_darray_append (ptr_to_ary , element ) ( \
46
- rb_darray_ensure_space((ptr_to_ary)) ? ( \
47
- rb_darray_set(*(ptr_to_ary), \
48
- (*(ptr_to_ary))->meta.size, \
49
- (element)), \
50
- ++((*(ptr_to_ary))->meta.size), \
51
- 1 \
45
+ #define rb_darray_append (ptr_to_ary , element ) ( \
46
+ rb_darray_ensure_space((ptr_to_ary), sizeof(**(ptr_to_ary)), sizeof((*(ptr_to_ary))->data[0])) ? ( \
47
+ rb_darray_set(*(ptr_to_ary), \
48
+ (*(ptr_to_ary))->meta.size, \
49
+ (element)), \
50
+ ++((*(ptr_to_ary))->meta.size), \
51
+ 1 \
52
52
) : 0)
53
53
54
+ // Remove the last element of the array.
55
+ //
56
+ #define rb_darray_pop_back (ary ) ((ary)->meta.size--)
57
+
54
58
// Iterate over items of the array in a for loop
55
59
//
56
60
#define rb_darray_foreach (ary , idx_name , elem_ptr_var ) \
57
61
for (int idx_name = 0; idx_name < rb_darray_size(ary) && ((elem_ptr_var) = rb_darray_ref(ary, idx_name)); ++idx_name)
58
62
63
+ // Make a dynamic array of a certain size. All bytes backing the elements are set to zero.
64
+ // Return 1 on success and 0 on failure.
65
+ //
66
+ // Note that NULL is a valid empty dynamic array.
67
+ //
68
+ // bool rb_darray_make(rb_darray(T) *ptr_to_ary, int32_t size);
69
+ //
70
+ #define rb_darray_make (ptr_to_ary , size ) rb_darray_make_impl((ptr_to_ary), size, sizeof(**(ptr_to_ary)), sizeof((*(ptr_to_ary))->data[0]))
71
+
59
72
typedef struct rb_darray_meta {
60
73
int32_t size ;
61
74
int32_t capa ;
@@ -87,25 +100,24 @@ rb_darray_free(void *ary)
87
100
free (ary );
88
101
}
89
102
90
- // Remove the last element of the array.
91
- //
92
- #define rb_darray_pop_back (ary ) ((ary)->meta.size--)
93
-
94
- // Internal macro
95
- // Ensure there is space for one more element. Return 1 on success and 0 on failure.
96
- // `ptr_to_ary` is evaluated multiple times.
97
- #define rb_darray_ensure_space (ptr_to_ary ) ( \
98
- (rb_darray_capa(*(ptr_to_ary)) > rb_darray_size(*(ptr_to_ary))) ? \
99
- 1 : \
100
- rb_darray_double(ptr_to_ary, sizeof((*(ptr_to_ary))->data[0])))
103
+ // Internal function. Calculate buffer size on malloc heap.
104
+ static inline size_t
105
+ rb_darray_buffer_size (int32_t capacity , size_t header_size , size_t element_size )
106
+ {
107
+ if (capacity == 0 ) return 0 ;
108
+ return header_size + (size_t )capacity * element_size ;
109
+ }
101
110
102
111
// Internal function
112
+ // Ensure there is space for one more element. Return 1 on success and 0 on failure.
113
+ // Note: header_size can be bigger than sizeof(rb_darray_meta_t) when T is __int128_t, for example.
103
114
static inline int
104
- rb_darray_double (void * ptr_to_ary , size_t element_size )
115
+ rb_darray_ensure_space (void * ptr_to_ary , size_t header_size , size_t element_size )
105
116
{
106
117
rb_darray_meta_t * * ptr_to_ptr_to_meta = ptr_to_ary ;
107
- const rb_darray_meta_t * meta = * ptr_to_ptr_to_meta ;
118
+ rb_darray_meta_t * meta = * ptr_to_ptr_to_meta ;
108
119
int32_t current_capa = rb_darray_capa (meta );
120
+ if (rb_darray_size (meta ) < current_capa ) return 1 ;
109
121
110
122
int32_t new_capa ;
111
123
// Calculate new capacity
@@ -119,11 +131,11 @@ rb_darray_double(void *ptr_to_ary, size_t element_size)
119
131
}
120
132
121
133
// Calculate new buffer size
122
- size_t current_buffer_size = element_size * ( size_t ) current_capa + ( meta ? sizeof ( * meta ) : 0 );
123
- size_t new_buffer_size = element_size * ( size_t ) new_capa + sizeof ( * meta );
134
+ size_t current_buffer_size = rb_darray_buffer_size ( current_capa , header_size , element_size );
135
+ size_t new_buffer_size = rb_darray_buffer_size ( new_capa , header_size , element_size );
124
136
if (new_buffer_size <= current_buffer_size ) return 0 ;
125
137
126
- rb_darray_meta_t * doubled_ary = realloc (* ptr_to_ptr_to_meta , new_buffer_size );
138
+ rb_darray_meta_t * doubled_ary = realloc (meta , new_buffer_size );
127
139
if (!doubled_ary ) return 0 ;
128
140
129
141
if (meta == NULL ) {
@@ -134,7 +146,32 @@ rb_darray_double(void *ptr_to_ary, size_t element_size)
134
146
135
147
doubled_ary -> capa = new_capa ;
136
148
137
- * ptr_to_ptr_to_meta = doubled_ary ;
149
+ // We don't have access to the type of the dynamic array in function context.
150
+ // Write out result with memcpy to avoid strict aliasing issue.
151
+ memcpy (ptr_to_ary , & doubled_ary , sizeof (doubled_ary ));
152
+ return 1 ;
153
+ }
154
+
155
+ static inline int
156
+ rb_darray_make_impl (void * ptr_to_ary , int32_t array_size , size_t header_size , size_t element_size )
157
+ {
158
+ rb_darray_meta_t * * ptr_to_ptr_to_meta = ptr_to_ary ;
159
+ if (array_size < 0 ) return 0 ;
160
+ if (array_size == 0 ) {
161
+ * ptr_to_ptr_to_meta = NULL ;
162
+ return 1 ;
163
+ }
164
+
165
+ size_t buffer_size = rb_darray_buffer_size (array_size , header_size , element_size );
166
+ rb_darray_meta_t * meta = calloc (buffer_size , 1 );
167
+ if (!meta ) return 0 ;
168
+
169
+ meta -> size = array_size ;
170
+ meta -> capa = array_size ;
171
+
172
+ // We don't have access to the type of the dynamic array in function context.
173
+ // Write out result with memcpy to avoid strict aliasing issue.
174
+ memcpy (ptr_to_ary , & meta , sizeof (meta ));
138
175
return 1 ;
139
176
}
140
177
0 commit comments