UNIXworkcode

1 # List Interface 2 3 The `list.h` header defines a common interface for all list implementations. 4 5 UCX already comes with two common list implementations ([linked list](linked_list.h.md) and [array list](array_list.h.md)) 6 that should cover most use cases. 7 But if you feel the need to implement your own list, you will find the instructions [below](#implement-own-list-structures). 8 9 ```C 10 #include <cx/linked_list.h> 11 12 CxList *cxLinkedListCreate(const CxAllocator *allocator, 13 size_t elem_size); 14 15 #include <cx/array_list.h> 16 17 CxList *cxArrayListCreate(const CxAllocator *allocator, 18 size_t elem_size, size_t initial_capacity); 19 ``` 20 21 The function `cxLinkedListCreate()` creates a new linked list with the specified `allocator` which stores elements of size `elem_size`. 22 The elements are supposed to be compared with the specified `comparator` (cf. [](#access-and-find)). 23 24 Array lists can be created with `cxArrayListCreate()` where the additional argument `initial_capacity` specifies for how many elements the underlying array shall be initially allocated. 25 26 If `CX_STORE_POINTERS` is used as `elem_size`, the actual element size will be `sizeof(void*)` and the list will behave slightly differently when accessing elements. 27 Lists that are storing pointers will always return the stored pointer directly, instead of returning a pointer into the list's memory, thus saving you from unnecessary dereferencing. 28 Conversely, when adding elements to the list, the pointers you specify (e.g., in `cxListAdd()` or `cxListInsert()`) are directly added to the list, instead of copying the contents pointed to by the argument. 29 30 > When you create a list which is storing pointers and do not specify a compare function, `cx_cmp_ptr` will be used by default. 31 32 > If you want to lazy-initialize lists, you can use the global `cxEmptyList` symbol as a placeholder instead of using a `NULL`-pointer. 33 > While you *must not* insert elements into that list, you can safely access this list or create iterators. 34 > This allows you to write clean code without checking for `NULL`-pointer everywhere. 35 > You still need to make sure that the placeholder is replaced with an actual list before inserting elements. 36 37 ## Example 38 39 In the following example we create a linked-list of regular expressions for filtering data. 40 41 ```C 42 #include <cx/linked_list.h> 43 #include <regex.h> 44 45 CxList *create_pattern_list(void) { 46 // create a linked list to store patterns 47 CxList *list = cxLinkedListCreate(NULL, sizeof(regex_t)); 48 49 // automatically free the pattern when list gets destroyed 50 cxSetDestructor(list, regfree); 51 52 return list; 53 } 54 55 int add_pattern(CxList *list, const char *value) { 56 // compile pattern and add it to the list, if successful 57 regex_t regex; 58 if (regcomp(&regex, value, REG_EXTENDED|REG_NOSUB)) { 59 return 1; 60 } else { 61 // take address of local variable 62 // since we are not storing pointers in this list, 63 // we get a copy of the data directly in the list 64 cxListAdd(list, &regex); 65 return 0; 66 } 67 } 68 69 bool matches_any(CxList *list, const char *text) { 70 CxIterator iter = cxListIterator(list); 71 cx_foreach(regex_t*, pat, iter) { 72 if (regexec(pat, text, 0, NULL, 0) == 0) { 73 return true; 74 } 75 } 76 return false; 77 } 78 ``` 79 80 If in the above example the list was created with `CX_STORE_POINTERS` instead of `sizeof(regex_t)`, 81 the `add_pattern()` function would need to be changed as follows: 82 83 ```C 84 int add_pattern(CxList *list, const char *value) { 85 // allocate memory here, because now we are storing pointers 86 regex_t *regex = malloc(sizeof(regex_t)); 87 if (!regex || regcomp(regex, value, REG_EXTENDED|REG_NOSUB)) { 88 return 1; 89 } else { 90 cxListAdd(list, regex); 91 return 0; 92 } 93 } 94 ``` 95 96 Also, just registering `regfree()` as a destructor is not enough anymore because the `regex_t` structure also needs to be freed. 97 Therefore, we would need to wrap the calls to `regfree()` and `free()` into an own destructor, which we then register with the list. 98 However, it should be clear by now that using `CX_STORE_POINTERS` is a bad choice for this use case to begin with. 99 100 As a rule of thumb: if you allocate memory for an element that you immediately put into the list, consider storing the element directly. 101 And if you are getting pointers to already allocated memory from somewhere else, and you just want to organize those elements in a list, then consider using `CX_STORE_POINTERS`. 102 103 ## Insert 104 105 ```C 106 #include <cx/list.h> 107 108 int cxListAdd(CxList *list, const void *elem); 109 110 int cxListInsert(CxList *list, size_t index, const void *elem); 111 112 void *cxListEmplace(CxList *list); 113 114 void *cxListEmplaceAt(CxList *list, size_t index); 115 116 CxIterator cxListEmplaceArray(CxList *list, size_t n); 117 118 CxIterator cxListEmplaceArrayAt(CxList *list, size_t index, size_t n); 119 120 int cxListInsertSorted(CxList *list, const void *elem); 121 122 int cxListInsertUnique(CxList *list, const void *elem); 123 124 size_t cxListAddArray(CxList *list, const void *array, size_t n); 125 126 size_t cxListInsertArray(CxList *list, size_t index, 127 const void *array, size_t n); 128 129 size_t cxListInsertSortedArray(CxList *list, 130 const void *array, size_t n); 131 132 size_t cxListInsertUniqueArray(CxList *list, 133 const void *array, size_t n); 134 135 int cxListInsertAfter(CxIterator *iter, const void *elem); 136 137 int cxListInsertBefore(CxIterator *iter, const void *elem); 138 ``` 139 140 The function `cxListAdd()` appends the element `elem` to the list and returns zero on success or non-zero when allocating the memory for the element fails. 141 Similarly, `cxListInsert()` adds the element at the specified `index`. 142 143 The functions `cxListEmplace()` and `cxListEmplaceAt()` behave like `cxListAdd()` and `cxListInsert()`, 144 except that they only allocate the memory and return a pointer to it, leaving it to the callee to copy the element data into it. 145 The same is true for `cxListEmplaceArray()` and `cxListEmplaceArrayAt()`, which allocate memory for `n` elements and return an iterator to the first element. 146 Be aware that when the list is storing pointers, the allocated memory will be for the pointer, not the actual element's data. 147 148 > If `cxListEmplaceArray()` or `cxListEmplaceArrayAt()` is unable to allocate memory for `n` elements, 149 > the iterator will iterate only over the elements that have been successfully allocated. 150 > The `elem_count` attribute of the iterator will be set to the number of successfully allocated elements. 151 > And the `index` attribute will count from zero to `elem_count - 1`. 152 > {style="note"} 153 154 The function `cxListInsertSorted()` inserts the element at the correct position so that the list remains sorted according to the list's compare function. 155 It is important that the list is already sorted before calling this function. 156 On the other hand, `cxListInsertUnique()` inserts the element only if it is not already in the list. 157 In this case it is strongly recommended that the list is already sorted but not required; the function will fall back to an inefficient algorithm when the list is not sorted. 158 159 When you are currently iterating through a list, you can insert elements before or after the current position of the iterator 160 with `cxListInsertBefore()` or `cxListInsertAfter()`, respectively. 161 162 If the list is storing pointers (cf. `cxCollectionStoresPointers()`), the pointer `elem` is directly added to the list. 163 Otherwise, the contents at the location pointed to by `elem` are copied to the list's memory with the element size specified during creation of the list. 164 165 On the other hand, the `array` argument for `cxListAddArray()`, `cxListInsertArray()`, `cxListInsertSortedArray()`, and `cxListInsertUniqueArray()` 166 must always point to an array of elements to be added (i.e., either an array of pointers or an array of actual elements). 167 168 The return values of the array-inserting functions are the number of elements that have been successfully _processed_. 169 Usually this is equivalent to the number of inserted elements, except for `cxListInsertUniqueArray()`, 170 where the actual number of inserted elements may be lower than the number of successfully processed elements. 171 172 > Implementations are required to optimize the insertion of a sorted array into a sorted list in linear time, 173 > while inserting each element separately via `cxListInsertSorted()` may take quadratic time. 174 > 175 > When used with sorted lists, the arrays passed to the functions must also be sorted according to the list's compare function. 176 > Only when `cxListInsertUniqueArray()` is used with a list that is not sorted, the array does not need to be sorted. 177 > 178 > The functions do not check if the passed arrays are sorted. 179 > {style="note"} 180 181 ## Access and Find 182 183 <link-summary>Functions for searching and accessing elements.</link-summary> 184 185 ```C 186 #include <cx/list.h> 187 188 void *cxListAt(const CxList *list, size_t index); 189 190 void *cxListFirst(const CxList *list); 191 192 void *cxListLast(const CxList *list); 193 194 int cxListSet(CxList *list, size_t index, const void *elem); 195 196 size_t cxListFind(const CxList *list, const void *elem); 197 198 size_t cxListFindRemove(CxList *list, const void *elem); 199 200 bool cxListContains(const CxList *list, const void *elem); 201 202 bool cxListIndexValid(const CxList *list, size_t index); 203 204 size_t cxListSize(const CxList *list); 205 ``` 206 207 The function `cxListAt()` accesses the element at the specified `index`. 208 If the list is storing pointers (i.e. `cxCollectionStoresPointers()` returns `true`), the pointer at the specified index is directly returned. 209 Otherwise, a pointer to the element at the specified index is returned. 210 If the index is out-of-bounds, the function returns `NULL`. 211 The functions `cxListFirst()` and `cxListLast()` behave like `cxListAt()`, 212 accessing the first or the last valid index, respectively, and returning `NULL` when the list is empty. 213 214 The function `cxListSet()` allows you to modify elements at a specific index in the list. 215 This function replaces the element at the specified `index` with the value pointed to by `elem`. 216 If the list is storing values directly (not pointers), the contents at the memory location `elem` will be copied into the list. 217 If the list is storing pointers, the pointer value itself will be stored. 218 The function returns `0` on success and `1` if the index is out of bounds. 219 220 On the other hand, `cxListFind()` searches for the element pointed to by `elem` by comparing each element in the list with the list's compare function 221 and returns the first index when the element was found. 222 Otherwise, the function returns the list size. 223 224 The function `cxListFindRemove()` behaves like `cxListFind()`, except that it also removes the first occurrence of the element from the list. 225 This will _also_ call destructor functions, if specified, so take special care when you continue to use `elem`, or when the list is storing pointers and the element appears more than once in the list. 226 227 The function `cxListContains()` returns `true` if and only if `cxListFind()` returns a valid index. 228 229 With `cxListIndexValid()` you can check the index returned by `cxListFind()` or `cxListFindRemove()`, 230 which is more convenient than comparing the return value if the return value of `cxListSize()`. 231 232 ## Remove 233 234 ```C 235 #include <cx/list.h> 236 237 int cxListRemove(CxList *list, size_t index); 238 239 size_t cxListRemoveArray(CxList *list, size_t index, size_t num); 240 241 size_t cxListFindRemove(CxList *list, const void *elem); 242 243 int cxListRemoveAndGet(CxList *list, size_t index, void *targetbuf); 244 245 int cxListRemoveAndGetFirst(CxList *list, void *targetbuf); 246 247 int cxListPopFront(CxList *list, void *targetbuf); 248 249 int cxListRemoveAndLast(CxList *list, void *targetbuf); 250 251 int cxListPop(CxList *list, void *targetbuf); 252 253 size_t cxListRemoveArrayAndGet(CxList *list, size_t index, size_t num, 254 void *targetbuf); 255 256 void cxListClear(CxList *list); 257 ``` 258 259 The function `cxListRemove()` removes the element at the specified index and returns zero, 260 or returns non-zero if the index is out-of-bounds. 261 The function `cxListRemoveArray()` removes `num` elements starting at `index` and returns the number of elements that have been removed. 262 For each removed element, the [destructor functions](collection.h.md#destructor-functions) are called. 263 264 The function `cxListFindRemove()` finds the first element within the list that is equivalent to the element pointed to by `elem` and removes it from the list. 265 This will _also_ call destructor functions, if specified, so take special care when you continue to use `elem`. 266 267 On the other hand, `cxListRemoveAndGet()` family of functions do not invoke the destructor functions 268 and instead copy the elements into the `targetbuf`, which must be large enough to hold the removed elements. 269 270 > Note that when the list was initialized with `CX_STORE_POINTERS`, 271 > the elements that will be copied to the `targetbuf` are the _pointers_. 272 > In contrast to other list functions, like `cxListAt()`, an automatic dereferencing does not happen here. 273 >{style="note"} 274 275 The function `cxListClear()` simply removes all elements from the list, invoking the destructor functions. 276 It behaves equivalently but is usually more efficient than calling `cxListRemove()` for every index. 277 278 ## Iterators 279 280 ```C 281 #include <cx/list.h> 282 283 CxIterator cxListIterator(const CxList *list); 284 285 CxIterator cxListBackwardsIterator(const CxList *list); 286 287 CxIterator cxListIteratorAt(const CxList *list, size_t index); 288 289 CxIterator cxListBackwardsIteratorAt(const CxList *list, size_t index); 290 ``` 291 292 The functions `cxListIterator()` and `cxListBackwardsIterator()` create iterators 293 that start and the first or the last element in the list and iterate through the entire list. 294 295 The functions `cxListIteratorAt()` and `cxListBackwardsIteratorAt()` start with the element at the specified index 296 and iterate until the end, or the beginning of the list, respectively. 297 298 Removing elements via an iterator will cause an invocation of the [destructor functions](collection.h.md#destructor-functions) for the removed element. 299 300 It is safe to specify an out-of-bounds index, or a `NULL` pointer, in which cases the returned iterator will behave like an iterator over an empty list. 301 302 ## Reorder 303 304 ```C 305 #include <cx/list.h> 306 307 int cxListSwap(CxList *list, size_t i, size_t j); 308 309 void cxListReverse(CxList *list); 310 311 void cxListSort(CxList *list); 312 ``` 313 314 The function `cxListSwap()` swaps two elements specified by the indices `i` and `j`. 315 The return value is non-zero if one of the indices is out-of-bounds. 316 317 The function `cxListReverse()` reorders all elements so that they appear in exactly the opposite order after invoking this function. 318 319 The function `cxListSort()` sorts all elements with respect to the list's compare function, 320 unless the list is already sorted (cf. `cxCollectionSorted()`), in which case the function immediately returns. 321 322 Default UCX implementations of the list interface make use of [small buffer optimizations](install.md#small-buffer-optimizations) when swapping elements. 323 324 > An invocation of `cxListSort` sets the `sorted` flag of the [collection](collection.h.md). 325 > Implementations usually make use of this flag to optimize search operations, if possible. 326 > For example, the [array list](array_list.h.md) implementation will use binary search 327 > for `cxListFind()` and similar operations when the list is sorted. 328 329 ## Compare 330 331 ```C 332 #include <cx/list.h> 333 334 int cxListCompare(const CxList *list, const CxList *other); 335 ``` 336 337 Arbitrary lists can be compared element-wise with `cxListCompare()`, as long as the compare functions of both lists are equivalent. 338 339 That means you can compare an UCX [array list](array_list.h.md) with a [linked list](linked_list.h.md), 340 and you could even compare lists that are storing pointers with lists that are not storing pointers. 341 342 However, the optimized list-internal compare implementation is only used when both the compare functions and the list classes are identical. 343 Otherwise, `cxListCompare()` will behave as if you were iterating through both lists and manually comparing the elements. 344 345 The return value of `cxListCompare()` is zero if the lists are element-wise equivalent. 346 If they are not, the non-zero return value equals the return value of the used compare function for the first pair of elements that are not equal. 347 348 ## Clone 349 350 ```C 351 #include <cx/allocator.h> 352 353 typedef void*(cx_clone_func)( 354 void *target, const void *source, 355 const CxAllocator *allocator, 356 void *data); 357 358 #include <cx/list.h> 359 360 int cxListClone(CxList *dst, const CxList *src, 361 cx_clone_func clone_func, 362 const CxAllocator *clone_allocator, 363 void *data); 364 365 int cxListCloneShallow(CxList *dst, const CxList *src); 366 367 int cxListDifference(CxList *dst, 368 const CxList *minuend, const CxList *subtrahend, 369 cx_clone_func clone_func, 370 const CxAllocator *clone_allocator, 371 void *data); 372 373 int cxListDifferenceShallow(CxList *dst, 374 const CxList *minuend, const CxList *subtrahend); 375 376 int cxListIntersection(CxList *dst, 377 const CxList *src, const CxList *other, 378 cx_clone_func clone_func, 379 const CxAllocator *clone_allocator, 380 void *data); 381 382 int cxListIntersectionShallow(CxList *dst, 383 const CxList *src, const CxList *other); 384 385 int cxListUnion(CxList *dst, 386 const CxList *src, const CxList *other, 387 cx_clone_func clone_func, 388 const CxAllocator *clone_allocator, 389 void *data); 390 391 int cxListUnionShallow(CxList *dst, 392 const CxList *src, const CxList *other); 393 ``` 394 395 With `cxListClone()` you can create deep copies of the elements in a list and insert them into another list. 396 The destination list does not need to be empty, in which case the elements will be appended. 397 Depending on the concrete list implementation, `cxListClone()` tries to allocate enough memory up-front before trying 398 to insert anything. 399 400 The function `cxListDifference()` clones elements from the `minuend` that are _not_ contained in the `subtrahend`, 401 while `cxListIntersection()` only clones elements from the `src` that are _also_ contained in the `other` list. 402 And `cxListUnion()` clones elements from `src` first, and from `other` only when they are _not_ contained in `src`. 403 404 All three functions, for union, difference, and intersection, are optimized for sorted lists. 405 In that case they will take linear time instead of quadratic time for the operation. 406 Also, `cxListUnion()` makes sure that the elements from `src` and `other` are cloned interleaving, 407 so that the result of the union is still sorted. 408 409 However, when the `dst` list already contains elements, all functions will only append the result of the operation to that list. 410 That means the `dst` list is only guaranteed to be sorted when it was empty and the input lists are all sorted. 411 412 Refer to the documentation of the [clone-function callback](allocator.h.md#clone-function) to learn how to implement it. 413 414 The _simple_ versions of the above functions use an internal shallow clone function 415 which uses `memcpy()` to copy the elements. 416 If the destination map is storing pointers, this internal clone function uses the current default allocator to allocate the memory. 417 418 The functions return zero if and only if all clone operations were successful. 419 420 > It is perfectly possible to clone items into a list of a different type. 421 > For example, you can clone elements from a list that is just storing pointers (`CX_STORE_POINTERS`) to a list that 422 > allocates the memory for the objects (and vice versa). 423 424 > The lists passed to the above functions must all be different. 425 > Passing the same pointer for different list arguments may result in erroneous behavior. 426 >{style="warning"} 427 428 ## Reserve and Shrink 429 430 ```C 431 #include <cx/list.h> 432 433 int cxListReserve(CxList *list, size_t count); 434 435 int cxListShrink(CxList *list); 436 ``` 437 438 If a list implementation allows overallocation, the function `cxListReserve()` can be used to reserve memory for a total of `count` elements. 439 On the other hand, you can use `cxListShrink()` to dispose of any overallocated memory and reduce the capacity to the actual number of currently stored elements. 440 Calling `cxListReserve()` with a `count` argument that is less than the current size of the list has no effect, and the function returns zero. 441 442 If allocating memory fails, `cxListReserve()` returns a non-zero value. 443 Since shrinking should never fail, `cxListShrink()` will usually always return zero, 444 but depending on the list (or allocator) implementation, the possibility for a non-zero return value is there. 445 446 List implementations that do not overallocate are advised to simply return zero. 447 That means, using those functions never does any harm and calls to them can be added whenever it seems appropriate. 448 Even if the currently used list implementation does not support overallocation, it may become extremely useful when the concrete implementation is exchanged later. 449 450 > The function `cxListReserve()` should not be confused with `cxListEmplaceArray()`. 451 > The former will only increase the capacity of a list which supports overallocation without changing the size. 452 > The latter will actually add real, uninitialized elements. 453 >{style="note"} 454 455 ## Dispose 456 457 ```C 458 #include <cx/list.h> 459 460 void cxListFree(CxList *list); 461 ``` 462 463 No matter with which function a `CxList` has been created, you can _always_ deallocate the entire memory with a call to `cxListFree()`. 464 465 The effect is equivalent to invoking `cxListClear()` plus deallocating the memory for the list structure. 466 That means, for each element in the list, the [destructor functions](collection.h.md#destructor-functions) are called before deallocating the list. 467 468 When the list has been storing pointers, make sure that either another reference to the same memory exists in your program, 469 or any of the destructor functions deallocates the memory. 470 Otherwise, there is a risk of a memory leak. 471 472 ## Implement own List Structures 473 474 If you want to create your own list implementation, this is extremely easy. 475 476 You need to define a function for creating your list and assign a `cx_list_class` structure with the pointers to your implementation. 477 Then you call the `cx_list_init()` helper function to initialize the collection sture. 478 This also automatically adds support for `CX_STORE_POINTERS` to your list. 479 480 ```C 481 // define the class with pointers to your functions 482 static cx_list_class my_list_class = { 483 my_list_deallocate, 484 my_list_insert_element, 485 my_list_insert_array, 486 my_list_insert_sorted, 487 my_list_insert_unique, 488 my_list_insert_iter, 489 my_list_remove, 490 my_list_clear, 491 my_list_swap, 492 my_list_at, 493 my_list_find_remove, 494 my_list_sort, 495 my_list_compare, 496 my_list_reverse, 497 my_list_change_capacity, 498 my_list_iterator, 499 }; 500 501 typedef struct { 502 struct cx_list_s base; 503 // your custom list data goes here 504 } my_list; 505 506 CxList *my_list_create( 507 const CxAllocator *allocator, 508 size_t elem_size 509 ) { 510 if (allocator == NULL) { 511 allocator = cxDefaultAllocator; 512 } 513 514 my_list *list = cxCalloc(allocator, 1, sizeof(my_list)); 515 if (list == NULL) return NULL; 516 cx_list_init((CxList*)list, &my_list_class, 517 allocator, elem_size); 518 519 // initialize your custom list data here 520 521 return (CxList *) list; 522 } 523 ``` 524 525 The required behavior for the implementations is described in the following table. 526 You can always look at the source code of the UCX implementations to get inspiration. 527 528 | Function | Description | 529 |-------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 530 | `clear` | Invoke destructor functions on all elements and remove them from the list. | 531 | `deallocate` | Invoke destructor functions on all elements and deallocate the entire list memory. | 532 | `insert_element` | Insert or allocate a single element at the specified index. Return a pointer to the allocated element or `NULL` on failure. | 533 | `insert_array` | Insert or allocate an array of elements starting at the specified index. Return the number of successfully inserted or allocated elements. | 534 | `insert_sorted` | Insert an array of sorted elements into a sorted list. Return the number of elements processed (equals the number of elements inserted in this case). | 535 | `insert_unique` | Insert an array of sorted unique elements into a sorted list. Return the number of elements processed (not the number of elements inserted, which might be lower). | 536 | `insert_iter` | Insert a single element depending on the iterator position. The third argument to this function is zero when the element shall be inserted after the iterator position and non-zero if it shall be inserted before the iterator position. The implementation is also responsible for adjusting the iterator, respectively. | 537 | `remove` | Remove multiple elements starting at the specified index. If a target buffer is specified, copy the elements to that buffer. Otherwise, invoke the destructor functions for the elements. Return the number of elements actually removed. | 538 | `swap` | Swap two elements by index. Return zero on success or non-zero when any index was out-of-bounds. | 539 | `at` | Return a pointer to the element at the specified index or `NULL` when the index is out-of-bounds. | 540 | `find_remove` | Search for the specified element with the list's compare function and return the index if found. If the `remove` argument is true, invoke the destructor functions and remove the element. Return the list size if the element is not found. | 541 | `sort` | Sort all elements in the list with respect to the list's compare function. | 542 | `compare` | This function pointer can be `NULL`, in which case an unoptimized fallback is used. You can implement an optimized compare function that compares the list of another list of the same class. | 543 | `reverse` | Reorder all elements in the list so that they appear in exactly the opposite order. | 544 | `change_capacity` | When your list supports overallocation, the capacity shall be changed to the specified value. Return non-zero on error and zero on success. When the list does not support overallocation, this function pointer can be `NULL`. | 545 | `iterator` | Create an iterator starting at the specified index. The Boolean argument indicates whether iteration is supposed to traverse backwards. | 546 547 > If you initialize your list with `cx_list_init()`, you do not have to worry about making a 548 > difference between storing pointers and storing elements, because your implementation will 549 > be automatically wrapped. 550 > This means you only have to handle the one single case described above. 551 552 ### Default Class Function Implementations 553 554 If you are satisfied with some of the default implementations, you can use some pre-defined functions. 555 Note, however, that those are not optimized for any specific list structure 556 and just serve as a quick and convenient solution if performance does not matter for your use case. 557 558 559 | Default Function | Description | 560 |---------------------------------|-----------------------------------------------------------------------------------| 561 | `cx_list_default_insert_array` | Falls back to multiple calls of `insert_element`. | 562 | `cx_list_default_insert_sorted` | Uses linear search to find insertion points. | 563 | `cx_list_default_insert_unique` | Like `cx_default_insert_sorted` but skips elements that are already contained. | 564 | `cx_list_default_sort` | Copies all elements to an array, applies `qsort()`, and copies the elements back. | 565 | `cx_list_default_swap` | Uses a temporarily allocated buffer to perform a three-way-swap. | 566 567 568 <seealso> 569 <category ref="apidoc"> 570 <a href="https://ucx.sourceforge.io/api/list_8h.html">list.h</a> 571 </category> 572 </seealso> 573