| |
1 /* |
| |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
| |
3 * |
| |
4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved. |
| |
5 * |
| |
6 * Redistribution and use in source and binary forms, with or without |
| |
7 * modification, are permitted provided that the following conditions are met: |
| |
8 * |
| |
9 * 1. Redistributions of source code must retain the above copyright |
| |
10 * notice, this list of conditions and the following disclaimer. |
| |
11 * |
| |
12 * 2. Redistributions in binary form must reproduce the above copyright |
| |
13 * notice, this list of conditions and the following disclaimer in the |
| |
14 * documentation and/or other materials provided with the distribution. |
| |
15 * |
| |
16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| |
17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| |
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| |
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| |
20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| |
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| |
26 * POSSIBILITY OF SUCH DAMAGE. |
| |
27 */ |
| |
28 |
| |
29 #include "cx/linked_list.h" |
| |
30 #include "cx/utils.h" |
| |
31 #include "cx/compare.h" |
| |
32 #include <string.h> |
| |
33 #include <assert.h> |
| |
34 |
| |
35 // LOW LEVEL LINKED LIST FUNCTIONS |
| |
36 |
| |
37 #define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) |
| |
38 #define ll_prev(node) CX_LL_PTR(node, loc_prev) |
| |
39 #define ll_next(node) CX_LL_PTR(node, loc_next) |
| |
40 #define ll_advance(node) CX_LL_PTR(node, loc_advance) |
| |
41 #define ll_data(node) (((char*)(node))+loc_data) |
| |
42 |
| |
43 void *cx_linked_list_at( |
| |
44 void const *start, |
| |
45 size_t start_index, |
| |
46 ptrdiff_t loc_advance, |
| |
47 size_t index |
| |
48 ) { |
| |
49 assert(start != NULL); |
| |
50 assert(loc_advance >= 0); |
| |
51 size_t i = start_index; |
| |
52 void const *cur = start; |
| |
53 while (i != index && cur != NULL) { |
| |
54 cur = ll_advance(cur); |
| |
55 i < index ? i++ : i--; |
| |
56 } |
| |
57 return (void *) cur; |
| |
58 } |
| |
59 |
| |
60 ssize_t cx_linked_list_find( |
| |
61 void const *start, |
| |
62 ptrdiff_t loc_advance, |
| |
63 ptrdiff_t loc_data, |
| |
64 cx_compare_func cmp_func, |
| |
65 void const *elem |
| |
66 ) { |
| |
67 void *dummy; |
| |
68 return cx_linked_list_find_node( |
| |
69 &dummy, start, |
| |
70 loc_advance, loc_data, |
| |
71 cmp_func, elem |
| |
72 ); |
| |
73 } |
| |
74 |
| |
75 ssize_t cx_linked_list_find_node( |
| |
76 void **result, |
| |
77 void const *start, |
| |
78 ptrdiff_t loc_advance, |
| |
79 ptrdiff_t loc_data, |
| |
80 cx_compare_func cmp_func, |
| |
81 void const *elem |
| |
82 ) { |
| |
83 assert(result != NULL); |
| |
84 assert(start != NULL); |
| |
85 assert(loc_advance >= 0); |
| |
86 assert(loc_data >= 0); |
| |
87 assert(cmp_func); |
| |
88 |
| |
89 void const *node = start; |
| |
90 ssize_t index = 0; |
| |
91 do { |
| |
92 void *current = ll_data(node); |
| |
93 if (cmp_func(current, elem) == 0) { |
| |
94 *result = (void*) node; |
| |
95 return index; |
| |
96 } |
| |
97 node = ll_advance(node); |
| |
98 index++; |
| |
99 } while (node != NULL); |
| |
100 *result = NULL; |
| |
101 return -1; |
| |
102 } |
| |
103 |
| |
104 void *cx_linked_list_first( |
| |
105 void const *node, |
| |
106 ptrdiff_t loc_prev |
| |
107 ) { |
| |
108 return cx_linked_list_last(node, loc_prev); |
| |
109 } |
| |
110 |
| |
111 void *cx_linked_list_last( |
| |
112 void const *node, |
| |
113 ptrdiff_t loc_next |
| |
114 ) { |
| |
115 assert(node != NULL); |
| |
116 assert(loc_next >= 0); |
| |
117 |
| |
118 void const *cur = node; |
| |
119 void const *last; |
| |
120 do { |
| |
121 last = cur; |
| |
122 } while ((cur = ll_next(cur)) != NULL); |
| |
123 |
| |
124 return (void *) last; |
| |
125 } |
| |
126 |
| |
127 void *cx_linked_list_prev( |
| |
128 void const *begin, |
| |
129 ptrdiff_t loc_next, |
| |
130 void const *node |
| |
131 ) { |
| |
132 assert(begin != NULL); |
| |
133 assert(node != NULL); |
| |
134 assert(loc_next >= 0); |
| |
135 if (begin == node) return NULL; |
| |
136 void const *cur = begin; |
| |
137 void const *next; |
| |
138 while (1) { |
| |
139 next = ll_next(cur); |
| |
140 if (next == node) return (void *) cur; |
| |
141 cur = next; |
| |
142 } |
| |
143 } |
| |
144 |
| |
145 void cx_linked_list_link( |
| |
146 void *left, |
| |
147 void *right, |
| |
148 ptrdiff_t loc_prev, |
| |
149 ptrdiff_t loc_next |
| |
150 ) { |
| |
151 assert(loc_next >= 0); |
| |
152 ll_next(left) = right; |
| |
153 if (loc_prev >= 0) { |
| |
154 ll_prev(right) = left; |
| |
155 } |
| |
156 } |
| |
157 |
| |
158 void cx_linked_list_unlink( |
| |
159 void *left, |
| |
160 void *right, |
| |
161 ptrdiff_t loc_prev, |
| |
162 ptrdiff_t loc_next |
| |
163 ) { |
| |
164 assert (loc_next >= 0); |
| |
165 assert(ll_next(left) == right); |
| |
166 ll_next(left) = NULL; |
| |
167 if (loc_prev >= 0) { |
| |
168 assert(ll_prev(right) == left); |
| |
169 ll_prev(right) = NULL; |
| |
170 } |
| |
171 } |
| |
172 |
| |
173 void cx_linked_list_add( |
| |
174 void **begin, |
| |
175 void **end, |
| |
176 ptrdiff_t loc_prev, |
| |
177 ptrdiff_t loc_next, |
| |
178 void *new_node |
| |
179 ) { |
| |
180 void *last; |
| |
181 if (end == NULL) { |
| |
182 assert(begin != NULL); |
| |
183 last = *begin == NULL ? NULL : cx_linked_list_last(*begin, loc_next); |
| |
184 } else { |
| |
185 last = *end; |
| |
186 } |
| |
187 cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, last, new_node, new_node); |
| |
188 } |
| |
189 |
| |
190 void cx_linked_list_prepend( |
| |
191 void **begin, |
| |
192 void **end, |
| |
193 ptrdiff_t loc_prev, |
| |
194 ptrdiff_t loc_next, |
| |
195 void *new_node |
| |
196 ) { |
| |
197 cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, NULL, new_node, new_node); |
| |
198 } |
| |
199 |
| |
200 void cx_linked_list_insert( |
| |
201 void **begin, |
| |
202 void **end, |
| |
203 ptrdiff_t loc_prev, |
| |
204 ptrdiff_t loc_next, |
| |
205 void *node, |
| |
206 void *new_node |
| |
207 ) { |
| |
208 cx_linked_list_insert_chain(begin, end, loc_prev, loc_next, node, new_node, new_node); |
| |
209 } |
| |
210 |
| |
211 void cx_linked_list_insert_chain( |
| |
212 void **begin, |
| |
213 void **end, |
| |
214 ptrdiff_t loc_prev, |
| |
215 ptrdiff_t loc_next, |
| |
216 void *node, |
| |
217 void *insert_begin, |
| |
218 void *insert_end |
| |
219 ) { |
| |
220 // find the end of the chain, if not specified |
| |
221 if (insert_end == NULL) { |
| |
222 insert_end = cx_linked_list_last(insert_begin, loc_next); |
| |
223 } |
| |
224 |
| |
225 // determine the successor |
| |
226 void *successor; |
| |
227 if (node == NULL) { |
| |
228 assert(begin != NULL || (end != NULL && loc_prev >= 0)); |
| |
229 if (begin != NULL) { |
| |
230 successor = *begin; |
| |
231 *begin = insert_begin; |
| |
232 } else { |
| |
233 successor = *end == NULL ? NULL : cx_linked_list_first(*end, loc_prev); |
| |
234 } |
| |
235 } else { |
| |
236 successor = ll_next(node); |
| |
237 cx_linked_list_link(node, insert_begin, loc_prev, loc_next); |
| |
238 } |
| |
239 |
| |
240 if (successor == NULL) { |
| |
241 // the list ends with the new chain |
| |
242 if (end != NULL) { |
| |
243 *end = insert_end; |
| |
244 } |
| |
245 } else { |
| |
246 cx_linked_list_link(insert_end, successor, loc_prev, loc_next); |
| |
247 } |
| |
248 } |
| |
249 |
| |
250 void cx_linked_list_remove( |
| |
251 void **begin, |
| |
252 void **end, |
| |
253 ptrdiff_t loc_prev, |
| |
254 ptrdiff_t loc_next, |
| |
255 void *node |
| |
256 ) { |
| |
257 assert(node != NULL); |
| |
258 assert(loc_next >= 0); |
| |
259 assert(loc_prev >= 0 || begin != NULL); |
| |
260 |
| |
261 // find adjacent nodes |
| |
262 void *next = ll_next(node); |
| |
263 void *prev; |
| |
264 if (loc_prev >= 0) { |
| |
265 prev = ll_prev(node); |
| |
266 } else { |
| |
267 prev = cx_linked_list_prev(*begin, loc_next, node); |
| |
268 } |
| |
269 |
| |
270 // update next pointer of prev node, or set begin |
| |
271 if (prev == NULL) { |
| |
272 if (begin != NULL) { |
| |
273 *begin = next; |
| |
274 } |
| |
275 } else { |
| |
276 ll_next(prev) = next; |
| |
277 } |
| |
278 |
| |
279 // update prev pointer of next node, or set end |
| |
280 if (next == NULL) { |
| |
281 if (end != NULL) { |
| |
282 *end = prev; |
| |
283 } |
| |
284 } else if (loc_prev >= 0) { |
| |
285 ll_prev(next) = prev; |
| |
286 } |
| |
287 } |
| |
288 |
| |
289 size_t cx_linked_list_size( |
| |
290 void const *node, |
| |
291 ptrdiff_t loc_next |
| |
292 ) { |
| |
293 assert(loc_next >= 0); |
| |
294 size_t size = 0; |
| |
295 while (node != NULL) { |
| |
296 node = ll_next(node); |
| |
297 size++; |
| |
298 } |
| |
299 return size; |
| |
300 } |
| |
301 |
| |
302 #ifndef CX_LINKED_LIST_SORT_SBO_SIZE |
| |
303 #define CX_LINKED_LIST_SORT_SBO_SIZE 1024 |
| |
304 #endif |
| |
305 |
| |
306 static void cx_linked_list_sort_merge( |
| |
307 ptrdiff_t loc_prev, |
| |
308 ptrdiff_t loc_next, |
| |
309 ptrdiff_t loc_data, |
| |
310 size_t length, |
| |
311 void *ls, |
| |
312 void *le, |
| |
313 void *re, |
| |
314 cx_compare_func cmp_func, |
| |
315 void **begin, |
| |
316 void **end |
| |
317 ) { |
| |
318 void *sbo[CX_LINKED_LIST_SORT_SBO_SIZE]; |
| |
319 void **sorted = length >= CX_LINKED_LIST_SORT_SBO_SIZE ? |
| |
320 malloc(sizeof(void *) * length) : sbo; |
| |
321 if (sorted == NULL) abort(); |
| |
322 void *rc, *lc; |
| |
323 |
| |
324 lc = ls; |
| |
325 rc = le; |
| |
326 size_t n = 0; |
| |
327 while (lc && lc != le && rc != re) { |
| |
328 if (cmp_func(ll_data(lc), ll_data(rc)) <= 0) { |
| |
329 sorted[n] = lc; |
| |
330 lc = ll_next(lc); |
| |
331 } else { |
| |
332 sorted[n] = rc; |
| |
333 rc = ll_next(rc); |
| |
334 } |
| |
335 n++; |
| |
336 } |
| |
337 while (lc && lc != le) { |
| |
338 sorted[n] = lc; |
| |
339 lc = ll_next(lc); |
| |
340 n++; |
| |
341 } |
| |
342 while (rc && rc != re) { |
| |
343 sorted[n] = rc; |
| |
344 rc = ll_next(rc); |
| |
345 n++; |
| |
346 } |
| |
347 |
| |
348 // Update pointer |
| |
349 if (loc_prev >= 0) ll_prev(sorted[0]) = NULL; |
| |
350 cx_for_n (i, length - 1) { |
| |
351 cx_linked_list_link(sorted[i], sorted[i + 1], loc_prev, loc_next); |
| |
352 } |
| |
353 ll_next(sorted[length - 1]) = NULL; |
| |
354 |
| |
355 *begin = sorted[0]; |
| |
356 *end = sorted[length-1]; |
| |
357 if (sorted != sbo) { |
| |
358 free(sorted); |
| |
359 } |
| |
360 } |
| |
361 |
| |
362 void cx_linked_list_sort( // NOLINT(misc-no-recursion) - purposely recursive function |
| |
363 void **begin, |
| |
364 void **end, |
| |
365 ptrdiff_t loc_prev, |
| |
366 ptrdiff_t loc_next, |
| |
367 ptrdiff_t loc_data, |
| |
368 cx_compare_func cmp_func |
| |
369 ) { |
| |
370 assert(begin != NULL); |
| |
371 assert(loc_next >= 0); |
| |
372 assert(loc_data >= 0); |
| |
373 assert(cmp_func); |
| |
374 |
| |
375 void *lc, *ls, *le, *re; |
| |
376 |
| |
377 // set start node |
| |
378 ls = *begin; |
| |
379 |
| |
380 // early exit when this list is empty |
| |
381 if (ls == NULL) return; |
| |
382 |
| |
383 // check how many elements are already sorted |
| |
384 lc = ls; |
| |
385 size_t ln = 1; |
| |
386 while (ll_next(lc) != NULL && cmp_func(ll_data(ll_next(lc)), ll_data(lc)) > 0) { |
| |
387 lc = ll_next(lc); |
| |
388 ln++; |
| |
389 } |
| |
390 le = ll_next(lc); |
| |
391 |
| |
392 // if first unsorted node is NULL, the list is already completely sorted |
| |
393 if (le != NULL) { |
| |
394 void *rc; |
| |
395 size_t rn = 1; |
| |
396 rc = le; |
| |
397 // skip already sorted elements |
| |
398 while (ll_next(rc) != NULL && cmp_func(ll_data(ll_next(rc)), ll_data(rc)) > 0) { |
| |
399 rc = ll_next(rc); |
| |
400 rn++; |
| |
401 } |
| |
402 re = ll_next(rc); |
| |
403 |
| |
404 // {ls,...,le->prev} and {rs,...,re->prev} are sorted - merge them |
| |
405 void *sorted_begin, *sorted_end; |
| |
406 cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, |
| |
407 ln + rn, ls, le, re, cmp_func, |
| |
408 &sorted_begin, &sorted_end); |
| |
409 |
| |
410 // Something left? Sort it! |
| |
411 size_t remainder_length = cx_linked_list_size(re, loc_next); |
| |
412 if (remainder_length > 0) { |
| |
413 void *remainder = re; |
| |
414 cx_linked_list_sort(&remainder, NULL, loc_prev, loc_next, loc_data, cmp_func); |
| |
415 |
| |
416 // merge sorted list with (also sorted) remainder |
| |
417 cx_linked_list_sort_merge(loc_prev, loc_next, loc_data, |
| |
418 ln + rn + remainder_length, |
| |
419 sorted_begin, remainder, NULL, cmp_func, |
| |
420 &sorted_begin, &sorted_end); |
| |
421 } |
| |
422 *begin = sorted_begin; |
| |
423 if (end) *end = sorted_end; |
| |
424 } |
| |
425 } |
| |
426 |
| |
427 int cx_linked_list_compare( |
| |
428 void const *begin_left, |
| |
429 void const *begin_right, |
| |
430 ptrdiff_t loc_advance, |
| |
431 ptrdiff_t loc_data, |
| |
432 cx_compare_func cmp_func |
| |
433 ) { |
| |
434 void const *left = begin_left, *right = begin_right; |
| |
435 |
| |
436 while (left != NULL && right != NULL) { |
| |
437 void const *left_data = ll_data(left); |
| |
438 void const *right_data = ll_data(right); |
| |
439 int result = cmp_func(left_data, right_data); |
| |
440 if (result != 0) return result; |
| |
441 left = ll_advance(left); |
| |
442 right = ll_advance(right); |
| |
443 } |
| |
444 |
| |
445 if (left != NULL) { return 1; } |
| |
446 else if (right != NULL) { return -1; } |
| |
447 else { return 0; } |
| |
448 } |
| |
449 |
| |
450 void cx_linked_list_reverse( |
| |
451 void **begin, |
| |
452 void **end, |
| |
453 ptrdiff_t loc_prev, |
| |
454 ptrdiff_t loc_next |
| |
455 ) { |
| |
456 assert(begin != NULL); |
| |
457 assert(loc_next >= 0); |
| |
458 |
| |
459 // swap all links |
| |
460 void *prev = NULL; |
| |
461 void *cur = *begin; |
| |
462 while (cur != NULL) { |
| |
463 void *next = ll_next(cur); |
| |
464 |
| |
465 ll_next(cur) = prev; |
| |
466 if (loc_prev >= 0) { |
| |
467 ll_prev(cur) = next; |
| |
468 } |
| |
469 |
| |
470 prev = cur; |
| |
471 cur = next; |
| |
472 } |
| |
473 |
| |
474 // update begin and end |
| |
475 if (end != NULL) { |
| |
476 *end = *begin; |
| |
477 } |
| |
478 *begin = prev; |
| |
479 } |
| |
480 |
| |
481 // HIGH LEVEL LINKED LIST IMPLEMENTATION |
| |
482 |
| |
483 typedef struct cx_linked_list_node cx_linked_list_node; |
| |
484 struct cx_linked_list_node { |
| |
485 cx_linked_list_node *prev; |
| |
486 cx_linked_list_node *next; |
| |
487 char payload[]; |
| |
488 }; |
| |
489 |
| |
490 #define CX_LL_LOC_PREV offsetof(cx_linked_list_node, prev) |
| |
491 #define CX_LL_LOC_NEXT offsetof(cx_linked_list_node, next) |
| |
492 #define CX_LL_LOC_DATA offsetof(cx_linked_list_node, payload) |
| |
493 |
| |
494 typedef struct { |
| |
495 struct cx_list_s base; |
| |
496 cx_linked_list_node *begin; |
| |
497 cx_linked_list_node *end; |
| |
498 } cx_linked_list; |
| |
499 |
| |
500 static cx_linked_list_node *cx_ll_node_at( |
| |
501 cx_linked_list const *list, |
| |
502 size_t index |
| |
503 ) { |
| |
504 if (index >= list->base.collection.size) { |
| |
505 return NULL; |
| |
506 } else if (index > list->base.collection.size / 2) { |
| |
507 return cx_linked_list_at(list->end, list->base.collection.size - 1, CX_LL_LOC_PREV, index); |
| |
508 } else { |
| |
509 return cx_linked_list_at(list->begin, 0, CX_LL_LOC_NEXT, index); |
| |
510 } |
| |
511 } |
| |
512 |
| |
513 static int cx_ll_insert_at( |
| |
514 struct cx_list_s *list, |
| |
515 cx_linked_list_node *node, |
| |
516 void const *elem |
| |
517 ) { |
| |
518 |
| |
519 // create the new new_node |
| |
520 cx_linked_list_node *new_node = cxMalloc(list->collection.allocator, |
| |
521 sizeof(cx_linked_list_node) + list->collection.elem_size); |
| |
522 |
| |
523 // sortir if failed |
| |
524 if (new_node == NULL) return 1; |
| |
525 |
| |
526 // initialize new new_node |
| |
527 new_node->prev = new_node->next = NULL; |
| |
528 memcpy(new_node->payload, elem, list->collection.elem_size); |
| |
529 |
| |
530 // insert |
| |
531 cx_linked_list *ll = (cx_linked_list *) list; |
| |
532 cx_linked_list_insert_chain( |
| |
533 (void **) &ll->begin, (void **) &ll->end, |
| |
534 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, |
| |
535 node, new_node, new_node |
| |
536 ); |
| |
537 |
| |
538 // increase the size and return |
| |
539 list->collection.size++; |
| |
540 return 0; |
| |
541 } |
| |
542 |
| |
543 static size_t cx_ll_insert_array( |
| |
544 struct cx_list_s *list, |
| |
545 size_t index, |
| |
546 void const *array, |
| |
547 size_t n |
| |
548 ) { |
| |
549 // out-of bounds and corner case check |
| |
550 if (index > list->collection.size || n == 0) return 0; |
| |
551 |
| |
552 // find position efficiently |
| |
553 cx_linked_list_node *node = index == 0 ? NULL : cx_ll_node_at((cx_linked_list *) list, index - 1); |
| |
554 |
| |
555 // perform first insert |
| |
556 if (0 != cx_ll_insert_at(list, node, array)) { |
| |
557 return 1; |
| |
558 } |
| |
559 |
| |
560 // is there more? |
| |
561 if (n == 1) return 1; |
| |
562 |
| |
563 // we now know exactly where we are |
| |
564 node = node == NULL ? ((cx_linked_list *) list)->begin : node->next; |
| |
565 |
| |
566 // we can add the remaining nodes and immedately advance to the inserted node |
| |
567 char const *source = array; |
| |
568 for (size_t i = 1; i < n; i++) { |
| |
569 source += list->collection.elem_size; |
| |
570 if (0 != cx_ll_insert_at(list, node, source)) { |
| |
571 return i; |
| |
572 } |
| |
573 node = node->next; |
| |
574 } |
| |
575 return n; |
| |
576 } |
| |
577 |
| |
578 static int cx_ll_insert_element( |
| |
579 struct cx_list_s *list, |
| |
580 size_t index, |
| |
581 void const *element |
| |
582 ) { |
| |
583 return 1 != cx_ll_insert_array(list, index, element, 1); |
| |
584 } |
| |
585 |
| |
586 static int cx_ll_remove( |
| |
587 struct cx_list_s *list, |
| |
588 size_t index |
| |
589 ) { |
| |
590 cx_linked_list *ll = (cx_linked_list *) list; |
| |
591 cx_linked_list_node *node = cx_ll_node_at(ll, index); |
| |
592 |
| |
593 // out-of-bounds check |
| |
594 if (node == NULL) return 1; |
| |
595 |
| |
596 // element destruction |
| |
597 cx_invoke_destructor(list, node->payload); |
| |
598 |
| |
599 // remove |
| |
600 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
| |
601 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
| |
602 |
| |
603 // adjust size |
| |
604 list->collection.size--; |
| |
605 |
| |
606 // free and return |
| |
607 cxFree(list->collection.allocator, node); |
| |
608 |
| |
609 return 0; |
| |
610 } |
| |
611 |
| |
612 static void cx_ll_clear(struct cx_list_s *list) { |
| |
613 if (list->collection.size == 0) return; |
| |
614 |
| |
615 cx_linked_list *ll = (cx_linked_list *) list; |
| |
616 cx_linked_list_node *node = ll->begin; |
| |
617 while (node != NULL) { |
| |
618 cx_invoke_destructor(list, node->payload); |
| |
619 cx_linked_list_node *next = node->next; |
| |
620 cxFree(list->collection.allocator, node); |
| |
621 node = next; |
| |
622 } |
| |
623 ll->begin = ll->end = NULL; |
| |
624 list->collection.size = 0; |
| |
625 } |
| |
626 |
| |
627 #ifndef CX_LINKED_LIST_SWAP_SBO_SIZE |
| |
628 #define CX_LINKED_LIST_SWAP_SBO_SIZE 128 |
| |
629 #endif |
| |
630 unsigned cx_linked_list_swap_sbo_size = CX_LINKED_LIST_SWAP_SBO_SIZE; |
| |
631 |
| |
632 static int cx_ll_swap( |
| |
633 struct cx_list_s *list, |
| |
634 size_t i, |
| |
635 size_t j |
| |
636 ) { |
| |
637 if (i >= list->collection.size || j >= list->collection.size) return 1; |
| |
638 if (i == j) return 0; |
| |
639 |
| |
640 // perform an optimized search that finds both elements in one run |
| |
641 cx_linked_list *ll = (cx_linked_list *) list; |
| |
642 size_t mid = list->collection.size / 2; |
| |
643 size_t left, right; |
| |
644 if (i < j) { |
| |
645 left = i; |
| |
646 right = j; |
| |
647 } else { |
| |
648 left = j; |
| |
649 right = i; |
| |
650 } |
| |
651 cx_linked_list_node *nleft, *nright; |
| |
652 if (left < mid && right < mid) { |
| |
653 // case 1: both items left from mid |
| |
654 nleft = cx_ll_node_at(ll, left); |
| |
655 assert(nleft != NULL); |
| |
656 nright = nleft; |
| |
657 for (size_t c = left; c < right; c++) { |
| |
658 nright = nright->next; |
| |
659 } |
| |
660 } else if (left >= mid && right >= mid) { |
| |
661 // case 2: both items right from mid |
| |
662 nright = cx_ll_node_at(ll, right); |
| |
663 assert(nright != NULL); |
| |
664 nleft = nright; |
| |
665 for (size_t c = right; c > left; c--) { |
| |
666 nleft = nleft->prev; |
| |
667 } |
| |
668 } else { |
| |
669 // case 3: one item left, one item right |
| |
670 |
| |
671 // chose the closest to begin / end |
| |
672 size_t closest; |
| |
673 size_t other; |
| |
674 size_t diff2boundary = list->collection.size - right - 1; |
| |
675 if (left <= diff2boundary) { |
| |
676 closest = left; |
| |
677 other = right; |
| |
678 nleft = cx_ll_node_at(ll, left); |
| |
679 } else { |
| |
680 closest = right; |
| |
681 other = left; |
| |
682 diff2boundary = left; |
| |
683 nright = cx_ll_node_at(ll, right); |
| |
684 } |
| |
685 |
| |
686 // is other element closer to us or closer to boundary? |
| |
687 if (right - left <= diff2boundary) { |
| |
688 // search other element starting from already found element |
| |
689 if (closest == left) { |
| |
690 nright = nleft; |
| |
691 for (size_t c = left; c < right; c++) { |
| |
692 nright = nright->next; |
| |
693 } |
| |
694 } else { |
| |
695 nleft = nright; |
| |
696 for (size_t c = right; c > left; c--) { |
| |
697 nleft = nleft->prev; |
| |
698 } |
| |
699 } |
| |
700 } else { |
| |
701 // search other element starting at the boundary |
| |
702 if (closest == left) { |
| |
703 nright = cx_ll_node_at(ll, other); |
| |
704 } else { |
| |
705 nleft = cx_ll_node_at(ll, other); |
| |
706 } |
| |
707 } |
| |
708 } |
| |
709 |
| |
710 if (list->collection.elem_size > CX_LINKED_LIST_SWAP_SBO_SIZE) { |
| |
711 cx_linked_list_node *prev = nleft->prev; |
| |
712 cx_linked_list_node *next = nright->next; |
| |
713 cx_linked_list_node *midstart = nleft->next; |
| |
714 cx_linked_list_node *midend = nright->prev; |
| |
715 |
| |
716 if (prev == NULL) { |
| |
717 ll->begin = nright; |
| |
718 } else { |
| |
719 prev->next = nright; |
| |
720 } |
| |
721 nright->prev = prev; |
| |
722 if (midstart == nright) { |
| |
723 // special case: both nodes are adjacent |
| |
724 nright->next = nleft; |
| |
725 nleft->prev = nright; |
| |
726 } else { |
| |
727 // likely case: a chain is between the two nodes |
| |
728 nright->next = midstart; |
| |
729 midstart->prev = nright; |
| |
730 midend->next = nleft; |
| |
731 nleft->prev = midend; |
| |
732 } |
| |
733 nleft->next = next; |
| |
734 if (next == NULL) { |
| |
735 ll->end = nleft; |
| |
736 } else { |
| |
737 next->prev = nleft; |
| |
738 } |
| |
739 } else { |
| |
740 // swap payloads to avoid relinking |
| |
741 char buf[CX_LINKED_LIST_SWAP_SBO_SIZE]; |
| |
742 memcpy(buf, nleft->payload, list->collection.elem_size); |
| |
743 memcpy(nleft->payload, nright->payload, list->collection.elem_size); |
| |
744 memcpy(nright->payload, buf, list->collection.elem_size); |
| |
745 } |
| |
746 |
| |
747 return 0; |
| |
748 } |
| |
749 |
| |
750 static void *cx_ll_at( |
| |
751 struct cx_list_s const *list, |
| |
752 size_t index |
| |
753 ) { |
| |
754 cx_linked_list *ll = (cx_linked_list *) list; |
| |
755 cx_linked_list_node *node = cx_ll_node_at(ll, index); |
| |
756 return node == NULL ? NULL : node->payload; |
| |
757 } |
| |
758 |
| |
759 static ssize_t cx_ll_find_remove( |
| |
760 struct cx_list_s *list, |
| |
761 void const *elem, |
| |
762 bool remove |
| |
763 ) { |
| |
764 if (remove) { |
| |
765 cx_linked_list *ll = ((cx_linked_list *) list); |
| |
766 cx_linked_list_node *node; |
| |
767 ssize_t index = cx_linked_list_find_node( |
| |
768 (void **) &node, |
| |
769 ll->begin, |
| |
770 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
| |
771 list->collection.cmpfunc, elem |
| |
772 ); |
| |
773 if (node != NULL) { |
| |
774 cx_invoke_destructor(list, node->payload); |
| |
775 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
| |
776 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
| |
777 list->collection.size--; |
| |
778 cxFree(list->collection.allocator, node); |
| |
779 } |
| |
780 return index; |
| |
781 } else { |
| |
782 return cx_linked_list_find( |
| |
783 ((cx_linked_list *) list)->begin, |
| |
784 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
| |
785 list->collection.cmpfunc, elem |
| |
786 ); |
| |
787 } |
| |
788 } |
| |
789 |
| |
790 static void cx_ll_sort(struct cx_list_s *list) { |
| |
791 cx_linked_list *ll = (cx_linked_list *) list; |
| |
792 cx_linked_list_sort((void **) &ll->begin, (void **) &ll->end, |
| |
793 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
| |
794 list->collection.cmpfunc); |
| |
795 } |
| |
796 |
| |
797 static void cx_ll_reverse(struct cx_list_s *list) { |
| |
798 cx_linked_list *ll = (cx_linked_list *) list; |
| |
799 cx_linked_list_reverse((void **) &ll->begin, (void **) &ll->end, CX_LL_LOC_PREV, CX_LL_LOC_NEXT); |
| |
800 } |
| |
801 |
| |
802 static int cx_ll_compare( |
| |
803 struct cx_list_s const *list, |
| |
804 struct cx_list_s const *other |
| |
805 ) { |
| |
806 cx_linked_list *left = (cx_linked_list *) list; |
| |
807 cx_linked_list *right = (cx_linked_list *) other; |
| |
808 return cx_linked_list_compare(left->begin, right->begin, |
| |
809 CX_LL_LOC_NEXT, CX_LL_LOC_DATA, |
| |
810 list->collection.cmpfunc); |
| |
811 } |
| |
812 |
| |
813 static bool cx_ll_iter_valid(void const *it) { |
| |
814 struct cx_iterator_s const *iter = it; |
| |
815 return iter->elem_handle != NULL; |
| |
816 } |
| |
817 |
| |
818 static void cx_ll_iter_next(void *it) { |
| |
819 struct cx_iterator_s *iter = it; |
| |
820 if (iter->base.remove) { |
| |
821 iter->base.remove = false; |
| |
822 struct cx_list_s *list = iter->src_handle.m; |
| |
823 cx_linked_list *ll = iter->src_handle.m; |
| |
824 cx_linked_list_node *node = iter->elem_handle; |
| |
825 iter->elem_handle = node->next; |
| |
826 cx_invoke_destructor(list, node->payload); |
| |
827 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
| |
828 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
| |
829 list->collection.size--; |
| |
830 cxFree(list->collection.allocator, node); |
| |
831 } else { |
| |
832 iter->index++; |
| |
833 cx_linked_list_node *node = iter->elem_handle; |
| |
834 iter->elem_handle = node->next; |
| |
835 } |
| |
836 } |
| |
837 |
| |
838 static void cx_ll_iter_prev(void *it) { |
| |
839 struct cx_iterator_s *iter = it; |
| |
840 if (iter->base.remove) { |
| |
841 iter->base.remove = false; |
| |
842 struct cx_list_s *list = iter->src_handle.m; |
| |
843 cx_linked_list *ll = iter->src_handle.m; |
| |
844 cx_linked_list_node *node = iter->elem_handle; |
| |
845 iter->elem_handle = node->prev; |
| |
846 iter->index--; |
| |
847 cx_invoke_destructor(list, node->payload); |
| |
848 cx_linked_list_remove((void **) &ll->begin, (void **) &ll->end, |
| |
849 CX_LL_LOC_PREV, CX_LL_LOC_NEXT, node); |
| |
850 list->collection.size--; |
| |
851 cxFree(list->collection.allocator, node); |
| |
852 } else { |
| |
853 iter->index--; |
| |
854 cx_linked_list_node *node = iter->elem_handle; |
| |
855 iter->elem_handle = node->prev; |
| |
856 } |
| |
857 } |
| |
858 |
| |
859 static void *cx_ll_iter_current(void const *it) { |
| |
860 struct cx_iterator_s const *iter = it; |
| |
861 cx_linked_list_node *node = iter->elem_handle; |
| |
862 return node->payload; |
| |
863 } |
| |
864 |
| |
865 static CxIterator cx_ll_iterator( |
| |
866 struct cx_list_s const *list, |
| |
867 size_t index, |
| |
868 bool backwards |
| |
869 ) { |
| |
870 CxIterator iter; |
| |
871 iter.index = index; |
| |
872 iter.src_handle.c = list; |
| |
873 iter.elem_handle = cx_ll_node_at((cx_linked_list const *) list, index); |
| |
874 iter.elem_size = list->collection.elem_size; |
| |
875 iter.elem_count = list->collection.size; |
| |
876 iter.base.valid = cx_ll_iter_valid; |
| |
877 iter.base.current = cx_ll_iter_current; |
| |
878 iter.base.next = backwards ? cx_ll_iter_prev : cx_ll_iter_next; |
| |
879 iter.base.mutating = false; |
| |
880 iter.base.remove = false; |
| |
881 return iter; |
| |
882 } |
| |
883 |
| |
884 static int cx_ll_insert_iter( |
| |
885 CxIterator *iter, |
| |
886 void const *elem, |
| |
887 int prepend |
| |
888 ) { |
| |
889 struct cx_list_s *list = iter->src_handle.m; |
| |
890 cx_linked_list_node *node = iter->elem_handle; |
| |
891 if (node != NULL) { |
| |
892 assert(prepend >= 0 && prepend <= 1); |
| |
893 cx_linked_list_node *choice[2] = {node, node->prev}; |
| |
894 int result = cx_ll_insert_at(list, choice[prepend], elem); |
| |
895 iter->index += prepend * (0 == result); |
| |
896 return result; |
| |
897 } else { |
| |
898 int result = cx_ll_insert_element(list, list->collection.size, elem); |
| |
899 iter->index = list->collection.size; |
| |
900 return result; |
| |
901 } |
| |
902 } |
| |
903 |
| |
904 static void cx_ll_destructor(CxList *list) { |
| |
905 cx_linked_list *ll = (cx_linked_list *) list; |
| |
906 |
| |
907 cx_linked_list_node *node = ll->begin; |
| |
908 while (node) { |
| |
909 cx_invoke_destructor(list, node->payload); |
| |
910 void *next = node->next; |
| |
911 cxFree(list->collection.allocator, node); |
| |
912 node = next; |
| |
913 } |
| |
914 |
| |
915 cxFree(list->collection.allocator, list); |
| |
916 } |
| |
917 |
| |
918 static cx_list_class cx_linked_list_class = { |
| |
919 cx_ll_destructor, |
| |
920 cx_ll_insert_element, |
| |
921 cx_ll_insert_array, |
| |
922 cx_ll_insert_iter, |
| |
923 cx_ll_remove, |
| |
924 cx_ll_clear, |
| |
925 cx_ll_swap, |
| |
926 cx_ll_at, |
| |
927 cx_ll_find_remove, |
| |
928 cx_ll_sort, |
| |
929 cx_ll_compare, |
| |
930 cx_ll_reverse, |
| |
931 cx_ll_iterator, |
| |
932 }; |
| |
933 |
| |
934 CxList *cxLinkedListCreate( |
| |
935 CxAllocator const *allocator, |
| |
936 cx_compare_func comparator, |
| |
937 size_t elem_size |
| |
938 ) { |
| |
939 if (allocator == NULL) { |
| |
940 allocator = cxDefaultAllocator; |
| |
941 } |
| |
942 |
| |
943 cx_linked_list *list = cxCalloc(allocator, 1, sizeof(cx_linked_list)); |
| |
944 if (list == NULL) return NULL; |
| |
945 |
| |
946 list->base.cl = &cx_linked_list_class; |
| |
947 list->base.collection.allocator = allocator; |
| |
948 |
| |
949 if (elem_size > 0) { |
| |
950 list->base.collection.elem_size = elem_size; |
| |
951 list->base.collection.cmpfunc = comparator; |
| |
952 } else { |
| |
953 list->base.collection.cmpfunc = comparator == NULL ? cx_cmp_ptr : comparator; |
| |
954 cxListStorePointers((CxList *) list); |
| |
955 } |
| |
956 |
| |
957 return (CxList *) list; |
| |
958 } |