Sparse 0.0.1
Sparse Array for C
Loading...
Searching...
No Matches
sparse.c
1#include "sparse.h"
2#include "dynarr.h"
3#include "vector.h"
4
5#include <string.h>
6#include <stdio.h>
7#include <assert.h>
8
9typedef struct
10{
11 size_t index;
12 char value[];
13}
14pair_t;
15
16
17typedef struct
18{
19 sparse_foreach_t func;
20 void *param;
21}
22adapt_foreach_param_t;
23
24
25typedef struct
26{
27 sparse_aggregate_t func;
28 void *param;
29}
30adapt_aggregate_param_t;
31
32
33/* ***
34* === Forward Declarations === *
35*** */
36
37/*
38* Used for finding insertion place for pair
39* value -> pair_t *pair_to_insert
40* element -> pair_t *pair_in_the_arraytor
41*/
42static ssize_t cmp_pairs_by_index(const void *const value, const void *const element, void *const param);
43
44/*
45* Used for binary search of the pair by providen index
46* value -> size_t *index
47* element -> pair_t *pair
48*/
49static ssize_t cmp_index_to_pair(const void *const value, const void *const element, void *const param);
50
51static bool match_by_index(const void *const element, void *const index);
52/*
53* Access header allocated in underlying storage layer.
54*/
55static sparse_header_t *get_sparse_header(const sparse_t *const array);
56
57static int adapt_foreach(const void *const element, void *const param);
58
59static int adapt_aggregate(const void *const element, void *const acc, void *const param);
60
61
62/* ***
63* === API Implementation === *
64*** */
65
66sparse_t *sparse_create_(const sparse_opts_t *const opts)
67{
68 assert(opts);
69
70 /* dynarr's `element_size` is the real size
71 of an allocation per element,
72 so we need to introduce separate property getter
73 for sparse array */
74 sparse_t *array = dynarr_create(
75 .element_size = sizeof(size_t) + calc_aligned_size(opts->element_size, ALIGNMENT),
76 .initial_cap = opts->initial_cap,
77 .ext_header_size = sizeof(sparse_header_t),
78 .grow_factor = opts->grow_factor,
79 .grow_threshold = opts->grow_threshold,
80 .shrink_threshold = opts->shrink_threshold
81 );
82
83 if (!array) return NULL;
84
85 sparse_header_t *ext_header = (sparse_header_t*)dynarr_get_ext_header(array);
86 *ext_header = (sparse_header_t) {
87 .element_size = opts->element_size
88 };
89
90 return array;
91}
92
93
94sparse_t *sparse_clone(const sparse_t *const array)
95{
96 assert(array);
97 return dynarr_clone(array);
98}
99
100
101void sparse_destroy(sparse_t *const array)
102{
103 assert(array);
104 dynarr_destroy(array);
105}
106
107
108size_t sparse_element_size(const sparse_t *const array)
109{
110 assert(array);
111 return get_sparse_header(array)->element_size;
112}
113
114
115size_t sparse_first_index(const sparse_t *const array)
116{
117 assert(array);
118 assert(sparse_size(array) != 0);
119
120 pair_t *first = dynarr_first(array);
121 return first->index;
122}
123
124
125size_t sparse_last_index(const sparse_t *const array)
126{
127 assert(array);
128 assert(sparse_size(array) != 0);
129
130 pair_t *last = dynarr_last(array);
131 return last->index;
132}
133
134
135size_t sparse_first_free_index(const sparse_t *const array)
136{
137 assert(array);
138
139 const size_t size = sparse_size(array);
140 size_t index = 0;
141
142 for (size_t i = 0; i < size; ++i)
143 {
144 pair_t *pair = (pair_t *)dynarr_get(array, i);
145 if (pair->index > index)
146 {
147 return index;
148 }
149 index = pair->index + 1;
150 }
151 return index;
152}
153
154
155size_t sparse_last_free_index(const sparse_t *const array)
156{
157 assert(array);
158 if (sparse_size(array) == 0) return 0;
159 pair_t *p = (pair_t *)dynarr_last(array);
160 return p->index + 1;
161}
162
163
164size_t sparse_size(const sparse_t *const array)
165{
166 assert(array);
167 return dynarr_size(array);
168}
169
170
171sparse_status_t sparse_insert(sparse_t **const array, const size_t index, const void *const value)
172{
173 assert(array && *array);
174 assert(value);
175
176 /* element for given index already exists */
177 if (vector_binary_find(*array, &index, dynarr_size(*array), cmp_index_to_pair, NULL))
178 {
179 return SPARSE_INSERT_INDEX_OVERRIDE;
180 }
181
182 size_t place;
183 dynarr_status_t status = dynarr_binary_reserve(array, &index, cmp_index_to_pair, NULL, &place);
184 if (DYNARR_SUCCESS != status) return (sparse_status_t)status;
185
186 pair_t *pair = dynarr_get(*array, place);
187 pair->index = index;
188 memcpy(pair->value, value, sparse_element_size(*array));
189
190 return SPARSE_SUCCESS;
191}
192
193
194sparse_status_t sparse_insert_reserve(sparse_t **const array, const size_t index)
195{
196 assert(array && *array);
197
198 /* element for given index already exists */
199 if (vector_binary_find(*array, &index, dynarr_size(*array), cmp_index_to_pair, NULL))
200 {
201 return SPARSE_INSERT_INDEX_OVERRIDE;
202 }
203
204 size_t place;
205 dynarr_status_t status = dynarr_binary_reserve(array, &index, cmp_index_to_pair, NULL, &place);
206 if (DYNARR_SUCCESS != status) return (sparse_status_t)status;
207
208 pair_t *pair = dynarr_get(*array, place);
209 pair->index = index;
210
211 return SPARSE_SUCCESS;
212}
213
214
215sparse_status_t sparse_remove(sparse_t **const array, const size_t index)
216{
217 assert(array && *array);
218
219 dynarr_status_t status = dynarr_remove_if(array, match_by_index, 1, (void *const)&index);
220
221 if (DYNARR_SHRINK_ERROR == status) return SPARSE_ALLOC_ERROR;
222 return SPARSE_SUCCESS;
223}
224
225
226void* sparse_get(const sparse_t *const array, const size_t index)
227{
228 assert(array);
229 pair_t *p = (pair_t *)vector_binary_find(array, &index, dynarr_size(array), cmp_index_to_pair, NULL);
230 if (!p) return NULL;
231 return p->value;
232}
233
234
235bool sparse_is_empty_element(const sparse_t *const array, const size_t index)
236{
237 assert(array);
238 return !vector_binary_find(array, &index, dynarr_size(array), cmp_index_to_pair, NULL);
239}
240
241
242int sparse_foreach(const sparse_t *const sparse,
243 const sparse_foreach_t func,
244 void *const param)
245{
246 assert(sparse);
247 assert(func);
248
249 adapt_foreach_param_t aparam = {
250 .func = func,
251 .param = param,
252 };
253
254 return dynarr_foreach(sparse, adapt_foreach, &aparam);
255}
256
257
258int sparse_aggregate(const sparse_t *const sparse,
259 const sparse_aggregate_t func,
260 void *const acc,
261 void *const param)
262{
263 assert(sparse);
264 assert(func);
265
266 adapt_aggregate_param_t aparam = {
267 .func = func,
268 .param = param,
269 };
270
271 return dynarr_aggregate(sparse, adapt_aggregate, acc, &aparam);
272}
273
274
275int sparse_transform(sparse_t *const sparse,
276 const sparse_transform_t func,
277 void *const param)
278{
279 assert(sparse);
280 assert(func);
281
282 return sparse_foreach(sparse, (sparse_foreach_t)func, param);
283}
284
285
286/* ***
287* === Static Functions === *
288*** */
289
290static ssize_t cmp_pairs_by_index(const void *const value, const void *const element, void *const param)
291{
292 (void)param;
293 return (ssize_t) ((const pair_t *)value)->index - ((const pair_t *)element)->index;
294}
295
296
297static ssize_t cmp_index_to_pair(const void *const value, const void *const element, void *const param)
298{
299 (void)param;
300 return (ssize_t)*(size_t*) value - ((const pair_t *) element)->index;
301}
302
303
304static bool match_by_index(const void *const element, void *const index)
305{
306 return ((pair_t*)element)->index == *(size_t*)index;
307}
308
309
310static sparse_header_t *get_sparse_header(const sparse_t *const array)
311{
312 return dynarr_get_ext_header(array);
313}
314
315
316static int adapt_foreach(const void *const element, void *const param)
317{
318 const pair_t *p = element;
319 adapt_foreach_param_t *adapt = param;
320 return adapt->func(p->index, &p->value, adapt->param);
321}
322
323
324static int adapt_aggregate(const void *const element, void *const acc, void *const param)
325{
326 const pair_t *p = element;
327 adapt_aggregate_param_t *adapt = param;
328 return adapt->func(p->index, &p->value, acc, adapt->param);
329}
dynarr_status_t
#define dynarr_create(...)
dynarr_status_t dynarr_binary_reserve(dynarr_t **const dynarr, const void *const value, const compare_t cmp, void *const param, size_t *const index)
int dynarr_foreach(const dynarr_t *const dynarr, const foreach_t func, void *const param)
void * dynarr_get_ext_header(const dynarr_t *const dynarr)
dynarr_t * dynarr_clone(const dynarr_t *const dynarr)
void * dynarr_first(const dynarr_t *const dynarr)
void * dynarr_last(const dynarr_t *const dynarr)
void * dynarr_get(const dynarr_t *const dynarr, const size_t index)
dynarr_status_t dynarr_remove_if(dynarr_t **const dynarr, const predicate_t predicate, const size_t limit, void *const param)
int dynarr_aggregate(const dynarr_t *const dynarr, const aggregate_t func, void *const acc, void *const param)
void dynarr_destroy(dynarr_t *const dynarr)
size_t dynarr_size(const dynarr_t *const dynarr)