src/array_list.c

Sat, 26 Nov 2022 16:58:41 +0100

author
Mike Becker <universe@uap-core.de>
date
Sat, 26 Nov 2022 16:58:41 +0100
changeset 630
ac5e7f789048
parent 629
6c81ee4f11ad
child 638
eafb45eefc51
permissions
-rw-r--r--

separate iterators and mutating iterators

Trade tons of code duplication for const-correctness.

universe@606 1 /*
universe@606 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@606 3 *
universe@606 4 * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
universe@606 5 *
universe@606 6 * Redistribution and use in source and binary forms, with or without
universe@606 7 * modification, are permitted provided that the following conditions are met:
universe@606 8 *
universe@606 9 * 1. Redistributions of source code must retain the above copyright
universe@606 10 * notice, this list of conditions and the following disclaimer.
universe@606 11 *
universe@606 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@606 13 * notice, this list of conditions and the following disclaimer in the
universe@606 14 * documentation and/or other materials provided with the distribution.
universe@606 15 *
universe@606 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@606 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@606 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@606 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@606 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@606 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@606 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@606 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@606 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@606 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@606 26 * POSSIBILITY OF SUCH DAMAGE.
universe@606 27 */
universe@606 28
universe@606 29 #include "cx/array_list.h"
universe@610 30 #include <assert.h>
universe@610 31 #include <string.h>
universe@611 32 #include <stdint.h>
universe@606 33
universe@628 34 // LOW LEVEL ARRAY LIST FUNCTIONS
universe@607 35
universe@612 36 enum cx_array_copy_result cx_array_copy(
universe@610 37 void **target,
universe@610 38 size_t *size,
universe@610 39 size_t *capacity,
universe@610 40 size_t index,
universe@610 41 void const *src,
universe@610 42 size_t elem_size,
universe@610 43 size_t elem_count,
universe@610 44 struct cx_array_reallocator_s *reallocator
universe@610 45 ) {
universe@628 46 // assert pointers
universe@610 47 assert(target != NULL);
universe@610 48 assert(size != NULL);
universe@610 49 assert(src != NULL);
universe@607 50
universe@628 51 // determine capacity
universe@610 52 size_t cap = capacity == NULL ? *size : *capacity;
universe@610 53
universe@628 54 // check if resize is required
universe@627 55 size_t minsize = index + elem_count;
universe@627 56 size_t newsize = *size < minsize ? minsize : *size;
universe@610 57 bool needrealloc = newsize > cap;
universe@610 58
universe@628 59 // reallocate if possible
universe@610 60 if (needrealloc) {
universe@628 61 // a reallocator and a capacity variable must be available
universe@610 62 if (reallocator == NULL || capacity == NULL) {
universe@610 63 return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED;
universe@610 64 }
universe@610 65
universe@628 66 // check, if we need to repair the src pointer
universe@611 67 uintptr_t targetaddr = (uintptr_t) *target;
universe@611 68 uintptr_t srcaddr = (uintptr_t) src;
universe@611 69 bool repairsrc = targetaddr <= srcaddr
universe@611 70 && srcaddr < targetaddr + cap * elem_size;
universe@611 71
universe@628 72 // calculate new capacity (next number divisible by 16)
universe@625 73 cap = newsize - (newsize % 16) + 16;
universe@625 74 assert(cap > newsize);
universe@610 75
universe@628 76 // perform reallocation
universe@610 77 void *newmem = reallocator->realloc(
universe@610 78 *target, cap, elem_size, reallocator
universe@610 79 );
universe@610 80 if (newmem == NULL) {
universe@610 81 return CX_ARRAY_COPY_REALLOC_FAILED;
universe@610 82 }
universe@610 83
universe@628 84 // repair src pointer, if necessary
universe@611 85 if (repairsrc) {
universe@611 86 src = ((char *) newmem) + (srcaddr - targetaddr);
universe@611 87 }
universe@611 88
universe@628 89 // store new pointer and capacity
universe@610 90 *target = newmem;
universe@610 91 *capacity = cap;
universe@610 92 }
universe@610 93
universe@628 94 // determine target pointer
universe@610 95 char *start = *target;
universe@610 96 start += index * elem_size;
universe@610 97
universe@628 98 // copy elements and set new size
universe@611 99 memmove(start, src, elem_count * elem_size);
universe@610 100 *size = newsize;
universe@610 101
universe@628 102 // return successfully
universe@610 103 return CX_ARRAY_COPY_SUCCESS;
universe@610 104 }
universe@607 105
universe@623 106 #define CX_ARRAY_SWAP_SBO_SIZE 512
universe@623 107
universe@623 108 void cx_array_swap(
universe@623 109 void *arr,
universe@623 110 size_t elem_size,
universe@623 111 size_t idx1,
universe@623 112 size_t idx2
universe@623 113 ) {
universe@628 114 // short circuit
universe@623 115 if (idx1 == idx2) return;
universe@623 116
universe@623 117 char sbo_mem[CX_ARRAY_SWAP_SBO_SIZE];
universe@623 118 void *tmp;
universe@623 119
universe@628 120 // decide if we can use the local buffer
universe@623 121 if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) {
universe@623 122 tmp = malloc(elem_size);
universe@628 123 // we don't want to enforce error handling
universe@623 124 if (tmp == NULL) abort();
universe@623 125 } else {
universe@623 126 tmp = sbo_mem;
universe@623 127 }
universe@623 128
universe@628 129 // calculate memory locations
universe@623 130 char *left = arr, *right = arr;
universe@623 131 left += idx1 * elem_size;
universe@623 132 right += idx2 * elem_size;
universe@623 133
universe@628 134 // three-way swap
universe@623 135 memcpy(tmp, left, elem_size);
universe@623 136 memcpy(left, right, elem_size);
universe@623 137 memcpy(right, tmp, elem_size);
universe@623 138
universe@628 139 // free dynamic memory, if it was needed
universe@623 140 if (tmp != sbo_mem) {
universe@623 141 free(tmp);
universe@623 142 }
universe@623 143 }
universe@623 144
universe@628 145 // HIGH LEVEL ARRAY LIST FUNCTIONS
universe@607 146
universe@607 147 typedef struct {
universe@607 148 struct cx_list_s base;
universe@607 149 void *data;
universe@610 150 struct cx_array_reallocator_s reallocator;
universe@607 151 } cx_array_list;
universe@607 152
universe@610 153 static void *cx_arl_realloc(
universe@610 154 void *array,
universe@610 155 size_t capacity,
universe@610 156 size_t elem_size,
universe@610 157 struct cx_array_reallocator_s *alloc
universe@610 158 ) {
universe@628 159 // retrieve the pointer to the list allocator
universe@610 160 CxAllocator const *al = alloc->ptr1;
universe@610 161
universe@628 162 // use the list allocator to reallocate the memory
universe@610 163 return cxRealloc(al, array, capacity * elem_size);
universe@610 164 }
universe@610 165
universe@607 166 static void cx_arl_destructor(struct cx_list_s *list) {
universe@610 167 cx_array_list *arl = (cx_array_list *) list;
universe@607 168 cxFree(list->allocator, arl->data);
universe@607 169 }
universe@607 170
universe@607 171 static int cx_arl_add(
universe@607 172 struct cx_list_s *list,
universe@607 173 void const *elem
universe@607 174 ) {
universe@610 175 cx_array_list *arl = (cx_array_list *) list;
universe@610 176 return cx_array_copy(
universe@610 177 &arl->data,
universe@610 178 &list->size,
universe@610 179 &list->capacity,
universe@610 180 list->size,
universe@610 181 elem,
universe@610 182 list->itemsize,
universe@610 183 1,
universe@610 184 &arl->reallocator
universe@610 185 );
universe@607 186 }
universe@607 187
universe@629 188 static size_t cx_arl_add_array(
universe@629 189 struct cx_list_s *list,
universe@629 190 void const *array,
universe@629 191 size_t n
universe@629 192 ) {
universe@629 193 cx_array_list *arl = (cx_array_list *) list;
universe@629 194 if (CX_ARRAY_COPY_SUCCESS == cx_array_copy(
universe@629 195 &arl->data,
universe@629 196 &list->size,
universe@629 197 &list->capacity,
universe@629 198 list->size,
universe@629 199 array,
universe@629 200 list->itemsize,
universe@629 201 n,
universe@629 202 &arl->reallocator
universe@629 203 )) {
universe@629 204 return n;
universe@629 205 } else {
universe@629 206 // array list implementation is "all or nothing"
universe@629 207 return 0;
universe@629 208 }
universe@629 209 }
universe@629 210
universe@607 211 static int cx_arl_insert(
universe@607 212 struct cx_list_s *list,
universe@607 213 size_t index,
universe@607 214 void const *elem
universe@607 215 ) {
universe@611 216 if (index > list->size) {
universe@611 217 return 1;
universe@611 218 } else if (index == list->size) {
universe@611 219 return cx_arl_add(list, elem);
universe@611 220 } else {
universe@611 221 cx_array_list *arl = (cx_array_list *) list;
universe@611 222
universe@628 223 // move elements starting at index to the right
universe@611 224 if (cx_array_copy(
universe@611 225 &arl->data,
universe@611 226 &list->size,
universe@611 227 &list->capacity,
universe@611 228 index + 1,
universe@611 229 ((char *) arl->data) + index * list->itemsize,
universe@611 230 list->itemsize,
universe@611 231 list->size - index,
universe@611 232 &arl->reallocator
universe@611 233 )) {
universe@611 234 return 1;
universe@611 235 }
universe@611 236
universe@628 237 // place the element
universe@611 238 memcpy(((char *) arl->data) + index * list->itemsize,
universe@611 239 elem, list->itemsize);
universe@611 240
universe@611 241 return 0;
universe@611 242 }
universe@607 243 }
universe@607 244
universe@607 245 static int cx_arl_insert_iter(
universe@630 246 struct cx_mut_iterator_s *iter,
universe@607 247 void const *elem,
universe@607 248 int prepend
universe@607 249 ) {
universe@619 250 struct cx_list_s *list = iter->src_handle;
universe@619 251 if (iter->index < list->size) {
universe@619 252 int result = cx_arl_insert(
universe@619 253 list,
universe@619 254 iter->index + 1 - prepend,
universe@619 255 elem
universe@619 256 );
universe@619 257 if (result == 0 && prepend != 0) {
universe@619 258 iter->index++;
universe@619 259 iter->elem_handle = ((char *) iter->elem_handle) + list->itemsize;
universe@619 260 }
universe@619 261 return result;
universe@619 262 } else {
universe@619 263 int result = cx_arl_add(list, elem);
universe@619 264 iter->index = list->size;
universe@619 265 return result;
universe@619 266 }
universe@607 267 }
universe@607 268
universe@607 269 static int cx_arl_remove(
universe@607 270 struct cx_list_s *list,
universe@607 271 size_t index
universe@607 272 ) {
universe@628 273 // out-of-bounds check
universe@613 274 if (index >= list->size) {
universe@613 275 return 1;
universe@613 276 }
universe@613 277
universe@628 278 // short-circuit removal of last element
universe@624 279 if (index == list->size - 1) {
universe@624 280 list->size--;
universe@624 281 return 0;
universe@624 282 }
universe@613 283
universe@628 284 // just move the elements starting at index to the left
universe@624 285 cx_array_list *arl = (cx_array_list *) list;
universe@613 286 int result = cx_array_copy(
universe@613 287 &arl->data,
universe@613 288 &list->size,
universe@613 289 &list->capacity,
universe@613 290 index,
universe@613 291 ((char *) arl->data) + (index + 1) * list->itemsize,
universe@613 292 list->itemsize,
universe@626 293 list->size - index - 1,
universe@613 294 &arl->reallocator
universe@613 295 );
universe@613 296 if (result == 0) {
universe@628 297 // decrease the size
universe@613 298 list->size--;
universe@613 299 }
universe@613 300 return result;
universe@607 301 }
universe@607 302
universe@610 303 static void *cx_arl_at(
universe@607 304 struct cx_list_s const *list,
universe@607 305 size_t index
universe@607 306 ) {
universe@610 307 if (index < list->size) {
universe@610 308 cx_array_list const *arl = (cx_array_list const *) list;
universe@610 309 char *space = arl->data;
universe@610 310 return space + index * list->itemsize;
universe@610 311 } else {
universe@610 312 return NULL;
universe@610 313 }
universe@607 314 }
universe@607 315
universe@607 316 static size_t cx_arl_find(
universe@607 317 struct cx_list_s const *list,
universe@607 318 void const *elem
universe@607 319 ) {
universe@614 320 char *cur = ((cx_array_list const *) list)->data;
universe@614 321
universe@614 322 for (size_t i = 0; i < list->size; i++) {
universe@614 323 if (0 == list->cmpfunc(elem, cur)) {
universe@614 324 return i;
universe@614 325 }
universe@614 326 cur += list->itemsize;
universe@614 327 }
universe@614 328
universe@614 329 return list->size;
universe@607 330 }
universe@607 331
universe@607 332 static void cx_arl_sort(struct cx_list_s *list) {
universe@615 333 qsort(((cx_array_list *) list)->data,
universe@615 334 list->size,
universe@615 335 list->itemsize,
universe@615 336 list->cmpfunc
universe@615 337 );
universe@607 338 }
universe@607 339
universe@607 340 static int cx_arl_compare(
universe@607 341 struct cx_list_s const *list,
universe@607 342 struct cx_list_s const *other
universe@607 343 ) {
universe@622 344 if (list->size == other->size) {
universe@622 345 char const *left = ((cx_array_list const *) list)->data;
universe@622 346 char const *right = ((cx_array_list const *) other)->data;
universe@622 347 for (size_t i = 0; i < list->size; i++) {
universe@622 348 int d = list->cmpfunc(left, right);
universe@622 349 if (d != 0) {
universe@622 350 return d;
universe@622 351 }
universe@622 352 left += list->itemsize;
universe@622 353 right += other->itemsize;
universe@622 354 }
universe@622 355 return 0;
universe@622 356 } else {
universe@622 357 return list->size < other->size ? -1 : 1;
universe@622 358 }
universe@607 359 }
universe@607 360
universe@607 361 static void cx_arl_reverse(struct cx_list_s *list) {
universe@623 362 if (list->size < 2) return;
universe@623 363 void *data = ((cx_array_list const *) list)->data;
universe@623 364 size_t half = list->size / 2;
universe@623 365 for (size_t i = 0; i < half; i++) {
universe@623 366 cx_array_swap(data, list->itemsize, i, list->size - 1 - i);
universe@623 367 }
universe@607 368 }
universe@607 369
universe@630 370 static bool cx_arl_iter_valid(void const *it) {
universe@630 371 struct cx_iterator_s const *iter = it;
universe@616 372 struct cx_list_s const *list = iter->src_handle;
universe@616 373 return iter->index < list->size;
universe@616 374 }
universe@616 375
universe@630 376 static void *cx_arl_iter_current(void const *it) {
universe@630 377 struct cx_iterator_s const *iter = it;
universe@616 378 return iter->elem_handle;
universe@616 379 }
universe@616 380
universe@630 381 static void cx_arl_iter_next(void *it) {
universe@630 382 struct cx_iterator_base_s *itbase = it;
universe@630 383 if (itbase->remove) {
universe@630 384 struct cx_mut_iterator_s *iter = it;
universe@630 385 itbase->remove = false;
universe@616 386 cx_arl_remove(iter->src_handle, iter->index);
universe@616 387 } else {
universe@630 388 struct cx_iterator_s *iter = it;
universe@616 389 iter->index++;
universe@620 390 iter->elem_handle =
universe@620 391 ((char *) iter->elem_handle)
universe@620 392 + ((struct cx_list_s const *) iter->src_handle)->itemsize;
universe@616 393 }
universe@616 394 }
universe@616 395
universe@630 396 static bool cx_arl_iter_flag_rm(void *it) {
universe@630 397 struct cx_iterator_base_s *iter = it;
universe@630 398 if (iter->mutating) {
universe@630 399 iter->remove = true;
universe@630 400 return true;
universe@630 401 } else {
universe@630 402 return false;
universe@630 403 }
universe@630 404 }
universe@630 405
universe@607 406 static struct cx_iterator_s cx_arl_iterator(
universe@630 407 struct cx_list_s const *list,
universe@607 408 size_t index
universe@607 409 ) {
universe@607 410 struct cx_iterator_s iter;
universe@607 411
universe@616 412 iter.index = index;
universe@616 413 iter.src_handle = list;
universe@616 414 iter.elem_handle = cx_arl_at(list, index);
universe@630 415 iter.base.valid = cx_arl_iter_valid;
universe@630 416 iter.base.current = cx_arl_iter_current;
universe@630 417 iter.base.next = cx_arl_iter_next;
universe@630 418 iter.base.flag_removal = cx_arl_iter_flag_rm;
universe@630 419 iter.base.remove = false;
universe@630 420 iter.base.mutating = false;
universe@616 421
universe@607 422 return iter;
universe@607 423 }
universe@607 424
universe@630 425 static struct cx_mut_iterator_s cx_arl_mut_iterator(
universe@630 426 struct cx_list_s *list,
universe@630 427 size_t index
universe@630 428 ) {
universe@630 429 CxIterator it = cx_arl_iterator(list, index);
universe@630 430 it.base.mutating = true;
universe@630 431
universe@630 432 // we know the iterators share the same memory layout
universe@630 433 CxMutIterator iter;
universe@630 434 memcpy(&iter, &it, sizeof(CxMutIterator));
universe@630 435 return iter;
universe@630 436 }
universe@630 437
universe@607 438 static cx_list_class cx_array_list_class = {
universe@607 439 cx_arl_destructor,
universe@607 440 cx_arl_add,
universe@629 441 cx_arl_add_array,
universe@607 442 cx_arl_insert,
universe@607 443 cx_arl_insert_iter,
universe@607 444 cx_arl_remove,
universe@607 445 cx_arl_at,
universe@607 446 cx_arl_find,
universe@607 447 cx_arl_sort,
universe@607 448 cx_arl_compare,
universe@607 449 cx_arl_reverse,
universe@607 450 cx_arl_iterator,
universe@630 451 cx_arl_mut_iterator,
universe@607 452 };
universe@607 453
universe@606 454 CxList *cxArrayListCreate(
universe@606 455 CxAllocator const *allocator,
universe@606 456 CxListComparator comparator,
universe@606 457 size_t item_size,
universe@606 458 size_t initial_capacity
universe@606 459 ) {
universe@607 460 cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list));
universe@607 461 if (list == NULL) return NULL;
universe@607 462
universe@607 463 list->data = cxCalloc(allocator, initial_capacity, item_size);
universe@607 464 if (list->data == NULL) {
universe@607 465 cxFree(allocator, list);
universe@607 466 return NULL;
universe@607 467 }
universe@607 468
universe@607 469 list->base.cl = &cx_array_list_class;
universe@607 470 list->base.allocator = allocator;
universe@607 471 list->base.cmpfunc = comparator;
universe@607 472 list->base.itemsize = item_size;
universe@607 473 list->base.capacity = initial_capacity;
universe@607 474
universe@628 475 // configure the reallocator
universe@610 476 list->reallocator.realloc = cx_arl_realloc;
universe@610 477 list->reallocator.ptr1 = (void *) allocator;
universe@610 478
universe@607 479 return (CxList *) list;
universe@606 480 }

mercurial