src/array.c

Mon, 30 Dec 2019 09:52:44 +0100

author
Mike Becker <universe@uap-core.de>
date
Mon, 30 Dec 2019 09:52:44 +0100
changeset 388
871a8ffe6c9d
parent 384
9b81a555c059
permissions
-rw-r--r--

merges closed feature/array branch

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@357 69 return 0;
universe@354 70 }
universe@354 71
universe@355 72 int ucx_array_util_set_a(UcxAllocator* alloc, void** array, size_t* capacity,
universe@369 73 size_t elmsize, size_t index, void* data) {
universe@355 74
universe@355 75 if(!alloc || !capacity || !array) {
universe@355 76 errno = EINVAL;
universe@355 77 return 1;
universe@355 78 }
universe@355 79
universe@355 80 size_t newcapacity = *capacity;
universe@355 81 while(index >= newcapacity) {
universe@355 82 if(ucx_szmul(newcapacity, 2, &newcapacity)) {
universe@355 83 errno = EOVERFLOW;
universe@355 84 return 1;
universe@355 85 }
universe@355 86 }
universe@355 87
universe@355 88 size_t memlen, offset;
universe@355 89 if(ucx_szmul(newcapacity, elmsize, &memlen)) {
universe@355 90 errno = EOVERFLOW;
universe@355 91 return 1;
universe@355 92 }
universe@355 93 /* we don't need to check index*elmsize - it is smaller than memlen */
universe@355 94
universe@355 95
universe@355 96 void* newptr = alrealloc(alloc, *array, memlen);
universe@355 97 if(newptr == NULL) {
universe@355 98 errno = ENOMEM; /* we cannot assume that every allocator sets this */
universe@355 99 return 1;
universe@355 100 }
universe@355 101 *array = newptr;
universe@355 102 *capacity = newcapacity;
universe@355 103
universe@355 104
universe@355 105 char* dest = *array;
universe@355 106 dest += elmsize*index;
universe@369 107 memcpy(dest, data, elmsize);
universe@355 108
universe@355 109 return 0;
universe@355 110 }
universe@355 111
universe@369 112 int ucx_array_util_setptr_a(UcxAllocator* alloc, void** array, size_t* capacity,
universe@369 113 size_t index, void* data) {
universe@369 114
universe@369 115 return ucx_array_util_set_a(alloc, array, capacity, sizeof(void*),
universe@369 116 index, &data);
universe@369 117 }
universe@369 118
universe@356 119 UcxArray* ucx_array_new(size_t capacity, size_t elemsize) {
universe@334 120 return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
universe@125 121 }
universe@125 122
universe@356 123 UcxArray* ucx_array_new_a(size_t capacity, size_t elemsize,
universe@334 124 UcxAllocator* allocator) {
universe@356 125 UcxArray* array = almalloc(allocator, sizeof(UcxArray));
universe@356 126 if(array) {
universe@356 127 ucx_array_init_a(array, capacity, elemsize, allocator);
universe@336 128 }
universe@334 129 return array;
universe@18 130 }
universe@18 131
universe@356 132 void ucx_array_init(UcxArray* array, size_t capacity, size_t elemsize) {
universe@356 133 ucx_array_init_a(array, capacity, elemsize, ucx_default_allocator());
universe@356 134 }
universe@356 135
universe@356 136 void ucx_array_init_a(UcxArray* array, size_t capacity, size_t elemsize,
universe@356 137 UcxAllocator* allocator) {
universe@18 138
universe@356 139 array->allocator = allocator;
universe@356 140 array->elemsize = elemsize;
universe@356 141 array->size = 0;
universe@356 142 array->data = alcalloc(allocator, capacity, elemsize);
universe@336 143
universe@356 144 if (array->data) {
universe@356 145 array->capacity = capacity;
universe@336 146 } else {
universe@356 147 array->capacity = 0;
universe@356 148 }
universe@356 149 }
universe@356 150
universe@356 151 int ucx_array_clone(UcxArray* dest, UcxArray const* src) {
universe@356 152 if (ucx_array_ensurecap(dest, src->capacity)) {
universe@356 153 return 1;
universe@336 154 }
universe@336 155
universe@356 156 dest->elemsize = src->elemsize;
universe@356 157 dest->size = src->size;
universe@356 158
universe@356 159 if (dest->data) {
universe@356 160 memcpy(dest->data, src->data, src->size*src->elemsize);
universe@356 161 }
universe@356 162
universe@356 163 return 0;
universe@18 164 }
universe@18 165
universe@356 166 int ucx_array_equals(UcxArray const *array1, UcxArray const *array2,
universe@334 167 cmp_func cmpfnc, void* data) {
universe@334 168
universe@356 169 if (array1->size != array2->size || array1->elemsize != array2->elemsize) {
universe@336 170 return 0;
universe@336 171 } else {
universe@356 172 if (array1->size == 0)
universe@336 173 return 1;
universe@336 174
universe@356 175 size_t elemsize;
universe@336 176 if (cmpfnc == NULL) {
universe@336 177 cmpfnc = ucx_cmp_mem;
universe@356 178 elemsize = array1->elemsize;
universe@356 179 data = &elemsize;
universe@336 180 }
universe@336 181
universe@356 182 for (size_t i = 0 ; i < array1->size ; i++) {
universe@336 183 int r = cmpfnc(
universe@336 184 ucx_array_at(array1, i),
universe@336 185 ucx_array_at(array2, i),
universe@336 186 data);
universe@336 187 if (r != 0)
universe@336 188 return 0;
universe@336 189 }
universe@336 190 return 1;
universe@336 191 }
universe@125 192 }
universe@125 193
universe@353 194 void ucx_array_destroy(UcxArray *array) {
universe@356 195 if(array->data)
universe@356 196 alfree(array->allocator, array->data);
universe@336 197 array->data = NULL;
universe@336 198 array->capacity = array->size = 0;
universe@8 199 }
universe@8 200
universe@356 201 void ucx_array_free(UcxArray *array) {
universe@356 202 ucx_array_destroy(array);
universe@356 203 alfree(array->allocator, array);
universe@356 204 }
universe@356 205
universe@354 206 int ucx_array_append_from(UcxArray *array, void *data, size_t count) {
universe@354 207 if (ucx_array_ensurecap(array, array->size + count))
universe@354 208 return 1;
universe@354 209
universe@356 210 void* dest = ucx_array_at(array, array->size);
universe@354 211 if (data) {
universe@354 212 memcpy(dest, data, array->elemsize*count);
universe@354 213 } else {
universe@354 214 memset(dest, 0, array->elemsize*count);
universe@354 215 }
universe@354 216 array->size += count;
universe@354 217
universe@354 218 return 0;
universe@354 219 }
universe@354 220
universe@354 221 int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) {
universe@354 222 if (ucx_array_ensurecap(array, array->size + count))
universe@354 223 return 1;
universe@354 224
universe@354 225 if (array->size > 0) {
universe@356 226 void *dest = ucx_array_at(array, count);
universe@354 227 memmove(dest, array->data, array->elemsize*array->size);
universe@336 228 }
universe@336 229
universe@336 230 if (data) {
universe@354 231 memcpy(array->data, data, array->elemsize*count);
universe@336 232 } else {
universe@354 233 memset(array->data, 0, array->elemsize*count);
universe@354 234 }
universe@354 235 array->size += count;
universe@354 236
universe@354 237 return 0;
universe@354 238 }
universe@354 239
universe@354 240 int ucx_array_set_from(UcxArray *array, size_t index,
universe@354 241 void *data, size_t count) {
universe@354 242 if (ucx_array_ensurecap(array, index + count))
universe@354 243 return 1;
universe@354 244
universe@354 245 if (index+count > array->size) {
universe@354 246 array->size = index+count;
universe@354 247 }
universe@354 248
universe@356 249 void *dest = ucx_array_at(array, index);
universe@354 250 if (data) {
universe@354 251 memcpy(dest, data, array->elemsize*count);
universe@354 252 } else {
universe@354 253 memset(dest, 0, array->elemsize*count);
universe@336 254 }
universe@336 255
universe@336 256 return 0;
universe@211 257 }
universe@211 258
universe@334 259 int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
universe@336 260
universe@336 261 if (array1->elemsize != array2->elemsize)
universe@336 262 return 1;
universe@336 263
universe@336 264 size_t capacity = array1->capacity+array2->capacity;
universe@336 265
universe@336 266 if (array1->capacity < capacity) {
universe@336 267 if (ucx_array_reserve(array1, capacity)) {
universe@336 268 return 1;
universe@336 269 }
universe@336 270 }
universe@336 271
universe@356 272 void* dest = ucx_array_at(array1, array1->size);
universe@336 273 memcpy(dest, array2->data, array2->size*array2->elemsize);
universe@336 274
universe@336 275 array1->size += array2->size;
universe@336 276
universe@336 277 return 0;
universe@7 278 }
universe@7 279
universe@356 280 void *ucx_array_at(UcxArray const *array, size_t index) {
universe@356 281 char* memory = array->data;
universe@356 282 char* loc = memory + index*array->elemsize;
universe@336 283 return loc;
universe@125 284 }
universe@125 285
universe@356 286 size_t ucx_array_find(UcxArray const *array, void *elem,
universe@356 287 cmp_func cmpfnc, void *data) {
universe@7 288
universe@356 289 size_t elemsize;
universe@336 290 if (cmpfnc == NULL) {
universe@336 291 cmpfnc = ucx_cmp_mem;
universe@356 292 elemsize = array->elemsize;
universe@356 293 data = &elemsize;
universe@336 294 }
universe@336 295
universe@356 296 if (array->size > 0) {
universe@356 297 for (size_t i = 0 ; i < array->size ; i++) {
universe@336 298 void* ptr = ucx_array_at(array, i);
universe@336 299 if (cmpfnc(ptr, elem, data) == 0) {
universe@336 300 return i;
universe@336 301 }
universe@336 302 }
universe@356 303 return array->size;
universe@336 304 } else {
universe@336 305 return 0;
universe@336 306 }
universe@7 307 }
universe@7 308
universe@356 309 int ucx_array_contains(UcxArray const *array, void *elem,
universe@356 310 cmp_func cmpfnc, void *data) {
universe@356 311 return ucx_array_find(array, elem, cmpfnc, data) != array->size;
universe@7 312 }
universe@7 313
universe@345 314 static void ucx_mergesort_merge(void *arrdata,size_t elemsize,
universe@345 315 cmp_func cmpfnc, void *data,
universe@336 316 size_t start, size_t mid, size_t end) {
universe@336 317
universe@345 318 char* array = arrdata;
universe@345 319
universe@336 320 size_t rightstart = mid + 1;
universe@336 321
universe@345 322 if (cmpfnc(array + mid*elemsize,
universe@345 323 array + rightstart*elemsize, data) <= 0) {
universe@336 324 /* already sorted */
universe@336 325 return;
universe@336 326 }
universe@336 327
universe@342 328 /* we need memory for one element */
universe@345 329 void *value = malloc(elemsize);
universe@336 330
universe@336 331 while (start <= mid && rightstart <= end) {
universe@345 332 if (cmpfnc(array + start*elemsize,
universe@345 333 array + rightstart*elemsize, data) <= 0) {
universe@336 334 start++;
universe@336 335 } else {
universe@342 336 /* save the value from the right */
universe@345 337 memcpy(value, array + rightstart*elemsize, elemsize);
universe@336 338
universe@342 339 /* shift all left elements one element to the right */
universe@336 340 size_t shiftcount = rightstart-start;
universe@345 341 void *startptr = array + start*elemsize;
universe@345 342 void *dest = array + (start+1)*elemsize;
universe@345 343 memmove(dest, startptr, shiftcount*elemsize);
universe@336 344
universe@342 345 /* bring the first value from the right to the left */
universe@345 346 memcpy(startptr, value, elemsize);
universe@336 347
universe@336 348 start++;
universe@336 349 mid++;
universe@336 350 rightstart++;
universe@336 351 }
universe@336 352 }
universe@336 353
universe@342 354 /* free the temporary memory */
universe@336 355 free(value);
universe@336 356 }
universe@336 357
universe@345 358 static void ucx_mergesort_impl(void *arrdata, size_t elemsize,
universe@345 359 cmp_func cmpfnc, void *data, size_t l, size_t r) {
universe@336 360 if (l < r) {
universe@336 361 size_t m = l + (r - l) / 2;
universe@336 362
universe@345 363 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m);
universe@345 364 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r);
universe@345 365 ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r);
universe@336 366 }
universe@336 367 }
universe@336 368
universe@345 369 static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize,
universe@345 370 cmp_func cmpfnc, void *data) {
universe@345 371
universe@345 372 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1);
universe@345 373 }
universe@345 374
universe@345 375 #ifdef USE_UCX_QSORT_R
universe@345 376 struct cmpfnc_swapargs_info {
universe@345 377 cmp_func func;
universe@345 378 void *data;
universe@345 379 };
universe@345 380
universe@345 381 static int cmp_func_swap_args(void *data, const void *x, const void *y) {
olaf@384 382 struct cmpfnc_swapargs_info* info = data;
universe@345 383 return info->func(x, y, info->data);
universe@345 384 }
universe@345 385
universe@345 386 static void ucx_qsort_r(void *array, size_t count, size_t elemsize,
universe@345 387 cmp_func cmpfnc, void *data) {
universe@345 388 struct cmpfnc_swapargs_info info;
universe@345 389 info.func = cmpfnc;
universe@345 390 info.data = data;
universe@345 391 qsort_r(array, count, elemsize, &info, cmp_func_swap_args);
universe@345 392 }
universe@345 393 #endif /* USE_UCX_QSORT_R */
universe@345 394
universe@356 395 void ucx_array_sort(UcxArray* array, cmp_func cmpfnc, void *data) {
universe@356 396 ucx_array_sort_impl(array->data, array->size, array->elemsize,
universe@356 397 cmpfnc, data);
universe@7 398 }
universe@7 399
universe@334 400 void ucx_array_remove(UcxArray *array, size_t index) {
universe@336 401 array->size--;
universe@336 402 if (index < array->size) {
universe@356 403 void* dest = ucx_array_at(array, index);
universe@356 404 void* src = ucx_array_at(array, index+1);
universe@336 405 memmove(dest, src, (array->size - index)*array->elemsize);
universe@336 406 }
universe@123 407 }
universe@123 408
universe@334 409 void ucx_array_remove_fast(UcxArray *array, size_t index) {
universe@336 410 array->size--;
universe@336 411 if (index < array->size) {
universe@356 412 void* dest = ucx_array_at(array, index);
universe@356 413 void* src = ucx_array_at(array, array->size);
universe@336 414 memcpy(dest, src, array->elemsize);
universe@336 415 }
universe@7 416 }
universe@7 417
universe@334 418 int ucx_array_shrink(UcxArray* array) {
universe@336 419 void* newptr = alrealloc(array->allocator, array->data,
universe@336 420 array->size*array->elemsize);
universe@336 421 if (newptr) {
universe@336 422 array->data = newptr;
universe@336 423 array->capacity = array->size;
universe@336 424 return 0;
universe@336 425 } else {
universe@336 426 return 1;
universe@336 427 }
universe@123 428 }
universe@123 429
universe@334 430 int ucx_array_resize(UcxArray* array, size_t capacity) {
universe@336 431 if (array->capacity >= capacity) {
universe@336 432 void* newptr = alrealloc(array->allocator, array->data,
universe@336 433 capacity*array->elemsize);
universe@336 434 if (newptr) {
universe@336 435 array->data = newptr;
universe@336 436 array->capacity = capacity;
universe@336 437 if (array->size > array->capacity) {
universe@336 438 array->size = array->capacity;
universe@336 439 }
universe@336 440 return 0;
universe@336 441 } else {
universe@336 442 return 1;
universe@336 443 }
universe@336 444 } else {
universe@336 445 return ucx_array_reserve(array, capacity);
universe@336 446 }
universe@87 447 }
universe@87 448
universe@334 449 int ucx_array_reserve(UcxArray* array, size_t capacity) {
universe@336 450 if (array->capacity > capacity) {
universe@336 451 return 0;
universe@336 452 } else {
universe@336 453 void* newptr = alrealloc(array->allocator, array->data,
universe@336 454 capacity*array->elemsize);
universe@336 455 if (newptr) {
universe@336 456 array->data = newptr;
universe@336 457 array->capacity = capacity;
universe@336 458 return 0;
universe@336 459 } else {
universe@336 460 return 1;
universe@336 461 }
universe@336 462 }
universe@7 463 }
universe@369 464
universe@369 465 int ucx_array_grow(UcxArray* array, size_t count) {
universe@369 466 return ucx_array_reserve(array, array->size+count);
universe@369 467 }

mercurial