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