28 |
28 |
29 #include "cx/linked_list.h" |
29 #include "cx/linked_list.h" |
30 #include "cx/compare.h" |
30 #include "cx/compare.h" |
31 #include <string.h> |
31 #include <string.h> |
32 #include <assert.h> |
32 #include <assert.h> |
|
33 #include <unistd.h> |
33 |
34 |
34 // LOW LEVEL LINKED LIST FUNCTIONS |
35 // LOW LEVEL LINKED LIST FUNCTIONS |
35 |
36 |
36 #define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) |
37 #define CX_LL_PTR(cur, off) (*(void**)(((char*)(cur))+(off))) |
37 #define ll_prev(node) CX_LL_PTR(node, loc_prev) |
38 #define ll_prev(node) CX_LL_PTR(node, loc_prev) |
242 assert(ll_next(new_node) == NULL); |
243 assert(ll_next(new_node) == NULL); |
243 cx_linked_list_insert_sorted_chain( |
244 cx_linked_list_insert_sorted_chain( |
244 begin, end, loc_prev, loc_next, new_node, cmp_func); |
245 begin, end, loc_prev, loc_next, new_node, cmp_func); |
245 } |
246 } |
246 |
247 |
247 void cx_linked_list_insert_sorted_chain( |
248 static void *cx_linked_list_insert_sorted_chain_impl( |
248 void **begin, |
249 void **begin, |
249 void **end, |
250 void **end, |
250 ptrdiff_t loc_prev, |
251 ptrdiff_t loc_prev, |
251 ptrdiff_t loc_next, |
252 ptrdiff_t loc_next, |
252 void *insert_begin, |
253 void *insert_begin, |
253 cx_compare_func cmp_func |
254 cx_compare_func cmp_func, |
|
255 bool allow_duplicates |
254 ) { |
256 ) { |
255 assert(begin != NULL); |
257 assert(begin != NULL); |
256 assert(loc_next >= 0); |
258 assert(loc_next >= 0); |
257 assert(insert_begin != NULL); |
259 assert(insert_begin != NULL); |
258 |
260 |
259 // track currently observed nodes |
261 // track currently observed nodes |
|
262 void *dup_start = NULL; |
|
263 void *dup_last = NULL; |
260 void *dest_prev = NULL; |
264 void *dest_prev = NULL; |
261 void *dest = *begin; |
265 void *dest = *begin; |
262 void *src = insert_begin; |
266 void *src = insert_begin; |
263 |
267 |
264 // special case: list is empty |
268 // special case: list is empty |
265 if (dest == NULL) { |
269 if (dest == NULL) { |
266 *begin = src; |
270 *begin = src; |
267 if (end != NULL) { |
271 if (end != NULL) { |
268 *end = cx_linked_list_last(src, loc_next); |
272 *end = cx_linked_list_last(src, loc_next); |
269 } |
273 } |
270 return; |
274 return NULL; |
271 } |
275 } |
272 |
276 |
273 // search the list for insertion points |
277 // search the list for insertion points |
274 while (dest != NULL && src != NULL) { |
278 while (dest != NULL && src != NULL) { |
275 // compare current list node with source node |
279 // compare current list node with source node |
276 // if less or equal, skip |
280 // if less or equal, skip |
277 if (cmp_func(dest, src) <= 0) { |
281 { |
278 dest_prev = dest; |
282 int d = cmp_func(dest, src); |
279 dest = ll_next(dest); |
283 if (d <= 0) { |
280 continue; |
284 if (!allow_duplicates && d == 0) { |
|
285 if (dup_last == NULL) { |
|
286 dup_start = src; |
|
287 dup_last = src; |
|
288 if (loc_prev >= 0) { |
|
289 ll_prev(dup_start) = NULL; |
|
290 } |
|
291 } else { |
|
292 cx_linked_list_link(dup_last, src, loc_prev, loc_next); |
|
293 dup_last = src; |
|
294 } |
|
295 src = ll_next(src); |
|
296 while (src != NULL && cmp_func(dest, src) == 0) { |
|
297 dup_last = src; |
|
298 src = ll_next(src); |
|
299 } |
|
300 } |
|
301 |
|
302 dest_prev = dest; |
|
303 dest = ll_next(dest); |
|
304 continue; |
|
305 } |
281 } |
306 } |
282 |
307 |
283 // determine chain of elements that can be inserted |
308 // determine chain of elements that can be inserted |
284 void *end_of_chain = src; |
309 void *end_of_chain = src; |
285 void *next_in_chain = ll_next(src); |
310 void *next_in_chain = ll_next(src); |
306 src = next_in_chain; |
331 src = next_in_chain; |
307 dest_prev = dest; |
332 dest_prev = dest; |
308 dest = ll_next(dest); |
333 dest = ll_next(dest); |
309 } |
334 } |
310 |
335 |
|
336 // skip duplicates in the remaining items |
|
337 if (!allow_duplicates && src != NULL) { |
|
338 if (cmp_func(dest_prev, src) == 0) { |
|
339 if (dup_last == NULL) { |
|
340 dup_start = src; |
|
341 dup_last = src; |
|
342 if (loc_prev >= 0) { |
|
343 ll_prev(dup_start) = NULL; |
|
344 } |
|
345 } else { |
|
346 cx_linked_list_link(dup_last, src, loc_prev, loc_next); |
|
347 dup_last = src; |
|
348 } |
|
349 src = ll_next(src); |
|
350 while (src != NULL && cmp_func(dest_prev, src) == 0) { |
|
351 dup_last = src; |
|
352 src = ll_next(src); |
|
353 } |
|
354 } |
|
355 } |
|
356 |
311 // insert remaining items |
357 // insert remaining items |
312 if (src != NULL) { |
358 if (src != NULL) { |
313 cx_linked_list_link(dest_prev, src, loc_prev, loc_next); |
359 cx_linked_list_link(dest_prev, src, loc_prev, loc_next); |
314 } |
360 } |
315 |
361 |
316 // determine new end of list, if requested |
362 // determine new end of list, if requested |
317 if (end != NULL) { |
363 if (end != NULL) { |
318 *end = cx_linked_list_last( |
364 *end = cx_linked_list_last( |
319 dest != NULL ? dest : dest_prev, loc_next); |
365 dest != NULL ? dest : dest_prev, loc_next); |
320 } |
366 } |
|
367 |
|
368 if (dup_last != NULL) { |
|
369 ll_next(dup_last) = NULL; |
|
370 } |
|
371 return dup_start; |
|
372 } |
|
373 |
|
374 void cx_linked_list_insert_sorted_chain( |
|
375 void **begin, |
|
376 void **end, |
|
377 ptrdiff_t loc_prev, |
|
378 ptrdiff_t loc_next, |
|
379 void *insert_begin, |
|
380 cx_compare_func cmp_func |
|
381 ) { |
|
382 void *ret = cx_linked_list_insert_sorted_chain_impl( |
|
383 begin, end, loc_prev, loc_next, |
|
384 insert_begin, cmp_func, true); |
|
385 assert(ret == NULL); |
|
386 } |
|
387 |
|
388 int cx_linked_list_insert_unique( |
|
389 void **begin, |
|
390 void **end, |
|
391 ptrdiff_t loc_prev, |
|
392 ptrdiff_t loc_next, |
|
393 void *new_node, |
|
394 cx_compare_func cmp_func |
|
395 ) { |
|
396 assert(ll_next(new_node) == NULL); |
|
397 return NULL != cx_linked_list_insert_unique_chain( |
|
398 begin, end, loc_prev, loc_next, new_node, cmp_func); |
|
399 } |
|
400 |
|
401 void *cx_linked_list_insert_unique_chain( |
|
402 void **begin, |
|
403 void **end, |
|
404 ptrdiff_t loc_prev, |
|
405 ptrdiff_t loc_next, |
|
406 void *insert_begin, |
|
407 cx_compare_func cmp_func |
|
408 ) { |
|
409 return cx_linked_list_insert_sorted_chain_impl( |
|
410 begin, end, loc_prev, loc_next, |
|
411 insert_begin, cmp_func, false); |
321 } |
412 } |
322 |
413 |
323 size_t cx_linked_list_remove_chain( |
414 size_t cx_linked_list_remove_chain( |
324 void **begin, |
415 void **begin, |
325 void **end, |
416 void **end, |
675 const char *left = (const char*)l + cx_ll_insert_sorted_loc_data; |
766 const char *left = (const char*)l + cx_ll_insert_sorted_loc_data; |
676 const char *right = (const char*)r + cx_ll_insert_sorted_loc_data; |
767 const char *right = (const char*)r + cx_ll_insert_sorted_loc_data; |
677 return cx_ll_insert_sorted_cmp_func(left, right); |
768 return cx_ll_insert_sorted_cmp_func(left, right); |
678 } |
769 } |
679 |
770 |
680 static size_t cx_ll_insert_sorted( |
771 static size_t cx_ll_insert_sorted_impl( |
681 struct cx_list_s *list, |
772 struct cx_list_s *list, |
682 const void *array, |
773 const void *array, |
683 size_t n |
774 size_t n, |
|
775 bool allow_duplicates |
684 ) { |
776 ) { |
685 cx_linked_list *ll = (cx_linked_list *) list; |
777 cx_linked_list *ll = (cx_linked_list *) list; |
686 |
778 |
687 // special case |
779 // special case |
688 if (n == 0) return 0; |
780 if (n == 0) return 0; |
709 CX_LL_PTR(prev, ll->loc_next) = NULL; |
801 CX_LL_PTR(prev, ll->loc_next) = NULL; |
710 |
802 |
711 // invoke the low level function |
803 // invoke the low level function |
712 cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; |
804 cx_ll_insert_sorted_cmp_func = list->collection.cmpfunc; |
713 cx_ll_insert_sorted_loc_data = ll->loc_data; |
805 cx_ll_insert_sorted_loc_data = ll->loc_data; |
714 cx_linked_list_insert_sorted_chain( |
806 if (allow_duplicates) { |
715 &ll->begin, |
807 cx_linked_list_insert_sorted_chain( |
716 &ll->end, |
808 &ll->begin, |
717 ll->loc_prev, |
809 &ll->end, |
718 ll->loc_next, |
810 ll->loc_prev, |
719 chain, |
811 ll->loc_next, |
720 cx_ll_insert_sorted_cmp_helper |
812 chain, |
721 ); |
813 cx_ll_insert_sorted_cmp_helper |
722 |
814 ); |
723 // adjust the list metadata |
815 list->collection.size += inserted; |
724 list->collection.size += inserted; |
816 } else { |
|
817 void *duplicates = cx_linked_list_insert_unique_chain( |
|
818 &ll->begin, |
|
819 &ll->end, |
|
820 ll->loc_prev, |
|
821 ll->loc_next, |
|
822 chain, |
|
823 cx_ll_insert_sorted_cmp_helper |
|
824 ); |
|
825 list->collection.size += inserted; |
|
826 // free the nodes that did not make it into the list |
|
827 while (duplicates != NULL) { |
|
828 void *next = CX_LL_PTR(duplicates, ll->loc_next); |
|
829 cxFree(list->collection.allocator, duplicates); |
|
830 duplicates = next; |
|
831 list->collection.size--; |
|
832 } |
|
833 } |
725 |
834 |
726 return inserted; |
835 return inserted; |
|
836 } |
|
837 |
|
838 static size_t cx_ll_insert_sorted( |
|
839 struct cx_list_s *list, |
|
840 const void *array, |
|
841 size_t n |
|
842 ) { |
|
843 return cx_ll_insert_sorted_impl(list, array, n, true); |
|
844 } |
|
845 |
|
846 static size_t cx_ll_insert_unique( |
|
847 struct cx_list_s *list, |
|
848 const void *array, |
|
849 size_t n |
|
850 ) { |
|
851 return cx_ll_insert_sorted_impl(list, array, n, false); |
727 } |
852 } |
728 |
853 |
729 static size_t cx_ll_remove( |
854 static size_t cx_ll_remove( |
730 struct cx_list_s *list, |
855 struct cx_list_s *list, |
731 size_t index, |
856 size_t index, |