Change licensing conditions to LGPL.
[mLib] / sub.c
1 /* -*-c-*-
2 *
3 * $Id: sub.c,v 1.2 1999/05/05 18:50:31 mdw Exp $
4 *
5 * Allocation of known-size blocks
6 *
7 * (c) 1998 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 Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 */
28
29 /*----- Revision history --------------------------------------------------*
30 *
31 * $Log: sub.c,v $
32 * Revision 1.2 1999/05/05 18:50:31 mdw
33 * Change licensing conditions to LGPL.
34 *
35 * Revision 1.1.1.1 1998/06/17 23:44:42 mdw
36 * Initial version of mLib
37 *
38 */
39
40 /*----- The big idea ------------------------------------------------------*
41 *
42 * This file provides an extra layer over @malloc@. It provides fast
43 * turnover for small blocks, and tries to minimise the per-block overhead.
44 *
45 * To do its job, @alloc@ must place an extra restriction on you: you must
46 * know the size of a block when you free it. Usually you'll have this
47 * information encoded in some way either in the block or in the thing that
48 * referenced it, so this isn't a hardship.
49 *
50 * It works fairly simply. If a request for a big block (as defined by the
51 * constants below) comes in, it gets sent on to @malloc@ unmolested. For
52 * small blocks, it goes straight to a `bin' -- a list containing free blocks
53 * of exactly that size, or the nearest bigger size we can manage. If the
54 * bin is empty, a `chunk' is allocated from @malloc@: this has enough room
55 * for lots of blocks of the requested size, so it ets split up and each
56 * individual small block is added to the bin list. The first block in the
57 * bin list is then removed and given to the caller. In this way, @malloc@
58 * only stores its information once for lots of little blocks, so we save
59 * memory. Because I know where the correct bin is just from the block size,
60 * and I don't need to do any searching at all in the usual case (because the
61 * list isn't empty) I can get a speed advantage too.
62 *
63 * This code is almost certainly not ANSI conformant, although I'm not
64 * actually sure. If some kind soul would let me know how seriously I've
65 * violated the standard, and whether this is easily fixable, I'd be
66 * grateful.
67 */
68
69 /*----- Header files ------------------------------------------------------*/
70
71 /* --- ANSI headers --- */
72
73 #include <stdio.h>
74 #include <stdlib.h>
75 #include <string.h>
76
77 /* --- Local headers --- */
78
79 #undef TRACK_ENABLE /* Can't track suballoc routines */
80 #include "alloc.h"
81
82 /*----- Configuration and tuning ------------------------------------------*/
83
84 /* --- The largest block I'll handle here --- *
85 *
86 * Anything larger will be handed on to @malloc@.
87 */
88
89 #define SUB_MAXBIN 256
90
91 /* --- Preferred chunk size --- *
92 *
93 * When a bin is empty, I'll allocate a large chunk of approximately this
94 * size and divvy it up into small bin-sized blocks.
95 */
96
97 #define SUB_CHUNK 4096
98
99 /*----- Other useful macros -----------------------------------------------*/
100
101 /* --- The granularity of bin buffers --- *
102 *
103 * All blocks allocated by the binner are a multiple of this size. I've
104 * chosen @void *@ because I need to store @void *@ things in here.
105 */
106
107 #define SUB_GRANULE sizeof(void *)
108
109 /* --- Finding the right bin for a given size --- *
110 *
111 * This chooses the correct bin for an allocation. Input is the size of
112 * block wanted; result is the bin index.
113 */
114
115 #define SUB_BIN(x) (((x) + SUB_GRANULE - 1) / SUB_GRANULE)
116
117 /* --- Convert a bin back to the block size --- *
118 *
119 * This gives the size of block contained in a given bin.
120 */
121
122 #define SUB_BINSZ(x) ((x) * SUB_GRANULE)
123
124 /* --- Number of bins required --- */
125
126 #define SUB_BINS (SUB_MAXBIN / SUB_GRANULE + 1)
127
128 /*----- Static variables --------------------------------------------------*/
129
130 static void *sub__bins[SUB_BINS];
131 static size_t sub__sizes[SUB_BINS];
132
133 /*----- Main code ---------------------------------------------------------*/
134
135 /* --- @sub_alloc@ --- *
136 *
137 * Arguments: @size_t s@ = size of chunk wanted
138 *
139 * Returns: Pointer to a block at least as large as the one wanted.
140 *
141 * Use: Allocates a small block of memory. If there is no more
142 * memory left, the exception @EXC_NOMEM@ is raised.
143 */
144
145 void *sub_alloc(size_t s)
146 {
147 int bin = SUB_BIN(s);
148 void *p;
149
150 /* --- Handle oversize blocks --- */
151
152 if (bin >= SUB_BINS)
153 return (xmalloc(s));
154
155 /* --- If the bin is empty, find some memory --- */
156
157 if (!sub__bins[bin]) {
158 char *p, *q;
159
160 p = xmalloc(sub__sizes[bin]);
161 q = p + sub__sizes[bin];
162
163 s = SUB_BINSZ(bin);
164
165 q -= s;
166 *(void **)q = 0;
167
168 while (q > p) {
169 q -= s;
170 *(void **)q = q + s;
171 }
172
173 sub__bins[bin] = p;
174 }
175
176 /* --- Extract the first block in the list --- */
177
178 p = sub__bins[bin];
179 sub__bins[bin] = *(void **)p;
180 return (p);
181 }
182
183 /* --- @sub_free@ --- *
184 *
185 * Arguments: @void *p@ = address of block to free
186 * @size_t s@ = size of block
187 *
188 * Returns: ---
189 *
190 * Use: Frees a block allocated by @sub_alloc@.
191 */
192
193 void sub_free(void *p, size_t s)
194 {
195 int bin = SUB_BIN(s);
196
197 if (bin >= SUB_BINS)
198 free(p);
199 else {
200 *(void **)p = sub__bins[bin];
201 sub__bins[bin] = p;
202 }
203 }
204
205 /* --- @sub_init@ --- *
206 *
207 * Arguments: ---
208 *
209 * Returns: ---
210 *
211 * Use: Initialises the magic allocator.
212 */
213
214 void sub_init(void)
215 {
216 int i;
217
218 /* --- Initialise the sizes bins --- */
219
220 for (i = 1; i < SUB_BINS; i++) {
221 sub__sizes[i] = ((SUB_CHUNK + SUB_BINSZ(i) - 1) /
222 SUB_BINSZ(i) * SUB_BINSZ(i));
223 }
224 }
225
226 /*----- Debugging code ----------------------------------------------------*/
227
228 #ifdef TEST_RIG
229
230 #define BLOCKS 1024
231 #define SIZE_MAX 2048
232 #define ITERATIONS 500000
233
234 int main(void)
235 {
236 static void *block[BLOCKS];
237 static size_t size[BLOCKS];
238 size_t allocced = 0;
239 int i;
240 long count;
241
242 sub_init();
243
244 for (count = 0; count < ITERATIONS; count++) {
245 i = rand() % BLOCKS;
246 if (block[i]) {
247 sub_free(block[i], size[i]);
248 block[i] = 0;
249 allocced -= size[i];
250 } else {
251 block[i] = sub_alloc(size[i] =
252 rand() % (SUB_MAXBIN - 128) + 128);
253 allocced += size[i];
254 memset(block[i], 0, size[i]); /* trample allocated storage */
255 }
256 }
257
258 return (0);
259 }
260
261 #endif
262
263 /*----- That's all, folks -------------------------------------------------*/