src/array.c

Tue, 24 Sep 2019 20:16:00 +0200

author
Mike Becker <universe@uap-core.de>
date
Tue, 24 Sep 2019 20:16:00 +0200
branch
feature/array
changeset 355
d315a068235a
parent 354
7fd13b9f8f60
child 356
77efe51c6c9a
permissions
-rw-r--r--

adds array utility functions for user defined arrays

universe@103 1 /*
universe@103 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
universe@103 3 *
universe@334 4 * Copyright 2019 Mike Becker, Olaf Wintermann All rights reserved.
universe@103 5 *
universe@103 6 * Redistribution and use in source and binary forms, with or without
universe@103 7 * modification, are permitted provided that the following conditions are met:
universe@103 8 *
universe@103 9 * 1. Redistributions of source code must retain the above copyright
universe@103 10 * notice, this list of conditions and the following disclaimer.
universe@103 11 *
universe@103 12 * 2. Redistributions in binary form must reproduce the above copyright
universe@103 13 * notice, this list of conditions and the following disclaimer in the
universe@103 14 * documentation and/or other materials provided with the distribution.
universe@103 15 *
universe@103 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
universe@103 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
universe@103 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
universe@103 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
universe@103 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
universe@103 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
universe@103 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
universe@103 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
universe@103 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
universe@103 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
universe@103 26 * POSSIBILITY OF SUCH DAMAGE.
universe@103 27 */
universe@103 28
universe@345 29 #define _GNU_SOURCE /* we want to use qsort_r(), if available */
olaf@348 30 #define __STDC_WANT_LIB_EXT1__ 1 /* use qsort_s, if available */
olaf@348 31
universe@345 32
universe@334 33 #include "ucx/array.h"
universe@336 34 #include "ucx/utils.h"
universe@4 35
universe@336 36 #include <string.h>
universe@345 37 #include <stdlib.h>
universe@355 38 #include <errno.h>
universe@345 39
universe@345 40 #ifndef UCX_ARRAY_DISABLE_QSORT
universe@346 41 #ifdef __GLIBC__
universe@345 42 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
universe@345 43 #define ucx_array_sort_impl qsort_r
universe@345 44 #endif /* glibc version >= 2.8 */
universe@346 45 #elif /* not __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__)
universe@345 46 #define ucx_array_sort_impl ucx_qsort_r
universe@345 47 #define USE_UCX_QSORT_R
olaf@348 48 #elif /* not (__APPLE || __FreeBSD__) */ defined(__sun)
olaf@348 49 #if __STDC_VERSION__ >= 201112L
olaf@348 50 #define ucx_array_sort_impl qsort_s
olaf@348 51 #endif
olaf@348 52 #endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */
universe@345 53 #endif /* UCX_ARRAY_DISABLE_QSORT */
universe@345 54
universe@345 55 #ifndef ucx_array_sort_impl
universe@345 56 #define ucx_array_sort_impl ucx_mergesort
universe@345 57 #endif
universe@334 58
universe@354 59 static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) {
universe@354 60 size_t required_capacity = array->capacity;
universe@354 61 while (reqcap > required_capacity) {
universe@354 62 if (required_capacity * 2 < required_capacity)
universe@354 63 return 1;
universe@354 64 required_capacity <<= 1;
universe@354 65 }
universe@354 66 if (ucx_array_reserve(array, required_capacity)) {
universe@354 67 return 1;
universe@354 68 }
universe@354 69 }
universe@354 70
universe@355 71 int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
universe@355 72 size_t elmsize, size_t index, ...) {
universe@355 73
universe@355 74 if(!alloc || !capacity || !array) {
universe@355 75 errno = EINVAL;
universe@355 76 return 1;
universe@355 77 }
universe@355 78
universe@355 79 size_t newcapacity = *capacity;
universe@355 80 while(index >= newcapacity) {
universe@355 81 if(ucx_szmul(newcapacity, 2, &newcapacity)) {
universe@355 82 errno = EOVERFLOW;
universe@355 83 return 1;
universe@355 84 }
universe@355 85 }
universe@355 86
universe@355 87 size_t memlen, offset;
universe@355 88 if(ucx_szmul(newcapacity, elmsize, &memlen)) {
universe@355 89 errno = EOVERFLOW;
universe@355 90 return 1;
universe@355 91 }
universe@355 92 /* we don't need to check index*elmsize - it is smaller than memlen */
universe@355 93
universe@355 94
universe@355 95 void* newptr = alrealloc(alloc, *array, memlen);
universe@355 96 if(newptr == NULL) {
universe@355 97 errno = ENOMEM; /* we cannot assume that every allocator sets this */
universe@355 98 return 1;
universe@355 99 }
universe@355 100 *array = newptr;
universe@355 101 *capacity = newcapacity;
universe@355 102
universe@355 103
universe@355 104 char* dest = *array;
universe@355 105 dest += elmsize*index;
universe@355 106
universe@355 107 va_list ap;
universe@355 108 va_start(ap, index);
universe@355 109 int elem = va_arg(ap, int);
universe@355 110 memcpy(dest, &elem, elmsize);
universe@355 111 va_end(ap);
universe@355 112
universe@355 113 return 0;
universe@355 114 }
universe@355 115
universe@334 116 UcxArray ucx_array_new(size_t capacity, size_t elemsize) {
universe@334 117 return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
universe@125 118 }
universe@125 119
universe@334 120 UcxArray ucx_array_new_a(size_t capacity, size_t elemsize,
universe@334 121 UcxAllocator* allocator) {
universe@334 122 UcxArray array;
universe@334 123
universe@336 124 array.allocator = allocator;
universe@336 125 array.elemsize = elemsize;
universe@336 126 array.size = 0;
universe@336 127 array.data = alcalloc(allocator, capacity, elemsize);
universe@336 128
universe@336 129 if (array.data) {
universe@336 130 array.capacity = capacity;
universe@336 131 } else {
universe@336 132 array.capacity = 0;
universe@336 133 }
universe@336 134
universe@334 135 return array;
universe@18 136 }
universe@18 137
universe@334 138 UcxArray ucx_array_clone(UcxArray array) {
universe@334 139 UcxArray clone;
universe@18 140
universe@336 141 clone.allocator = array.allocator;
universe@336 142 clone.elemsize = array.elemsize;
universe@336 143 clone.size = array.size;
universe@336 144 clone.data = alcalloc(array.allocator, array.capacity, array.elemsize);
universe@336 145
universe@336 146 if (clone.data) {
universe@336 147 clone.capacity = array.capacity;
universe@336 148 memcpy(clone.data, array.data, array.size*array.elemsize);
universe@336 149 } else {
universe@336 150 clone.capacity = clone.size = 0;
universe@336 151 }
universe@336 152
universe@334 153 return clone;
universe@18 154 }
universe@18 155
universe@334 156 int ucx_array_equals(UcxArray array1, UcxArray array2,
universe@334 157 cmp_func cmpfnc, void* data) {
universe@334 158
universe@336 159 if (array1.size != array2.size || array1.elemsize != array2.elemsize) {
universe@336 160 return 0;
universe@336 161 } else {
universe@336 162 if (array1.size == 0)
universe@336 163 return 1;
universe@336 164
universe@336 165 if (cmpfnc == NULL) {
universe@336 166 cmpfnc = ucx_cmp_mem;
universe@336 167 data = &array1.elemsize;
universe@336 168 }
universe@336 169
universe@336 170 for (size_t i = 0 ; i < array1.size ; i++) {
universe@336 171 int r = cmpfnc(
universe@336 172 ucx_array_at(array1, i),
universe@336 173 ucx_array_at(array2, i),
universe@336 174 data);
universe@336 175 if (r != 0)
universe@336 176 return 0;
universe@336 177 }
universe@336 178 return 1;
universe@336 179 }
universe@125 180 }
universe@125 181
universe@353 182 void ucx_array_destroy(UcxArray *array) {
universe@336 183 alfree(array->allocator, array->data);
universe@336 184 array->data = NULL;
universe@336 185 array->capacity = array->size = 0;
universe@8 186 }
universe@8 187
universe@354 188 int ucx_array_append_from(UcxArray *array, void *data, size_t count) {
universe@354 189 if (ucx_array_ensurecap(array, array->size + count))
universe@354 190 return 1;
universe@354 191
universe@354 192 void* dest = ucx_array_at(*array, array->size);
universe@354 193 if (data) {
universe@354 194 memcpy(dest, data, array->elemsize*count);
universe@354 195 } else {
universe@354 196 memset(dest, 0, array->elemsize*count);
universe@354 197 }
universe@354 198 array->size += count;
universe@354 199
universe@354 200 return 0;
universe@354 201 }
universe@354 202
universe@354 203 int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) {
universe@354 204 if (ucx_array_ensurecap(array, array->size + count))
universe@354 205 return 1;
universe@354 206
universe@354 207 if (array->size > 0) {
universe@354 208 void *dest = ucx_array_at(*array, count);
universe@354 209 memmove(dest, array->data, array->elemsize*array->size);
universe@336 210 }
universe@336 211
universe@336 212 if (data) {
universe@354 213 memcpy(array->data, data, array->elemsize*count);
universe@336 214 } else {
universe@354 215 memset(array->data, 0, array->elemsize*count);
universe@354 216 }
universe@354 217 array->size += count;
universe@354 218
universe@354 219 return 0;
universe@354 220 }
universe@354 221
universe@354 222 int ucx_array_set_from(UcxArray *array, size_t index,
universe@354 223 void *data, size_t count) {
universe@354 224 if (ucx_array_ensurecap(array, index + count))
universe@354 225 return 1;
universe@354 226
universe@354 227 if (index+count > array->size) {
universe@354 228 array->size = index+count;
universe@354 229 }
universe@354 230
universe@354 231 void *dest = ucx_array_at(*array, index);
universe@354 232 if (data) {
universe@354 233 memcpy(dest, data, array->elemsize*count);
universe@354 234 } else {
universe@354 235 memset(dest, 0, array->elemsize*count);
universe@336 236 }
universe@336 237
universe@336 238 return 0;
universe@211 239 }
universe@211 240
universe@354 241 int ucx_array_appendv(UcxArray *array, ...) {
universe@354 242 va_list ap;
universe@354 243 va_start(ap, array);
universe@354 244 int elem = va_arg(ap, int);
universe@354 245 int ret = ucx_array_append_from(array, &elem, 1);
universe@354 246 va_end(ap);
universe@354 247 return ret;
universe@125 248 }
universe@125 249
universe@354 250 int ucx_array_prependv(UcxArray *array, ...) {
universe@354 251 va_list ap;
universe@354 252 va_start(ap, array);
universe@354 253 int elem = va_arg(ap, int);
universe@354 254 int ret = ucx_array_prepend_from(array, &elem, 1);
universe@354 255 va_end(ap);
universe@354 256 return ret;
universe@354 257 }
universe@354 258
universe@354 259 int ucx_array_setv(UcxArray *array, size_t index, ...) {
universe@354 260 va_list ap;
universe@354 261 va_start(ap, index);
universe@354 262 int elem = va_arg(ap, int);
universe@354 263 int ret = ucx_array_set_from(array, index, &elem, 1);
universe@354 264 va_end(ap);
universe@354 265 return ret;
universe@337 266 }
universe@337 267
universe@334 268 int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
universe@336 269
universe@336 270 if (array1->elemsize != array2->elemsize)
universe@336 271 return 1;
universe@336 272
universe@336 273 size_t capacity = array1->capacity+array2->capacity;
universe@336 274
universe@336 275 if (array1->capacity < capacity) {
universe@336 276 if (ucx_array_reserve(array1, capacity)) {
universe@336 277 return 1;
universe@336 278 }
universe@336 279 }
universe@336 280
universe@336 281 void* dest = ucx_array_at(*array1, array1->size);
universe@336 282 memcpy(dest, array2->data, array2->size*array2->elemsize);
universe@336 283
universe@336 284 array1->size += array2->size;
universe@336 285
universe@336 286 return 0;
universe@7 287 }
universe@7 288
universe@334 289 void *ucx_array_at(UcxArray array, size_t index) {
universe@336 290 char* memory = array.data;
universe@336 291 char* loc = memory + index*array.elemsize;
universe@336 292 return loc;
universe@125 293 }
universe@125 294
universe@334 295 size_t ucx_array_find(UcxArray array, void *elem, cmp_func cmpfnc, void *data) {
universe@7 296
universe@336 297 if (cmpfnc == NULL) {
universe@336 298 cmpfnc = ucx_cmp_mem;
universe@336 299 data = &array.elemsize;
universe@336 300 }
universe@336 301
universe@336 302 if (array.size > 0) {
universe@336 303 for (size_t i = 0 ; i < array.size ; i++) {
universe@336 304 void* ptr = ucx_array_at(array, i);
universe@336 305 if (cmpfnc(ptr, elem, data) == 0) {
universe@336 306 return i;
universe@336 307 }
universe@336 308 }
universe@336 309 return array.size;
universe@336 310 } else {
universe@336 311 return 0;
universe@336 312 }
universe@7 313 }
universe@7 314
universe@334 315 int ucx_array_contains(UcxArray array, void *elem, cmp_func cmpfnc, void *data) {
universe@334 316 return ucx_array_find(array, elem, cmpfnc, data) != array.size;
universe@7 317 }
universe@7 318
universe@345 319 static void ucx_mergesort_merge(void *arrdata,size_t elemsize,
universe@345 320 cmp_func cmpfnc, void *data,
universe@336 321 size_t start, size_t mid, size_t end) {
universe@336 322
universe@345 323 char* array = arrdata;
universe@345 324
universe@336 325 size_t rightstart = mid + 1;
universe@336 326
universe@345 327 if (cmpfnc(array + mid*elemsize,
universe@345 328 array + rightstart*elemsize, data) <= 0) {
universe@336 329 /* already sorted */
universe@336 330 return;
universe@336 331 }
universe@336 332
universe@342 333 /* we need memory for one element */
universe@345 334 void *value = malloc(elemsize);
universe@336 335
universe@336 336 while (start <= mid && rightstart <= end) {
universe@345 337 if (cmpfnc(array + start*elemsize,
universe@345 338 array + rightstart*elemsize, data) <= 0) {
universe@336 339 start++;
universe@336 340 } else {
universe@342 341 /* save the value from the right */
universe@345 342 memcpy(value, array + rightstart*elemsize, elemsize);
universe@336 343
universe@342 344 /* shift all left elements one element to the right */
universe@336 345 size_t shiftcount = rightstart-start;
universe@345 346 void *startptr = array + start*elemsize;
universe@345 347 void *dest = array + (start+1)*elemsize;
universe@345 348 memmove(dest, startptr, shiftcount*elemsize);
universe@336 349
universe@342 350 /* bring the first value from the right to the left */
universe@345 351 memcpy(startptr, value, elemsize);
universe@336 352
universe@336 353 start++;
universe@336 354 mid++;
universe@336 355 rightstart++;
universe@336 356 }
universe@336 357 }
universe@336 358
universe@342 359 /* free the temporary memory */
universe@336 360 free(value);
universe@336 361 }
universe@336 362
universe@345 363 static void ucx_mergesort_impl(void *arrdata, size_t elemsize,
universe@345 364 cmp_func cmpfnc, void *data, size_t l, size_t r) {
universe@336 365 if (l < r) {
universe@336 366 size_t m = l + (r - l) / 2;
universe@336 367
universe@345 368 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m);
universe@345 369 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r);
universe@345 370 ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r);
universe@336 371 }
universe@336 372 }
universe@336 373
universe@345 374 static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize,
universe@345 375 cmp_func cmpfnc, void *data) {
universe@345 376
universe@345 377 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1);
universe@345 378 }
universe@345 379
universe@345 380 #ifdef USE_UCX_QSORT_R
universe@345 381 struct cmpfnc_swapargs_info {
universe@345 382 cmp_func func;
universe@345 383 void *data;
universe@345 384 };
universe@345 385
universe@345 386 static int cmp_func_swap_args(void *data, const void *x, const void *y) {
olaf@348 387 cmpfnc_swapargs_info* info = data;
universe@345 388 return info->func(x, y, info->data);
universe@345 389 }
universe@345 390
universe@345 391 static void ucx_qsort_r(void *array, size_t count, size_t elemsize,
universe@345 392 cmp_func cmpfnc, void *data) {
universe@345 393 struct cmpfnc_swapargs_info info;
universe@345 394 info.func = cmpfnc;
universe@345 395 info.data = data;
universe@345 396 qsort_r(array, count, elemsize, &info, cmp_func_swap_args);
universe@345 397 }
universe@345 398 #endif /* USE_UCX_QSORT_R */
universe@345 399
universe@345 400 void ucx_array_sort(UcxArray array, cmp_func cmpfnc, void *data) {
universe@345 401 ucx_array_sort_impl(array.data, array.size, array.elemsize, cmpfnc, data);
universe@7 402 }
universe@7 403
universe@334 404 void ucx_array_remove(UcxArray *array, size_t index) {
universe@336 405 array->size--;
universe@336 406 if (index < array->size) {
universe@336 407 void* dest = ucx_array_at(*array, index);
universe@336 408 void* src = ucx_array_at(*array, index+1);
universe@336 409 memmove(dest, src, (array->size - index)*array->elemsize);
universe@336 410 }
universe@123 411 }
universe@123 412
universe@334 413 void ucx_array_remove_fast(UcxArray *array, size_t index) {
universe@336 414 array->size--;
universe@336 415 if (index < array->size) {
universe@336 416 void* dest = ucx_array_at(*array, index);
universe@336 417 void* src = ucx_array_at(*array, array->size);
universe@336 418 memcpy(dest, src, array->elemsize);
universe@336 419 }
universe@7 420 }
universe@7 421
universe@334 422 int ucx_array_shrink(UcxArray* array) {
universe@336 423 void* newptr = alrealloc(array->allocator, array->data,
universe@336 424 array->size*array->elemsize);
universe@336 425 if (newptr) {
universe@336 426 array->data = newptr;
universe@336 427 array->capacity = array->size;
universe@336 428 return 0;
universe@336 429 } else {
universe@336 430 return 1;
universe@336 431 }
universe@123 432 }
universe@123 433
universe@334 434 int ucx_array_resize(UcxArray* array, size_t capacity) {
universe@336 435 if (array->capacity >= capacity) {
universe@336 436 void* newptr = alrealloc(array->allocator, array->data,
universe@336 437 capacity*array->elemsize);
universe@336 438 if (newptr) {
universe@336 439 array->data = newptr;
universe@336 440 array->capacity = capacity;
universe@336 441 if (array->size > array->capacity) {
universe@336 442 array->size = array->capacity;
universe@336 443 }
universe@336 444 return 0;
universe@336 445 } else {
universe@336 446 return 1;
universe@336 447 }
universe@336 448 } else {
universe@336 449 return ucx_array_reserve(array, capacity);
universe@336 450 }
universe@87 451 }
universe@87 452
universe@334 453 int ucx_array_reserve(UcxArray* array, size_t capacity) {
universe@336 454 if (array->capacity > capacity) {
universe@336 455 return 0;
universe@336 456 } else {
universe@336 457 void* newptr = alrealloc(array->allocator, array->data,
universe@336 458 capacity*array->elemsize);
universe@336 459 if (newptr) {
universe@336 460 array->data = newptr;
universe@336 461 array->capacity = capacity;
universe@336 462 return 0;
universe@336 463 } else {
universe@336 464 return 1;
universe@336 465 }
universe@336 466 }
universe@7 467 }

mercurial