More changes. Still embryonic.
[u/mdw/catacomb] / mparena.c
CommitLineData
d3409d5e 1/* -*-c-*-
2 *
97d21728 3 * $Id: mparena.c,v 1.4 1999/12/10 23:28:52 mdw Exp $
d3409d5e 4 *
5 * Allocation and freeing of MP buffers
6 *
7 * (c) 1999 Straylight/Edgeware
8 */
9
10/*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Catacomb.
13 *
14 * Catacomb 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 * Catacomb 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 Catacomb; 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: mparena.c,v $
97d21728 33 * Revision 1.4 1999/12/10 23:28:52 mdw
34 * Memory allocation counting.
35 *
48d66950 36 * Revision 1.3 1999/11/22 13:58:00 mdw
37 * Document the tweakables.
38 *
12a9a6e5 39 * Revision 1.2 1999/11/21 22:14:19 mdw
40 * Fix bug. Improve diagnostic capabilities.
41 *
d3409d5e 42 * Revision 1.1 1999/11/17 18:02:16 mdw
43 * New multiprecision integer arithmetic suite.
44 *
45 */
46
47/*----- Header files ------------------------------------------------------*/
48
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52
53#include <mLib/alloc.h>
54#include <mLib/sub.h>
55
56#include "mparena.h"
57
12a9a6e5 58/*----- Tweakables --------------------------------------------------------*/
59
48d66950 60/* --- @MPARENA_TRIVIAL@ --- *
61 *
62 * Make the allocator a passthrough. It immediately calls the underlying
63 * allocation functions rather than attempting to keep track of blocks
64 * itself.
65 */
66
12a9a6e5 67/* #define MPARENA_TRIVIAL */
68
48d66950 69/* --- @MPARENA_DEBUG@ --- *
70 *
71 * The name of an output trace file to which logging information about the
72 * state of arena trees should be written. If unset, no logging is done.
73 */
74
12a9a6e5 75/* #define MPARENA_DEBUG "mparena.out" */
76
d3409d5e 77/*----- Default allocator -------------------------------------------------*/
78
79static void *defalloc(mparena *a, size_t sz) { return xmalloc(sz); }
80static void deffree(mparena *a, void *p) { free(p); }
81
97d21728 82mparena_ops mparena_defaultops = { defalloc, deffree };
d3409d5e 83
84/*----- Static variables --------------------------------------------------*/
85
12a9a6e5 86#ifdef MPARENA_DEBUG
87 static FILE *debugfp = 0;
88
89# define MPARENA_OPENFILE do { \
90 if (!debugfp) { \
91 if ((debugfp = fopen(MPARENA_DEBUG, "w")) == 0) { \
92 fprintf(stderr, "couldn't open debug output file\n"); \
93 exit(EXIT_FAILURE); \
94 } \
95 } \
96 } while (0)
97
98#endif
99
97d21728 100static mparena arena = MPARENA_INIT;
d3409d5e 101
102#define MPARENA_RESOLVE(a) do { \
103 if ((a) == MPARENA_GLOBAL) \
104 (a) = &arena; \
105} while (0)
106
107/*----- Main code ---------------------------------------------------------*/
108
109/* --- @tdump@ --- *
110 *
111 * Arguments: @mparena_node *n@ = pointer to tree node to dump
112 *
113 * Returns: ---
114 *
115 * Use: Recursively dumps out the allocation tree.
116 */
117
12a9a6e5 118#ifdef MPARENA_DEBUG
119
d3409d5e 120static void tdump(mparena_node *n)
121{
122 if (!n)
12a9a6e5 123 putc('*', debugfp);
d3409d5e 124 else {
12a9a6e5 125 putc('(', debugfp);
d3409d5e 126 tdump(n->left);
12a9a6e5 127 fprintf(debugfp, ", %u, ", n->v[0]);
d3409d5e 128 tdump(n->right);
12a9a6e5 129 putc(')', debugfp);
d3409d5e 130 }
131}
132
12a9a6e5 133#endif
134
d3409d5e 135/* --- @mparena_create@ --- *
136 *
137 * Arguments: @mparena *a@ = pointer to arena block
138 *
139 * Returns: ---
140 *
141 * Use: Initializes an MP arena so that blocks can be allocated from
142 * it.
143 */
144
145void mparena_create(mparena *a)
146{
147 a->root = 0;
97d21728 148 a->n = 0;
149 a->ops = &mparena_defaultops;
d3409d5e 150}
151
152/* --- @mparena_setops@ --- *
153 *
154 * Arguments: @mparena *a@ = pointer to arena block
155 * @mparena_ops *ops@ = pointer to operations block or null
156 *
157 * Returns: The previous operations block.
158 *
159 * Use: Sets or queries the operations attached to an arena.
160 */
161
162mparena_ops *mparena_setops(mparena *a, mparena_ops *ops)
163{
164 mparena_ops *o;
165 MPARENA_RESOLVE(a);
166 o = a->ops;
167 if (ops)
168 a->ops = ops;
169 return (0);
170}
171
172/* --- @mparena_destroy@ --- *
173 *
174 * Arguments: @mparena *a@ = pointer to arena block
175 *
176 * Returns: ---
177 *
178 * Use: Frees an MP arena, and all the vectors held within it. The
179 * blocks which are currently allocated can be freed into some
180 * other arena.
181 */
182
183static void tfree(mparena *a, mparena_node *n)
184{
185 a->ops->free(a, n->v);
186 if (n->left)
187 tfree(a, n->left);
188 if (n->right)
189 tfree(a, n->right);
190 DESTROY(n);
191}
192
193void mparena_destroy(mparena *a)
194{
195 tfree(a, a->root);
196 a->root = 0;
197}
198
97d21728 199/* --- @mparena_count@ --- *
200 *
201 * Arguments: @mparena *a@ = pointer to arena block
202 *
203 * Returns: Number of allocated blocks from this arena.
204 *
205 * Use: Reports the number of blocks allocated from the arena and not
206 * yet freed.
207 */
208
209unsigned mparena_count(mparena *a)
210{
211 MPARENA_RESOLVE(a);
212 return (a->n);
213}
214
d3409d5e 215/* --- @mpalloc@ --- *
216 *
217 * Arguments: @mparena *a@ = pointer to arena block
218 * @size_t sz@ = number of digits required
219 *
220 * Returns: Pointer to a suitably sized block.
221 *
222 * Use: Allocates a lump of data suitable for use as an array of MP
223 * digits.
224 */
225
12a9a6e5 226#ifdef MPARENA_TRIVIAL
227
228mpw *mpalloc(mparena *a, size_t sz)
229{
230 MPARENA_RESOLVE(a);
231 return (a->ops->alloc(a, MPWS(sz)));
232}
233
234#else
235
d3409d5e 236mpw *mpalloc(mparena *a, size_t sz)
237{
238 mparena_node **nn, *n;
239 mpw *v;
240
241 MPARENA_RESOLVE(a);
242 nn = &a->root;
243
12a9a6e5 244#ifdef MPARENA_DEBUG
245 MPARENA_OPENFILE;
246 fprintf(debugfp, "alloc %u\n before: ", sz);
247 tdump(a->root); putc('\n', debugfp);
d3409d5e 248#endif
249
250 /* --- First, find a block which is big enough --- */
251
252again:
253 n = *nn;
254 if (!n) {
12a9a6e5 255#ifdef MPARENA_DEBUG
256 fputs(" failed\n", debugfp);
257#endif
d3409d5e 258 v = a->ops->alloc(a, MPWS(sz + 1));
259 v[0] = sz;
97d21728 260 a->n++;
d3409d5e 261 return (v + 1);
262 }
263 if (n->v[0] < sz) {
264 nn = &n->right;
265 goto again;
266 }
267
268 /* --- Now try to find a smaller block which is suitable --- */
269
270 while (n->left && n->left->v[0] >= sz) {
271 nn = &n->left;
272 n = *nn;
273 }
274
275 /* --- If the block we've got is still too large, start digging --- */
276
12a9a6e5 277 if (n->v[0] > sz * 2) {
d3409d5e 278 nn = &n->left;
279 goto again;
280 }
281
282 /* --- I've now found a suitable block --- */
283
284 v = n->v;
285
286 /* --- Remove this node from the tree --- */
287
288 if (!n->left)
289 *nn = n->right;
290 else if (!n->right)
291 *nn = n->left;
292 else {
293 mparena_node *left = n->left;
294 mparena_node *p = *nn = n->right;
295 while (p->left)
296 p = p->left;
297 p->left = left;
298 }
299
12a9a6e5 300#ifdef MPARENA_DEBUG
301 fputs(" after: ", debugfp);
302 tdump(a->root); putc('\n', debugfp);
303#endif
304
d3409d5e 305 /* --- Get rid of this node now --- */
306
307 DESTROY(n);
97d21728 308 a->n++;
d3409d5e 309 return (v + 1);
310}
311
12a9a6e5 312#endif
313
d3409d5e 314/* --- @mpfree@ --- *
315 *
316 * Arguments: @mparena *a@ = pointer to arena block
317 * @mpw *v@ = pointer to allocated vector
318 *
319 * Returns: ---
320 *
97d21728 321 * Use: Returns an MP vector to an arena.
d3409d5e 322 */
323
12a9a6e5 324#ifdef MPARENA_TRIVIAL
325
326void mpfree(mparena *a, mpw *v)
327{
328 MPARENA_RESOLVE(a);
329 a->ops->free(a, v);
330}
331
332#else
333
d3409d5e 334void mpfree(mparena *a, mpw *v)
335{
336 mparena_node **nn, *n;
337 size_t sz = *--v;
338
339 MPARENA_RESOLVE(a);
d3409d5e 340
12a9a6e5 341#ifdef MPARENA_DEBUG
342 MPARENA_OPENFILE;
343 fprintf(debugfp, "free %u\n before: ", sz);
344 tdump(a->root); putc('\n', debugfp);
345#endif
346
347 nn = &a->root;
d3409d5e 348 while (*nn) {
349 n = *nn;
350 if (n->v[0] > sz)
351 nn = &n->left;
352 else
353 nn = &n->right;
354 }
355
356 n = CREATE(mparena_node);
357 n->left = n->right = 0;
358 n->v = v;
359 *nn = n;
97d21728 360 a->n--;
d3409d5e 361
12a9a6e5 362#ifdef MPARENA_DEBUG
363 fputs(" after: ", debugfp);
364 tdump(a->root); putc('\n', debugfp);
d3409d5e 365#endif
366}
367
12a9a6e5 368#endif
369
d3409d5e 370/*----- That's all, folks -------------------------------------------------*/