src/array.c

Sat, 10 Aug 2019 11:12:49 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 10 Aug 2019 11:12:49 +0200
branch
feature/array
changeset 354
7fd13b9f8f60
parent 353
135ce35d5108
child 355
d315a068235a
permissions
-rw-r--r--

improves array append/prepend/set interface

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@345 38
universe@345 39 #ifndef UCX_ARRAY_DISABLE_QSORT
universe@346 40 #ifdef __GLIBC__
universe@345 41 #if __GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 8)
universe@345 42 #define ucx_array_sort_impl qsort_r
universe@345 43 #endif /* glibc version >= 2.8 */
universe@346 44 #elif /* not __GLIBC__ */ defined(__APPLE__) || defined(__FreeBSD__)
universe@345 45 #define ucx_array_sort_impl ucx_qsort_r
universe@345 46 #define USE_UCX_QSORT_R
olaf@348 47 #elif /* not (__APPLE || __FreeBSD__) */ defined(__sun)
olaf@348 48 #if __STDC_VERSION__ >= 201112L
olaf@348 49 #define ucx_array_sort_impl qsort_s
olaf@348 50 #endif
olaf@348 51 #endif /* __GLIBC__, __APLE__, __FreeBSD__, __sun */
universe@345 52 #endif /* UCX_ARRAY_DISABLE_QSORT */
universe@345 53
universe@345 54 #ifndef ucx_array_sort_impl
universe@345 55 #define ucx_array_sort_impl ucx_mergesort
universe@345 56 #endif
universe@334 57
universe@354 58 static int ucx_array_ensurecap(UcxArray *array, size_t reqcap) {
universe@354 59 size_t required_capacity = array->capacity;
universe@354 60 while (reqcap > required_capacity) {
universe@354 61 if (required_capacity * 2 < required_capacity)
universe@354 62 return 1;
universe@354 63 required_capacity <<= 1;
universe@354 64 }
universe@354 65 if (ucx_array_reserve(array, required_capacity)) {
universe@354 66 return 1;
universe@354 67 }
universe@354 68 }
universe@354 69
universe@334 70 UcxArray ucx_array_new(size_t capacity, size_t elemsize) {
universe@334 71 return ucx_array_new_a(capacity, elemsize, ucx_default_allocator());
universe@125 72 }
universe@125 73
universe@334 74 UcxArray ucx_array_new_a(size_t capacity, size_t elemsize,
universe@334 75 UcxAllocator* allocator) {
universe@334 76 UcxArray array;
universe@334 77
universe@336 78 array.allocator = allocator;
universe@336 79 array.elemsize = elemsize;
universe@336 80 array.size = 0;
universe@336 81 array.data = alcalloc(allocator, capacity, elemsize);
universe@336 82
universe@336 83 if (array.data) {
universe@336 84 array.capacity = capacity;
universe@336 85 } else {
universe@336 86 array.capacity = 0;
universe@336 87 }
universe@336 88
universe@334 89 return array;
universe@18 90 }
universe@18 91
universe@334 92 UcxArray ucx_array_clone(UcxArray array) {
universe@334 93 UcxArray clone;
universe@18 94
universe@336 95 clone.allocator = array.allocator;
universe@336 96 clone.elemsize = array.elemsize;
universe@336 97 clone.size = array.size;
universe@336 98 clone.data = alcalloc(array.allocator, array.capacity, array.elemsize);
universe@336 99
universe@336 100 if (clone.data) {
universe@336 101 clone.capacity = array.capacity;
universe@336 102 memcpy(clone.data, array.data, array.size*array.elemsize);
universe@336 103 } else {
universe@336 104 clone.capacity = clone.size = 0;
universe@336 105 }
universe@336 106
universe@334 107 return clone;
universe@18 108 }
universe@18 109
universe@334 110 int ucx_array_equals(UcxArray array1, UcxArray array2,
universe@334 111 cmp_func cmpfnc, void* data) {
universe@334 112
universe@336 113 if (array1.size != array2.size || array1.elemsize != array2.elemsize) {
universe@336 114 return 0;
universe@336 115 } else {
universe@336 116 if (array1.size == 0)
universe@336 117 return 1;
universe@336 118
universe@336 119 if (cmpfnc == NULL) {
universe@336 120 cmpfnc = ucx_cmp_mem;
universe@336 121 data = &array1.elemsize;
universe@336 122 }
universe@336 123
universe@336 124 for (size_t i = 0 ; i < array1.size ; i++) {
universe@336 125 int r = cmpfnc(
universe@336 126 ucx_array_at(array1, i),
universe@336 127 ucx_array_at(array2, i),
universe@336 128 data);
universe@336 129 if (r != 0)
universe@336 130 return 0;
universe@336 131 }
universe@336 132 return 1;
universe@336 133 }
universe@125 134 }
universe@125 135
universe@353 136 void ucx_array_destroy(UcxArray *array) {
universe@336 137 alfree(array->allocator, array->data);
universe@336 138 array->data = NULL;
universe@336 139 array->capacity = array->size = 0;
universe@8 140 }
universe@8 141
universe@354 142 int ucx_array_append_from(UcxArray *array, void *data, size_t count) {
universe@354 143 if (ucx_array_ensurecap(array, array->size + count))
universe@354 144 return 1;
universe@354 145
universe@354 146 void* dest = ucx_array_at(*array, array->size);
universe@354 147 if (data) {
universe@354 148 memcpy(dest, data, array->elemsize*count);
universe@354 149 } else {
universe@354 150 memset(dest, 0, array->elemsize*count);
universe@354 151 }
universe@354 152 array->size += count;
universe@354 153
universe@354 154 return 0;
universe@354 155 }
universe@354 156
universe@354 157 int ucx_array_prepend_from(UcxArray *array, void *data, size_t count) {
universe@354 158 if (ucx_array_ensurecap(array, array->size + count))
universe@354 159 return 1;
universe@354 160
universe@354 161 if (array->size > 0) {
universe@354 162 void *dest = ucx_array_at(*array, count);
universe@354 163 memmove(dest, array->data, array->elemsize*array->size);
universe@336 164 }
universe@336 165
universe@336 166 if (data) {
universe@354 167 memcpy(array->data, data, array->elemsize*count);
universe@336 168 } else {
universe@354 169 memset(array->data, 0, array->elemsize*count);
universe@354 170 }
universe@354 171 array->size += count;
universe@354 172
universe@354 173 return 0;
universe@354 174 }
universe@354 175
universe@354 176 int ucx_array_set_from(UcxArray *array, size_t index,
universe@354 177 void *data, size_t count) {
universe@354 178 if (ucx_array_ensurecap(array, index + count))
universe@354 179 return 1;
universe@354 180
universe@354 181 if (index+count > array->size) {
universe@354 182 array->size = index+count;
universe@354 183 }
universe@354 184
universe@354 185 void *dest = ucx_array_at(*array, index);
universe@354 186 if (data) {
universe@354 187 memcpy(dest, data, array->elemsize*count);
universe@354 188 } else {
universe@354 189 memset(dest, 0, array->elemsize*count);
universe@336 190 }
universe@336 191
universe@336 192 return 0;
universe@211 193 }
universe@211 194
universe@354 195 int ucx_array_appendv(UcxArray *array, ...) {
universe@354 196 va_list ap;
universe@354 197 va_start(ap, array);
universe@354 198 int elem = va_arg(ap, int);
universe@354 199 int ret = ucx_array_append_from(array, &elem, 1);
universe@354 200 va_end(ap);
universe@354 201 return ret;
universe@125 202 }
universe@125 203
universe@354 204 int ucx_array_prependv(UcxArray *array, ...) {
universe@354 205 va_list ap;
universe@354 206 va_start(ap, array);
universe@354 207 int elem = va_arg(ap, int);
universe@354 208 int ret = ucx_array_prepend_from(array, &elem, 1);
universe@354 209 va_end(ap);
universe@354 210 return ret;
universe@354 211 }
universe@354 212
universe@354 213 int ucx_array_setv(UcxArray *array, size_t index, ...) {
universe@354 214 va_list ap;
universe@354 215 va_start(ap, index);
universe@354 216 int elem = va_arg(ap, int);
universe@354 217 int ret = ucx_array_set_from(array, index, &elem, 1);
universe@354 218 va_end(ap);
universe@354 219 return ret;
universe@337 220 }
universe@337 221
universe@334 222 int ucx_array_concat(UcxArray *array1, const UcxArray *array2) {
universe@336 223
universe@336 224 if (array1->elemsize != array2->elemsize)
universe@336 225 return 1;
universe@336 226
universe@336 227 size_t capacity = array1->capacity+array2->capacity;
universe@336 228
universe@336 229 if (array1->capacity < capacity) {
universe@336 230 if (ucx_array_reserve(array1, capacity)) {
universe@336 231 return 1;
universe@336 232 }
universe@336 233 }
universe@336 234
universe@336 235 void* dest = ucx_array_at(*array1, array1->size);
universe@336 236 memcpy(dest, array2->data, array2->size*array2->elemsize);
universe@336 237
universe@336 238 array1->size += array2->size;
universe@336 239
universe@336 240 return 0;
universe@7 241 }
universe@7 242
universe@334 243 void *ucx_array_at(UcxArray array, size_t index) {
universe@336 244 char* memory = array.data;
universe@336 245 char* loc = memory + index*array.elemsize;
universe@336 246 return loc;
universe@125 247 }
universe@125 248
universe@334 249 size_t ucx_array_find(UcxArray array, void *elem, cmp_func cmpfnc, void *data) {
universe@7 250
universe@336 251 if (cmpfnc == NULL) {
universe@336 252 cmpfnc = ucx_cmp_mem;
universe@336 253 data = &array.elemsize;
universe@336 254 }
universe@336 255
universe@336 256 if (array.size > 0) {
universe@336 257 for (size_t i = 0 ; i < array.size ; i++) {
universe@336 258 void* ptr = ucx_array_at(array, i);
universe@336 259 if (cmpfnc(ptr, elem, data) == 0) {
universe@336 260 return i;
universe@336 261 }
universe@336 262 }
universe@336 263 return array.size;
universe@336 264 } else {
universe@336 265 return 0;
universe@336 266 }
universe@7 267 }
universe@7 268
universe@334 269 int ucx_array_contains(UcxArray array, void *elem, cmp_func cmpfnc, void *data) {
universe@334 270 return ucx_array_find(array, elem, cmpfnc, data) != array.size;
universe@7 271 }
universe@7 272
universe@345 273 static void ucx_mergesort_merge(void *arrdata,size_t elemsize,
universe@345 274 cmp_func cmpfnc, void *data,
universe@336 275 size_t start, size_t mid, size_t end) {
universe@336 276
universe@345 277 char* array = arrdata;
universe@345 278
universe@336 279 size_t rightstart = mid + 1;
universe@336 280
universe@345 281 if (cmpfnc(array + mid*elemsize,
universe@345 282 array + rightstart*elemsize, data) <= 0) {
universe@336 283 /* already sorted */
universe@336 284 return;
universe@336 285 }
universe@336 286
universe@342 287 /* we need memory for one element */
universe@345 288 void *value = malloc(elemsize);
universe@336 289
universe@336 290 while (start <= mid && rightstart <= end) {
universe@345 291 if (cmpfnc(array + start*elemsize,
universe@345 292 array + rightstart*elemsize, data) <= 0) {
universe@336 293 start++;
universe@336 294 } else {
universe@342 295 /* save the value from the right */
universe@345 296 memcpy(value, array + rightstart*elemsize, elemsize);
universe@336 297
universe@342 298 /* shift all left elements one element to the right */
universe@336 299 size_t shiftcount = rightstart-start;
universe@345 300 void *startptr = array + start*elemsize;
universe@345 301 void *dest = array + (start+1)*elemsize;
universe@345 302 memmove(dest, startptr, shiftcount*elemsize);
universe@336 303
universe@342 304 /* bring the first value from the right to the left */
universe@345 305 memcpy(startptr, value, elemsize);
universe@336 306
universe@336 307 start++;
universe@336 308 mid++;
universe@336 309 rightstart++;
universe@336 310 }
universe@336 311 }
universe@336 312
universe@342 313 /* free the temporary memory */
universe@336 314 free(value);
universe@336 315 }
universe@336 316
universe@345 317 static void ucx_mergesort_impl(void *arrdata, size_t elemsize,
universe@345 318 cmp_func cmpfnc, void *data, size_t l, size_t r) {
universe@336 319 if (l < r) {
universe@336 320 size_t m = l + (r - l) / 2;
universe@336 321
universe@345 322 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, l, m);
universe@345 323 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, m + 1, r);
universe@345 324 ucx_mergesort_merge(arrdata, elemsize, cmpfnc, data, l, m, r);
universe@336 325 }
universe@336 326 }
universe@336 327
universe@345 328 static void ucx_mergesort(void *arrdata, size_t count, size_t elemsize,
universe@345 329 cmp_func cmpfnc, void *data) {
universe@345 330
universe@345 331 ucx_mergesort_impl(arrdata, elemsize, cmpfnc, data, 0, count-1);
universe@345 332 }
universe@345 333
universe@345 334 #ifdef USE_UCX_QSORT_R
universe@345 335 struct cmpfnc_swapargs_info {
universe@345 336 cmp_func func;
universe@345 337 void *data;
universe@345 338 };
universe@345 339
universe@345 340 static int cmp_func_swap_args(void *data, const void *x, const void *y) {
olaf@348 341 cmpfnc_swapargs_info* info = data;
universe@345 342 return info->func(x, y, info->data);
universe@345 343 }
universe@345 344
universe@345 345 static void ucx_qsort_r(void *array, size_t count, size_t elemsize,
universe@345 346 cmp_func cmpfnc, void *data) {
universe@345 347 struct cmpfnc_swapargs_info info;
universe@345 348 info.func = cmpfnc;
universe@345 349 info.data = data;
universe@345 350 qsort_r(array, count, elemsize, &info, cmp_func_swap_args);
universe@345 351 }
universe@345 352 #endif /* USE_UCX_QSORT_R */
universe@345 353
universe@345 354 void ucx_array_sort(UcxArray array, cmp_func cmpfnc, void *data) {
universe@345 355 ucx_array_sort_impl(array.data, array.size, array.elemsize, cmpfnc, data);
universe@7 356 }
universe@7 357
universe@334 358 void ucx_array_remove(UcxArray *array, size_t index) {
universe@336 359 array->size--;
universe@336 360 if (index < array->size) {
universe@336 361 void* dest = ucx_array_at(*array, index);
universe@336 362 void* src = ucx_array_at(*array, index+1);
universe@336 363 memmove(dest, src, (array->size - index)*array->elemsize);
universe@336 364 }
universe@123 365 }
universe@123 366
universe@334 367 void ucx_array_remove_fast(UcxArray *array, size_t index) {
universe@336 368 array->size--;
universe@336 369 if (index < array->size) {
universe@336 370 void* dest = ucx_array_at(*array, index);
universe@336 371 void* src = ucx_array_at(*array, array->size);
universe@336 372 memcpy(dest, src, array->elemsize);
universe@336 373 }
universe@7 374 }
universe@7 375
universe@334 376 int ucx_array_shrink(UcxArray* array) {
universe@336 377 void* newptr = alrealloc(array->allocator, array->data,
universe@336 378 array->size*array->elemsize);
universe@336 379 if (newptr) {
universe@336 380 array->data = newptr;
universe@336 381 array->capacity = array->size;
universe@336 382 return 0;
universe@336 383 } else {
universe@336 384 return 1;
universe@336 385 }
universe@123 386 }
universe@123 387
universe@334 388 int ucx_array_resize(UcxArray* array, size_t capacity) {
universe@336 389 if (array->capacity >= capacity) {
universe@336 390 void* newptr = alrealloc(array->allocator, array->data,
universe@336 391 capacity*array->elemsize);
universe@336 392 if (newptr) {
universe@336 393 array->data = newptr;
universe@336 394 array->capacity = capacity;
universe@336 395 if (array->size > array->capacity) {
universe@336 396 array->size = array->capacity;
universe@336 397 }
universe@336 398 return 0;
universe@336 399 } else {
universe@336 400 return 1;
universe@336 401 }
universe@336 402 } else {
universe@336 403 return ucx_array_reserve(array, capacity);
universe@336 404 }
universe@87 405 }
universe@87 406
universe@334 407 int ucx_array_reserve(UcxArray* array, size_t capacity) {
universe@336 408 if (array->capacity > capacity) {
universe@336 409 return 0;
universe@336 410 } else {
universe@336 411 void* newptr = alrealloc(array->allocator, array->data,
universe@336 412 capacity*array->elemsize);
universe@336 413 if (newptr) {
universe@336 414 array->data = newptr;
universe@336 415 array->capacity = capacity;
universe@336 416 return 0;
universe@336 417 } else {
universe@336 418 return 1;
universe@336 419 }
universe@336 420 }
universe@7 421 }

mercurial