libcstl
All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Modules
array.c
Go to the documentation of this file.
1/*!
2 * @file
3 */
4
5#include "cstl/array.h"
6
7/*! @private */
8static inline void * __cstl_raw_array_at(
9 const void * const arr,
10 const size_t size, const size_t at)
11{
12 return (void *)((uintptr_t)arr + (at * size));
13}
14
15void cstl_raw_array_reverse(void * const arr,
16 const size_t count, const size_t size,
17 cstl_swap_func_t * const swap,
18 void * const t)
19{
20 int i, j;
21
22 for (i = 0, j = count - 1; i < j; i++, j--) {
23 swap(__cstl_raw_array_at(arr, size, i),
24 __cstl_raw_array_at(arr, size, j),
25 t,
26 size);
27 }
28}
29
30ssize_t cstl_raw_array_search(const void * const arr,
31 const size_t count, const size_t size,
32 const void * const ex,
33 cstl_compare_func_t * const cmp,
34 void * const priv)
35{
36 int i, j;
37
38 for (i = 0, j = count - 1; i <= j;) {
39 const int n = (i + j) / 2;
40 const int eq = cmp(ex, __cstl_raw_array_at(arr, size, n), priv);
41
42 if (eq == 0) {
43 return n;
44 } else if (eq < 0) {
45 j = n - 1;
46 } else {
47 i = n + 1;
48 }
49 }
50
51 return -1;
52}
53
54ssize_t cstl_raw_array_find(const void * const arr,
55 const size_t count, const size_t size,
56 const void * const ex,
57 cstl_compare_func_t * const cmp,
58 void * const priv)
59{
60 size_t i;
61
62 for (i = 0; i < count; i++) {
63 if (cmp(ex, __cstl_raw_array_at(arr, size, i), priv) == 0) {
64 return i;
65 }
66 }
67
68 return -1;
69}
70
71/*!
72 * @private
73 *
74 * given a value (at the location p), walk inward from the two ends
75 * while the left end is smaller than the given value and the right end
76 * is bigger than the given value. if, when both traversals have stopped,
77 * the locations of the stopped are still in their respective halves of
78 * the array, swap the values at the locations. (this moves the values
79 * greater than the chosen "middle" to the lower half and vice versa.)
80 * continue the process until the respective indexes cross
81 */
82static size_t cstl_raw_array_qsort_p(
83 void * const arr, const size_t count, const size_t size,
84 const void * p,
85 cstl_compare_func_t * const cmp, void * const priv,
86 cstl_swap_func_t * const swap, void * const t)
87{
88 size_t i, j;
89 void * a, * b;
90
91 i = 0; j = count - 1;
92 a = b = NULL;
93
94 do {
95 if (a != b) {
96 swap(a, b, t, size);
97 /*
98 * it's possible that the chosen value that we're
99 * "pivoting" around gets swapped. if that occurs,
100 * keep track of its location so that the correct
101 * value continues to be used for comparisons.
102 */
103 if (p == a) {
104 p = b;
105 } else if (p == b) {
106 p = a;
107 }
108
109 i++; j--;
110 }
111
112 /*
113 * walk forward from the beginning until a value greater than
114 * or equal to the pivot is found
115 */
116 while (cmp(a = __cstl_raw_array_at(arr, size, i), p, priv) < 0) {
117 i++;
118 }
119
120 /*
121 * walk backward from the end until a value less than or equal
122 * to the pivot value is found
123 */
124 while (cmp(b = __cstl_raw_array_at(arr, size, j), p, priv) > 0) {
125 j--;
126 }
127 } while (i < j);
128
129 return j;
130}
131
132/*! @private */
133static void cstl_raw_array_qsort(
134 void * const arr, const size_t count, const size_t size,
135 cstl_compare_func_t * const cmp, void * const priv,
136 cstl_swap_func_t * const swap, void * const tmp,
137 const cstl_sort_algorithm_t algo)
138{
139 if (count > 1) {
140 size_t p;
141
142 /*
143 * the choice of the pivot location/value is the
144 * subject of much debate. a bad pivot choice will
145 * result in the worst case behavior of the algorithm.
146 * this code implements a couple of common mitigation
147 * strategies.
148 */
149
150 if (algo == CSTL_SORT_ALGORITHM_QUICK_R) {
151 /*
152 * choose the pivot randomly. there's no guarantee
153 * that we won't encounter worst-case behavior, but
154 * randomization combats someone intentionally trying
155 * to slow performance by choosing a bad initial
156 * ordering. that said, the rand() function isn't
157 * beyond being broken/anticipated, and generating a
158 * random number is not without cost
159 */
160 p = rand() % count;
161 } else if (algo == CSTL_SORT_ALGORITHM_QUICK_M) {
162 /*
163 * the median-of-three scheme looks at the first, middle,
164 * and last elements in the array. it sorts them, and then
165 * uses the middle value/location as the pivot. note that,
166 * in the case of a 2 or 3 element array, this operation
167 * results in a completely sorted array. on average, this
168 * version wins on speed because it avoids another
169 * partitioning and recursion below.
170 */
171 void * const beg = __cstl_raw_array_at(arr, size, 0);
172 void * const end = __cstl_raw_array_at(arr, size, count - 1);
173 void * mid;
174
175 p = (count - 1) / 2;
176 mid = __cstl_raw_array_at(arr, size, p);
177
178 /*
179 * there are six possibilities for the ordering of elements.
180 * one possibility is that they are already in order. the
181 * remaining possibilities require either one or two swaps.
182 * three of those require swapping the outer elements, and
183 * after doing so, two of those convert to one of the
184 * remaining two possibilities that only require one swap.
185 */
186 if (cmp(end, beg, priv) < 0) {
187 swap(end, beg, tmp, size);
188 }
189 if (cmp(mid, beg, priv) < 0) {
190 swap(mid, beg, tmp, size);
191 } else if (cmp(end, mid, priv) < 0) {
192 swap(end, mid, tmp, size);
193 }
194 } else {
195 /* basic quicksort; just use the first element */
196 p = 0;
197 }
198
199 if (algo != CSTL_SORT_ALGORITHM_QUICK_M || count > 3) {
200 /*
201 * if the array is not already sorted, partition
202 * the array around the pivot value.
203 */
204 const size_t m = cstl_raw_array_qsort_p(
205 arr, count, size,
206 __cstl_raw_array_at(arr, size, p),
207 cmp, priv,
208 swap, tmp);
209
210 /*
211 * sort the arrays on either side of the partition.
212 * note that these calls see their partition as the
213 * entire array. they don't understand that they
214 * might be sorting portions of a larger array
215 */
216 cstl_raw_array_qsort(
217 arr, m + 1, size,
218 cmp, priv,
219 swap, tmp,
220 algo);
221 cstl_raw_array_qsort(
222 __cstl_raw_array_at(arr, size, m + 1), count - m - 1, size,
223 cmp, priv,
224 swap, tmp,
225 algo);
226 }
227 }
228}
229
230/*!
231 * @private
232 *
233 * this function assumes that the array is a heap with the root node
234 * at element 0 with each node's children located at 2n+1 and 2n+2.
235 *
236 * for the purpose of this function, descendants of n are assumed to
237 * already be in the correct locations to form a heap with n as their
238 * root. n may or may not be in the correct location with respect to
239 * its descendants, and this function will push n down through its
240 * descendants until the heap rooted at the original location is valid.
241 */
242static void cstl_raw_array_hsort_b(
243 void * const arr, const size_t count, const size_t size,
244 size_t n,
245 cstl_compare_func_t * const cmp, void * const priv,
246 cstl_swap_func_t * const swap, void * const tmp)
247{
248 size_t c;
249
250 c = SIZE_MAX;
251 do {
252 size_t l, r;
253
254 if (c < SIZE_MAX) {
255 /*
256 * this block swaps n with its child on every
257 * iteration of the loop except the first
258 */
259 swap(__cstl_raw_array_at(arr, size, n),
260 __cstl_raw_array_at(arr, size, c),
261 tmp,
262 size);
263 n = c;
264 }
265
266 l = 2 * n + 1;
267 r = l + 1;
268
269 /*
270 * the goal here is to find the greatest of n and its
271 * children. after the block below, c either points to
272 * n or the greater child of n that is (also) greater
273 * than n. if, at the end, c points to n, then n is
274 * already in the correct position, and the job is done.
275 * if not, then loop around, push n down, and try again.
276 */
277
278 c = n;
279 if (l < count
280 && cmp(__cstl_raw_array_at(arr, size, l),
281 __cstl_raw_array_at(arr, size, c),
282 priv) > 0) {
283 c = l;
284 }
285 if (r < count
286 && cmp(__cstl_raw_array_at(arr, size, r),
287 __cstl_raw_array_at(arr, size, c),
288 priv) > 0) {
289 c = r;
290 }
291 } while (n != c);
292}
293
294/*! @private */
295void cstl_raw_array_hsort(
296 void * const arr, const size_t count, const size_t size,
297 cstl_compare_func_t * const cmp, void * const priv,
298 cstl_swap_func_t * const swap, void * const tmp)
299{
300 if (count > 1) {
301 ssize_t i;
302
303 /*
304 * assume the array is organized as a binary tree rooted at 0
305 * with child nodes at 2n+1 and 2n+2. to make a heap out it,
306 * first assume that all leaves correctly form individual heaps
307 * of one element each.
308 *
309 * the loop below skips all the leaf elements and starts with the
310 * first element that has one or more children. the hsort_b()
311 * function reorders that element with respect to its children such
312 * that they form a heap. the loop then continues with each node,
313 * moving toward the root. at each step, all descendants of the
314 * current element form a heap with only the current element
315 * (possibly) being out of place
316 */
317 for (i = count / 2 - 1; i >= 0; i--) {
318 cstl_raw_array_hsort_b(
319 arr, count, size, i, cmp, priv, swap, tmp);
320 }
321
322 /*
323 * with the heap now formed, the greatest element is at the front
324 * of the array. swap the front element with the last element. this
325 * has the effect of moving the greatest item into its correct,
326 * sorted position and invalidating the heap by placing a (likely)
327 * incorrect item at the top. shorten the array by one, and then
328 * fix the heap by pushing the new, incorrect root down to the
329 * correct position. the new heap is formed with one less item,
330 * at which point, the process repeats.
331 */
332 for (i = count - 1; i > 0; i--) {
333 swap(arr, __cstl_raw_array_at(arr, size, i), tmp, size);
334 cstl_raw_array_hsort_b(
335 arr, i, size, 0, cmp, priv, swap, tmp);
336 }
337 }
338}
339
341 void * const arr, const size_t count, const size_t size,
342 cstl_compare_func_t * const cmp, void * const priv,
343 cstl_swap_func_t * const swap, void * const tmp,
344 const cstl_sort_algorithm_t algo)
345{
346 switch (algo) {
350 cstl_raw_array_qsort(arr, count, size, cmp, priv, swap, tmp, algo);
351 break;
353 cstl_raw_array_hsort(arr, count, size, cmp, priv, swap, tmp);
354 break;
355 default:
357 arr, count, size, cmp, priv, swap, tmp,
359 break;
360 }
361}
362
363/*! @private */
364struct cstl_raw_array
365{
366 /*! @privatesection */
367
368 /*
369 * @base consists of @nm elements,
370 * each of @sz bytes
371 */
372 size_t sz, nm;
373 void * buf;
374};
375
377 const size_t nm, const size_t sz)
378{
379 struct cstl_raw_array * ra;
380
381 cstl_shared_ptr_reset(&a->ptr);
382 cstl_shared_ptr_alloc(&a->ptr, sizeof(*ra) + nm * sz, NULL);
383
384 ra = cstl_shared_ptr_get(&a->ptr);
385 if (ra != NULL) {
386 ra->sz = sz;
387 ra->nm = nm;
388
389 ra->buf = ra + 1;
390
391 a->len = nm;
392 }
393}
394
396 void * const buf, const size_t nm, const size_t sz)
397{
398 struct cstl_raw_array * ra;
399
400 cstl_array_alloc(a, 0, sz);
401 ra = cstl_shared_ptr_get(&a->ptr);
402 if (ra != NULL) {
403 ra->nm = nm;
404 ra->buf = buf;
405 a->len = nm;
406 }
407}
408
409void cstl_array_release(cstl_array_t * const a, void ** const buf)
410{
411 const struct cstl_raw_array * const ra =
413 void * b = NULL;
414
415 if (ra != NULL
416 && ra->buf != ra + 1
417 && cstl_shared_ptr_unique(&a->ptr)) {
418 b = ra->buf;
420 }
421
422 if (buf != NULL) {
423 *buf = b;
424 }
425}
426
427const void * cstl_array_data_const(const cstl_array_t * const a)
428{
429 const struct cstl_raw_array * const ra =
431 if (ra != NULL) {
432 return ra->buf;
433 }
434 return NULL;
435}
436
437const void * cstl_array_at_const(const cstl_array_t * a, size_t i)
438{
439 if (i >= a->len) {
440 abort();
441 } else {
442 const struct cstl_raw_array * const ra =
444
445 return __cstl_raw_array_at(ra->buf, ra->sz, a->off + i);
446 }
447}
448
450 const size_t beg, const size_t end,
451 cstl_array_t * const s)
452{
453 const struct cstl_raw_array * const ra =
455
456 if (ra == NULL
457 || end < beg
458 || a->off + end > ra->nm) {
459 abort();
460 }
461
462 s->off = a->off + beg;
463 s->len = end - beg;
464 if (a != s) {
465 cstl_shared_ptr_share(&a->ptr, &s->ptr);
466 }
467}
468
470{
471 const struct cstl_raw_array * const ra =
473 if (ra == NULL) {
474 abort();
475 }
476 a->off = 0;
477 a->len = ra->nm;
478 if (a != s) {
479 cstl_shared_ptr_share(&s->ptr, &a->ptr);
480 }
481}
482
483#ifdef __cfg_test__
484// GCOV_EXCL_START
485#include "internal/check.h"
486
487START_TEST(create)
488{
490 cstl_array_alloc(&a, 30, sizeof(int));
492}
493END_TEST
494
495START_TEST(slice)
496{
499
500 cstl_array_alloc(&a, 30, sizeof(int));
501 ck_assert_int_eq(cstl_array_size(&a), 30);
502
503 cstl_array_slice(&a, 20, 30, &s);
504 ck_assert_int_eq(cstl_array_size(&s), 10);
505
506 ck_assert_ptr_eq(cstl_array_at(&a, 20), cstl_array_at(&s, 0));
507
509 ck_assert_int_eq(cstl_array_size(&a), 0);
510 cstl_array_unslice(&s, &a);
511 ck_assert_int_eq(cstl_array_size(&a), 30);
512
514 ck_assert_int_eq(cstl_array_size(&s), 10);
516
517 ck_assert_signal(SIGABRT, cstl_array_unslice(&s, &s));
518}
519END_TEST
520
521START_TEST(set)
522{
523 void * p;
524 int _[32];
527
528 ck_assert_ptr_null(cstl_array_data(&a));
529 cstl_array_set(&a, _, sizeof(_) / sizeof(*_), sizeof(*_));
530 ck_assert_int_eq(cstl_array_size(&a), 32);
531
532 ck_assert_ptr_eq(cstl_array_data(&a), _);
533
534 cstl_array_slice(&a, 10, 20, &s);
535 ck_assert_int_eq(cstl_array_size(&s), 10);
536
538 cstl_array_release(&a, &p);
539 ck_assert_ptr_eq(p, _);
540}
541END_TEST
542
543START_TEST(access_before)
544{
546
547 cstl_array_alloc(&a, 30, sizeof(int));
548 ck_assert_int_eq(cstl_array_size(&a), 30);
549
550 ck_assert_signal(SIGABRT, cstl_array_at(&a, -1));
551
553}
554END_TEST
555
556START_TEST(access_after)
557{
559
560 cstl_array_alloc(&a, 30, sizeof(int));
561 ck_assert_int_eq(cstl_array_size(&a), 30);
562
563 ck_assert_signal(SIGABRT, cstl_array_at(&a, 30));
564
566}
567END_TEST
568
569START_TEST(big_slice)
570{
573
574 cstl_array_alloc(&a, 30, sizeof(int));
575 ck_assert_int_eq(cstl_array_size(&a), 30);
576
577 ck_assert_signal(SIGABRT, cstl_array_slice(&a, 20, 31, &s));
578
580}
581END_TEST
582
583START_TEST(invalid_slice)
584{
587
588 cstl_array_alloc(&a, 30, sizeof(int));
589 ck_assert_int_eq(cstl_array_size(&a), 30);
590
591 ck_assert_signal(SIGABRT, cstl_array_slice(&a, 20, 10, &s));
592
594}
595END_TEST
596
597Suite * array_suite(void)
598{
599 Suite * const s = suite_create("array");
600
601 TCase * tc;
602
603 tc = tcase_create("array");
604 tcase_add_test(tc, create);
605 tcase_add_test(tc, slice);
606 tcase_add_test(tc, set);
607 tcase_add_test(tc, access_before);
608 tcase_add_test(tc, access_after);
609 tcase_add_test(tc, big_slice);
610 tcase_add_test(tc, invalid_slice);
611
612 suite_add_tcase(s, tc);
613
614 return s;
615}
616
617// GCOV_EXCL_STOP
618#endif
cstl_sort_algorithm_t
Enumeration indicating the desired sort algorithm.
Definition common.h:24
@ CSTL_SORT_ALGORITHM_QUICK_M
Median-of-three quicksort.
Definition common.h:30
@ CSTL_SORT_ALGORITHM_QUICK
Quicksort.
Definition common.h:26
@ CSTL_SORT_ALGORITHM_HEAP
Heapsort.
Definition common.h:32
@ CSTL_SORT_ALGORITHM_DEFAULT
Unspecified default algorithm.
Definition common.h:35
@ CSTL_SORT_ALGORITHM_QUICK_R
Randomized quicksort.
Definition common.h:28
void cstl_swap_func_t(void *a, void *b, void *t, size_t len)
Type of function called to swap two objects.
Definition common.h:118
int cstl_compare_func_t(const void *obj1, const void *obj2, void *priv)
Function type for comparing (in)equality of two objects.
Definition common.h:49
void cstl_array_alloc(cstl_array_t *const a, const size_t nm, const size_t sz)
Allocate an array to be managed.
Definition array.c:376
ssize_t cstl_raw_array_find(const void *const arr, const size_t count, const size_t size, const void *const ex, cstl_compare_func_t *const cmp, void *const priv)
Perform a linear search of the array.
Definition array.c:54
void cstl_raw_array_sort(void *const arr, const size_t count, const size_t size, cstl_compare_func_t *const cmp, void *const priv, cstl_swap_func_t *const swap, void *const tmp, const cstl_sort_algorithm_t algo)
Sort the array using the specified algorithm.
Definition array.c:340
ssize_t cstl_raw_array_search(const void *const arr, const size_t count, const size_t size, const void *const ex, cstl_compare_func_t *const cmp, void *const priv)
Perform a binary search of the array.
Definition array.c:30
void cstl_array_set(cstl_array_t *const a, void *const buf, const size_t nm, const size_t sz)
Manage an externally allocated array.
Definition array.c:395
static size_t cstl_array_size(const cstl_array_t *const a)
Get the number of elements in the array.
Definition array.h:76
void cstl_array_release(cstl_array_t *const a, void **const buf)
Release an externally allocated array.
Definition array.c:409
static void * cstl_array_data(cstl_array_t *const a)
Return a pointer to the underlying array.
Definition array.h:159
const void * cstl_array_at_const(const cstl_array_t *a, size_t i)
Return a pointer to an element in the array.
Definition array.c:437
const void * cstl_array_data_const(const cstl_array_t *const a)
Return a pointer to the underlying array.
Definition array.c:427
static void cstl_array_reset(cstl_array_t *const a)
Drop a reference to memory managed by this object.
Definition array.h:143
void cstl_array_slice(cstl_array_t *const a, const size_t beg, const size_t end, cstl_array_t *const s)
Create an array object referring to a slice of another.
Definition array.c:449
void cstl_array_unslice(cstl_array_t *const s, cstl_array_t *const a)
Create an array object referring to the entire underlying array.
Definition array.c:469
static void * cstl_array_at(cstl_array_t *const a, const size_t i)
Return a pointer to an element in the array.
Definition array.h:176
void cstl_raw_array_reverse(void *const arr, const size_t count, const size_t size, cstl_swap_func_t *const swap, void *const t)
Reverse the contents of an array.
Definition array.c:15
#define DECLARE_CSTL_ARRAY(NAME)
Declare (and initialize) an array object at compile time.
Definition array.h:54
void cstl_shared_ptr_reset(cstl_shared_ptr_t *sp)
Stop managing the underlying memory via this object.
Definition memory.c:128
bool cstl_shared_ptr_unique(const cstl_shared_ptr_t *sp)
Determine if a shared pointer uniquely owns the underlying memory.
Definition memory.c:92
static void * cstl_shared_ptr_get(cstl_shared_ptr_t *const sp)
Get a pointer to the memory managed by the object.
Definition memory.h:450
void cstl_shared_ptr_share(const cstl_shared_ptr_t *ex, cstl_shared_ptr_t *n)
Create a new shared pointer object to manage the underlying memory.
Definition memory.c:113
void cstl_shared_ptr_alloc(cstl_shared_ptr_t *sp, size_t sz, cstl_xtor_func_t *clr)
Dynamically allocated memory to be shared via the object.
Definition memory.c:65
const void * cstl_shared_ptr_get_const(const cstl_shared_ptr_t *)
Get a pointer to the memory managed by the object.
Definition memory.c:103
The array object.
Definition array.h:31