chiark / gitweb /
New multiprecision integer arithmetic suite.
[catacomb] / mparena.c
1 /* -*-c-*-
2  *
3  * $Id: mparena.c,v 1.1 1999/11/17 18:02:16 mdw Exp $
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 $
33  * Revision 1.1  1999/11/17 18:02:16  mdw
34  * New multiprecision integer arithmetic suite.
35  *
36  */
37
38 /*----- Header files ------------------------------------------------------*/
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include <mLib/alloc.h>
45 #include <mLib/sub.h>
46
47 #include "mparena.h"
48
49 /*----- Default allocator -------------------------------------------------*/
50
51 static void *defalloc(mparena *a, size_t sz) { return xmalloc(sz); }
52 static void deffree(mparena *a, void *p) { free(p); }
53
54 mparena_ops mparena_defops = { defalloc, deffree };
55
56 /*----- Static variables --------------------------------------------------*/
57
58 static mparena arena = { 0, &mparena_defops };
59
60 #define MPARENA_RESOLVE(a) do {                                         \
61   if ((a) == MPARENA_GLOBAL)                                            \
62     (a) = &arena;                                                       \
63 } while (0)
64
65 /*----- Main code ---------------------------------------------------------*/
66
67 /* --- @tdump@ --- *
68  *
69  * Arguments:   @mparena_node *n@ = pointer to tree node to dump
70  *
71  * Returns:     ---
72  *
73  * Use:         Recursively dumps out the allocation tree.
74  */
75
76 static void tdump(mparena_node *n)
77 {
78   if (!n)
79     putchar('*');
80   else {
81     putchar('(');
82     tdump(n->left);
83     printf(", %u, ", n->v[0]);
84     tdump(n->right);
85     putchar(')');
86   }
87 }
88
89 /* --- @mparena_create@ --- *
90  *
91  * Arguments:   @mparena *a@ = pointer to arena block
92  *
93  * Returns:     ---
94  *
95  * Use:         Initializes an MP arena so that blocks can be allocated from
96  *              it.
97  */
98
99 void mparena_create(mparena *a)
100 {
101   a->root = 0;
102   a->ops = &mparena_defops;
103 }
104
105 /* --- @mparena_setops@ --- *
106  *
107  * Arguments:   @mparena *a@ = pointer to arena block
108  *              @mparena_ops *ops@ = pointer to operations block or null
109  *
110  * Returns:     The previous operations block.
111  *
112  * Use:         Sets or queries the operations attached to an arena.
113  */
114
115 mparena_ops *mparena_setops(mparena *a, mparena_ops *ops)
116 {
117   mparena_ops *o;
118   MPARENA_RESOLVE(a);
119   o = a->ops;
120   if (ops)
121     a->ops = ops;
122   return (0);
123 }
124
125 /* --- @mparena_destroy@ --- *
126  *
127  * Arguments:   @mparena *a@ = pointer to arena block
128  *
129  * Returns:     ---
130  *
131  * Use:         Frees an MP arena, and all the vectors held within it.  The
132  *              blocks which are currently allocated can be freed into some
133  *              other arena.
134  */
135
136 static void tfree(mparena *a, mparena_node *n)
137 {
138   a->ops->free(a, n->v);
139   if (n->left)
140     tfree(a, n->left);
141   if (n->right)
142     tfree(a, n->right);
143   DESTROY(n);
144 }
145
146 void mparena_destroy(mparena *a)
147 {
148   tfree(a, a->root);
149   a->root = 0;
150 }
151
152 /* --- @mpalloc@ --- *
153  *
154  * Arguments:   @mparena *a@ = pointer to arena block
155  *              @size_t sz@ = number of digits required
156  *
157  * Returns:     Pointer to a suitably sized block.
158  *
159  * Use:         Allocates a lump of data suitable for use as an array of MP
160  *              digits.
161  */
162
163 mpw *mpalloc(mparena *a, size_t sz)
164 {
165   mparena_node **nn, *n;
166   mpw *v;
167
168   MPARENA_RESOLVE(a);
169   nn = &a->root;
170
171 #ifdef notdef
172   printf("*** alloc %u\n", sz);
173   tdump(a->root); putchar('\n');
174 #endif
175
176   /* --- First, find a block which is big enough --- */
177
178 again:
179   n = *nn;
180   if (!n) {
181     v = a->ops->alloc(a, MPWS(sz + 1));
182     v[0] = sz;
183     return (v + 1);
184   }
185   if (n->v[0] < sz) {
186     nn = &n->right;
187     goto again;
188   }
189
190   /* --- Now try to find a smaller block which is suitable --- */
191
192   while (n->left && n->left->v[0] >= sz) {
193     nn = &n->left;
194     n = *nn;
195   }
196
197   /* --- If the block we've got is still too large, start digging --- */
198
199   if (n->v[0] >= sz * 2) {
200     nn = &n->left;
201     goto again;
202   }
203
204   /* --- I've now found a suitable block --- */
205
206   v = n->v;
207
208   /* --- Remove this node from the tree --- */
209
210   if (!n->left)
211     *nn = n->right;
212   else if (!n->right)
213     *nn = n->left;
214   else {
215     mparena_node *left = n->left;
216     mparena_node *p = *nn = n->right;
217     while (p->left)
218       p = p->left;
219     p->left = left;
220   }
221
222   /* --- Get rid of this node now --- */
223
224   DESTROY(n);
225   return (v + 1);
226 }
227
228 /* --- @mpfree@ --- *
229  *
230  * Arguments:   @mparena *a@ = pointer to arena block
231  *              @mpw *v@ = pointer to allocated vector
232  *
233  * Returns:     ---
234  *
235  * Use:         Returns an MP vector to an arena.  It doesn't have to be
236  *              returned to the arena from which it was allocated.
237  */
238
239 void mpfree(mparena *a, mpw *v)
240 {
241   mparena_node **nn, *n;
242   size_t sz = *--v;
243
244   MPARENA_RESOLVE(a);
245   nn = &a->root;
246
247   while (*nn) {
248     n = *nn;
249     if (n->v[0] > sz)
250       nn = &n->left;
251     else
252       nn = &n->right;
253   }
254
255   n = CREATE(mparena_node);
256   n->left = n->right = 0;
257   n->v = v;
258   *nn = n;
259
260 #ifdef notdef
261   printf("*** free %u\n", sz);
262   tdump(a->root); putchar('\n');
263 #endif
264 }
265
266 /*----- That's all, folks -------------------------------------------------*/