Line | Branch | Exec | Source |
---|---|---|---|
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/list.h" | ||
30 | |||
31 | #include <string.h> | ||
32 | |||
33 | // <editor-fold desc="Store Pointers Functionality"> | ||
34 | |||
35 | static _Thread_local cx_compare_func cx_pl_cmpfunc_impl; | ||
36 | |||
37 | 31453 | static int cx_pl_cmpfunc( | |
38 | const void *l, | ||
39 | const void *r | ||
40 | ) { | ||
41 | 31453 | void *const *lptr = l; | |
42 | 31453 | void *const *rptr = r; | |
43 |
1/2✓ Branch 0 taken 31453 times.
✗ Branch 1 not taken.
|
31453 | const void *left = lptr == NULL ? NULL : *lptr; |
44 |
1/2✓ Branch 0 taken 31453 times.
✗ Branch 1 not taken.
|
31453 | const void *right = rptr == NULL ? NULL : *rptr; |
45 | 31453 | return cx_pl_cmpfunc_impl(left, right); | |
46 | } | ||
47 | |||
48 | 120 | static void cx_pl_hack_cmpfunc(const struct cx_list_s *list) { | |
49 | // cast away const - this is the hacky thing | ||
50 | 120 | struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; | |
51 | 120 | cx_pl_cmpfunc_impl = l->cmpfunc; | |
52 | 120 | l->cmpfunc = cx_pl_cmpfunc; | |
53 | 120 | } | |
54 | |||
55 | 120 | static void cx_pl_unhack_cmpfunc(const struct cx_list_s *list) { | |
56 | // cast away const - this is the hacky thing | ||
57 | 120 | struct cx_collection_s *l = (struct cx_collection_s*) &list->collection; | |
58 | 120 | l->cmpfunc = cx_pl_cmpfunc_impl; | |
59 | 120 | } | |
60 | |||
61 | 72 | static void cx_pl_destructor(struct cx_list_s *list) { | |
62 | 72 | list->climpl->deallocate(list); | |
63 | 72 | } | |
64 | |||
65 | 5384 | static int cx_pl_insert_element( | |
66 | struct cx_list_s *list, | ||
67 | size_t index, | ||
68 | const void *element | ||
69 | ) { | ||
70 | 5384 | return list->climpl->insert_element(list, index, &element); | |
71 | } | ||
72 | |||
73 | 8 | static size_t cx_pl_insert_array( | |
74 | struct cx_list_s *list, | ||
75 | size_t index, | ||
76 | const void *array, | ||
77 | size_t n | ||
78 | ) { | ||
79 | 8 | return list->climpl->insert_array(list, index, array, n); | |
80 | } | ||
81 | |||
82 | 32 | static size_t cx_pl_insert_sorted( | |
83 | struct cx_list_s *list, | ||
84 | const void *array, | ||
85 | size_t n | ||
86 | ) { | ||
87 | 32 | cx_pl_hack_cmpfunc(list); | |
88 | 32 | size_t result = list->climpl->insert_sorted(list, array, n); | |
89 | 32 | cx_pl_unhack_cmpfunc(list); | |
90 | 32 | return result; | |
91 | } | ||
92 | |||
93 | 10 | static int cx_pl_insert_iter( | |
94 | struct cx_iterator_s *iter, | ||
95 | const void *elem, | ||
96 | int prepend | ||
97 | ) { | ||
98 | 10 | struct cx_list_s *list = iter->src_handle.m; | |
99 | 10 | return list->climpl->insert_iter(iter, &elem, prepend); | |
100 | } | ||
101 | |||
102 | 22 | static size_t cx_pl_remove( | |
103 | struct cx_list_s *list, | ||
104 | size_t index, | ||
105 | size_t num, | ||
106 | void *targetbuf | ||
107 | ) { | ||
108 | 22 | return list->climpl->remove(list, index, num, targetbuf); | |
109 | } | ||
110 | |||
111 | 6 | static void cx_pl_clear(struct cx_list_s *list) { | |
112 | 6 | list->climpl->clear(list); | |
113 | 6 | } | |
114 | |||
115 | 44 | static int cx_pl_swap( | |
116 | struct cx_list_s *list, | ||
117 | size_t i, | ||
118 | size_t j | ||
119 | ) { | ||
120 | 44 | return list->climpl->swap(list, i, j); | |
121 | } | ||
122 | |||
123 | 2698 | static void *cx_pl_at( | |
124 | const struct cx_list_s *list, | ||
125 | size_t index | ||
126 | ) { | ||
127 | 2698 | void **ptr = list->climpl->at(list, index); | |
128 |
2/2✓ Branch 0 taken 2696 times.
✓ Branch 1 taken 2 times.
|
2698 | return ptr == NULL ? NULL : *ptr; |
129 | } | ||
130 | |||
131 | 68 | static size_t cx_pl_find_remove( | |
132 | struct cx_list_s *list, | ||
133 | const void *elem, | ||
134 | bool remove | ||
135 | ) { | ||
136 | 68 | cx_pl_hack_cmpfunc(list); | |
137 | 68 | size_t ret = list->climpl->find_remove(list, &elem, remove); | |
138 | 68 | cx_pl_unhack_cmpfunc(list); | |
139 | 68 | return ret; | |
140 | } | ||
141 | |||
142 | 6 | static void cx_pl_sort(struct cx_list_s *list) { | |
143 | 6 | cx_pl_hack_cmpfunc(list); | |
144 | 6 | list->climpl->sort(list); | |
145 | 6 | cx_pl_unhack_cmpfunc(list); | |
146 | 6 | } | |
147 | |||
148 | 14 | static int cx_pl_compare( | |
149 | const struct cx_list_s *list, | ||
150 | const struct cx_list_s *other | ||
151 | ) { | ||
152 | 14 | cx_pl_hack_cmpfunc(list); | |
153 | 14 | int ret = list->climpl->compare(list, other); | |
154 | 14 | cx_pl_unhack_cmpfunc(list); | |
155 | 14 | return ret; | |
156 | } | ||
157 | |||
158 | 2 | static void cx_pl_reverse(struct cx_list_s *list) { | |
159 | 2 | list->climpl->reverse(list); | |
160 | 2 | } | |
161 | |||
162 | 3084 | static void *cx_pl_iter_current(const void *it) { | |
163 | 3084 | const struct cx_iterator_s *iter = it; | |
164 | 3084 | void **ptr = iter->base.current_impl(it); | |
165 |
1/2✓ Branch 0 taken 3084 times.
✗ Branch 1 not taken.
|
3084 | return ptr == NULL ? NULL : *ptr; |
166 | } | ||
167 | |||
168 | 112 | static struct cx_iterator_s cx_pl_iterator( | |
169 | const struct cx_list_s *list, | ||
170 | size_t index, | ||
171 | bool backwards | ||
172 | ) { | ||
173 | 112 | struct cx_iterator_s iter = list->climpl->iterator(list, index, backwards); | |
174 | 112 | iter.base.current_impl = iter.base.current; | |
175 | 112 | iter.base.current = cx_pl_iter_current; | |
176 | 112 | return iter; | |
177 | } | ||
178 | |||
179 | static cx_list_class cx_pointer_list_class = { | ||
180 | cx_pl_destructor, | ||
181 | cx_pl_insert_element, | ||
182 | cx_pl_insert_array, | ||
183 | cx_pl_insert_sorted, | ||
184 | cx_pl_insert_iter, | ||
185 | cx_pl_remove, | ||
186 | cx_pl_clear, | ||
187 | cx_pl_swap, | ||
188 | cx_pl_at, | ||
189 | cx_pl_find_remove, | ||
190 | cx_pl_sort, | ||
191 | cx_pl_compare, | ||
192 | cx_pl_reverse, | ||
193 | cx_pl_iterator, | ||
194 | }; | ||
195 | // </editor-fold> | ||
196 | |||
197 | // <editor-fold desc="empty list implementation"> | ||
198 | |||
199 | 2 | static void cx_emptyl_noop(cx_attr_unused CxList *list) { | |
200 | // this is a noop, but MUST be implemented | ||
201 | 2 | } | |
202 | |||
203 | 2 | static void *cx_emptyl_at( | |
204 | cx_attr_unused const struct cx_list_s *list, | ||
205 | cx_attr_unused size_t index | ||
206 | ) { | ||
207 | 2 | return NULL; | |
208 | } | ||
209 | |||
210 | 2 | static size_t cx_emptyl_find_remove( | |
211 | cx_attr_unused struct cx_list_s *list, | ||
212 | cx_attr_unused const void *elem, | ||
213 | cx_attr_unused bool remove | ||
214 | ) { | ||
215 | 2 | return 0; | |
216 | } | ||
217 | |||
218 | 8 | static bool cx_emptyl_iter_valid(cx_attr_unused const void *iter) { | |
219 | 8 | return false; | |
220 | } | ||
221 | |||
222 | 10 | static CxIterator cx_emptyl_iterator( | |
223 | const struct cx_list_s *list, | ||
224 | size_t index, | ||
225 | cx_attr_unused bool backwards | ||
226 | ) { | ||
227 | 10 | CxIterator iter = {0}; | |
228 | 10 | iter.src_handle.c = list; | |
229 | 10 | iter.index = index; | |
230 | 10 | iter.base.valid = cx_emptyl_iter_valid; | |
231 | 10 | return iter; | |
232 | } | ||
233 | |||
234 | static cx_list_class cx_empty_list_class = { | ||
235 | cx_emptyl_noop, | ||
236 | NULL, | ||
237 | NULL, | ||
238 | NULL, | ||
239 | NULL, | ||
240 | NULL, | ||
241 | cx_emptyl_noop, | ||
242 | NULL, | ||
243 | cx_emptyl_at, | ||
244 | cx_emptyl_find_remove, | ||
245 | cx_emptyl_noop, | ||
246 | NULL, | ||
247 | cx_emptyl_noop, | ||
248 | cx_emptyl_iterator, | ||
249 | }; | ||
250 | |||
251 | CxList cx_empty_list = { | ||
252 | { | ||
253 | NULL, | ||
254 | NULL, | ||
255 | 0, | ||
256 | 0, | ||
257 | NULL, | ||
258 | NULL, | ||
259 | NULL, | ||
260 | false, | ||
261 | true, | ||
262 | }, | ||
263 | &cx_empty_list_class, | ||
264 | NULL | ||
265 | }; | ||
266 | |||
267 | CxList *const cxEmptyList = &cx_empty_list; | ||
268 | |||
269 | // </editor-fold> | ||
270 | |||
271 | #define invoke_list_func(name, list, ...) \ | ||
272 | ((list)->climpl == NULL ? (list)->cl->name : (list)->climpl->name) \ | ||
273 | (list, __VA_ARGS__) | ||
274 | |||
275 | 40 | size_t cx_list_default_insert_array( | |
276 | struct cx_list_s *list, | ||
277 | size_t index, | ||
278 | const void *data, | ||
279 | size_t n | ||
280 | ) { | ||
281 | 40 | size_t elem_size = list->collection.elem_size; | |
282 | 40 | const char *src = data; | |
283 | 40 | size_t i = 0; | |
284 |
2/2✓ Branch 0 taken 654 times.
✓ Branch 1 taken 40 times.
|
694 | for (; i < n; i++) { |
285 |
3/4✓ Branch 0 taken 610 times.
✓ Branch 1 taken 44 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 654 times.
|
654 | if (0 != invoke_list_func( |
286 | insert_element, list, index + i, | ||
287 | ✗ | src + (i * elem_size))) return i; | |
288 | } | ||
289 | 40 | return i; | |
290 | } | ||
291 | |||
292 | 32 | size_t cx_list_default_insert_sorted( | |
293 | struct cx_list_s *list, | ||
294 | const void *sorted_data, | ||
295 | size_t n | ||
296 | ) { | ||
297 | // corner case | ||
298 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 32 times.
|
32 | if (n == 0) return 0; |
299 | |||
300 | 32 | size_t elem_size = list->collection.elem_size; | |
301 | 32 | cx_compare_func cmp = list->collection.cmpfunc; | |
302 | 32 | const char *src = sorted_data; | |
303 | |||
304 | // track indices and number of inserted items | ||
305 | 32 | size_t di = 0, si = 0, inserted = 0; | |
306 | |||
307 | // search the list for insertion points | ||
308 |
2/2✓ Branch 0 taken 164 times.
✓ Branch 1 taken 12 times.
|
176 | for (; di < list->collection.size; di++) { |
309 |
2/2✓ Branch 0 taken 82 times.
✓ Branch 1 taken 82 times.
|
164 | const void *list_elm = invoke_list_func(at, list, di); |
310 | |||
311 | // compare current list element with first source element | ||
312 | // if less or equal, skip | ||
313 |
2/2✓ Branch 1 taken 124 times.
✓ Branch 2 taken 40 times.
|
164 | if (cmp(list_elm, src) <= 0) { |
314 | 124 | continue; | |
315 | } | ||
316 | |||
317 | // determine number of consecutive elements that can be inserted | ||
318 | 40 | size_t ins = 1; | |
319 | 40 | const char *next = src; | |
320 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 20 times.
|
60 | while (++si < n) { |
321 | 40 | next += elem_size; | |
322 | // once we become larger than the list elem, break | ||
323 |
2/2✓ Branch 1 taken 20 times.
✓ Branch 2 taken 20 times.
|
40 | if (cmp(list_elm, next) <= 0) { |
324 | 20 | break; | |
325 | } | ||
326 | // otherwise, we can insert one more | ||
327 | 20 | ins++; | |
328 | } | ||
329 | |||
330 | // insert the elements at location si | ||
331 |
2/2✓ Branch 0 taken 24 times.
✓ Branch 1 taken 16 times.
|
40 | if (ins == 1) { |
332 |
3/4✓ Branch 0 taken 12 times.
✓ Branch 1 taken 12 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 24 times.
|
24 | if (0 != invoke_list_func( |
333 | ✗ | insert_element, list, di, src)) return inserted; | |
334 | } else { | ||
335 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 8 times.
|
16 | size_t r = invoke_list_func(insert_array, list, di, src, ins); |
336 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 16 times.
|
16 | if (r < ins) return inserted + r; |
337 | } | ||
338 | 40 | inserted += ins; | |
339 | 40 | di += ins; | |
340 | |||
341 | // everything inserted? | ||
342 |
2/2✓ Branch 0 taken 20 times.
✓ Branch 1 taken 20 times.
|
40 | if (inserted == n) return inserted; |
343 | 20 | src = next; | |
344 | } | ||
345 | |||
346 | // insert remaining items | ||
347 |
1/2✓ Branch 0 taken 12 times.
✗ Branch 1 not taken.
|
12 | if (si < n) { |
348 |
2/2✓ Branch 0 taken 6 times.
✓ Branch 1 taken 6 times.
|
12 | inserted += invoke_list_func(insert_array, list, di, src, n - si); |
349 | } | ||
350 | |||
351 | 12 | return inserted; | |
352 | } | ||
353 | |||
354 | 4 | void cx_list_default_sort(struct cx_list_s *list) { | |
355 | 4 | size_t elem_size = list->collection.elem_size; | |
356 | 4 | size_t list_size = list->collection.size; | |
357 | 4 | void *tmp = malloc(elem_size * list_size); | |
358 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 4 times.
|
4 | if (tmp == NULL) abort(); |
359 | |||
360 | // copy elements from source array | ||
361 | 4 | char *loc = tmp; | |
362 |
2/2✓ Branch 0 taken 1000 times.
✓ Branch 1 taken 4 times.
|
1004 | for (size_t i = 0; i < list_size; i++) { |
363 |
2/2✓ Branch 0 taken 500 times.
✓ Branch 1 taken 500 times.
|
1000 | void *src = invoke_list_func(at, list, i); |
364 | 1000 | memcpy(loc, src, elem_size); | |
365 | 1000 | loc += elem_size; | |
366 | } | ||
367 | |||
368 | // qsort | ||
369 | 4 | qsort(tmp, list_size, elem_size, | |
370 | 4 | list->collection.cmpfunc); | |
371 | |||
372 | // copy elements back | ||
373 | 4 | loc = tmp; | |
374 |
2/2✓ Branch 0 taken 1000 times.
✓ Branch 1 taken 4 times.
|
1004 | for (size_t i = 0; i < list_size; i++) { |
375 |
2/2✓ Branch 0 taken 500 times.
✓ Branch 1 taken 500 times.
|
1000 | void *dest = invoke_list_func(at, list, i); |
376 | 1000 | memcpy(dest, loc, elem_size); | |
377 | 1000 | loc += elem_size; | |
378 | } | ||
379 | |||
380 | 4 | free(tmp); | |
381 | 4 | } | |
382 | |||
383 | 44 | int cx_list_default_swap(struct cx_list_s *list, size_t i, size_t j) { | |
384 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 40 times.
|
44 | if (i == j) return 0; |
385 |
2/2✓ Branch 0 taken 8 times.
✓ Branch 1 taken 32 times.
|
40 | if (i >= list->collection.size) return 1; |
386 |
2/2✓ Branch 0 taken 4 times.
✓ Branch 1 taken 28 times.
|
32 | if (j >= list->collection.size) return 1; |
387 | |||
388 | 28 | size_t elem_size = list->collection.elem_size; | |
389 | |||
390 | 28 | void *tmp = malloc(elem_size); | |
391 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 28 times.
|
28 | if (tmp == NULL) return 1; |
392 | |||
393 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
|
28 | void *ip = invoke_list_func(at, list, i); |
394 |
2/2✓ Branch 0 taken 14 times.
✓ Branch 1 taken 14 times.
|
28 | void *jp = invoke_list_func(at, list, j); |
395 | |||
396 | 28 | memcpy(tmp, ip, elem_size); | |
397 | 28 | memcpy(ip, jp, elem_size); | |
398 | 28 | memcpy(jp, tmp, elem_size); | |
399 | |||
400 | 28 | free(tmp); | |
401 | |||
402 | 28 | return 0; | |
403 | } | ||
404 | |||
405 | 153 | void cx_list_init( | |
406 | struct cx_list_s *list, | ||
407 | struct cx_list_class_s *cl, | ||
408 | const struct cx_allocator_s *allocator, | ||
409 | cx_compare_func comparator, | ||
410 | size_t elem_size | ||
411 | ) { | ||
412 | 153 | list->cl = cl; | |
413 | 153 | list->collection.allocator = allocator; | |
414 | 153 | list->collection.cmpfunc = comparator; | |
415 |
2/2✓ Branch 0 taken 81 times.
✓ Branch 1 taken 72 times.
|
153 | if (elem_size > 0) { |
416 | 81 | list->collection.elem_size = elem_size; | |
417 | } else { | ||
418 | 72 | list->collection.elem_size = sizeof(void *); | |
419 |
2/2✓ Branch 0 taken 2 times.
✓ Branch 1 taken 70 times.
|
72 | if (list->collection.cmpfunc == NULL) { |
420 | 2 | list->collection.cmpfunc = cx_cmp_ptr; | |
421 | } | ||
422 | 72 | list->collection.store_pointer = true; | |
423 | 72 | list->climpl = list->cl; | |
424 | 72 | list->cl = &cx_pointer_list_class; | |
425 | } | ||
426 | 153 | } | |
427 | |||
428 | 177 | int cxListCompare( | |
429 | const CxList *list, | ||
430 | const CxList *other | ||
431 | ) { | ||
432 | 177 | bool cannot_optimize = false; | |
433 | |||
434 | // if one is storing pointers but the other is not | ||
435 | 177 | cannot_optimize |= list->collection.store_pointer ^ other->collection.store_pointer; | |
436 | |||
437 | // if one class is wrapped but the other is not | ||
438 | 177 | cannot_optimize |= (list->climpl == NULL) ^ (other->climpl == NULL); | |
439 | |||
440 | // if the compare functions do not match or both are NULL | ||
441 |
2/2✓ Branch 0 taken 93 times.
✓ Branch 1 taken 84 times.
|
177 | if (!cannot_optimize) { |
442 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 65 times.
|
93 | cx_compare_func list_cmp = (cx_compare_func) (list->climpl != NULL ? |
443 | 93 | list->climpl->compare : list->cl->compare); | |
444 |
2/2✓ Branch 0 taken 28 times.
✓ Branch 1 taken 65 times.
|
93 | cx_compare_func other_cmp = (cx_compare_func) (other->climpl != NULL ? |
445 | 93 | other->climpl->compare : other->cl->compare); | |
446 | 93 | cannot_optimize |= list_cmp != other_cmp; | |
447 | 93 | cannot_optimize |= list_cmp == NULL; | |
448 | } | ||
449 | |||
450 |
2/2✓ Branch 0 taken 149 times.
✓ Branch 1 taken 28 times.
|
177 | if (cannot_optimize) { |
451 | // lists are definitely different - cannot use internal compare function | ||
452 |
2/2✓ Branch 0 taken 105 times.
✓ Branch 1 taken 44 times.
|
149 | if (list->collection.size == other->collection.size) { |
453 | 105 | CxIterator left = list->cl->iterator(list, 0, false); | |
454 | 105 | CxIterator right = other->cl->iterator(other, 0, false); | |
455 |
2/2✓ Branch 0 taken 3184 times.
✓ Branch 1 taken 65 times.
|
3249 | for (size_t i = 0; i < list->collection.size; i++) { |
456 | 3184 | void *leftValue = cxIteratorCurrent(left); | |
457 | 3184 | void *rightValue = cxIteratorCurrent(right); | |
458 | 3184 | int d = list->collection.cmpfunc(leftValue, rightValue); | |
459 |
2/2✓ Branch 0 taken 40 times.
✓ Branch 1 taken 3144 times.
|
3184 | if (d != 0) { |
460 | 40 | return d; | |
461 | } | ||
462 | 3144 | cxIteratorNext(left); | |
463 | 3144 | cxIteratorNext(right); | |
464 | } | ||
465 | 65 | return 0; | |
466 | } else { | ||
467 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 22 times.
|
44 | return list->collection.size < other->collection.size ? -1 : 1; |
468 | } | ||
469 | } else { | ||
470 | // lists are compatible | ||
471 | 28 | return list->cl->compare(list, other); | |
472 | } | ||
473 | } | ||
474 | |||
475 | 29 | CxIterator cxListMutIteratorAt( | |
476 | CxList *list, | ||
477 | size_t index | ||
478 | ) { | ||
479 | 29 | CxIterator it = list->cl->iterator(list, index, false); | |
480 | 29 | it.base.mutating = true; | |
481 | 29 | return it; | |
482 | } | ||
483 | |||
484 | 13 | CxIterator cxListMutBackwardsIteratorAt( | |
485 | CxList *list, | ||
486 | size_t index | ||
487 | ) { | ||
488 | 13 | CxIterator it = list->cl->iterator(list, index, true); | |
489 | 13 | it.base.mutating = true; | |
490 | 13 | return it; | |
491 | } | ||
492 | |||
493 | 154 | void cxListFree(CxList *list) { | |
494 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 154 times.
|
154 | if (list == NULL) return; |
495 | 154 | list->cl->deallocate(list); | |
496 | } | ||
497 |