src/array_list.c

Sun, 20 Nov 2022 17:22:37 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 20 Nov 2022 17:22:37 +0100
changeset 625
a4c4a50c067a
parent 624
b0bdff7d8203
child 626
254cc61c71a0
permissions
-rw-r--r--

fix calculation of new capacity in cx_array_copy()

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@607 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@610 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@610 51 /* determine capacity */
universe@610 52 size_t cap = capacity == NULL ? *size : *capacity;
universe@610 53
universe@610 54 /* check if resize is required */
universe@610 55 size_t newsize = index + elem_count;
universe@610 56 bool needrealloc = newsize > cap;
universe@610 57
universe@610 58 /* reallocate if possible */
universe@610 59 if (needrealloc) {
universe@610 60 /* a reallocator and a capacity variable must be available */
universe@610 61 if (reallocator == NULL || capacity == NULL) {
universe@610 62 return CX_ARRAY_COPY_REALLOC_NOT_SUPPORTED;
universe@610 63 }
universe@610 64
universe@611 65 /* check, if we need to repair the src pointer */
universe@611 66 uintptr_t targetaddr = (uintptr_t) *target;
universe@611 67 uintptr_t srcaddr = (uintptr_t) src;
universe@611 68 bool repairsrc = targetaddr <= srcaddr
universe@611 69 && srcaddr < targetaddr + cap * elem_size;
universe@611 70
universe@625 71 /* calculate new capacity (next number divisible by 16) */
universe@625 72 cap = newsize - (newsize % 16) + 16;
universe@625 73 assert(cap > newsize);
universe@610 74
universe@610 75 /* perform reallocation */
universe@610 76 void *newmem = reallocator->realloc(
universe@610 77 *target, cap, elem_size, reallocator
universe@610 78 );
universe@610 79 if (newmem == NULL) {
universe@610 80 return CX_ARRAY_COPY_REALLOC_FAILED;
universe@610 81 }
universe@610 82
universe@611 83 /* repair src pointer, if necessary */
universe@611 84 if (repairsrc) {
universe@611 85 src = ((char *) newmem) + (srcaddr - targetaddr);
universe@611 86 }
universe@611 87
universe@610 88 /* store new pointer and capacity */
universe@610 89 *target = newmem;
universe@610 90 *capacity = cap;
universe@610 91 }
universe@610 92
universe@610 93 /* determine target pointer */
universe@610 94 char *start = *target;
universe@610 95 start += index * elem_size;
universe@610 96
universe@610 97 /* copy elements and set new size */
universe@611 98 memmove(start, src, elem_count * elem_size);
universe@610 99 *size = newsize;
universe@610 100
universe@610 101 /* return successfully */
universe@610 102 return CX_ARRAY_COPY_SUCCESS;
universe@610 103 }
universe@607 104
universe@623 105 #define CX_ARRAY_SWAP_SBO_SIZE 512
universe@623 106
universe@623 107 void cx_array_swap(
universe@623 108 void *arr,
universe@623 109 size_t elem_size,
universe@623 110 size_t idx1,
universe@623 111 size_t idx2
universe@623 112 ) {
universe@623 113 /* short circuit */
universe@623 114 if (idx1 == idx2) return;
universe@623 115
universe@623 116 char sbo_mem[CX_ARRAY_SWAP_SBO_SIZE];
universe@623 117 void *tmp;
universe@623 118
universe@623 119 /* decide if we can use the local buffer */
universe@623 120 if (elem_size > CX_ARRAY_SWAP_SBO_SIZE) {
universe@623 121 tmp = malloc(elem_size);
universe@623 122 /* we don't want to enforce error handling */
universe@623 123 if (tmp == NULL) abort();
universe@623 124 } else {
universe@623 125 tmp = sbo_mem;
universe@623 126 }
universe@623 127
universe@623 128 /* calculate memory locations */
universe@623 129 char *left = arr, *right = arr;
universe@623 130 left += idx1 * elem_size;
universe@623 131 right += idx2 * elem_size;
universe@623 132
universe@623 133 /* three-way swap */
universe@623 134 memcpy(tmp, left, elem_size);
universe@623 135 memcpy(left, right, elem_size);
universe@623 136 memcpy(right, tmp, elem_size);
universe@623 137
universe@623 138 /* free dynamic memory, if it was needed */
universe@623 139 if (tmp != sbo_mem) {
universe@623 140 free(tmp);
universe@623 141 }
universe@623 142 }
universe@623 143
universe@607 144 /* HIGH LEVEL ARRAY LIST FUNCTIONS */
universe@607 145
universe@607 146 typedef struct {
universe@607 147 struct cx_list_s base;
universe@607 148 void *data;
universe@610 149 struct cx_array_reallocator_s reallocator;
universe@607 150 } cx_array_list;
universe@607 151
universe@610 152 static void *cx_arl_realloc(
universe@610 153 void *array,
universe@610 154 size_t capacity,
universe@610 155 size_t elem_size,
universe@610 156 struct cx_array_reallocator_s *alloc
universe@610 157 ) {
universe@610 158 /* retrieve the pointer to the list allocator */
universe@610 159 CxAllocator const *al = alloc->ptr1;
universe@610 160
universe@610 161 /* use the list allocator to reallocate the memory */
universe@610 162 return cxRealloc(al, array, capacity * elem_size);
universe@610 163 }
universe@610 164
universe@607 165 static void cx_arl_destructor(struct cx_list_s *list) {
universe@610 166 cx_array_list *arl = (cx_array_list *) list;
universe@607 167 cxFree(list->allocator, arl->data);
universe@607 168 }
universe@607 169
universe@607 170 static int cx_arl_add(
universe@607 171 struct cx_list_s *list,
universe@607 172 void const *elem
universe@607 173 ) {
universe@610 174 cx_array_list *arl = (cx_array_list *) list;
universe@610 175 return cx_array_copy(
universe@610 176 &arl->data,
universe@610 177 &list->size,
universe@610 178 &list->capacity,
universe@610 179 list->size,
universe@610 180 elem,
universe@610 181 list->itemsize,
universe@610 182 1,
universe@610 183 &arl->reallocator
universe@610 184 );
universe@607 185 }
universe@607 186
universe@607 187 static int cx_arl_insert(
universe@607 188 struct cx_list_s *list,
universe@607 189 size_t index,
universe@607 190 void const *elem
universe@607 191 ) {
universe@611 192 if (index > list->size) {
universe@611 193 return 1;
universe@611 194 } else if (index == list->size) {
universe@611 195 return cx_arl_add(list, elem);
universe@611 196 } else {
universe@611 197 cx_array_list *arl = (cx_array_list *) list;
universe@611 198
universe@611 199 /* move elements starting at index to the right */
universe@611 200 if (cx_array_copy(
universe@611 201 &arl->data,
universe@611 202 &list->size,
universe@611 203 &list->capacity,
universe@611 204 index + 1,
universe@611 205 ((char *) arl->data) + index * list->itemsize,
universe@611 206 list->itemsize,
universe@611 207 list->size - index,
universe@611 208 &arl->reallocator
universe@611 209 )) {
universe@611 210 return 1;
universe@611 211 }
universe@611 212
universe@611 213 /* place the element */
universe@611 214 memcpy(((char *) arl->data) + index * list->itemsize,
universe@611 215 elem, list->itemsize);
universe@611 216
universe@611 217 return 0;
universe@611 218 }
universe@607 219 }
universe@607 220
universe@607 221 static int cx_arl_insert_iter(
universe@607 222 struct cx_iterator_s *iter,
universe@607 223 void const *elem,
universe@607 224 int prepend
universe@607 225 ) {
universe@619 226 struct cx_list_s *list = iter->src_handle;
universe@619 227 if (iter->index < list->size) {
universe@619 228 int result = cx_arl_insert(
universe@619 229 list,
universe@619 230 iter->index + 1 - prepend,
universe@619 231 elem
universe@619 232 );
universe@619 233 if (result == 0 && prepend != 0) {
universe@619 234 iter->index++;
universe@619 235 iter->elem_handle = ((char *) iter->elem_handle) + list->itemsize;
universe@619 236 }
universe@619 237 return result;
universe@619 238 } else {
universe@619 239 int result = cx_arl_add(list, elem);
universe@619 240 iter->index = list->size;
universe@619 241 return result;
universe@619 242 }
universe@607 243 }
universe@607 244
universe@607 245 static int cx_arl_remove(
universe@607 246 struct cx_list_s *list,
universe@607 247 size_t index
universe@607 248 ) {
universe@613 249 /* out-of-bounds check */
universe@613 250 if (index >= list->size) {
universe@613 251 return 1;
universe@613 252 }
universe@613 253
universe@624 254 /* short-circuit removal of last element */
universe@624 255 if (index == list->size - 1) {
universe@624 256 list->size--;
universe@624 257 return 0;
universe@624 258 }
universe@613 259
universe@613 260 /* just move the elements starting at index to the left */
universe@624 261 cx_array_list *arl = (cx_array_list *) list;
universe@613 262 int result = cx_array_copy(
universe@613 263 &arl->data,
universe@613 264 &list->size,
universe@613 265 &list->capacity,
universe@613 266 index,
universe@613 267 ((char *) arl->data) + (index + 1) * list->itemsize,
universe@613 268 list->itemsize,
universe@613 269 list->size - index,
universe@613 270 &arl->reallocator
universe@613 271 );
universe@613 272 if (result == 0) {
universe@613 273 /* decrease the size */
universe@613 274 list->size--;
universe@613 275 }
universe@613 276 return result;
universe@607 277 }
universe@607 278
universe@610 279 static void *cx_arl_at(
universe@607 280 struct cx_list_s const *list,
universe@607 281 size_t index
universe@607 282 ) {
universe@610 283 if (index < list->size) {
universe@610 284 cx_array_list const *arl = (cx_array_list const *) list;
universe@610 285 char *space = arl->data;
universe@610 286 return space + index * list->itemsize;
universe@610 287 } else {
universe@610 288 return NULL;
universe@610 289 }
universe@607 290 }
universe@607 291
universe@607 292 static size_t cx_arl_find(
universe@607 293 struct cx_list_s const *list,
universe@607 294 void const *elem
universe@607 295 ) {
universe@614 296 char *cur = ((cx_array_list const *) list)->data;
universe@614 297
universe@614 298 for (size_t i = 0; i < list->size; i++) {
universe@614 299 if (0 == list->cmpfunc(elem, cur)) {
universe@614 300 return i;
universe@614 301 }
universe@614 302 cur += list->itemsize;
universe@614 303 }
universe@614 304
universe@614 305 return list->size;
universe@607 306 }
universe@607 307
universe@607 308 static void cx_arl_sort(struct cx_list_s *list) {
universe@615 309 qsort(((cx_array_list *) list)->data,
universe@615 310 list->size,
universe@615 311 list->itemsize,
universe@615 312 list->cmpfunc
universe@615 313 );
universe@607 314 }
universe@607 315
universe@607 316 static int cx_arl_compare(
universe@607 317 struct cx_list_s const *list,
universe@607 318 struct cx_list_s const *other
universe@607 319 ) {
universe@622 320 if (list->size == other->size) {
universe@622 321 char const *left = ((cx_array_list const *) list)->data;
universe@622 322 char const *right = ((cx_array_list const *) other)->data;
universe@622 323 for (size_t i = 0; i < list->size; i++) {
universe@622 324 int d = list->cmpfunc(left, right);
universe@622 325 if (d != 0) {
universe@622 326 return d;
universe@622 327 }
universe@622 328 left += list->itemsize;
universe@622 329 right += other->itemsize;
universe@622 330 }
universe@622 331 return 0;
universe@622 332 } else {
universe@622 333 return list->size < other->size ? -1 : 1;
universe@622 334 }
universe@607 335 }
universe@607 336
universe@607 337 static void cx_arl_reverse(struct cx_list_s *list) {
universe@623 338 if (list->size < 2) return;
universe@623 339 void *data = ((cx_array_list const *) list)->data;
universe@623 340 size_t half = list->size / 2;
universe@623 341 for (size_t i = 0; i < half; i++) {
universe@623 342 cx_array_swap(data, list->itemsize, i, list->size - 1 - i);
universe@623 343 }
universe@607 344 }
universe@607 345
universe@616 346 static bool cx_arl_iter_valid(struct cx_iterator_s const *iter) {
universe@616 347 struct cx_list_s const *list = iter->src_handle;
universe@616 348 return iter->index < list->size;
universe@616 349 }
universe@616 350
universe@616 351 static void *cx_arl_iter_current(struct cx_iterator_s const *iter) {
universe@616 352 return iter->elem_handle;
universe@616 353 }
universe@616 354
universe@616 355 static void cx_arl_iter_next(struct cx_iterator_s *iter) {
universe@616 356 if (iter->remove) {
universe@616 357 iter->remove = false;
universe@616 358 cx_arl_remove(iter->src_handle, iter->index);
universe@616 359 } else {
universe@616 360 iter->index++;
universe@620 361 iter->elem_handle =
universe@620 362 ((char *) iter->elem_handle)
universe@620 363 + ((struct cx_list_s const *) iter->src_handle)->itemsize;
universe@616 364 }
universe@616 365 }
universe@616 366
universe@607 367 static struct cx_iterator_s cx_arl_iterator(
universe@607 368 struct cx_list_s *list,
universe@607 369 size_t index
universe@607 370 ) {
universe@607 371 struct cx_iterator_s iter;
universe@607 372
universe@616 373 iter.index = index;
universe@616 374 iter.src_handle = list;
universe@616 375 iter.elem_handle = cx_arl_at(list, index);
universe@616 376 iter.valid = cx_arl_iter_valid;
universe@616 377 iter.current = cx_arl_iter_current;
universe@616 378 iter.next = cx_arl_iter_next;
universe@616 379 iter.remove = false;
universe@616 380
universe@607 381 return iter;
universe@607 382 }
universe@607 383
universe@607 384 static cx_list_class cx_array_list_class = {
universe@607 385 cx_arl_destructor,
universe@607 386 cx_arl_add,
universe@607 387 cx_arl_insert,
universe@607 388 cx_arl_insert_iter,
universe@607 389 cx_arl_remove,
universe@607 390 cx_arl_at,
universe@607 391 cx_arl_find,
universe@607 392 cx_arl_sort,
universe@607 393 cx_arl_compare,
universe@607 394 cx_arl_reverse,
universe@607 395 cx_arl_iterator,
universe@607 396 };
universe@607 397
universe@606 398 CxList *cxArrayListCreate(
universe@606 399 CxAllocator const *allocator,
universe@606 400 CxListComparator comparator,
universe@606 401 size_t item_size,
universe@606 402 size_t initial_capacity
universe@606 403 ) {
universe@607 404 cx_array_list *list = cxCalloc(allocator, 1, sizeof(cx_array_list));
universe@607 405 if (list == NULL) return NULL;
universe@607 406
universe@607 407 list->data = cxCalloc(allocator, initial_capacity, item_size);
universe@607 408 if (list->data == NULL) {
universe@607 409 cxFree(allocator, list);
universe@607 410 return NULL;
universe@607 411 }
universe@607 412
universe@607 413 list->base.cl = &cx_array_list_class;
universe@607 414 list->base.allocator = allocator;
universe@607 415 list->base.cmpfunc = comparator;
universe@607 416 list->base.itemsize = item_size;
universe@607 417 list->base.capacity = initial_capacity;
universe@607 418
universe@610 419 /* configure the reallocator */
universe@610 420 list->reallocator.realloc = cx_arl_realloc;
universe@610 421 list->reallocator.ptr1 = (void *) allocator;
universe@610 422
universe@607 423 return (CxList *) list;
universe@606 424 }

mercurial