libcstl
Loading...
Searching...
No Matches
dlist.c
Go to the documentation of this file.
1/*!
2 * @file
3 */
4
5#include "cstl/dlist.h"
6
7/*! @private */
8static void * __cstl_dlist_element(const struct cstl_dlist * const l,
9 const struct cstl_dlist_node * const n)
10{
11 return (void *)((uintptr_t)n - l->off);
12}
13
14/*! @private */
15static struct cstl_dlist_node * __cstl_dlist_node(
16 const struct cstl_dlist * const l,
17 const void * const e)
18{
19 return (void *)((uintptr_t)e + l->off);
20}
21
22/*! @private */
23static struct cstl_dlist_node ** __cstl_dlist_next(
24 struct cstl_dlist_node * const n)
25{
26 return &n->n;
27}
28
29/*! @private */
30static struct cstl_dlist_node ** __cstl_dlist_prev(
31 struct cstl_dlist_node * const n)
32{
33 return &n->p;
34}
35
36/*! @private */
37static void __cstl_dlist_insert(struct cstl_dlist * const l,
38 struct cstl_dlist_node * const p,
39 struct cstl_dlist_node * const n)
40{
41 n->n = p->n;
42 n->p = p;
43
44 /* node after p/n points back to n */
45 n->n->p = n;
46 /* p points to n as its next */
47 p->n = n;
48
49 l->size++;
50}
51
52void cstl_dlist_insert(struct cstl_dlist * const l,
53 void * const pe, void * const e)
54{
55 __cstl_dlist_insert(l, __cstl_dlist_node(l, pe), __cstl_dlist_node(l, e));
56}
57
58/*! @private */
59static void * __cstl_dlist_erase(struct cstl_dlist * const l,
60 struct cstl_dlist_node * const n)
61{
62 n->n->p = n->p;
63 n->p->n = n->n;
64
65 l->size--;
66
67 return __cstl_dlist_element(l, n);
68}
69
70void cstl_dlist_erase(struct cstl_dlist * const l, void * const e)
71{
72 __cstl_dlist_erase(l, __cstl_dlist_node(l, e));
73}
74
75void * cstl_dlist_front(struct cstl_dlist * const l)
76{
77 if (l->size > 0) {
78 return __cstl_dlist_element(l, l->h.n);
79 }
80 return NULL;
81}
82
83void * cstl_dlist_back(struct cstl_dlist * const l)
84{
85 if (l->size > 0) {
86 return __cstl_dlist_element(l, l->h.p);
87 }
88 return NULL;
89}
90
91void cstl_dlist_push_front(struct cstl_dlist * const l, void * const e)
92{
93 __cstl_dlist_insert(l, &l->h, __cstl_dlist_node(l, e));
94}
95
96void cstl_dlist_push_back(struct cstl_dlist * const l, void * const e)
97{
98 __cstl_dlist_insert(l, l->h.p, __cstl_dlist_node(l, e));
99}
100
101void * cstl_dlist_pop_front(struct cstl_dlist * const l)
102{
103 if (l->size > 0) {
104 return __cstl_dlist_erase(l, l->h.n);
105 }
106 return NULL;
107}
108
109void * cstl_dlist_pop_back(struct cstl_dlist * const l)
110{
111 if (l->size > 0) {
112 return __cstl_dlist_erase(l, l->h.p);
113 }
114 return NULL;
115}
116
117int cstl_dlist_foreach(struct cstl_dlist * const l,
118 cstl_visit_func_t * const visit, void * const p,
119 const cstl_dlist_foreach_dir_t dir)
120{
121 struct cstl_dlist_node ** (* next)(struct cstl_dlist_node *);
122 struct cstl_dlist_node * c, * n;
123 int res = 0;
124
125 switch (dir) {
126 default:
128 next = __cstl_dlist_next;
129 break;
131 next = __cstl_dlist_prev;
132 break;
133 }
134
135 for (c = *next(&l->h), n = *next(c);
136 res == 0 && c != &l->h;
137 c = n, n = *next(c)) {
138 res = visit(__cstl_dlist_element(l, c), p);
139 }
140
141 return res;
142}
143
144/*! @private */
145struct cstl_dlist_find_priv
146{
148 void * p;
149 const void * e;
150};
151
152/*! @private */
153static int cstl_dlist_find_visit(void * const e, void * const p)
154{
155 struct cstl_dlist_find_priv * lfp = p;
156
157 if (lfp->cmp(lfp->e, e, lfp->p) == 0) {
158 lfp->e = e;
159 return 1;
160 }
161
162 return 0;
163}
164
165void * cstl_dlist_find(const struct cstl_dlist * const l,
166 const void * const e,
167 cstl_compare_func_t * const cmp, void * const cmp_p,
168 const cstl_dlist_foreach_dir_t dir)
169{
170 struct cstl_dlist_find_priv lfp;
171
172 lfp.cmp = cmp;
173 lfp.p = cmp_p;
174 lfp.e = e;
175
176 if (cstl_dlist_foreach((struct cstl_dlist *)l,
177 cstl_dlist_find_visit, &lfp, dir) > 0) {
178 return (void *)lfp.e;
179 }
180
181 return NULL;
182}
183
184void cstl_dlist_swap(struct cstl_dlist * const a, struct cstl_dlist * const b)
185{
186 struct cstl_dlist t;
187
188 cstl_swap(a, b, &t, sizeof(t));
189
190#ifndef NO_DOC
191#define CSTL_DLIST_SWAP_FIX(L) \
192 do { \
193 if (L->size == 0) { \
194 L->h.n = L->h.p = &L->h; \
195 } else { \
196 L->h.n->p = L->h.p->n = &L->h; \
197 } \
198 } while (0)
199
200 CSTL_DLIST_SWAP_FIX(a);
201 CSTL_DLIST_SWAP_FIX(b);
202
203#undef CSTL_DLIST_SWAP_FIX
204#endif
205}
206
207void cstl_dlist_clear(struct cstl_dlist * const l,
208 cstl_xtor_func_t * const clr)
209{
210 while (l->size > 0) {
211 clr(__cstl_dlist_erase(l, l->h.n), NULL);
212 }
213}
214
215/*
216 * time complexity is linear. the number of
217 * loops/operations required is n/2, though.
218 */
219void cstl_dlist_reverse(struct cstl_dlist * const l)
220{
221 struct cstl_dlist_node * i, * j, * k;
222
223 /*
224 * i points to the first item in the list; j points
225 * to the last. the objects at i and j are swapped,
226 * and then i and j are advanced toward the center.
227 * the loop terminates when i and j point to the same
228 * or adjacent (with i still before j) nodes.
229 *
230 * k is used as a temporary variable
231 */
232 for (i = l->h.n, j = l->h.p;
233 i != j && i->n != j;
234 k = i->p, i = j->n, j = k) {
235 struct cstl_dlist_node t;
236
237 /* point the nodes surrounding i at j... */
238 i->p->n = j;
239 i->n->p = j;
240
241 /* ...and the nodes surrounding j at i */
242 j->n->p = i;
243 j->p->n = i;
244
245 /* then swap i and j */
246 cstl_swap(i, j, &t, sizeof(t));
247 }
248
249 /*
250 * if i and j point to the same node, no need to
251 * swap them. if they're different, they need to
252 * be swapped, but special handling is required
253 */
254
255 if (i->n == j) {
256 /*
257 * point the nodes before i and after j
258 * at j and i, respectively
259 */
260 i->p->n = j;
261 j->n->p = i;
262
263 /* then fix up i and j's pointers */
264 i->n = j->n;
265 j->n = i;
266
267 j->p = i->p;
268 i->p = j;
269 }
270}
271
272void cstl_dlist_concat(struct cstl_dlist * const d, struct cstl_dlist * const s)
273{
274 if (d != s && d->off == s->off && s->size > 0) {
275 /* beginning of s points back at end of d */
276 s->h.n->p = d->h.p;
277 /* end of s points at head of d */
278 s->h.p->n = &d->h;
279
280 /* end of d points at beginning of s */
281 d->h.p->n = s->h.n;
282 /* head of d points back at end of s */
283 d->h.p = s->h.p;
284
285 d->size += s->size;
286
287 /* leave the original list in a usable state */
288 cstl_dlist_init(s, s->off);
289 }
290}
291
292void cstl_dlist_sort(struct cstl_dlist * const l,
293 cstl_compare_func_t * const cmp, void * const priv)
294{
295 /* no need to sort a list with a size of one */
296
297 if (l->size > 1) {
298 struct cstl_dlist _l[2];
299 struct cstl_dlist_node * t;
300
301 /*
302 * break the list l into two halves,
303 * into _l[0] and _l[1]
304 */
305
306 cstl_dlist_init(&_l[0], l->off);
307 cstl_dlist_init(&_l[1], l->off);
308
309 /* find the middle of the list */
310 for (t = &l->h;
311 _l[0].size < l->size / 2;
312 t = t->n, _l[0].size++)
313 ;
314
315 _l[0].h.n = l->h.n;
316 _l[0].h.p = t;
317
318 _l[1].h.n = t->n;
319 _l[1].h.p = l->h.p;
320
321 _l[0].h.n->p = _l[0].h.p->n = &_l[0].h;
322 _l[1].h.n->p = _l[1].h.p->n = &_l[1].h;
323
324 _l[1].size = l->size - _l[0].size;
325 cstl_dlist_init(l, l->off);
326
327 /* sort the two halves */
328 cstl_dlist_sort(&_l[0], cmp, priv);
329 cstl_dlist_sort(&_l[1], cmp, priv);
330
331 /*
332 * merge the two sorted halves back together by
333 * comparing the nodes at the front of each list
334 * and moving the smaller one to the output
335 */
336 while (_l[0].size > 0 && _l[1].size > 0) {
337 struct cstl_dlist_node * n;
338 struct cstl_dlist * ol;
339
340 if (cmp(__cstl_dlist_element(&_l[0], _l[0].h.n),
341 __cstl_dlist_element(&_l[1], _l[1].h.n),
342 priv) <= 0) {
343 ol = &_l[0];
344 } else {
345 ol = &_l[1];
346 }
347
348 n = ol->h.n;
349 __cstl_dlist_erase(ol, n);
350 __cstl_dlist_insert(l, l->h.p, n);
351 }
352
353 /* append whatever is left to the output */
354 if (_l[0].size > 0) {
355 cstl_dlist_concat(l, &_l[0]);
356 } else {
357 cstl_dlist_concat(l, &_l[1]);
358 }
359 }
360}
361
362#ifdef __cfg_test__
363// GCOV_EXCL_START
364#include <check.h>
365#include <stdlib.h>
366
367struct integer
368{
369 int v;
370 struct cstl_dlist_node ln;
371};
372
373static int cmp_integer(const void * const a, const void * const b,
374 void * const p)
375{
376 (void)p;
377 return ((struct integer *)a)->v - ((struct integer *)b)->v;
378}
379
380static void __test_cstl_dlist_free(void * const p, void * const x)
381{
382 (void)x;
383 free(p);
384}
385
386static void __test__cstl_dlist_fill(struct cstl_dlist * l, const size_t n)
387{
388 unsigned int i;
389
390 for (i = 0; i < n; i++) {
391 struct integer * in = malloc(sizeof(*in));
392
393 in->v = rand() % n;
395 }
396
397 ck_assert_uint_eq(n, cstl_dlist_size(l));
398}
399
400START_TEST(simple)
401{
402 DECLARE_CSTL_DLIST(l, struct integer, ln);
403 struct integer a, b, c;
404
405 a.v = 0; b.v = 1; c.v = 2;
406
407 ck_assert_int_eq(cstl_dlist_size(&l), 0);
408 ck_assert_ptr_null(cstl_dlist_front(&l));
409 ck_assert_ptr_null(cstl_dlist_back(&l));
410
411 cstl_dlist_push_front(&l, &a);
412 ck_assert_int_eq(cstl_dlist_size(&l), 1);
413 ck_assert_ptr_eq(cstl_dlist_front(&l), &a);
414 ck_assert_ptr_eq(cstl_dlist_back(&l), &a);
415
416 cstl_dlist_insert(&l, &a, &b);
417 ck_assert_int_eq(cstl_dlist_size(&l), 2);
418 ck_assert_ptr_eq(cstl_dlist_front(&l), &a);
419 ck_assert_ptr_eq(cstl_dlist_back(&l), &b);
420
421 cstl_dlist_push_back(&l, &c);
422 ck_assert_int_eq(cstl_dlist_size(&l), 3);
423 ck_assert_ptr_eq(cstl_dlist_front(&l), &a);
424 ck_assert_ptr_eq(cstl_dlist_back(&l), &c);
425
426 ck_assert_ptr_eq(
428 &b, cmp_integer, NULL,
430 &b);
431
432 ck_assert_ptr_eq(&a, cstl_dlist_pop_front(&l));
433 ck_assert_int_eq(cstl_dlist_size(&l), 2);
434
435 ck_assert_ptr_null(
437 &a, cmp_integer, NULL,
439
440 ck_assert_ptr_eq(&c, cstl_dlist_pop_back(&l));
441 ck_assert_int_eq(cstl_dlist_size(&l), 1);
442 cstl_dlist_erase(&l, &b);
443 ck_assert_int_eq(cstl_dlist_size(&l), 0);
444
445 ck_assert_ptr_null(cstl_dlist_pop_front(&l));
446 ck_assert_ptr_null(cstl_dlist_pop_back(&l));
447}
448END_TEST
449
450START_TEST(fill)
451{
452 static const size_t n = 100;
453 DECLARE_CSTL_DLIST(l, struct integer, ln);
454
455 __test__cstl_dlist_fill(&l, n);
456
457 cstl_dlist_clear(&l, __test_cstl_dlist_free);
458 ck_assert_uint_eq(cstl_dlist_size(&l), 0);
459}
460END_TEST
461
462START_TEST(concat)
463{
464 static const size_t n = 4;
465 DECLARE_CSTL_DLIST(l1, struct integer, ln);
466 DECLARE_CSTL_DLIST(l2, struct integer, ln);
467
468 __test__cstl_dlist_fill(&l1, n);
469 __test__cstl_dlist_fill(&l2, n);
470
471 cstl_dlist_concat(&l1, &l2);
472 ck_assert_uint_eq(cstl_dlist_size(&l1), 2 * n);
473 ck_assert_uint_eq(cstl_dlist_size(&l2), 0);
474
475 cstl_dlist_clear(&l1, __test_cstl_dlist_free);
476 cstl_dlist_clear(&l2, __test_cstl_dlist_free);
477}
478END_TEST
479
480static int cstl_dlist_verify_sorted_fwd(void * const e, void * const p)
481{
482 struct integer ** in = p;
483 if (*in != NULL) {
484 ck_assert_int_ge(((struct integer *)e)->v, (*in)->v);
485 }
486 *in = e;
487 return 0;
488}
489
490static int cstl_dlist_verify_sorted_rev(void * const e, void * const p)
491{
492 struct integer ** in = p;
493 if (*in != NULL) {
494 ck_assert_int_le(((struct integer *)e)->v, (*in)->v);
495 }
496 *in = e;
497 return 0;
498}
499
500START_TEST(sort)
501{
502 static const size_t n = 100;
503 DECLARE_CSTL_DLIST(l, struct integer, ln);
504
505 struct integer * in;
506
507 __test__cstl_dlist_fill(&l, n);
508
509 cstl_dlist_sort(&l, cmp_integer, NULL);
510 ck_assert_uint_eq(n, cstl_dlist_size(&l));
511 in = NULL;
513 cstl_dlist_verify_sorted_fwd, &in,
515 in = NULL;
517 cstl_dlist_verify_sorted_rev, &in,
519
520 cstl_dlist_clear(&l, __test_cstl_dlist_free);
521 ck_assert_uint_eq(cstl_dlist_size(&l), 0);
522}
523END_TEST
524
525START_TEST(reverse)
526{
527 static const size_t n = 100;
528 DECLARE_CSTL_DLIST(l, struct integer, ln);
529
530 struct integer * in = NULL;
531
532 __test__cstl_dlist_fill(&l, n);
533
534 cstl_dlist_sort(&l, cmp_integer, NULL);
537 cstl_dlist_verify_sorted_rev, &in,
539
540 cstl_dlist_clear(&l, __test_cstl_dlist_free);
541 ck_assert_uint_eq(cstl_dlist_size(&l), 0);
542}
543END_TEST
544
545START_TEST(swap)
546{
547 DECLARE_CSTL_DLIST(l1, struct integer, ln);
548 DECLARE_CSTL_DLIST(l2, struct integer, ln);
549
550 __test__cstl_dlist_fill(&l1, 0);
551 cstl_dlist_swap(&l1, &l2);
552 ck_assert_int_eq(cstl_dlist_size(&l1), 0);
553 ck_assert_int_eq(cstl_dlist_size(&l2), 0);
554 cstl_dlist_clear(&l1, __test_cstl_dlist_free);
555 cstl_dlist_clear(&l2, __test_cstl_dlist_free);
556
557 __test__cstl_dlist_fill(&l1, 1);
558 cstl_dlist_swap(&l1, &l2);
559 ck_assert_int_eq(cstl_dlist_size(&l1), 0);
560 ck_assert_int_eq(cstl_dlist_size(&l2), 1);
561 cstl_dlist_clear(&l1, __test_cstl_dlist_free);
562 cstl_dlist_clear(&l2, __test_cstl_dlist_free);
563
564 __test__cstl_dlist_fill(&l1, 2);
565 cstl_dlist_swap(&l1, &l2);
566 ck_assert_int_eq(cstl_dlist_size(&l1), 0);
567 ck_assert_int_eq(cstl_dlist_size(&l2), 2);
568 cstl_dlist_clear(&l1, __test_cstl_dlist_free);
569 cstl_dlist_clear(&l2, __test_cstl_dlist_free);
570
571 __test__cstl_dlist_fill(&l1, 2);
572 __test__cstl_dlist_fill(&l2, 3);
573 cstl_dlist_swap(&l1, &l2);
574 ck_assert_int_eq(cstl_dlist_size(&l1), 3);
575 ck_assert_int_eq(cstl_dlist_size(&l2), 2);
576 cstl_dlist_clear(&l1, __test_cstl_dlist_free);
577 cstl_dlist_clear(&l2, __test_cstl_dlist_free);
578}
579END_TEST
580
581Suite * dlist_suite(void)
582{
583 Suite * const s = suite_create("dlist");
584
585 TCase * tc;
586
587 tc = tcase_create("dlist");
588 tcase_add_test(tc, simple);
589 tcase_add_test(tc, fill);
590 tcase_add_test(tc, concat);
591 tcase_add_test(tc, sort);
592 tcase_add_test(tc, reverse);
593 tcase_add_test(tc, swap);
594 suite_add_tcase(s, tc);
595
596 return s;
597}
598
599// GCOV_EXCL_STOP
600#endif
static void cstl_swap(void *const x, void *const y, void *const t, const size_t sz)
Swap values at two memory locations via use of a third.
Definition common.h:137
void cstl_xtor_func_t(void *obj, void *priv)
Type for functions called to construct, clear, or destroy an object.
Definition common.h:97
int cstl_visit_func_t(void *obj, void *priv)
Type for visit callbacks from objects supporting foreach.
Definition common.h:65
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
#define DECLARE_CSTL_DLIST(NAME, TYPE, MEMB)
(Statically) declare and initialize a list
Definition dlist.h:97
static void cstl_dlist_init(struct cstl_dlist *const l, const size_t off)
Initialize a list object.
Definition dlist.h:108
void * cstl_dlist_front(struct cstl_dlist *const l)
Get a pointer to the first object in the list.
Definition dlist.c:75
void cstl_dlist_reverse(struct cstl_dlist *const l)
Reverse the order of items in the list.
Definition dlist.c:219
void cstl_dlist_clear(struct cstl_dlist *const l, cstl_xtor_func_t *const clr)
Remove objects from and reinitialize a list.
Definition dlist.c:207
void * cstl_dlist_pop_front(struct cstl_dlist *const l)
Remove the first item in the list and return it.
Definition dlist.c:101
void cstl_dlist_erase(struct cstl_dlist *const l, void *const e)
Remove an object from the list.
Definition dlist.c:70
static size_t cstl_dlist_size(const struct cstl_dlist *const l)
Get the number of objects in the list.
Definition dlist.h:126
void cstl_dlist_swap(struct cstl_dlist *const a, struct cstl_dlist *const b)
Swap the list objects at the two given locations.
Definition dlist.c:184
void cstl_dlist_concat(struct cstl_dlist *const d, struct cstl_dlist *const s)
Append one list to the end of another.
Definition dlist.c:272
void * cstl_dlist_find(const struct cstl_dlist *const l, const void *const e, cstl_compare_func_t *const cmp, void *const cmp_p, const cstl_dlist_foreach_dir_t dir)
Perform a linear search for an object.
Definition dlist.c:165
void * cstl_dlist_pop_back(struct cstl_dlist *const l)
Remove the last item in the list and return it.
Definition dlist.c:109
void cstl_dlist_push_front(struct cstl_dlist *const l, void *const e)
Insert a new object at the front of the list.
Definition dlist.c:91
cstl_dlist_foreach_dir_t
The direction in which to traverse the list during cstl_dlist_foreach()
Definition dlist.h:239
void cstl_dlist_push_back(struct cstl_dlist *const l, void *const e)
Insert a new object at the back of the list.
Definition dlist.c:96
int cstl_dlist_foreach(struct cstl_dlist *const l, cstl_visit_func_t *const visit, void *const p, const cstl_dlist_foreach_dir_t dir)
Call a user-supplied function for each object in a list.
Definition dlist.c:117
void * cstl_dlist_back(struct cstl_dlist *const l)
Get a pointer to the last object in the list.
Definition dlist.c:83
void cstl_dlist_sort(struct cstl_dlist *const l, cstl_compare_func_t *const cmp, void *const priv)
Sort the items in a list.
Definition dlist.c:292
void cstl_dlist_insert(struct cstl_dlist *const l, void *const pe, void *const e)
Insert a new object into the list.
Definition dlist.c:52
@ CSTL_DLIST_FOREACH_DIR_FWD
Traverse the list from front to back.
Definition dlist.h:241
@ CSTL_DLIST_FOREACH_DIR_REV
Traverse the list from back to front.
Definition dlist.h:243
Node to anchor an element within a list.
Definition dlist.h:46
List object.
Definition dlist.h:60