1 /******************************************************************************/
2 /* pfixtools: a collection of postfix related tools */
4 /* ________________________________________________________________________ */
6 /* Redistribution and use in source and binary forms, with or without */
7 /* modification, are permitted provided that the following conditions */
10 /* 1. Redistributions of source code must retain the above copyright */
11 /* notice, this list of conditions and the following disclaimer. */
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 /* 3. The names of its contributors may not be used to endorse or promote */
16 /* products derived from this software without specific prior written */
19 /* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND */
20 /* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE */
21 /* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR */
22 /* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS */
23 /* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR */
24 /* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF */
25 /* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS */
26 /* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN */
27 /* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) */
28 /* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF */
29 /* THE POSSIBILITY OF SUCH DAMAGE. */
30 /******************************************************************************/
33 * Copyright © 2008 Florent Bruneau
36 #ifndef PFIXTOOLS_ARRAY_H
37 #define PFIXTOOLS_ARRAY_H
43 #define PRIV_ARRAY(Type) \
48 unsigned locked : 1; \
51 /** Declare type PA(Type).
53 #define PARRAY(Type) \
54 typedef PRIV_ARRAY(Type*) Type ## _ptr_array_t; \
55 static inline Type ## _ptr_array_t *Type ## _ptr_array_new(void) \
57 return p_new(Type ## _ptr_array_t, 1); \
60 static inline void Type ## _ptr_array_delete(Type ## _ptr_array_t **array) \
63 if ((*array)->locked) { \
64 array_unlock(**array); \
66 array_wipe(**array); \
71 /** Declare types A(Type) and PA(Type).
74 typedef PRIV_ARRAY(Type) Type ## _array_t; \
76 static inline Type ## _array_t *Type ## _array_new(void) \
78 return p_new(Type ## _array_t, 1); \
81 static inline void Type ## _array_delete(Type ## _array_t **array) \
84 if ((*array)->locked) { \
85 array_unlock(**array); \
87 array_wipe(**array); \
94 /** Type A(Type) is a dynamic array of elements of type @c Type.
96 #define A(Type) Type ## _array_t
98 /** Type PA(Type) is a dynamic array of pointers to type @c Type.
100 #define PA(Type) Type ## _ptr_array_t
102 #define ARRAY_INIT { NULL, 0, 0, false }
104 #define array_init(array) (array) = ARRAY_INIT
106 #define array_can_edit(array) (!(array).locked)
108 #define array_ensure_can_edit(array) \
109 assert(array_can_edit(array) && "Trying to edit array while it is locked")
111 #define array_wipe(array) \
113 array_ensure_can_edit(array); \
114 p_delete(&(array).data); \
120 /******* MEMORY MANAGEMENT *******/
122 /** Return the len of the array (number of elements contained in the array).
124 #define array_len(array) ((array).len)
126 /** Return the capacity of the array (number of elements the array can contain
127 * without growing its internal buffer).
129 #define array_size(array) ((array).size)
131 /** Return the number of free places in the array.
133 #define array_free_space(array) (array_size(array) - array_len(array))
135 /** Return the size of an element of the array.
137 #define array_elt_len(array) (sizeof(*(array).data))
139 /** Return the number of bytes used by the content of the array.
141 #define array_byte_len(array) ((array).len * array_elt_len(array))
143 /** Ensure the capacity of the array if *at least* @c goal *elements*.
145 #define array_ensure_capacity(array, goal) \
147 array_ensure_can_edit(array); \
148 if ((array).size < (goal)) { \
149 const typeof((array).size) required_size = (goal); \
150 typeof((array).size) next_size = (array).size; \
152 next_size = p_alloc_nr(next_size); \
153 } while (next_size < required_size); \
154 p_allocgrow(&(array).data, next_size, &(array).size); \
158 /** Ensure the array contains place for *at least* @c delta more elements.
160 #define array_ensure_capacity_delta(array, delta) \
161 array_ensure_capacity(array, (array).len + (delta))
163 /** Ensure the array can contain @c goal elements.
165 #define array_ensure_exact_capacity(array, goal) \
166 if ((array).size < (goal)) { \
167 array_ensure_can_edit(array); \
168 p_allocgrow(&(array).data, (goal), &(array).size); \
171 /** Shrink capacity of the array to MAX(len, @c cap).
173 #define array_shrink(array, cap) \
175 array_ensure_can_edit(array); \
176 if ((cap) < (array).size && (array).size != (array).len) { \
177 p_shrink(&(array).data, MAX((array).len, (cap)), &(array).size); \
181 /** Ensure the capacity of the array does not exceed its len.
183 #define array_adjust(array) array_shrink(array, 0)
185 #define array_lock(array) \
187 || (mlock((array).data, array_byte_len(array)) == 0 \
188 && ((array).locked = true)))
190 #define array_unlock(array) \
191 if ((array).locked) { \
192 (void)munlock((array).data, array_byte_len(array)); \
193 (array).locked = false; \
197 /******* ADDING ELEMENTS *******/
199 #define array_add(array, obj) \
201 array_ensure_capacity_delta(array, 1); \
202 (array).data[(array).len++] = (obj); \
205 #define array_append(array, objs, Len) \
207 const typeof((array).len) __len = (Len); \
208 array_ensure_capacity_delta(array, __len); \
209 memcpy((array).data + (array).len, objs, \
210 __len * sizeof(*(array).data)); \
211 (array).len += __len; \
215 /******* ACCESSSING ELEMENTS ********/
217 /** Getting the n'th element of the array.
219 #define array_elt(array, n) ((array).data[(n)])
221 #define array_first(array) array_elt(array, 0)
222 #define array_last(array) array_elt(array, (array).len - 1)
224 #define array_pop_last(array) array_elt(array, --((array).len))
226 /** Getting a pointer to the n'th element of the array.
228 #define array_ptr(array, n) ((array).data + (n))
230 #define array_start(array) array_ptr((array), 0)
231 #define array_end(array) array_ptr((array), array_len(array))
233 /****** TRAVERSING AN ARRAY *******/
235 /** Gives the position of pointer ptr in the array.
236 * This macro may only be used withing a loop. @ref foreach.
238 #define array_pos(array, ptr) ((ptr) - array_start(array))
240 /** Build a loop over the elements of an array.
245 * foreach (MyType* element, array) {
246 * do_something(element);
251 * * remember the loop must be ended with }} (the foreach macro contains
252 * a not-matched opening brace).
253 * * the macro uses a counter name __Ai, so you cannot build imbricated
254 * array enumerations. You SHOULD NOT use this counter in your code since
255 * it is part of the internal API and may change in the future.
258 #define foreach(var, array) \
259 for (uint32_t __Ai = 0 ; __Ai < (array).len ; ++__Ai) { \
260 var = array_ptr(array, __Ai);
262 /** Execute @c action for each element of the array.
265 * static void do_something(MyType *element) { ... }
269 * array_foreach(array, do_something);
272 #define array_foreach(array, action) \
273 for (uint32_t __Ai = 0 ; __Ai < (array).len ; ++__Ai) { \
274 action(array_ptr(array, __Ai)); \
277 /** Wipe each element of the array using @c wipe, then wipe the array.
279 #define array_deep_wipe(array, wipe) \
281 array_foreach(array, wipe); \