3 * $Id: sub.c,v 1.7 2003/05/18 15:10:20 mdw Exp $
5 * Allocation of known-size blocks
7 * (c) 1998 Straylight/Edgeware
10 /*----- Licensing notice --------------------------------------------------*
12 * This file is part of the mLib utilities library.
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.
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.
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,
30 /*----- Revision history --------------------------------------------------*
33 * Revision 1.7 2003/05/18 15:10:20 mdw
34 * Add debugging mode which just uses the underlying arena.
36 * Revision 1.6 2000/06/17 10:35:51 mdw
37 * Major overhaul for arena support.
39 * Revision 1.5 1999/05/19 20:27:11 mdw
40 * Change naming to match newer mLib conventions.
42 * Revision 1.4 1999/05/13 22:48:55 mdw
43 * Change `-ise' to `-ize' throughout.
45 * Revision 1.3 1999/05/06 19:51:35 mdw
46 * Reformatted the LGPL notice a little bit.
48 * Revision 1.2 1999/05/05 18:50:31 mdw
49 * Change licensing conditions to LGPL.
51 * Revision 1.1.1.1 1998/06/17 23:44:42 mdw
52 * Initial version of mLib
56 /*----- The big idea ------------------------------------------------------*
58 * This file provides an extra layer over @malloc@. It provides fast
59 * turnover for small blocks, and tries to minimize the per-block overhead.
61 * To do its job, @alloc@ must place an extra restriction on you: you must
62 * know the size of a block when you free it. Usually you'll have this
63 * information encoded in some way either in the block or in the thing that
64 * referenced it, so this isn't a hardship.
66 * It works fairly simply. If a request for a big block (as defined by the
67 * constants below) comes in, it gets sent on to @malloc@ unmolested. For
68 * small blocks, it goes straight to a `bin' -- a list containing free blocks
69 * of exactly that size, or the nearest bigger size we can manage. If the
70 * bin is empty, a `chunk' is allocated from @malloc@: this has enough room
71 * for lots of blocks of the requested size, so it ets split up and each
72 * individual small block is added to the bin list. The first block in the
73 * bin list is then removed and given to the caller. In this way, @malloc@
74 * only stores its information once for lots of little blocks, so we save
75 * memory. Because I know where the correct bin is just from the block size,
76 * and I don't need to do any searching at all in the usual case (because the
77 * list isn't empty) I can get a speed advantage too.
79 * This code is almost certainly not ANSI conformant, although I'm not
80 * actually sure. If some kind soul would let me know how seriously I've
81 * violated the standard, and whether this is easily fixable, I'd be
85 /*----- Header files ------------------------------------------------------*/
87 /* --- ANSI headers --- */
94 /* --- Local headers --- */
100 /*----- Configuration tweaks ----------------------------------------------*/
102 /* #define SUBARENA_TRIVIAL */
104 /*----- Static variables --------------------------------------------------*/
106 static size_t sizes[SUB_BINS];
108 /*----- Global variables --------------------------------------------------*/
112 #ifdef SUBARENA_TRIVIAL
114 typedef struct sub_link {
115 struct sub_link *next;
122 /*----- Main code ---------------------------------------------------------*/
124 /* --- @subarena_create@ --- *
126 * Arguments: @subarena *s@ = pointer to arena to initialize
127 * @arena *a@ = pointer to underlying arena block
131 * Use: Initialize a suballocation arena based on an underlying large
135 void subarena_create(subarena *s, arena *a)
137 #ifdef SUBARENA_TRIVIAL
143 for (i = 0; i < SUB_BINS; i++)
149 /* --- @subarena_destroy@ --- *
151 * Arguments: @subarena *s@ = pointer to arena to destroy
155 * Use: Destroys a suballocation arena, freeing all of the memory it
156 * contains back to the underlying large blocks arena.
159 void subarena_destroy(subarena *s)
161 #ifdef SUBARENA_TRIVIAL
165 for (l = s->bin[0]; l; l = ll) {
175 for (i = 0; i < SUB_BINS; i++) {
187 /* --- @subarena_alloc@ --- *
189 * Arguments: @subarena *s@ = pointer to arena
190 * @size_t s@ = size of chunk wanted
192 * Returns: Pointer to a block at least as large as the one wanted.
194 * Use: Allocates a small block of memory from the given pool. The
195 * exception @EXC_NOMEM@ is raised if the underlying arena is
199 void *subarena_alloc(subarena *s, size_t sz)
201 #ifdef SUBARENA_TRIVIAL
207 subarena_create(s, arena_global);
209 if ((l = a_alloc(s->a, sizeof(*l))) == 0)
211 if ((p = a_alloc(s->a, sz)) == 0) {
226 /* --- Ensure that everything is initialized --- */
229 subarena_create(s, arena_global);
231 /* --- Handle oversize blocks --- */
234 if (bin >= SUB_BINS) {
235 void *p = A_ALLOC(s->a, sz);
241 /* --- If the bin is empty, find some memory --- */
246 p = A_ALLOC(s->a, sizes[bin]);
258 *(void **)q = q + sz;
264 /* --- Extract the first block in the list --- */
267 s->bin[bin] = *(void **)p;
273 /* --- @subarena_free@ --- *
275 * Arguments: @subarena *s@ = pointer to arena
276 * @void *p@ = address of block to free
277 * @size_t s@ = size of block
281 * Use: Frees a block allocated by @subarena_alloc@.
284 void subarena_free(subarena *s, void *p, size_t sz)
286 #ifdef SUBARENA_TRIVIAL
288 sub_link *lh = s->bin[0], **l, *ll;
290 for (l = &lh; *l && (*l)->p != p; l = &(*l)->next)
294 assert(ll->sz == sz);
302 int bin = SUB_BIN(sz);
307 *(void **)p = s->bin[bin];
314 /*----- Compatibility stuff -----------------------------------------------*/
316 /* --- @sub_alloc@ --- *
318 * Arguments: @size_t s@ = size of chunk wanted
320 * Returns: Pointer to a block at least as large as the one wanted.
322 * Use: Allocates a small block of memory from the @sub_global@ pool.
325 void *(sub_alloc)(size_t sz) { return sub_alloc(sz); }
327 /* --- @sub_free@ --- *
329 * Arguments: @void *p@ = address of block to free
330 * @size_t s@ = size of block
334 * Use: Frees a block allocated by @sub_alloc@.
337 void (sub_free)(void *p, size_t sz) { sub_free(p, sz); }
339 /* --- @sub_init@ --- *
345 * Use: Initializes the magic allocator.
350 #ifndef SUBARENA_TRIVIAL
353 /* --- Initialize the sizes bins --- */
355 for (i = 1; i < SUB_BINS; i++) {
356 sizes[i] = ((SUB_CHUNK + SUB_BINSZ(i) - 1) /
357 SUB_BINSZ(i) * SUB_BINSZ(i));
362 /*----- Debugging code ----------------------------------------------------*/
367 #define SIZE_MAX 2048
368 #define ITERATIONS 500000
372 static void *block[BLOCKS];
373 static size_t size[BLOCKS];
380 for (count = 0; count < ITERATIONS; count++) {
383 sub_free(block[i], size[i]);
387 block[i] = sub_alloc(size[i] =
388 rand() % (SUB_MAXBIN - 128) + 128);
390 memset(block[i], 0, size[i]); /* trample allocated storage */
399 /*----- That's all, folks -------------------------------------------------*/