1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 #include "cx/array_list.h"
30 #include "cx/compare.h"
31 #include <assert.h>
32 #include <string.h>
33
34
35
36 static void *cx_array_default_realloc(
37 void *array,
38 size_t capacity,
39 size_t elem_size,
40 __attribute__((__unused__))
struct cx_array_reallocator_s *alloc
41 ) {
42 return realloc(array, capacity * elem_size);
43 }
44
45 struct cx_array_reallocator_s cx_array_default_reallocator_impl = {
46 cx_array_default_realloc,
NULL,
NULL,
0,
0
47 };
48
49 struct cx_array_reallocator_s *cx_array_default_reallocator = &cx_array_default_reallocator_impl;
50
51
52
53 enum cx_array_result cx_array_copy(
54 void **target,
55 size_t *size,
56 size_t *capacity,
57 size_t index,
58 void const *src,
59 size_t elem_size,
60 size_t elem_count,
61 struct cx_array_reallocator_s *reallocator
62 ) {
63
64 assert(target !=
NULL);
65 assert(size !=
NULL);
66 assert(src !=
NULL);
67
68
69 size_t cap = capacity ==
NULL ? *size : *capacity;
70
71
72 size_t minsize = index + elem_count;
73 size_t newsize = *size < minsize ? minsize : *size;
74 bool needrealloc = newsize > cap;
75
76
77 if (needrealloc) {
78
79 if (reallocator ==
NULL || capacity ==
NULL) {
80 return CX_ARRAY_REALLOC_NOT_SUPPORTED;
81 }
82
83
84 uintptr_t targetaddr = (
uintptr_t) *target;
85 uintptr_t srcaddr = (
uintptr_t) src;
86 bool repairsrc = targetaddr <= srcaddr
87 && srcaddr < targetaddr + cap * elem_size;
88
89
90 cap = newsize - (newsize %
16) +
16;
91 assert(cap > newsize);
92
93
94 void *newmem = reallocator->realloc(
95 *target, cap, elem_size, reallocator
96 );
97 if (newmem ==
NULL) {
98 return CX_ARRAY_REALLOC_FAILED;
99 }
100
101
102 if (repairsrc) {
103 src = ((
char *) newmem) + (srcaddr - targetaddr);
104 }
105
106
107 *target = newmem;
108 *capacity = cap;
109 }
110
111
112 char *start = *target;
113 start += index * elem_size;
114
115
116 memmove(start, src, elem_count * elem_size);
117 *size = newsize;
118
119
120 return CX_ARRAY_SUCCESS;
121 }
122
123 #ifndef CX_ARRAY_SWAP_SBO_SIZE
124 #define CX_ARRAY_SWAP_SBO_SIZE 128
125 #endif
126 unsigned cx_array_swap_sbo_size =
CX_ARRAY_SWAP_SBO_SIZE;
127
128 void cx_array_swap(
129 void *arr,
130 size_t elem_size,
131 size_t idx1,
132 size_t idx2
133 ) {
134 assert(arr !=
NULL);
135
136
137 if (idx1 == idx2)
return;
138
139 char sbo_mem[
CX_ARRAY_SWAP_SBO_SIZE];
140 void *tmp;
141
142
143 if (elem_size >
CX_ARRAY_SWAP_SBO_SIZE) {
144 tmp = malloc(elem_size);
145
146 if (tmp ==
NULL) abort();
147 }
else {
148 tmp = sbo_mem;
149 }
150
151
152 char *left = arr, *right = arr;
153 left += idx1 * elem_size;
154 right += idx2 * elem_size;
155
156
157 memcpy(tmp, left, elem_size);
158 memcpy(left, right, elem_size);
159 memcpy(right, tmp, elem_size);
160
161
162 if (tmp != sbo_mem) {
163 free(tmp);
164 }
165 }
166
167
168
169 typedef struct {
170 struct cx_list_s base;
171 void *data;
172 size_t capacity;
173 struct cx_array_reallocator_s reallocator;
174 } cx_array_list;
175
176 static void *cx_arl_realloc(
177 void *array,
178 size_t capacity,
179 size_t elem_size,
180 struct cx_array_reallocator_s *alloc
181 ) {
182
183 CxAllocator
const *al = alloc->ptr1;
184
185
186 return cxRealloc(al, array, capacity * elem_size);
187 }
188
189 static void cx_arl_destructor(
struct cx_list_s *list) {
190 cx_array_list *arl = (cx_array_list *) list;
191
192 char *ptr = arl->data;
193
194 if (list->collection.simple_destructor) {
195 for (
size_t i =
0; i < list->collection.size; i++) {
196 cx_invoke_simple_destructor(list, ptr);
197 ptr += list->collection.elem_size;
198 }
199 }
200 if (list->collection.advanced_destructor) {
201 for (
size_t i =
0; i < list->collection.size; i++) {
202 cx_invoke_advanced_destructor(list, ptr);
203 ptr += list->collection.elem_size;
204 }
205 }
206
207 cxFree(list->collection.allocator, arl->data);
208 cxFree(list->collection.allocator, list);
209 }
210
211 static size_t cx_arl_insert_array(
212 struct cx_list_s *list,
213 size_t index,
214 void const *array,
215 size_t n
216 ) {
217
218 if (index > list->collection.size || n ==
0)
return 0;
219
220
221 cx_array_list *arl = (cx_array_list *) list;
222
223
224 if (index < list->collection.size) {
225 char const *first_to_move = (
char const *) arl->data;
226 first_to_move += index * list->collection.elem_size;
227 size_t elems_to_move = list->collection.size - index;
228 size_t start_of_moved = index + n;
229
230 if (
CX_ARRAY_SUCCESS != cx_array_copy(
231 &arl->data,
232 &list->collection.size,
233 &arl->capacity,
234 start_of_moved,
235 first_to_move,
236 list->collection.elem_size,
237 elems_to_move,
238 &arl->reallocator
239 )) {
240
241 return 0;
242 }
243 }
244
245
246
247
248
249
250 if (
CX_ARRAY_SUCCESS == cx_array_copy(
251 &arl->data,
252 &list->collection.size,
253 &arl->capacity,
254 index,
255 array,
256 list->collection.elem_size,
257 n,
258 &arl->reallocator
259 )) {
260 return n;
261 }
else {
262
263 return 0;
264 }
265 }
266
267 static int cx_arl_insert_element(
268 struct cx_list_s *list,
269 size_t index,
270 void const *element
271 ) {
272 return 1 != cx_arl_insert_array(list, index, element,
1);
273 }
274
275 static int cx_arl_insert_iter(
276 struct cx_iterator_s *iter,
277 void const *elem,
278 int prepend
279 ) {
280 struct cx_list_s *list = iter->src_handle.m;
281 if (iter->index < list->collection.size) {
282 int result = cx_arl_insert_element(
283 list,
284 iter->index +
1 - prepend,
285 elem
286 );
287 if (result ==
0 && prepend !=
0) {
288 iter->index++;
289 iter->elem_handle = ((
char *) iter->elem_handle) + list->collection.elem_size;
290 }
291 return result;
292 }
else {
293 int result = cx_arl_insert_element(list, list->collection.size, elem);
294 iter->index = list->collection.size;
295 return result;
296 }
297 }
298
299 static int cx_arl_remove(
300 struct cx_list_s *list,
301 size_t index
302 ) {
303 cx_array_list *arl = (cx_array_list *) list;
304
305
306 if (index >= list->collection.size) {
307 return 1;
308 }
309
310
311 cx_invoke_destructor(list, ((
char *) arl->data) + index * list->collection.elem_size);
312
313
314 if (index == list->collection.size -
1) {
315 list->collection.size--;
316 return 0;
317 }
318
319
320 int result = cx_array_copy(
321 &arl->data,
322 &list->collection.size,
323 &arl->capacity,
324 index,
325 ((
char *) arl->data) + (index +
1) * list->collection.elem_size,
326 list->collection.elem_size,
327 list->collection.size - index -
1,
328 &arl->reallocator
329 );
330
331
332 assert(result ==
0);
333
334
335 list->collection.size--;
336
337 return 0;
338 }
339
340 static void cx_arl_clear(
struct cx_list_s *list) {
341 if (list->collection.size ==
0)
return;
342
343 cx_array_list *arl = (cx_array_list *) list;
344 char *ptr = arl->data;
345
346 if (list->collection.simple_destructor) {
347 for (
size_t i =
0; i < list->collection.size; i++) {
348 cx_invoke_simple_destructor(list, ptr);
349 ptr += list->collection.elem_size;
350 }
351 }
352 if (list->collection.advanced_destructor) {
353 for (
size_t i =
0; i < list->collection.size; i++) {
354 cx_invoke_advanced_destructor(list, ptr);
355 ptr += list->collection.elem_size;
356 }
357 }
358
359 memset(arl->data,
0, list->collection.size * list->collection.elem_size);
360 list->collection.size =
0;
361 }
362
363 static int cx_arl_swap(
364 struct cx_list_s *list,
365 size_t i,
366 size_t j
367 ) {
368 if (i >= list->collection.size || j >= list->collection.size)
return 1;
369 cx_array_list *arl = (cx_array_list *) list;
370 cx_array_swap(arl->data, list->collection.elem_size, i, j);
371 return 0;
372 }
373
374 static void *cx_arl_at(
375 struct cx_list_s
const *list,
376 size_t index
377 ) {
378 if (index < list->collection.size) {
379 cx_array_list
const *arl = (cx_array_list
const *) list;
380 char *space = arl->data;
381 return space + index * list->collection.elem_size;
382 }
else {
383 return NULL;
384 }
385 }
386
387 static ssize_t cx_arl_find_remove(
388 struct cx_list_s *list,
389 void const *elem,
390 bool remove
391 ) {
392 assert(list->collection.cmpfunc !=
NULL);
393 assert(list->collection.size <
SIZE_MAX /
2);
394 char *cur = ((cx_array_list
const *) list)->data;
395
396 for (
ssize_t i =
0; i < (
ssize_t) list->collection.size; i++) {
397 if (
0 == list->collection.cmpfunc(elem, cur)) {
398 if (remove) {
399 if (
0 == cx_arl_remove(list, i)) {
400 return i;
401 }
else {
402 return -
1;
403 }
404 }
else {
405 return i;
406 }
407 }
408 cur += list->collection.elem_size;
409 }
410
411 return -
1;
412 }
413
414 static void cx_arl_sort(
struct cx_list_s *list) {
415 assert(list->collection.cmpfunc !=
NULL);
416 qsort(((cx_array_list *) list)->data,
417 list->collection.size,
418 list->collection.elem_size,
419 list->collection.cmpfunc
420 );
421 }
422
423 static int cx_arl_compare(
424 struct cx_list_s
const *list,
425 struct cx_list_s
const *other
426 ) {
427 assert(list->collection.cmpfunc !=
NULL);
428 if (list->collection.size == other->collection.size) {
429 char const *left = ((cx_array_list
const *) list)->data;
430 char const *right = ((cx_array_list
const *) other)->data;
431 for (
size_t i =
0; i < list->collection.size; i++) {
432 int d = list->collection.cmpfunc(left, right);
433 if (d !=
0) {
434 return d;
435 }
436 left += list->collection.elem_size;
437 right += other->collection.elem_size;
438 }
439 return 0;
440 }
else {
441 return list->collection.size < other->collection.size ? -
1 :
1;
442 }
443 }
444
445 static void cx_arl_reverse(
struct cx_list_s *list) {
446 if (list->collection.size <
2)
return;
447 void *data = ((cx_array_list
const *) list)->data;
448 size_t half = list->collection.size /
2;
449 for (
size_t i =
0; i < half; i++) {
450 cx_array_swap(data, list->collection.elem_size, i, list->collection.size -
1 - i);
451 }
452 }
453
454 static bool cx_arl_iter_valid(
void const *it) {
455 struct cx_iterator_s
const *iter = it;
456 struct cx_list_s
const *list = iter->src_handle.c;
457 return iter->index < list->collection.size;
458 }
459
460 static void *cx_arl_iter_current(
void const *it) {
461 struct cx_iterator_s
const *iter = it;
462 return iter->elem_handle;
463 }
464
465 static void cx_arl_iter_next(
void *it) {
466 struct cx_iterator_s *iter = it;
467 if (iter->base.remove) {
468 iter->base.remove = false;
469 cx_arl_remove(iter->src_handle.m, iter->index);
470 }
else {
471 iter->index++;
472 iter->elem_handle =
473 ((
char *) iter->elem_handle)
474 + ((
struct cx_list_s
const *) iter->src_handle.c)->collection.elem_size;
475 }
476 }
477
478 static void cx_arl_iter_prev(
void *it) {
479 struct cx_iterator_s *iter = it;
480 cx_array_list
const* list = iter->src_handle.c;
481 if (iter->base.remove) {
482 iter->base.remove = false;
483 cx_arl_remove(iter->src_handle.m, iter->index);
484 }
485 iter->index--;
486 if (iter->index < list->base.collection.size) {
487 iter->elem_handle = ((
char *) list->data)
488 + iter->index * list->base.collection.elem_size;
489 }
490 }
491
492
493 static struct cx_iterator_s cx_arl_iterator(
494 struct cx_list_s
const *list,
495 size_t index,
496 bool backwards
497 ) {
498 struct cx_iterator_s iter;
499
500 iter.index = index;
501 iter.src_handle.c = list;
502 iter.elem_handle = cx_arl_at(list, index);
503 iter.elem_size = list->collection.elem_size;
504 iter.elem_count = list->collection.size;
505 iter.base.valid = cx_arl_iter_valid;
506 iter.base.current = cx_arl_iter_current;
507 iter.base.next = backwards ? cx_arl_iter_prev : cx_arl_iter_next;
508 iter.base.remove = false;
509 iter.base.mutating = false;
510
511 return iter;
512 }
513
514 static cx_list_class cx_array_list_class = {
515 cx_arl_destructor,
516 cx_arl_insert_element,
517 cx_arl_insert_array,
518 cx_arl_insert_iter,
519 cx_arl_remove,
520 cx_arl_clear,
521 cx_arl_swap,
522 cx_arl_at,
523 cx_arl_find_remove,
524 cx_arl_sort,
525 cx_arl_compare,
526 cx_arl_reverse,
527 cx_arl_iterator,
528 };
529
530 CxList *cxArrayListCreate(
531 CxAllocator
const *allocator,
532 cx_compare_func comparator,
533 size_t elem_size,
534 size_t initial_capacity
535 ) {
536 if (allocator ==
NULL) {
537 allocator = cxDefaultAllocator;
538 }
539
540 cx_array_list *list = cxCalloc(allocator,
1,
sizeof(cx_array_list));
541 if (list ==
NULL)
return NULL;
542
543 list->base.cl = &cx_array_list_class;
544 list->base.collection.allocator = allocator;
545 list->capacity = initial_capacity;
546
547 if (elem_size >
0) {
548 list->base.collection.elem_size = elem_size;
549 list->base.collection.cmpfunc = comparator;
550 }
else {
551 elem_size =
sizeof(
void *);
552 list->base.collection.cmpfunc = comparator ==
NULL ? cx_cmp_ptr : comparator;
553 cxListStorePointers((CxList *) list);
554 }
555
556
557 list->data = cxCalloc(allocator, initial_capacity, elem_size);
558 if (list->data ==
NULL) {
559 cxFree(allocator, list);
560 return NULL;
561 }
562
563
564 list->reallocator.realloc = cx_arl_realloc;
565 list->reallocator.ptr1 = (
void *) allocator;
566
567 return (CxList *) list;
568 }
569