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