2 * Copyright (C) 2014 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
5 * Copyright (C) 2013 Martin Willi
6 * Copyright (C) 2013 revosec AG
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
13 * This program is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19 #define _GNU_SOURCE /* for qsort_r() */
25 * Data is an allocated block, with potentially unused head and tail:
27 * "esize" each (or sizeof(void*) if esize = 0)
28 * /-\ /-\ /-\ /-\ /-\ /-\
30 * +---------------+-------------------------------+---------------+
31 * | h | e | a | d | e | l | e | m | e | n | t | s | t | a | i | l |
32 * +---------------+-------------------------------+---------------+
34 * \--------------/ \-----------------------------/ \-------------/
36 * "head" "count" "tail"
40 /** number of elements currently in array (not counting head/tail) */
42 /** size of each element, 0 for a pointer based array */
44 /** allocated but unused elements at array front */
46 /** allocated but unused elements at array end */
52 /** maximum number of unused head/tail elements before cleanup */
53 #define ARRAY_MAX_UNUSED 32
56 * Get the actual size of a number of elements
58 static size_t get_size(array_t
*array
, u_int32_t num
)
62 return array
->esize
* num
;
64 return sizeof(void*) * num
;
68 * Increase allocated but unused tail room to at least "room"
70 static void make_tail_room(array_t
*array
, u_int8_t room
)
72 if (array
->tail
< room
)
74 array
->data
= realloc(array
->data
,
75 get_size(array
, array
->count
+ array
->head
+ room
));
81 * Increase allocated but unused head room to at least "room"
83 static void make_head_room(array_t
*array
, u_int8_t room
)
85 if (array
->head
< room
)
87 u_int8_t increase
= room
- array
->head
;
89 array
->data
= realloc(array
->data
,
90 get_size(array
, array
->count
+ array
->tail
+ room
));
91 memmove(array
->data
+ get_size(array
, increase
), array
->data
,
92 get_size(array
, array
->count
+ array
->tail
+ array
->head
));
98 * Make space for an item at index using tail room
100 static void insert_tail(array_t
*array
, int idx
)
102 make_tail_room(array
, 1);
103 /* move up all elements after idx by one */
104 memmove(array
->data
+ get_size(array
, array
->head
+ idx
+ 1),
105 array
->data
+ get_size(array
, array
->head
+ idx
),
106 get_size(array
, array
->count
- idx
));
113 * Make space for an item at index using head room
115 static void insert_head(array_t
*array
, int idx
)
117 make_head_room(array
, 1);
118 /* move down all elements before idx by one */
119 memmove(array
->data
+ get_size(array
, array
->head
- 1),
120 array
->data
+ get_size(array
, array
->head
),
121 get_size(array
, idx
));
128 * Remove an item, increase tail
130 static void remove_tail(array_t
*array
, int idx
)
132 /* move all items after idx one down */
133 memmove(array
->data
+ get_size(array
, idx
+ array
->head
),
134 array
->data
+ get_size(array
, idx
+ array
->head
+ 1),
135 get_size(array
, array
->count
- idx
));
141 * Remove an item, increase head
143 static void remove_head(array_t
*array
, int idx
)
145 /* move all items before idx one up */
146 memmove(array
->data
+ get_size(array
, array
->head
+ 1),
147 array
->data
+ get_size(array
, array
->head
), get_size(array
, idx
));
152 array_t
*array_create(u_int esize
, u_int8_t reserve
)
162 array
->data
= malloc(array
->tail
* array
->esize
);
167 int array_count(array_t
*array
)
176 void array_compress(array_t
*array
)
185 memmove(array
->data
, array
->data
+ get_size(array
, array
->head
),
186 get_size(array
, array
->count
+ array
->tail
));
192 array
->data
= realloc(array
->data
, get_size(array
, array
->count
));
199 /** public enumerator interface */
201 /** enumerated array */
203 /** current index +1, initialized at 0 */
205 } array_enumerator_t
;
207 METHOD(enumerator_t
, enumerate
, bool,
208 array_enumerator_t
*this, void **out
)
212 if (this->idx
>= this->array
->count
)
217 pos
= this->array
->data
+
218 get_size(this->array
, this->idx
+ this->array
->head
);
219 if (this->array
->esize
)
221 /* for element based arrays we return a pointer to the element */
226 /* for pointer based arrays we return the pointer directly */
233 enumerator_t
* array_create_enumerator(array_t
*array
)
235 array_enumerator_t
*enumerator
;
239 return enumerator_create_empty();
244 .enumerate
= (void*)_enumerate
,
245 .destroy
= (void*)free
,
249 return &enumerator
->public;
252 void array_remove_at(array_t
*array
, enumerator_t
*public)
254 array_enumerator_t
*enumerator
= (array_enumerator_t
*)public;
258 array_remove(array
, --enumerator
->idx
, NULL
);
262 void array_insert_create(array_t
**array
, int idx
, void *ptr
)
266 *array
= array_create(0, 0);
268 array_insert(*array
, idx
, ptr
);
271 void array_insert_enumerator(array_t
*array
, int idx
, enumerator_t
*enumerator
)
275 while (enumerator
->enumerate(enumerator
, &ptr
))
277 array_insert(array
, idx
, ptr
);
279 enumerator
->destroy(enumerator
);
282 void array_insert(array_t
*array
, int idx
, void *data
)
284 if (idx
< 0 || idx
<= array_count(array
))
290 idx
= array_count(array
);
293 if (array
->head
&& !array
->tail
)
295 insert_head(array
, idx
);
297 else if (array
->tail
&& !array
->head
)
299 insert_tail(array
, idx
);
301 else if (idx
> array_count(array
) / 2)
303 insert_tail(array
, idx
);
307 insert_head(array
, idx
);
310 pos
= array
->data
+ get_size(array
, array
->head
+ idx
);
313 memcpy(pos
, data
, get_size(array
, 1));
317 /* pointer based array, copy pointer value */
323 bool array_get(array_t
*array
, int idx
, void *data
)
329 if (idx
>= 0 && idx
>= array_count(array
))
335 if (array_count(array
) == 0)
339 idx
= array_count(array
) - 1;
343 memcpy(data
, array
->data
+ get_size(array
, array
->head
+ idx
),
349 bool array_remove(array_t
*array
, int idx
, void *data
)
351 if (!array_get(array
, idx
, data
))
355 if (idx
> array_count(array
) / 2)
357 remove_tail(array
, idx
);
363 idx
= array_count(array
) - 1;
365 remove_head(array
, idx
);
367 if (array
->head
+ array
->tail
> ARRAY_MAX_UNUSED
)
369 array_compress(array
);
377 /** comparison function */
378 int (*cmp
)(const void*,const void*,void*);
379 /** optional user arg */
383 #ifdef HAVE_QSORT_R_GNU
384 static int compare_elements(const void *a
, const void *b
, void *arg
)
385 #else /* HAVE_QSORT_R_BSD */
386 static int compare_elements(void *arg
, const void *a
, const void *b
)
389 sort_data_t
*data
= (sort_data_t
*)arg
;
391 if (data
->array
->esize
)
393 return data
->cmp(a
, b
, data
->arg
);
395 return data
->cmp(*(void**)a
, *(void**)b
, data
->arg
);
398 void array_sort(array_t
*array
, int (*cmp
)(const void*,const void*,void*),
410 start
= array
->data
+ get_size(array
, array
->head
);
412 #ifdef HAVE_QSORT_R_GNU
413 qsort_r(start
, array
->count
, get_size(array
, 1), compare_elements
,
415 #else /* HAVE_QSORT_R_BSD */
416 qsort_r(start
, array
->count
, get_size(array
, 1), &data
,
427 /** comparison function */
428 int (*cmp
)(const void*,const void*);
431 static int search_elements(const void *a
, const void *b
)
433 bsearch_data_t
*data
= (bsearch_data_t
*)a
;
435 if (data
->array
->esize
)
437 return data
->cmp(data
->key
, b
);
439 return data
->cmp(data
->key
, *(void**)b
);
442 int array_bsearch(array_t
*array
, const void *key
,
443 int (*cmp
)(const void*,const void*), void *out
)
449 bsearch_data_t data
= {
456 start
= array
->data
+ get_size(array
, array
->head
);
458 item
= bsearch(&data
, start
, array
->count
, get_size(array
, 1),
464 memcpy(out
, item
, get_size(array
, 1));
466 idx
= (item
- start
) / get_size(array
, 1);
472 void array_invoke(array_t
*array
, array_callback_t cb
, void *user
)
479 for (i
= array
->head
; i
< array
->count
+ array
->head
; i
++)
481 obj
= array
->data
+ get_size(array
, i
);
484 /* dereference if we store store pointers */
487 cb(obj
, i
- array
->head
, user
);
492 void array_invoke_offset(array_t
*array
, size_t offset
)
496 void (*method
)(void *data
);
500 for (i
= array
->head
; i
< array
->count
+ array
->head
; i
++)
502 obj
= array
->data
+ get_size(array
, i
);
505 /* dereference if we store store pointers */
508 method
= *(void**)(obj
+ offset
);
514 void array_destroy(array_t
*array
)
523 void array_destroy_function(array_t
*array
, array_callback_t cb
, void *user
)
525 array_invoke(array
, cb
, user
);
526 array_destroy(array
);
529 void array_destroy_offset(array_t
*array
, size_t offset
)
531 array_invoke_offset(array
, offset
);
532 array_destroy(array
);