blob: 5178b6698ef576148380d1360e391595c42f81c9 [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
Jack Jansen46ed2761996-10-23 15:46:57 +000055#ifdef USE_MALLOC_DEBUG
56/* You may also selectively enable some of these (but some are interdependent) */
Jack Jansenfa4fd8e1995-01-18 13:48:31 +000057#define DEBUG
58#define DEBUG2
59#define MSTATS
60#define RCHECK
Jack Jansen05cf7e01996-09-30 14:42:28 +000061#define VCHECK
Jack Jansen46ed2761996-10-23 15:46:57 +000062#endif /* USE_MALLOC_DEBUG */
Jack Jansenfa4fd8e1995-01-18 13:48:31 +000063
64typedef unsigned char u_char;
65typedef unsigned long u_long;
66typedef unsigned int u_int;
67typedef unsigned short u_short;
68typedef u_long caddr_t;
69
70#include <Memory.h>
71#include <stdlib.h>
72#include <string.h>
73
74#define NULL 0
75
76static void morecore();
77
78/*
79 * The overhead on a block is at least 4 bytes. When free, this space
80 * contains a pointer to the next free block, and the bottom two bits must
81 * be zero. When in use, the first byte is set to MAGIC, and the second
82 * byte is the size index. The remaining bytes are for alignment.
83 * If range checking is enabled then a second word holds the size of the
84 * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
85 * The order of elements is critical: ov_magic must overlay the low order
86 * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
87 */
88union overhead {
89 union overhead *ov_next; /* when free */
90 struct {
91 u_char ovu_magic0; /* magic number */
92 u_char ovu_index; /* bucket # */
93 u_char ovu_unused; /* unused */
94 u_char ovu_magic1; /* other magic number */
95#ifdef RCHECK
96 u_short ovu_rmagic; /* range magic number */
97 u_long ovu_size; /* actual block size */
98#endif
99 } ovu;
100#define ov_magic0 ovu.ovu_magic0
101#define ov_magic1 ovu.ovu_magic1
102#define ov_index ovu.ovu_index
103#define ov_rmagic ovu.ovu_rmagic
104#define ov_size ovu.ovu_size
105};
106
107#define MAGIC 0xef /* magic # on accounting info */
108#define RMAGIC 0x5555 /* magic # on range info */
109
110#ifdef RCHECK
111#define RSLOP sizeof (u_short)
112#else
113#define RSLOP 0
114#endif
115
116#define OVERHEAD (sizeof(union overhead) + RSLOP)
117
118/*
119 * nextf[i] is the pointer to the next free block of size 2^(i+3). The
120 * smallest allocatable block is 8 bytes. The overhead information
121 * precedes the data area returned to the user.
122 */
123#define NBUCKETS 11
124#define MAXMALLOC (1<<(NBUCKETS+2))
125static union overhead *nextf[NBUCKETS];
126
127#ifdef MSTATS
128/*
129 * nmalloc[i] is the difference between the number of mallocs and frees
130 * for a given block size.
131 */
132static u_int nmalloc[NBUCKETS+1];
133#include <stdio.h>
134#endif
135
136#if defined(DEBUG) || defined(RCHECK) || defined(DEBUG2)
137#define ASSERT(p) if (!(p)) botch(# p)
138#include <stdio.h>
139static
140botch(s)
141 char *s;
142{
143 fprintf(stderr, "\r\nmalloc assertion botched: %s\r\n", s);
144 (void) fflush(stderr); /* just in case user buffered it */
145 abort();
146}
147#else
148#define ASSERT(p)
149#endif
150
151void *
152malloc(nbytes)
153 size_t nbytes;
154{
155 register union overhead *op;
Jack Jansena6a55e91995-08-31 13:51:13 +0000156 register long bucket;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000157 register unsigned amt;
158
159 /*
160 ** First the simple case: we simple allocate big blocks directly
161 */
162 if ( nbytes + OVERHEAD >= MAXMALLOC ) {
163 op = (union overhead *)NewPtr(nbytes+OVERHEAD);
164 if ( op == NULL )
165 return NULL;
166 op->ov_magic0 = op->ov_magic1 = MAGIC;
167 op->ov_index = 0xff;
168#ifdef MSTATS
169 nmalloc[NBUCKETS]++;
170#endif
171#ifdef RCHECK
172 /*
173 * Record allocated size of block and
174 * bound space with magic numbers.
175 */
176 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
177 op->ov_rmagic = RMAGIC;
178 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
179#endif
180 return (void *)(op+1);
181 }
182 /*
183 * Convert amount of memory requested into closest block size
184 * stored in hash buckets which satisfies request.
185 * Account for space used per block for accounting.
186 */
187#ifndef RCHECK
188 amt = 8; /* size of first bucket */
189 bucket = 0;
190#else
191 amt = 16; /* size of first bucket */
192 bucket = 1;
193#endif
194 while (nbytes + OVERHEAD > amt) {
195 amt <<= 1;
196 if (amt == 0)
197 return (NULL);
198 bucket++;
199 }
200#ifdef DEBUG2
201 ASSERT( bucket < NBUCKETS );
202#endif
203 /*
204 * If nothing in hash bucket right now,
205 * request more memory from the system.
206 */
207 if ((op = nextf[bucket]) == NULL) {
208 morecore(bucket);
209 if ((op = nextf[bucket]) == NULL)
210 return (NULL);
211 }
212 /* remove from linked list */
213 nextf[bucket] = op->ov_next;
214 op->ov_magic0 = op->ov_magic1 = MAGIC;
215 op->ov_index = bucket;
216#ifdef MSTATS
217 nmalloc[bucket]++;
218#endif
219#ifdef RCHECK
220 /*
221 * Record allocated size of block and
222 * bound space with magic numbers.
223 */
224 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
225 op->ov_rmagic = RMAGIC;
226 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
227#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000228#ifdef VCHECK
229 memset((char *)(op+1), 0x41, nbytes);
230#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000231 return ((char *)(op + 1));
232}
233
234/*
235 * Allocate more memory to the indicated bucket.
236 */
237static void
238morecore(bucket)
239 int bucket;
240{
241 register union overhead *op;
242 register long sz; /* size of desired block */
243 long amt; /* amount to allocate */
244 int nblks; /* how many blocks we get */
245
246 /*
247 * sbrk_size <= 0 only for big, FLUFFY, requests (about
248 * 2^30 bytes on a VAX, I think) or for a negative arg.
249 */
250 sz = 1 << (bucket + 3);
251#ifdef DEBUG2
252 ASSERT(sz > 0);
253#endif
254 amt = MAXMALLOC;
255 nblks = amt / sz;
256#ifdef DEBUG2
257 ASSERT(nblks*sz == amt);
258#endif
259 op = (union overhead *)NewPtr(amt);
260 /* no more room! */
261 if (op == NULL)
262 return;
263 /*
264 * Add new memory allocated to that on
265 * free list for this hash bucket.
266 */
267 nextf[bucket] = op;
268 while (--nblks > 0) {
269 op->ov_next = (union overhead *)((caddr_t)op + sz);
270 op = (union overhead *)((caddr_t)op + sz);
271 }
272 op->ov_next = (union overhead *)NULL;
273}
274
275void
276free(cp)
277 void *cp;
278{
279 register long size;
280 register union overhead *op;
281
282 if (cp == NULL)
283 return;
284 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
285#ifdef DEBUG
286 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
287 ASSERT(op->ov_magic1 == MAGIC);
288#else
289 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
290 return; /* sanity */
291#endif
292#ifdef RCHECK
293 ASSERT(op->ov_rmagic == RMAGIC);
294 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
295#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000296#ifdef VCHECK
297 memset(cp, 43, op->ov_size);
298#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000299 size = op->ov_index;
300 if ( size == 0xff ) {
301#ifdef MSTATS
302 nmalloc[NBUCKETS]--;
303#endif
304 DisposPtr((Ptr)op);
305 return;
306 }
307 ASSERT(size < NBUCKETS);
308 op->ov_next = nextf[size]; /* also clobbers ov_magic */
309 nextf[size] = op;
310#ifdef MSTATS
311 nmalloc[size]--;
312#endif
313}
314
315void *
316realloc(cp, nbytes)
317 void *cp;
318 size_t nbytes;
319{
320 int i;
321 union overhead *op;
322 int expensive;
323 u_long maxsize;
324
325 if (cp == NULL)
326 return (malloc(nbytes));
327 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
328#ifdef DEBUG
329 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
330 ASSERT(op->ov_magic1 == MAGIC);
331#else
332 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
333 return NULL; /* sanity */
334#endif
335#ifdef RCHECK
336 ASSERT(op->ov_rmagic == RMAGIC);
337 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
338#endif
339 i = op->ov_index;
340 /*
341 ** First the malloc/copy cases
342 */
343 expensive = 0;
344 if ( i == 0xff ) {
Jack Jansenf2e51291995-01-22 16:44:49 +0000345 /* Big block. See if it has to stay big */
346 if (nbytes+OVERHEAD > MAXMALLOC) {
347 /* Yup, try to resize it first */
348 SetPtrSize((Ptr)op, nbytes+OVERHEAD);
349 if ( MemError() == 0 ) {
350#ifdef RCHECK
351 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
352 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
353#endif
354 return cp;
355 }
356 /* Nope, failed. Take the long way */
357 }
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000358 maxsize = GetPtrSize((Ptr)op);
Jack Jansenf2e51291995-01-22 16:44:49 +0000359 expensive = 1;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000360 } else {
361 maxsize = 1 << (i+3);
362 if ( nbytes + OVERHEAD > maxsize )
363 expensive = 1;
364 else if ( i > 0 && nbytes + OVERHEAD < (maxsize/2) )
365 expensive = 1;
366 }
367 if ( expensive ) {
368 void *newp;
369
370 newp = malloc(nbytes);
371 if ( newp == NULL ) {
372 return NULL;
373 }
374 maxsize -= OVERHEAD;
375 if ( maxsize < nbytes )
376 nbytes = maxsize;
377 memcpy(newp, cp, nbytes);
378 free(cp);
379 return newp;
380 }
381 /*
382 ** Ok, we don't have to copy, it fits as-is
383 */
384#ifdef RCHECK
385 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
386 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
387#endif
388 return(cp);
389}
390
391
392#ifdef MSTATS
393/*
394 * mstats - print out statistics about malloc
395 *
396 * Prints two lines of numbers, one showing the length of the free list
397 * for each size category, the second showing the number of mallocs -
398 * frees for each size category.
399 */
400mstats(s)
401 char *s;
402{
403 register int i, j;
404 register union overhead *p;
405 int totfree = 0,
406 totused = 0;
407
408 fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
409 for (i = 0; i < NBUCKETS; i++) {
410 for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
411 ;
412 fprintf(stderr, " %d", j);
413 totfree += j * (1 << (i + 3));
414 }
415 fprintf(stderr, "\nused:\t");
416 for (i = 0; i < NBUCKETS; i++) {
417 fprintf(stderr, " %d", nmalloc[i]);
418 totused += nmalloc[i] * (1 << (i + 3));
419 }
420 fprintf(stderr, "\n\tTotal small in use: %d, total free: %d\n",
421 totused, totfree);
422 fprintf(stderr, "\n\tNumber of big (>%d) blocks in use: %d\n", MAXMALLOC, nmalloc[NBUCKETS]);
423}
424#endif