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