chiark / gitweb /
Various bug fixes: understand requests for help properly, and fix the
[mLib] / darray.h
CommitLineData
3745e24b 1/* -*-c-*-
2 *
20eb516f 3 * $Id: darray.h,v 1.5 2000/06/17 10:37:39 mdw Exp $
3745e24b 4 *
5 * Dynamically growing dense arrays
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of the mLib utilities library.
13 *
14 * mLib is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU Library General Public License as
16 * published by the Free Software Foundation; either version 2 of the
17 * License, or (at your option) any later version.
18 *
19 * mLib is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public
25 * License along with mLib; if not, write to the Free
26 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27 * MA 02111-1307, USA.
28 */
29
30/*----- Revision history --------------------------------------------------*
31 *
32 * $Log: darray.h,v $
20eb516f 33 * Revision 1.5 2000/06/17 10:37:39 mdw
34 * Add support for arena management.
35 *
c6e0eaf0 36 * Revision 1.4 1999/12/10 23:42:04 mdw
37 * Change header file guard names.
38 *
d4e0e50d 39 * Revision 1.3 1999/11/05 14:32:43 mdw
40 * Minor change in argument naming.
41 *
85bb21f7 42 * Revision 1.2 1999/10/29 22:59:22 mdw
43 * New array adjustment macros for unsigned arguments.
44 *
3745e24b 45 * Revision 1.1 1999/10/22 22:37:26 mdw
46 * New dynamic array implementation replaces `dynarray.h'.
47 *
48 */
49
c6e0eaf0 50#ifndef MLIB_DARRAY_H
51#define MLIB_DARRAY_H
3745e24b 52
53#ifdef __cplusplus
54 extern "C" {
55#endif
56
57/*----- Header files ------------------------------------------------------*/
58
59#include <stdio.h>
60#include <stdlib.h>
61#include <string.h>
62
c6e0eaf0 63#ifndef MLIB_ALLOC_H
3745e24b 64# include "alloc.h"
65#endif
66
c6e0eaf0 67#ifndef MLIB_EXC_H
3745e24b 68# include "exc.h"
69#endif
70
71/*----- Various important constants ---------------------------------------*/
72
73/* --- @DAEXC_UFLOW@, @DAEXC_OFLOW@ --- *
74 *
75 * Underflow and overflow exceptions raised by @DA_SHIFT@, and by @DA_POP@
76 * and @DA_SHIFT@.
77 */
78
79#define DAEXC_UFLOW EXC_ALLOCN(EXC_MLIB, 0)
80#define DAEXC_OFLOW EXC_ALLOCN(EXC_MLIB, 1)
81
82/*----- Data structures ---------------------------------------------------*/
83
84/* --- Base structure for dynamic arrays --- *
85 *
86 * An actual array has a `vector' @v@ in addition to this data (which is
87 * tucked away in the @b@ member). The vector contains the actual storage
88 * for the array elements.
89 *
90 * The vector pointer @v@ potentially points somewhere in the middle of the
91 * allocated storage. The @off@ member explains how far into the storage the
92 * vector points. The allocated storage is sufficient for @sz + off@ items
93 * to be stored. Valid array indices are integers between 0 (inclusive) and
94 * @len@ (exclusive). Thus, from @v@ onwards, there is space for @sz@
95 * elements, and of these, @sz - len@ are currently not considered to be
96 * within the array's bounds.
97 *
98 * The @push@ and @unshift@ counts record how many times these operations
99 * have been performed since the last extension of the array. They are used
100 * by the extension algorithm to decide how to position the data offset.
101 *
102 * Try to use the access macros rather than the structure members.
103 */
104
105typedef struct da_base {
106 size_t sz; /* Size of allocated vector */
107 size_t len; /* Length of useful portion */
108 size_t off; /* Offset of @v@ into space */
109 unsigned push, unshift; /* Pushes/unshifts since growth */
20eb516f 110 arena *a; /* Pointer to allocation arena */
3745e24b 111} da_base;
112
113/* --- @DA_DECL@ --- *
114 *
115 * Arguments: @atype@ = type name for the array
116 * @type@ = item type for the array
117 *
118 * Use: Declares a structure for decribing a dynamic array.
119 */
120
d4e0e50d 121#define DA_DECL(type_v, type) \
122 typedef struct type_v { da_base b; type *v; } type_v
3745e24b 123
124/*----- Initialization, creation and destruction --------------------------*/
125
20eb516f 126#define DA_INIT { { 0, 0, 0, 0, 0, &arena_stdlib }, 0 }
3745e24b 127
128/* --- @DA_CREATE@ --- *
129 *
130 * Arguments: @a@ = pointer to an array block (multiply evaluated)
131 *
132 * Use: Initializes an array block.
133 */
134
20eb516f 135#define DA_CREATE(aa) do { \
136 (aa)->b.sz = (aa)->b.len = 0; \
137 (aa)->b.off = 0; \
138 (aa)->b.push = (aa)->b.unshift = 0; \
139 (aa)->b.a = &arena_stdlib; \
140 (aa)->v = 0; \
3745e24b 141} while (0)
142
143/* --- @DA_DESTROY@ --- *
144 *
145 * Arguments: @a@ = pointer to an array block (multiply evaluated)
146 *
147 * Use: Destroys an array. The array is left valid but empty.
148 */
149
20eb516f 150#define DA_DESTROY(aa) do { \
151 if ((aa)->v) \
152 x_free((aa)->b.a, (aa)->v - (aa)->b.off); \
153 DA_CREATE(aa); \
3745e24b 154} while (0)
155
156/*----- Storage reservation -----------------------------------------------*/
157
158/* --- @DA_ENSURE@ --- *
159 *
160 * Arguments: @a@ = pointer to an array block (multiply evaluated)
161 * @n@ = required number of spare items in the array
162 *
163 * Use: Ensures that there are at least @n@ spare slots at the end of
164 * the array.
165 */
166
167#define DA_ENSURE(a, n) do { \
168 size_t _n = (n); \
169 if (_n > (a)->b.sz - (a)->b.len) \
170 (a)->v = da_ensure(&(a)->b, (a)->v, sizeof((a)->v[0]), _n); \
171 else \
172 (a)->b.push += _n; \
173} while (0)
174
175/* --- @DA_SHUNT@ --- *
176 *
177 * Arguments: @a@ = pointer to an array block (multiply evaluated)
178 * @n@ = required number of spare items in the array
179 *
180 * Use: Ensures that there are at least @n@ spare slots at the start
181 * of the array.
182 */
183
184#define DA_SHUNT(a, n) do { \
185 size_t _n = (n); \
186 if (_n > (a)->b.off) \
187 (a)->v = da_shunt(&(a)->b, (a)->v, sizeof((a)->v[0]), _n); \
188 else \
189 (a)->b.unshift += _n; \
190} while (0)
191
192/* --- @DA_TIDY@ --- *
193 *
194 * Arguments: @a@ = pointer to an array block (multiply evaluated)
195 *
196 * Use: Reduces the amount of storage required by an array to its
197 * minimum possible.
198 */
199
200#define DA_TIDY(a) ((a)->v = da_tidy(&(a)->b, (a)->v, sizeof((a)->v[0])))
201
85bb21f7 202/* --- @DA_RESET@ --- *
203 *
204 * Arguments: @a@ = pointer to array block
205 *
206 * Use: Removes all the items from the named array. This might not
207 * be a good idea. No storage is freed.
208 */
209
210#define DA_RESET(a) ((a)->b.len = 0)
211
3745e24b 212/*----- Access operations -------------------------------------------------*/
213
214/* --- @DA@ --- *
215 *
216 * Arguments: @a@ = pointer to array block
217 *
218 * Use: Expands to a reference to the array proper. Given an array
219 * @a@, item @i@ may be located using the expression @DA(a)[i]@.
220 */
221
222#define DA(a) ((a)->v + 0)
223
224/* --- @DA_LEN@ --- *
225 *
226 * Arguments: @a@ = pointer to array block
227 *
228 * Use: Expands to the number of elements in the array. Elements are
229 * assigned integer indices in the half-open interval
230 * [0, @DA_LEN(a)@). Don't change the length directly; use
231 * @DA_EXTEND@ instead.
232 */
233
234#define DA_LEN(a) ((a)->b.len + 0)
235
236/* --- @DA_SPARE@ --- *
237 *
238 * Arguments: @a@ = pointer to array block (multiply evaluated)
239 *
240 * Use: Evaluates to the number of spare array elements above the
241 * end of the array.
242 */
243
244#define DA_SPARE(a) ((a)->b.sz - (a)->b.len)
245
246/* --- @DA_INCLUDE@ --- *
247 *
248 * Arguments: @a@ = pointer to array block (multiply evaluated)
249 * @i@ = index into array
250 *
251 * Use: Extends the array (if necessary) so that it includes the
252 * index @i@.
253 */
254
255#define DA_INCLUDE(a, i) do { \
256 size_t _i = (i); \
257 size_t _len = DA_LEN(a); \
258 if (_i >= _len) { \
259 size_t _nn = _i - _len + 1; \
260 DA_ENSURE(a, _nn); \
261 DA_UNSAFE_EXTEND(a, _nn); \
262 } \
263} while (0)
264
265/* --- @DA_OFFSET@ --- *
266 *
267 * Arguments: @a@ = pointer to array block
268 *
269 * Use: Evaluates to the number of spare elements before the
270 * beginning of the array. Don't modify the offset directly;
271 * use @DA_SLIDE@ instead.
272 */
273
274#define DA_OFFSET(a) ((a)->b.off + 0)
275
276/* --- @DA_EXTEND@ --- *
277 *
278 * Arguments: @a@ = pointer to array block (multiply evaluated)
279 * @n@ = number of slots to add (multiply evaluated)
280 *
281 * Use: Extends the end of the array by @n@ elements. The exception
282 * @DAEXC_OFLOW@ is thrown if there is not enough space for the
283 * new elements (i.e., @n > DA_SPARE(a)@) -- call @DA_ENSURE@ to
284 * prevent this from happening. The integer @n@ may be
285 * negative; @DAEXC_UFLOW@ is called if @n < DA_LEN(a)@.
286 */
287
288#define DA_EXTEND(a, n) do { \
289 if ((n) > 0 && (n) > DA_SPARE(a)) \
290 THROW(DAEXC_OFLOW); \
291 else if ((n) < 0 && -(n) > DA_LEN(a)) \
292 THROW(DAEXC_UFLOW); \
293 DA_UNSAFE_EXTEND(a, n); \
294} while (0)
295
85bb21f7 296/* --- @DA_UNSAFE_EXTEND@ --- *
3745e24b 297 *
298 * Arguments: @a@ = pointer to array block (multiply evaluated)
299 * @n@ = number of slots to add (multiply evaluated)
300 *
301 * Use: As for @DA_EXTEND@, only it doesn't check for errors.
302 */
303
304#define DA_UNSAFE_EXTEND(a, n) do { \
305 (a)->b.len += (n); \
306} while (0)
307
308/* --- @DA_SLIDE@ --- *
309 *
310 * Arguments: @a@ = pointer to array block (multiply evaluated)
311 * @n@ = number of positions to slide the array (multiply
312 * evaluated)
313 *
314 *
315 * Use: Slides the array elements by @n@ positions. A positive @n@
316 * slides upwards, introducing new elements at the bottom; a
317 * negative @n@ slides downwards, removing low-numbered
318 * elements. Formally, what was at index @i - n@ before the
319 * slide is moved to index @i@. It is an error to slide by more
320 * than @DA_OFFSET(a)@ or less than @-DA_LEN(a)@. The exception
321 * @DAEXC_OFLOW@ is raised in the former case, and @DAEXC_UFLOW@
322 * in the latter.
323 */
324
325#define DA_SLIDE(a, n) do { \
326 if ((n) > 0 && (n) > DA_OFFSET(a)) \
327 THROW(DAEXC_OFLOW); \
328 else if ((n) < 0 && -(n) > DA_LEN(a)) \
329 THROW(DAEXC_UFLOW); \
330 DA_UNSAFE_SLIDE((a), (n)); \
331} while (0)
332
333/* --- @DA_UNSAFE_SLIDE@ --- *
334 *
335 * Arguments: @a@ = pointer to array block (multiply evaluated)
336 * @n@ = number of positions to slide the array (multiply
337 * evaluated)
338 *
339 * Use: As for @DA_SLIDE@, only it doesn't check for errors.
340 */
341
342#define DA_UNSAFE_SLIDE(a, n) do { \
343 (a)->v -= (n); \
344 (a)->b.len += (n); \
345 (a)->b.sz += (n); \
346 (a)->b.off -= (n); \
347} while (0)
348
85bb21f7 349/* --- @DA_SHRINK@ --- *
350 *
351 * Arguments: @a@ = pointer to array block (multiply evaluated)
352 * @n@ = number of slots to remove (multiply evaluated)
353 *
354 * Use: As for @DA_EXTEND@, with the sense of the argument reversed.
355 */
356
357#define DA_SHRINK(a, n) do { \
358 if ((n) > 0 && (n) > DA_LEN(a)) \
359 THROW(DAEXC_UFLOW); \
360 else if ((n) < 0 && -(n) > DA_SPARE(a)) \
361 THROW(DAEXC_OFLOW); \
362 DA_UNSAFE_SHRINK(a, n); \
363} while (0)
364
365/* --- @DA_UNSAFE_SHRINK@ --- *
366 *
367 * Arguments: @a@ = pointer to array block (multiply evaluated)
368 * @n@ = number of slots to add (multiply evaluated)
369 *
370 * Use: As for @DA_SHRINK@, only it doesn't check for errors.
371 */
372
373#define DA_UNSAFE_SHRINK(a, n) do { \
374 (a)->b.len -= (n); \
375} while (0)
376
377/* --- @DA_UNSLIDE@ --- *
378 *
379 * Arguments: @a@ = pointer to array block (multiply evaluated)
380 * @n@ = number of positions to slide the array (multiply
381 * evaluated)
382 *
383 *
384 * Use: As for @DA_SLIDE@, only in the other direction.
385 */
386
387#define DA_UNSLIDE(a, n) do { \
388 if ((n) > 0 && (n) > DA_LEN(a)) \
389 THROW(DAEXC_UFLOW); \
390 else if ((n) < 0 && -(n) > DA_OFFSET(a)) \
391 THROW(DAEXC_OFLOW); \
392 DA_UNSAFE_UNSLIDE((a), (n)); \
393} while (0)
394
395/* --- @DA_UNSAFE_UNSLIDE@ --- *
396 *
397 * Arguments: @a@ = pointer to array block (multiply evaluated)
398 * @n@ = number of positions to slide the array (multiply
399 * evaluated)
400 *
401 * Use: As for @DA_UNSLIDE@, only it doesn't check for errors.
402 */
403
404#define DA_UNSAFE_UNSLIDE(a, n) do { \
405 (a)->v += (n); \
406 (a)->b.len -= (n); \
407 (a)->b.sz -= (n); \
408 (a)->b.off += (n); \
409} while (0)
410
3745e24b 411/*----- Stack-like operations ---------------------------------------------*/
412
413/* --- @DA_PUSH@ --- *
414 *
415 * Arguments: @a@ = pointer to an array block (multiply evaluated)
416 * @x@ = item to append to the end
417 *
418 * Use: Appends @x@ as the final element in the array @a@.
419 */
420
421#define DA_PUSH(a, x) do { \
422 DA_ENSURE(a, 1); \
423 DA(a)[(a)->b.len++] = x; \
424} while (0)
425
426/* --- @DA_POP@ --- *
427 *
428 * Arguments: @a@ = pointer to an array block (multiply evaluated)
429 *
430 * Use: Evaluates to the final element in array @a@. The element is
431 * removed. An exception @DAEXC_UFLOW@ is raised if there is
432 * no item available to pop.
433 */
434
435#define DA_POP(a) \
436 ((a)->b.len ? ((void)0) : THROW(DAEXC_UFLOW), \
437 DA(a)[--(a)->b.len])
438
439/* --- @DA_UNSHIFT@ --- *
440 *
441 * Arguments: @a@ = pointer to an array block (multiply evaluated)
442 * @x@ = the new element to insert
443 *
444 * Use: Inserts a new element at the beginning of an array. This
445 * might take a while.
446 */
447
448#define DA_UNSHIFT(a, x) do { \
449 DA_SHUNT(a, 1); \
450 DA_UNSAFE_SLIDE(a, 1); \
451 DA(a)[0] = x; \
452} while (0)
453
454/* --- @DA_SHIFT@ --- *
455 *
456 * Arguments: @a@ = pointer to an array block (multiply evaluated)
457 *
458 * Use: Evaluates to the initial element in array @a@. The element
459 * is removed, and all other elements are shifted down one
460 * place. The exception @DAEXC_UFLOW@ is raised if there is no
461 * element to return.
462 */
463
464#define DA_SHIFT(a) \
465 ((a)->b.len ? ((void)0) : THROW(DAEXC_UFLOW), \
466 (a)->b.len--, \
467 (a)->b.sz--, \
468 (a)->b.off++, \
469 *(a)->v++)
470
471/*----- Functions provided ------------------------------------------------*/
472
473/* --- @da_ensure@ --- *
474 *
475 * Arguments: @da_base *b@ = pointer to array base structure
476 * @void *v@ = pointer to array vector
477 * @size_t sz@ = size of individual array elements
478 * @size_t n@ = number of items required at the end
479 *
480 * Returns: Pointer to newly allocated or adjusted array vector.
481 *
482 * Use: Extends a dynamic array to accommodate a number of new items
483 * at its end. This function is a helper for the @DA_ENSURE@
484 * macro, which should be used by preference.
485 */
486
487extern void *da_ensure(da_base */*b*/, void */*v*/,
488 size_t /*sz*/, size_t /*n*/);
489
490/* --- @da_shunt@ --- *
491 *
492 * Arguments: @da_base *b@ = pointer to array base structure
493 * @void *v@ = pointer to array vector
494 * @size_t sz@ = size of the array elements
495 * @size_t n@ = number of items required at the start
496 *
497 * Returns: Pointer to appropriately bodged vector.
498 *
499 * Use: Extends an array to accommodate items inserted at its front.
500 * This function is a helper for the @DA_SHUNT@ macro, which
501 * should be used by preference.
502 */
503
504extern void *da_shunt(da_base */*b*/, void */*v*/,
505 size_t /*sz*/, size_t /*n*/);
506
507/* --- @da_tidy@ --- *
508 *
509 * Arguments: @da_base *b@ = pointer to array base structure
510 * @void *v@ = pointer to vector
511 * @size_t sz@ = size of the array elements
512 *
513 * Returns: Newly allocated vector.
514 *
515 * Use: Minimizes the space occupied by an array. This function is a
516 * helper for the @DA_TIDY@ macro, which should be used by
517 * preference.
518 */
519
520extern void *da_tidy(da_base */*b*/, void */*v*/, size_t /*sz*/);
521
522/*----- That's all, folks -------------------------------------------------*/
523
524#ifdef __cplusplus
525 }
526#endif
527
528#endif