1 /* Copyright (C) 2013-2016, The Regents of The University of Michigan.
2 All rights reserved.
3 This software was developed in the APRIL Robotics Lab under the
4 direction of Edwin Olson, ebolson@umich.edu. This software may be
5 available under alternative licensing terms; contact the address above.
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright notice,
11 this list of conditions and the following disclaimer in the documentation
12 and/or other materials provided with the distribution.
13 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 The views and conclusions contained in the software and documentation are those
24 of the authors and should not be interpreted as representing official policies,
25 either expressed or implied, of the Regents of The University of Michigan.
26 */
27
28 #ifndef _ZARRAY_H
29 #define _ZARRAY_H
30 //#pragma once
31
32 #include <assert.h>
33 #include <stddef.h>
34 #include <stdlib.h>
35 #include <string.h>
36
37 #ifdef __cplusplus
38 // extern "C" {
39 #endif
40
41 /**
42 * Defines a structure which acts as a resize-able array ala Java's ArrayList.
43 */
44 typedef struct zarray zarray_t;
45 struct zarray {
46 size_t el_sz; // size of each element
47
48 int size; // how many elements?
49 int alloc; // we've allocated storage for how many elements?
50 char *data;
51 };
52
53 /**
54 * Creates and returns a variable array structure capable of holding elements of
55 * the specified size. It is the caller's responsibility to call zarray_destroy()
56 * on the returned array when it is no longer needed.
57 */
zarray_create(size_t el_sz)58 static inline zarray_t *zarray_create(size_t el_sz)
59 {
60 assert(el_sz > 0);
61
62 zarray_t *za = (zarray_t *)calloc(1, sizeof(zarray_t));
63 za->el_sz = el_sz;
64 return za;
65 }
66
67 /**
68 * Frees all resources associated with the variable array structure which was
69 * created by zarray_create(). After calling, 'za' will no longer be valid for storage.
70 */
zarray_destroy(zarray_t * za)71 static inline void zarray_destroy(zarray_t *za)
72 {
73 if (za == NULL)
74 return;
75
76 if (za->data != NULL)
77 free(za->data);
78 memset(za, 0, sizeof(zarray_t));
79 free(za);
80 }
81
82 /** Allocate a new zarray that contains a copy of the data in the argument. **/
zarray_copy(const zarray_t * za)83 static inline zarray_t *zarray_copy(const zarray_t *za)
84 {
85 assert(za != NULL);
86
87 zarray_t *zb = (zarray_t *)calloc(1, sizeof(zarray_t));
88 zb->el_sz = za->el_sz;
89 zb->size = za->size;
90 zb->alloc = za->alloc;
91 zb->data = (char *)malloc(zb->alloc * zb->el_sz);
92 memcpy(zb->data, za->data, za->size * za->el_sz);
93 return zb;
94 }
95
iceillog2(int v)96 static int iceillog2(int v)
97 {
98 v--;
99 v |= v >> 1;
100 v |= v >> 2;
101 v |= v >> 4;
102 v |= v >> 8;
103 v |= v >> 16;
104 v++;
105 return v;
106 }
107
108 /**
109 * Allocate a new zarray that contains a subset of the original
110 * elements. NOTE: end index is EXCLUSIVE, that is one past the last
111 * element you want.
112 */
zarray_copy_subset(const zarray_t * za,int start_idx,int end_idx_exclusive)113 static inline zarray_t *zarray_copy_subset(const zarray_t *za, int start_idx, int end_idx_exclusive)
114 {
115 zarray_t *out = (zarray_t *)calloc(1, sizeof(zarray_t));
116 out->el_sz = za->el_sz;
117 out->size = end_idx_exclusive - start_idx;
118 out->alloc = iceillog2(out->size); // round up pow 2
119 out->data = (char *)malloc(out->alloc * out->el_sz);
120 memcpy(out->data, za->data + (start_idx * out->el_sz), out->size * out->el_sz);
121 return out;
122 }
123
124 /**
125 * Retrieves the number of elements currently being contained by the passed
126 * array, which may be different from its capacity. The index of the last element
127 * in the array will be one less than the returned value.
128 */
zarray_size(const zarray_t * za)129 static inline int zarray_size(const zarray_t *za)
130 {
131 assert(za != NULL);
132
133 return za->size;
134 }
135
136 /**
137 * Returns 1 if zarray_size(za) == 0,
138 * returns 0 otherwise.
139 */
140 /*
141 JUST CALL zarray_size
142 int zarray_isempty(const zarray_t *za)
143 {
144 assert(za != NULL);
145 if (za->size <= 0)
146 return 1;
147 else
148 return 0;
149 }
150 */
151
152 /**
153 * Allocates enough internal storage in the supplied variable array structure to
154 * guarantee that the supplied number of elements (capacity) can be safely stored.
155 */
156 #include <stdio.h>
zarray_ensure_capacity(zarray_t * za,int capacity)157 static inline void zarray_ensure_capacity(zarray_t *za, int capacity)
158 {
159 assert(za != NULL);
160
161 if (capacity <= za->alloc)
162 return;
163
164 while (za->alloc < capacity) {
165 za->alloc *= 2;
166 if (za->alloc < 8)
167 za->alloc = 8;
168 }
169
170 // za->data = (char *)realloc(za->data, za->alloc * za->el_sz);
171 char *tmp = (char *)realloc(za->data, za->alloc * za->el_sz);
172 if (tmp != NULL) {
173 za->data = tmp;
174 }
175 }
176
177 /**
178 * Adds a new element to the end of the supplied array, and sets its value
179 * (by copying) from the data pointed to by the supplied pointer 'p'.
180 * Automatically ensures that enough storage space is available for the new element.
181 */
zarray_add(zarray_t * za,const void * p)182 static inline void zarray_add(zarray_t *za, const void *p)
183 {
184 assert(za != NULL);
185 assert(p != NULL);
186
187 zarray_ensure_capacity(za, za->size + 1);
188
189 memcpy(&za->data[za->size * za->el_sz], p, za->el_sz);
190 za->size++;
191 }
192
193 /**
194 * Retrieves the element from the supplied array located at the zero-based
195 * index of 'idx' and copies its value into the variable pointed to by the pointer
196 * 'p'.
197 */
zarray_get(const zarray_t * za,int idx,void * p)198 static inline void zarray_get(const zarray_t *za, int idx, void *p)
199 {
200 assert(za != NULL);
201 assert(p != NULL);
202 assert(idx >= 0);
203 assert(idx < za->size);
204
205 memcpy(p, &za->data[idx * za->el_sz], za->el_sz);
206 }
207
208 /**
209 * Similar to zarray_get(), but returns a "live" pointer to the internal
210 * storage, avoiding a memcpy. This pointer is not valid across
211 * operations which might move memory around (i.e. zarray_remove_value(),
212 * zarray_remove_index(), zarray_insert(), zarray_sort(), zarray_clear()).
213 * 'p' should be a pointer to the pointer which will be set to the internal address.
214 */
zarray_get_volatile(const zarray_t * za,int idx,void * p)215 inline static void zarray_get_volatile(const zarray_t *za, int idx, void *p)
216 {
217 assert(za != NULL);
218 assert(p != NULL);
219 assert(idx >= 0);
220 assert(idx < za->size);
221
222 *((void **)p) = &za->data[idx * za->el_sz];
223 }
224
zarray_truncate(zarray_t * za,int sz)225 inline static void zarray_truncate(zarray_t *za, int sz)
226 {
227 assert(za != NULL);
228 assert(sz <= za->size);
229 za->size = sz;
230 }
231
232 /**
233 * Copies the memory array used internally by zarray to store its owned
234 * elements to the address pointed by 'buffer'. It is the caller's responsibility
235 * to allocate zarray_size()*el_sz bytes for the copy to be stored and
236 * to free the memory when no longer needed. The memory allocated at 'buffer'
237 * and the internal zarray storage must not overlap. 'buffer_bytes' should be
238 * the size of the 'buffer' memory space, in bytes, and must be at least
239 * zarray_size()*el_sz.
240 *
241 * Returns the number of bytes copied into 'buffer'.
242 */
zarray_copy_data(const zarray_t * za,void * buffer,size_t buffer_bytes)243 static inline size_t zarray_copy_data(const zarray_t *za, void *buffer, size_t buffer_bytes)
244 {
245 assert(za != NULL);
246 assert(buffer != NULL);
247 assert(buffer_bytes >= za->el_sz * za->size);
248 memcpy(buffer, za->data, za->el_sz * za->size);
249 (void)buffer_bytes; // To avoid a warning on iOS
250 return za->el_sz * za->size;
251 }
252
253 /**
254 * Removes the entry at index 'idx'.
255 * If shuffle is true, the last element in the array will be placed in
256 * the newly-open space; if false, the zarray is compacted.
257 */
zarray_remove_index(zarray_t * za,int idx,int shuffle)258 static inline void zarray_remove_index(zarray_t *za, int idx, int shuffle)
259 {
260 assert(za != NULL);
261 assert(idx >= 0);
262 assert(idx < za->size);
263
264 if (shuffle) {
265 if (idx < za->size - 1)
266 memcpy(&za->data[idx * za->el_sz], &za->data[((size_t)(za->size) - (size_t)1) * (za->el_sz)], za->el_sz);
267 za->size--;
268 return;
269 } else {
270 // size = 10, idx = 7. Should copy 2 entries (at idx=8 and idx=9).
271 // size = 10, idx = 9. Should copy 0 entries.
272 int ncopy = za->size - idx - 1;
273 if (ncopy > 0)
274 memmove(&za->data[idx * za->el_sz], &za->data[((size_t)(idx) + (size_t)1) * (za->el_sz)], ncopy * za->el_sz);
275 za->size--;
276 return;
277 }
278 }
279
280 /**
281 * Remove the entry whose value is equal to the value pointed to by 'p'.
282 * If shuffle is true, the last element in the array will be placed in
283 * the newly-open space; if false, the zarray is compacted. At most
284 * one element will be removed.
285 *
286 * Note that objects will be compared using memcmp over the full size
287 * of the value. If the value is a struct that contains padding,
288 * differences in the padding bytes can cause comparisons to
289 * fail. Thus, it remains best practice to bzero all structs so that
290 * the padding is set to zero.
291 *
292 * Returns the number of elements removed (0 or 1).
293 */
294 // remove the entry whose value is equal to the value pointed to by p.
295 // if shuffle is true, the last element in the array will be placed in
296 // the newly-open space; if false, the zarray is compacted.
zarray_remove_value(zarray_t * za,const void * p,int shuffle)297 static inline int zarray_remove_value(zarray_t *za, const void *p, int shuffle)
298 {
299 assert(za != NULL);
300 assert(p != NULL);
301
302 for (int idx = 0; idx < za->size; idx++) {
303 if (!memcmp(p, &za->data[idx * za->el_sz], za->el_sz)) {
304 zarray_remove_index(za, idx, shuffle);
305 return 1;
306 }
307 }
308
309 return 0;
310 }
311
312 /**
313 * Creates a new entry and inserts it into the array so that it will have the
314 * index 'idx' (i.e. before the item which currently has that index). The value
315 * of the new entry is set to (copied from) the data pointed to by 'p'. 'idx'
316 * can be one larger than the current max index to place the new item at the end
317 * of the array, or zero to add it to an empty array.
318 */
zarray_insert(zarray_t * za,int idx,const void * p)319 static inline void zarray_insert(zarray_t *za, int idx, const void *p)
320 {
321 assert(za != NULL);
322 assert(p != NULL);
323 assert(idx >= 0);
324 assert(idx <= za->size);
325
326 zarray_ensure_capacity(za, za->size + 1);
327 // size = 10, idx = 7. Should copy three entries (idx=7, idx=8, idx=9)
328 int ncopy = za->size - idx;
329
330 memmove(&za->data[((size_t)(idx) + (size_t)1) * (za->el_sz)], &za->data[idx * za->el_sz], ncopy * za->el_sz);
331 memcpy(&za->data[idx * za->el_sz], p, za->el_sz);
332
333 za->size++;
334 }
335
336 /**
337 * Sets the value of the current element at index 'idx' by copying its value from
338 * the data pointed to by 'p'. The previous value of the changed element will be
339 * copied into the data pointed to by 'outp' if it is not null.
340 */
zarray_set(zarray_t * za,int idx,const void * p,void * outp)341 static inline void zarray_set(zarray_t *za, int idx, const void *p, void *outp)
342 {
343 assert(za != NULL);
344 assert(p != NULL);
345 assert(idx >= 0);
346 assert(idx < za->size);
347
348 if (outp != NULL)
349 memcpy(outp, &za->data[idx * za->el_sz], za->el_sz);
350
351 memcpy(&za->data[idx * za->el_sz], p, za->el_sz);
352 }
353
354 /**
355 * Calls the supplied function for every element in the array in index order.
356 * The map function will be passed a pointer to each element in turn and must
357 * have the following format:
358 *
359 * void map_function(element_type *element)
360 */
zarray_map(zarray_t * za,void (* f)(void *))361 static inline void zarray_map(zarray_t *za, void (*f)(void *))
362 {
363 assert(za != NULL);
364 assert(f != NULL);
365
366 for (int idx = 0; idx < za->size; idx++)
367 f(&za->data[idx * za->el_sz]);
368 }
369
370 /**
371 * Calls the supplied function for every element in the array in index order.
372 * HOWEVER values are passed to the function, not pointers to values. In the
373 * case where the zarray stores object pointers, zarray_vmap allows you to
374 * pass in the object's destroy function (or free) directly. Can only be used
375 * with zarray's which contain pointer data. The map function should have the
376 * following format:
377 *
378 * void map_function(element_type *element)
379 */
380 void zarray_vmap(zarray_t *za, void (*f)(void *));
381
382 /**
383 * Removes all elements from the array and sets its size to zero. Pointers to
384 * any data elements obtained i.e. by zarray_get_volatile() will no longer be
385 * valid.
386 */
zarray_clear(zarray_t * za)387 static inline void zarray_clear(zarray_t *za)
388 {
389 assert(za != NULL);
390 za->size = 0;
391 }
392
393 /**
394 * Determines whether any element in the array has a value which matches the
395 * data pointed to by 'p'.
396 *
397 * Returns 1 if a match was found anywhere in the array, else 0.
398 */
zarray_contains(const zarray_t * za,const void * p)399 static inline int zarray_contains(const zarray_t *za, const void *p)
400 {
401 assert(za != NULL);
402 assert(p != NULL);
403
404 for (int idx = 0; idx < za->size; idx++) {
405 if (!memcmp(p, &za->data[idx * za->el_sz], za->el_sz)) {
406 return 1;
407 }
408 }
409
410 return 0;
411 }
412
413 /**
414 * Uses qsort() to sort the elements contained by the array in ascending order.
415 * Uses the supplied comparison function to determine the appropriate order.
416 *
417 * The comparison function will be passed a pointer to two elements to be compared
418 * and should return a measure of the difference between them (see strcmp()).
419 * I.e. it should return a negative number if the first element is 'less than'
420 * the second, zero if they are equivalent, and a positive number if the first
421 * element is 'greater than' the second. The function should have the following format:
422 *
423 * int comparison_function(const element_type *first, const element_type *second)
424 *
425 * zstrcmp() can be used as the comparison function for string elements, which
426 * will call strcmp() internally.
427 */
zarray_sort(zarray_t * za,int (* compar)(const void *,const void *))428 static inline void zarray_sort(zarray_t *za, int (*compar)(const void *, const void *))
429 {
430 assert(za != NULL);
431 assert(compar != NULL);
432 if (za->size == 0)
433 return;
434
435 qsort(za->data, za->size, za->el_sz, compar);
436 }
437
438 /**
439 * A comparison function for comparing strings which can be used by zarray_sort()
440 * to sort arrays with char* elements.
441 */
442 int zstrcmp(const void *a_pp, const void *b_pp);
443
444 /**
445 * Find the index of an element, or return -1 if not found. Remember that p is
446 * a pointer to the element.
447 **/
448 // returns -1 if not in array. Remember p is a pointer to the item.
zarray_index_of(const zarray_t * za,const void * p)449 static inline int zarray_index_of(const zarray_t *za, const void *p)
450 {
451 assert(za != NULL);
452 assert(p != NULL);
453
454 for (int i = 0; i < za->size; i++) {
455 if (!memcmp(p, &za->data[i * za->el_sz], za->el_sz))
456 return i;
457 }
458
459 return -1;
460 }
461
462 /**
463 * Add all elements from 'source' into 'dest'. el_size must be the same
464 * for both lists
465 **/
zarray_add_all(zarray_t * dest,const zarray_t * source)466 static inline void zarray_add_all(zarray_t *dest, const zarray_t *source)
467 {
468 assert(dest->el_sz == source->el_sz);
469
470 // Don't allocate on stack because el_sz could be larger than ~8 MB
471 // stack size
472 char *tmp = (char *)calloc(1, dest->el_sz);
473
474 for (int i = 0; i < zarray_size(source); i++) {
475 zarray_get(source, i, tmp);
476 zarray_add(dest, tmp);
477 }
478
479 free(tmp);
480 }
481
482 #ifdef __cplusplus
483 //}
484 #endif
485
486 #endif
487