src/array.c

Wed, 07 Aug 2019 21:14:58 +0200

author
Mike Becker <universe@uap-core.de>
date
Wed, 07 Aug 2019 21:14:58 +0200
branch
feature/array
changeset 345
6089eb30a51a
parent 342
8f0a3c00d1d2
child 346
1a9c112f4116
permissions
-rw-r--r--

ucx_array_sort() uses qsort_r(), if available

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

mercurial