blob: 897d500cb51b31a4a9bb374d861c9889be0958c6 [file] [log] [blame]
Jack Jansenfa4fd8e1995-01-18 13:48:31 +00001/*
2 * Attempt at a memory allocator for the Mac, Jack Jansen, CWI, 1995.
3 *
4 * Code adapted from BSD malloc, which is:
5 *
6 * Copyright (c) 1983 Regents of the University of California.
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by the University of
20 * California, Berkeley and its contributors.
21 * 4. Neither the name of the University nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#if defined(LIBC_SCCS) && !defined(lint)
39/*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/
40static char *rcsid = "$Id$";
41#endif /* LIBC_SCCS and not lint */
42
43/*
44 * malloc.c (Caltech) 2/21/82
45 * Chris Kingsley, kingsley@cit-20. Modified by Jack Jansen, CWI.
46 *
47 * This is a very fast storage allocator. It allocates blocks of a small
48 * number of different sizes, and keeps free lists of each size. Blocks that
49 * don't exactly fit are passed up to the next larger size.
50 *
51 * Blocks over a certain size are directly allocated by calling NewPtr.
52 *
53 */
54
55
56#define DEBUG
57#define DEBUG2
58#define MSTATS
59#define RCHECK
Jack Jansen05cf7e01996-09-30 14:42:28 +000060#define VCHECK
Jack Jansenfa4fd8e1995-01-18 13:48:31 +000061
62typedef unsigned char u_char;
63typedef unsigned long u_long;
64typedef unsigned int u_int;
65typedef unsigned short u_short;
66typedef u_long caddr_t;
67
68#include <Memory.h>
69#include <stdlib.h>
70#include <string.h>
71
72#define NULL 0
73
74static void morecore();
75
76/*
77 * The overhead on a block is at least 4 bytes. When free, this space
78 * contains a pointer to the next free block, and the bottom two bits must
79 * be zero. When in use, the first byte is set to MAGIC, and the second
80 * byte is the size index. The remaining bytes are for alignment.
81 * If range checking is enabled then a second word holds the size of the
82 * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
83 * The order of elements is critical: ov_magic must overlay the low order
84 * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
85 */
86union overhead {
87 union overhead *ov_next; /* when free */
88 struct {
89 u_char ovu_magic0; /* magic number */
90 u_char ovu_index; /* bucket # */
91 u_char ovu_unused; /* unused */
92 u_char ovu_magic1; /* other magic number */
93#ifdef RCHECK
94 u_short ovu_rmagic; /* range magic number */
95 u_long ovu_size; /* actual block size */
96#endif
97 } ovu;
98#define ov_magic0 ovu.ovu_magic0
99#define ov_magic1 ovu.ovu_magic1
100#define ov_index ovu.ovu_index
101#define ov_rmagic ovu.ovu_rmagic
102#define ov_size ovu.ovu_size
103};
104
105#define MAGIC 0xef /* magic # on accounting info */
106#define RMAGIC 0x5555 /* magic # on range info */
107
108#ifdef RCHECK
109#define RSLOP sizeof (u_short)
110#else
111#define RSLOP 0
112#endif
113
114#define OVERHEAD (sizeof(union overhead) + RSLOP)
115
116/*
117 * nextf[i] is the pointer to the next free block of size 2^(i+3). The
118 * smallest allocatable block is 8 bytes. The overhead information
119 * precedes the data area returned to the user.
120 */
121#define NBUCKETS 11
122#define MAXMALLOC (1<<(NBUCKETS+2))
123static union overhead *nextf[NBUCKETS];
124
125#ifdef MSTATS
126/*
127 * nmalloc[i] is the difference between the number of mallocs and frees
128 * for a given block size.
129 */
130static u_int nmalloc[NBUCKETS+1];
131#include <stdio.h>
132#endif
133
134#if defined(DEBUG) || defined(RCHECK) || defined(DEBUG2)
135#define ASSERT(p) if (!(p)) botch(# p)
136#include <stdio.h>
137static
138botch(s)
139 char *s;
140{
141 fprintf(stderr, "\r\nmalloc assertion botched: %s\r\n", s);
142 (void) fflush(stderr); /* just in case user buffered it */
143 abort();
144}
145#else
146#define ASSERT(p)
147#endif
148
149void *
150malloc(nbytes)
151 size_t nbytes;
152{
153 register union overhead *op;
Jack Jansena6a55e91995-08-31 13:51:13 +0000154 register long bucket;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000155 register unsigned amt;
156
157 /*
158 ** First the simple case: we simple allocate big blocks directly
159 */
160 if ( nbytes + OVERHEAD >= MAXMALLOC ) {
161 op = (union overhead *)NewPtr(nbytes+OVERHEAD);
162 if ( op == NULL )
163 return NULL;
164 op->ov_magic0 = op->ov_magic1 = MAGIC;
165 op->ov_index = 0xff;
166#ifdef MSTATS
167 nmalloc[NBUCKETS]++;
168#endif
169#ifdef RCHECK
170 /*
171 * Record allocated size of block and
172 * bound space with magic numbers.
173 */
174 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
175 op->ov_rmagic = RMAGIC;
176 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
177#endif
178 return (void *)(op+1);
179 }
180 /*
181 * Convert amount of memory requested into closest block size
182 * stored in hash buckets which satisfies request.
183 * Account for space used per block for accounting.
184 */
185#ifndef RCHECK
186 amt = 8; /* size of first bucket */
187 bucket = 0;
188#else
189 amt = 16; /* size of first bucket */
190 bucket = 1;
191#endif
192 while (nbytes + OVERHEAD > amt) {
193 amt <<= 1;
194 if (amt == 0)
195 return (NULL);
196 bucket++;
197 }
198#ifdef DEBUG2
199 ASSERT( bucket < NBUCKETS );
200#endif
201 /*
202 * If nothing in hash bucket right now,
203 * request more memory from the system.
204 */
205 if ((op = nextf[bucket]) == NULL) {
206 morecore(bucket);
207 if ((op = nextf[bucket]) == NULL)
208 return (NULL);
209 }
210 /* remove from linked list */
211 nextf[bucket] = op->ov_next;
212 op->ov_magic0 = op->ov_magic1 = MAGIC;
213 op->ov_index = bucket;
214#ifdef MSTATS
215 nmalloc[bucket]++;
216#endif
217#ifdef RCHECK
218 /*
219 * Record allocated size of block and
220 * bound space with magic numbers.
221 */
222 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
223 op->ov_rmagic = RMAGIC;
224 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
225#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000226#ifdef VCHECK
227 memset((char *)(op+1), 0x41, nbytes);
228#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000229 return ((char *)(op + 1));
230}
231
232/*
233 * Allocate more memory to the indicated bucket.
234 */
235static void
236morecore(bucket)
237 int bucket;
238{
239 register union overhead *op;
240 register long sz; /* size of desired block */
241 long amt; /* amount to allocate */
242 int nblks; /* how many blocks we get */
243
244 /*
245 * sbrk_size <= 0 only for big, FLUFFY, requests (about
246 * 2^30 bytes on a VAX, I think) or for a negative arg.
247 */
248 sz = 1 << (bucket + 3);
249#ifdef DEBUG2
250 ASSERT(sz > 0);
251#endif
252 amt = MAXMALLOC;
253 nblks = amt / sz;
254#ifdef DEBUG2
255 ASSERT(nblks*sz == amt);
256#endif
257 op = (union overhead *)NewPtr(amt);
258 /* no more room! */
259 if (op == NULL)
260 return;
261 /*
262 * Add new memory allocated to that on
263 * free list for this hash bucket.
264 */
265 nextf[bucket] = op;
266 while (--nblks > 0) {
267 op->ov_next = (union overhead *)((caddr_t)op + sz);
268 op = (union overhead *)((caddr_t)op + sz);
269 }
270 op->ov_next = (union overhead *)NULL;
271}
272
273void
274free(cp)
275 void *cp;
276{
277 register long size;
278 register union overhead *op;
279
280 if (cp == NULL)
281 return;
282 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
283#ifdef DEBUG
284 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
285 ASSERT(op->ov_magic1 == MAGIC);
286#else
287 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
288 return; /* sanity */
289#endif
290#ifdef RCHECK
291 ASSERT(op->ov_rmagic == RMAGIC);
292 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
293#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000294#ifdef VCHECK
295 memset(cp, 43, op->ov_size);
296#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000297 size = op->ov_index;
298 if ( size == 0xff ) {
299#ifdef MSTATS
300 nmalloc[NBUCKETS]--;
301#endif
302 DisposPtr((Ptr)op);
303 return;
304 }
305 ASSERT(size < NBUCKETS);
306 op->ov_next = nextf[size]; /* also clobbers ov_magic */
307 nextf[size] = op;
308#ifdef MSTATS
309 nmalloc[size]--;
310#endif
311}
312
313void *
314realloc(cp, nbytes)
315 void *cp;
316 size_t nbytes;
317{
318 int i;
319 union overhead *op;
320 int expensive;
321 u_long maxsize;
322
323 if (cp == NULL)
324 return (malloc(nbytes));
325 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
326#ifdef DEBUG
327 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
328 ASSERT(op->ov_magic1 == MAGIC);
329#else
330 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
331 return NULL; /* sanity */
332#endif
333#ifdef RCHECK
334 ASSERT(op->ov_rmagic == RMAGIC);
335 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
336#endif
337 i = op->ov_index;
338 /*
339 ** First the malloc/copy cases
340 */
341 expensive = 0;
342 if ( i == 0xff ) {
Jack Jansenf2e51291995-01-22 16:44:49 +0000343 /* Big block. See if it has to stay big */
344 if (nbytes+OVERHEAD > MAXMALLOC) {
345 /* Yup, try to resize it first */
346 SetPtrSize((Ptr)op, nbytes+OVERHEAD);
347 if ( MemError() == 0 ) {
348#ifdef RCHECK
349 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
350 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
351#endif
352 return cp;
353 }
354 /* Nope, failed. Take the long way */
355 }
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000356 maxsize = GetPtrSize((Ptr)op);
Jack Jansenf2e51291995-01-22 16:44:49 +0000357 expensive = 1;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000358 } else {
359 maxsize = 1 << (i+3);
360 if ( nbytes + OVERHEAD > maxsize )
361 expensive = 1;
362 else if ( i > 0 && nbytes + OVERHEAD < (maxsize/2) )
363 expensive = 1;
364 }
365 if ( expensive ) {
366 void *newp;
367
368 newp = malloc(nbytes);
369 if ( newp == NULL ) {
370 return NULL;
371 }
372 maxsize -= OVERHEAD;
373 if ( maxsize < nbytes )
374 nbytes = maxsize;
375 memcpy(newp, cp, nbytes);
376 free(cp);
377 return newp;
378 }
379 /*
380 ** Ok, we don't have to copy, it fits as-is
381 */
382#ifdef RCHECK
383 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
384 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
385#endif
386 return(cp);
387}
388
389
390#ifdef MSTATS
391/*
392 * mstats - print out statistics about malloc
393 *
394 * Prints two lines of numbers, one showing the length of the free list
395 * for each size category, the second showing the number of mallocs -
396 * frees for each size category.
397 */
398mstats(s)
399 char *s;
400{
401 register int i, j;
402 register union overhead *p;
403 int totfree = 0,
404 totused = 0;
405
406 fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
407 for (i = 0; i < NBUCKETS; i++) {
408 for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
409 ;
410 fprintf(stderr, " %d", j);
411 totfree += j * (1 << (i + 3));
412 }
413 fprintf(stderr, "\nused:\t");
414 for (i = 0; i < NBUCKETS; i++) {
415 fprintf(stderr, " %d", nmalloc[i]);
416 totused += nmalloc[i] * (1 << (i + 3));
417 }
418 fprintf(stderr, "\n\tTotal small in use: %d, total free: %d\n",
419 totused, totfree);
420 fprintf(stderr, "\n\tNumber of big (>%d) blocks in use: %d\n", MAXMALLOC, nmalloc[NBUCKETS]);
421}
422#endif