blob: 38bd32a857b7e4a171133cd0b58f3b16e015526e [file] [log] [blame]
Jack Jansenfa4fd8e1995-01-18 13:48:31 +00001/*
Jack Jansen42218ce1997-01-31 16:15:11 +00002 * Attempt at a memory allocator for the Mac, Jack Jansen, CWI, 1995-1997.
Jack Jansenfa4fd8e1995-01-18 13:48:31 +00003 *
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
Jack Jansen8d1ac021997-05-29 14:57:45 +000064/*
65 * Set the next define if you want to return memory that is aligned to 32-byte
66 * boundaries. This allows 604 (and, to a lesser extent, any PPC) programs to
67 * make better use of the L1 cache.
68 */
69/* #define USE_CACHE_ALIGNED 8 /* The alignment (in 4-byte words) */
70
Jack Jansenfa4fd8e1995-01-18 13:48:31 +000071typedef unsigned char u_char;
72typedef unsigned long u_long;
73typedef unsigned int u_int;
74typedef unsigned short u_short;
75typedef u_long caddr_t;
76
77#include <Memory.h>
78#include <stdlib.h>
79#include <string.h>
80
Jack Jansen9ae898b2000-07-11 21:16:03 +000081#ifndef NULL
Jack Jansenfa4fd8e1995-01-18 13:48:31 +000082#define NULL 0
Jack Jansen9ae898b2000-07-11 21:16:03 +000083#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +000084
85static void morecore();
86
87/*
88 * The overhead on a block is at least 4 bytes. When free, this space
89 * contains a pointer to the next free block, and the bottom two bits must
90 * be zero. When in use, the first byte is set to MAGIC, and the second
91 * byte is the size index. The remaining bytes are for alignment.
92 * If range checking is enabled then a second word holds the size of the
93 * requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
94 * The order of elements is critical: ov_magic must overlay the low order
95 * bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
96 */
97union overhead {
98 union overhead *ov_next; /* when free */
99 struct {
100 u_char ovu_magic0; /* magic number */
101 u_char ovu_index; /* bucket # */
102 u_char ovu_unused; /* unused */
103 u_char ovu_magic1; /* other magic number */
104#ifdef RCHECK
105 u_short ovu_rmagic; /* range magic number */
106 u_long ovu_size; /* actual block size */
107#endif
108 } ovu;
109#define ov_magic0 ovu.ovu_magic0
110#define ov_magic1 ovu.ovu_magic1
111#define ov_index ovu.ovu_index
112#define ov_rmagic ovu.ovu_rmagic
113#define ov_size ovu.ovu_size
Jack Jansen3c2871e1997-02-03 15:06:45 +0000114#ifdef USE_CACHE_ALIGNED
Jack Jansenb4ef4c61997-02-01 23:44:50 +0000115 struct cachealigner {
Jack Jansen3c2871e1997-02-03 15:06:45 +0000116 u_long ovalign[USE_CACHE_ALIGNED];
Jack Jansen666212d1997-02-24 14:00:52 +0000117 } ovu_aligner;
Jack Jansenb4ef4c61997-02-01 23:44:50 +0000118#endif /* USE_CACHE_ALIGN */
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000119};
120
121#define MAGIC 0xef /* magic # on accounting info */
122#define RMAGIC 0x5555 /* magic # on range info */
123
124#ifdef RCHECK
125#define RSLOP sizeof (u_short)
126#else
127#define RSLOP 0
128#endif
129
130#define OVERHEAD (sizeof(union overhead) + RSLOP)
131
132/*
133 * nextf[i] is the pointer to the next free block of size 2^(i+3). The
134 * smallest allocatable block is 8 bytes. The overhead information
135 * precedes the data area returned to the user.
136 */
137#define NBUCKETS 11
138#define MAXMALLOC (1<<(NBUCKETS+2))
139static union overhead *nextf[NBUCKETS];
140
141#ifdef MSTATS
142/*
143 * nmalloc[i] is the difference between the number of mallocs and frees
144 * for a given block size.
145 */
146static u_int nmalloc[NBUCKETS+1];
147#include <stdio.h>
148#endif
149
150#if defined(DEBUG) || defined(RCHECK) || defined(DEBUG2)
151#define ASSERT(p) if (!(p)) botch(# p)
152#include <stdio.h>
153static
154botch(s)
155 char *s;
156{
157 fprintf(stderr, "\r\nmalloc assertion botched: %s\r\n", s);
158 (void) fflush(stderr); /* just in case user buffered it */
159 abort();
160}
161#else
162#define ASSERT(p)
163#endif
164
165void *
166malloc(nbytes)
167 size_t nbytes;
168{
169 register union overhead *op;
Jack Jansena6a55e91995-08-31 13:51:13 +0000170 register long bucket;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000171 register unsigned amt;
172
173 /*
174 ** First the simple case: we simple allocate big blocks directly
175 */
176 if ( nbytes + OVERHEAD >= MAXMALLOC ) {
177 op = (union overhead *)NewPtr(nbytes+OVERHEAD);
178 if ( op == NULL )
179 return NULL;
180 op->ov_magic0 = op->ov_magic1 = MAGIC;
181 op->ov_index = 0xff;
182#ifdef MSTATS
183 nmalloc[NBUCKETS]++;
184#endif
185#ifdef RCHECK
186 /*
187 * Record allocated size of block and
188 * bound space with magic numbers.
189 */
190 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
191 op->ov_rmagic = RMAGIC;
192 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
193#endif
194 return (void *)(op+1);
195 }
196 /*
197 * Convert amount of memory requested into closest block size
198 * stored in hash buckets which satisfies request.
199 * Account for space used per block for accounting.
200 */
201#ifndef RCHECK
202 amt = 8; /* size of first bucket */
203 bucket = 0;
204#else
205 amt = 16; /* size of first bucket */
206 bucket = 1;
207#endif
208 while (nbytes + OVERHEAD > amt) {
209 amt <<= 1;
210 if (amt == 0)
211 return (NULL);
212 bucket++;
213 }
214#ifdef DEBUG2
215 ASSERT( bucket < NBUCKETS );
216#endif
217 /*
218 * If nothing in hash bucket right now,
219 * request more memory from the system.
220 */
221 if ((op = nextf[bucket]) == NULL) {
222 morecore(bucket);
223 if ((op = nextf[bucket]) == NULL)
224 return (NULL);
225 }
226 /* remove from linked list */
227 nextf[bucket] = op->ov_next;
228 op->ov_magic0 = op->ov_magic1 = MAGIC;
229 op->ov_index = bucket;
230#ifdef MSTATS
231 nmalloc[bucket]++;
232#endif
233#ifdef RCHECK
234 /*
235 * Record allocated size of block and
236 * bound space with magic numbers.
237 */
238 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
239 op->ov_rmagic = RMAGIC;
240 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
241#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000242#ifdef VCHECK
243 memset((char *)(op+1), 0x41, nbytes);
244#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000245 return ((char *)(op + 1));
246}
247
248/*
249 * Allocate more memory to the indicated bucket.
250 */
251static void
252morecore(bucket)
253 int bucket;
254{
255 register union overhead *op;
256 register long sz; /* size of desired block */
257 long amt; /* amount to allocate */
258 int nblks; /* how many blocks we get */
259
260 /*
261 * sbrk_size <= 0 only for big, FLUFFY, requests (about
262 * 2^30 bytes on a VAX, I think) or for a negative arg.
263 */
264 sz = 1 << (bucket + 3);
265#ifdef DEBUG2
266 ASSERT(sz > 0);
267#endif
268 amt = MAXMALLOC;
269 nblks = amt / sz;
270#ifdef DEBUG2
271 ASSERT(nblks*sz == amt);
272#endif
Jack Jansen3c2871e1997-02-03 15:06:45 +0000273#ifdef USE_CACHE_ALIGNED
274 op = (union overhead *)NewPtr(amt+4*USE_CACHE_ALIGNED);
275#else
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000276 op = (union overhead *)NewPtr(amt);
Jack Jansen3c2871e1997-02-03 15:06:45 +0000277#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000278 /* no more room! */
279 if (op == NULL)
280 return;
Jack Jansen3c2871e1997-02-03 15:06:45 +0000281#ifdef USE_CACHE_ALIGNED
282#define ALIGN_MASK (4*USE_CACHE_ALIGNED-1)
283 while ((long)op & ALIGN_MASK )
284 op = (union overhead *)((long)op+1);
285#endif /* USE_CACHE_ALIGNED */
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000286 /*
287 * Add new memory allocated to that on
288 * free list for this hash bucket.
289 */
290 nextf[bucket] = op;
291 while (--nblks > 0) {
292 op->ov_next = (union overhead *)((caddr_t)op + sz);
293 op = (union overhead *)((caddr_t)op + sz);
294 }
295 op->ov_next = (union overhead *)NULL;
296}
297
298void
299free(cp)
300 void *cp;
301{
302 register long size;
303 register union overhead *op;
304
305 if (cp == NULL)
306 return;
307 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
308#ifdef DEBUG
309 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
310 ASSERT(op->ov_magic1 == MAGIC);
311#else
312 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
313 return; /* sanity */
314#endif
315#ifdef RCHECK
316 ASSERT(op->ov_rmagic == RMAGIC);
317 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
318#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000319#ifdef VCHECK
320 memset(cp, 43, op->ov_size);
321#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000322 size = op->ov_index;
323 if ( size == 0xff ) {
324#ifdef MSTATS
325 nmalloc[NBUCKETS]--;
326#endif
Jack Jansen184c1601997-04-08 15:27:29 +0000327 DisposePtr((Ptr)op);
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000328 return;
329 }
330 ASSERT(size < NBUCKETS);
331 op->ov_next = nextf[size]; /* also clobbers ov_magic */
332 nextf[size] = op;
333#ifdef MSTATS
334 nmalloc[size]--;
335#endif
336}
337
338void *
339realloc(cp, nbytes)
340 void *cp;
341 size_t nbytes;
342{
343 int i;
344 union overhead *op;
345 int expensive;
346 u_long maxsize;
347
348 if (cp == NULL)
349 return (malloc(nbytes));
350 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
351#ifdef DEBUG
352 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
353 ASSERT(op->ov_magic1 == MAGIC);
354#else
355 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
356 return NULL; /* sanity */
357#endif
358#ifdef RCHECK
359 ASSERT(op->ov_rmagic == RMAGIC);
360 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
361#endif
362 i = op->ov_index;
363 /*
364 ** First the malloc/copy cases
365 */
366 expensive = 0;
367 if ( i == 0xff ) {
Jack Jansenf2e51291995-01-22 16:44:49 +0000368 /* Big block. See if it has to stay big */
369 if (nbytes+OVERHEAD > MAXMALLOC) {
370 /* Yup, try to resize it first */
371 SetPtrSize((Ptr)op, nbytes+OVERHEAD);
372 if ( MemError() == 0 ) {
373#ifdef RCHECK
374 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
375 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
376#endif
377 return cp;
378 }
379 /* Nope, failed. Take the long way */
380 }
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000381 maxsize = GetPtrSize((Ptr)op);
Jack Jansenf2e51291995-01-22 16:44:49 +0000382 expensive = 1;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000383 } else {
384 maxsize = 1 << (i+3);
385 if ( nbytes + OVERHEAD > maxsize )
386 expensive = 1;
387 else if ( i > 0 && nbytes + OVERHEAD < (maxsize/2) )
388 expensive = 1;
389 }
390 if ( expensive ) {
391 void *newp;
392
393 newp = malloc(nbytes);
394 if ( newp == NULL ) {
395 return NULL;
396 }
397 maxsize -= OVERHEAD;
398 if ( maxsize < nbytes )
399 nbytes = maxsize;
400 memcpy(newp, cp, nbytes);
401 free(cp);
402 return newp;
403 }
404 /*
405 ** Ok, we don't have to copy, it fits as-is
406 */
407#ifdef RCHECK
408 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
409 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
410#endif
411 return(cp);
412}
413
414
415#ifdef MSTATS
416/*
417 * mstats - print out statistics about malloc
418 *
419 * Prints two lines of numbers, one showing the length of the free list
420 * for each size category, the second showing the number of mallocs -
421 * frees for each size category.
422 */
423mstats(s)
424 char *s;
425{
426 register int i, j;
427 register union overhead *p;
428 int totfree = 0,
429 totused = 0;
430
431 fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
432 for (i = 0; i < NBUCKETS; i++) {
433 for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
434 ;
435 fprintf(stderr, " %d", j);
436 totfree += j * (1 << (i + 3));
437 }
438 fprintf(stderr, "\nused:\t");
439 for (i = 0; i < NBUCKETS; i++) {
440 fprintf(stderr, " %d", nmalloc[i]);
441 totused += nmalloc[i] * (1 << (i + 3));
442 }
443 fprintf(stderr, "\n\tTotal small in use: %d, total free: %d\n",
444 totused, totfree);
445 fprintf(stderr, "\n\tNumber of big (>%d) blocks in use: %d\n", MAXMALLOC, nmalloc[NBUCKETS]);
446}
447#endif