UNIXworkcode

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