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