blob: fdf91d5198d4bdb1d3ab5afa6d381074a0d78adc [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
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
Jack Jansen3c2871e1997-02-03 15:06:45 +0000105#ifdef USE_CACHE_ALIGNED
Jack Jansenb4ef4c61997-02-01 23:44:50 +0000106 struct cachealigner {
Jack Jansen3c2871e1997-02-03 15:06:45 +0000107 u_long ovalign[USE_CACHE_ALIGNED];
Jack Jansenb4ef4c61997-02-01 23:44:50 +0000108 };
109#endif /* USE_CACHE_ALIGN */
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000110};
111
112#define MAGIC 0xef /* magic # on accounting info */
113#define RMAGIC 0x5555 /* magic # on range info */
114
115#ifdef RCHECK
116#define RSLOP sizeof (u_short)
117#else
118#define RSLOP 0
119#endif
120
121#define OVERHEAD (sizeof(union overhead) + RSLOP)
122
123/*
124 * nextf[i] is the pointer to the next free block of size 2^(i+3). The
125 * smallest allocatable block is 8 bytes. The overhead information
126 * precedes the data area returned to the user.
127 */
128#define NBUCKETS 11
129#define MAXMALLOC (1<<(NBUCKETS+2))
130static union overhead *nextf[NBUCKETS];
131
132#ifdef MSTATS
133/*
134 * nmalloc[i] is the difference between the number of mallocs and frees
135 * for a given block size.
136 */
137static u_int nmalloc[NBUCKETS+1];
138#include <stdio.h>
139#endif
140
141#if defined(DEBUG) || defined(RCHECK) || defined(DEBUG2)
142#define ASSERT(p) if (!(p)) botch(# p)
143#include <stdio.h>
144static
145botch(s)
146 char *s;
147{
148 fprintf(stderr, "\r\nmalloc assertion botched: %s\r\n", s);
149 (void) fflush(stderr); /* just in case user buffered it */
150 abort();
151}
152#else
153#define ASSERT(p)
154#endif
155
156void *
157malloc(nbytes)
158 size_t nbytes;
159{
160 register union overhead *op;
Jack Jansena6a55e91995-08-31 13:51:13 +0000161 register long bucket;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000162 register unsigned amt;
163
164 /*
165 ** First the simple case: we simple allocate big blocks directly
166 */
167 if ( nbytes + OVERHEAD >= MAXMALLOC ) {
168 op = (union overhead *)NewPtr(nbytes+OVERHEAD);
169 if ( op == NULL )
170 return NULL;
171 op->ov_magic0 = op->ov_magic1 = MAGIC;
172 op->ov_index = 0xff;
173#ifdef MSTATS
174 nmalloc[NBUCKETS]++;
175#endif
176#ifdef RCHECK
177 /*
178 * Record allocated size of block and
179 * bound space with magic numbers.
180 */
181 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
182 op->ov_rmagic = RMAGIC;
183 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
184#endif
185 return (void *)(op+1);
186 }
187 /*
188 * Convert amount of memory requested into closest block size
189 * stored in hash buckets which satisfies request.
190 * Account for space used per block for accounting.
191 */
192#ifndef RCHECK
193 amt = 8; /* size of first bucket */
194 bucket = 0;
195#else
196 amt = 16; /* size of first bucket */
197 bucket = 1;
198#endif
199 while (nbytes + OVERHEAD > amt) {
200 amt <<= 1;
201 if (amt == 0)
202 return (NULL);
203 bucket++;
204 }
205#ifdef DEBUG2
206 ASSERT( bucket < NBUCKETS );
207#endif
208 /*
209 * If nothing in hash bucket right now,
210 * request more memory from the system.
211 */
212 if ((op = nextf[bucket]) == NULL) {
213 morecore(bucket);
214 if ((op = nextf[bucket]) == NULL)
215 return (NULL);
216 }
217 /* remove from linked list */
218 nextf[bucket] = op->ov_next;
219 op->ov_magic0 = op->ov_magic1 = MAGIC;
220 op->ov_index = bucket;
221#ifdef MSTATS
222 nmalloc[bucket]++;
223#endif
224#ifdef RCHECK
225 /*
226 * Record allocated size of block and
227 * bound space with magic numbers.
228 */
229 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
230 op->ov_rmagic = RMAGIC;
231 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
232#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000233#ifdef VCHECK
234 memset((char *)(op+1), 0x41, nbytes);
235#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000236 return ((char *)(op + 1));
237}
238
239/*
240 * Allocate more memory to the indicated bucket.
241 */
242static void
243morecore(bucket)
244 int bucket;
245{
246 register union overhead *op;
247 register long sz; /* size of desired block */
248 long amt; /* amount to allocate */
249 int nblks; /* how many blocks we get */
250
251 /*
252 * sbrk_size <= 0 only for big, FLUFFY, requests (about
253 * 2^30 bytes on a VAX, I think) or for a negative arg.
254 */
255 sz = 1 << (bucket + 3);
256#ifdef DEBUG2
257 ASSERT(sz > 0);
258#endif
259 amt = MAXMALLOC;
260 nblks = amt / sz;
261#ifdef DEBUG2
262 ASSERT(nblks*sz == amt);
263#endif
Jack Jansen3c2871e1997-02-03 15:06:45 +0000264#ifdef USE_CACHE_ALIGNED
265 op = (union overhead *)NewPtr(amt+4*USE_CACHE_ALIGNED);
266#else
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000267 op = (union overhead *)NewPtr(amt);
Jack Jansen3c2871e1997-02-03 15:06:45 +0000268#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000269 /* no more room! */
270 if (op == NULL)
271 return;
Jack Jansen3c2871e1997-02-03 15:06:45 +0000272#ifdef USE_CACHE_ALIGNED
273#define ALIGN_MASK (4*USE_CACHE_ALIGNED-1)
274 while ((long)op & ALIGN_MASK )
275 op = (union overhead *)((long)op+1);
276#endif /* USE_CACHE_ALIGNED */
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000277 /*
278 * Add new memory allocated to that on
279 * free list for this hash bucket.
280 */
281 nextf[bucket] = op;
282 while (--nblks > 0) {
283 op->ov_next = (union overhead *)((caddr_t)op + sz);
284 op = (union overhead *)((caddr_t)op + sz);
285 }
286 op->ov_next = (union overhead *)NULL;
287}
288
289void
290free(cp)
291 void *cp;
292{
293 register long size;
294 register union overhead *op;
295
296 if (cp == NULL)
297 return;
298 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
299#ifdef DEBUG
300 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
301 ASSERT(op->ov_magic1 == MAGIC);
302#else
303 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
304 return; /* sanity */
305#endif
306#ifdef RCHECK
307 ASSERT(op->ov_rmagic == RMAGIC);
308 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
309#endif
Jack Jansen05cf7e01996-09-30 14:42:28 +0000310#ifdef VCHECK
311 memset(cp, 43, op->ov_size);
312#endif
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000313 size = op->ov_index;
314 if ( size == 0xff ) {
315#ifdef MSTATS
316 nmalloc[NBUCKETS]--;
317#endif
318 DisposPtr((Ptr)op);
319 return;
320 }
321 ASSERT(size < NBUCKETS);
322 op->ov_next = nextf[size]; /* also clobbers ov_magic */
323 nextf[size] = op;
324#ifdef MSTATS
325 nmalloc[size]--;
326#endif
327}
328
329void *
330realloc(cp, nbytes)
331 void *cp;
332 size_t nbytes;
333{
334 int i;
335 union overhead *op;
336 int expensive;
337 u_long maxsize;
338
339 if (cp == NULL)
340 return (malloc(nbytes));
341 op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
342#ifdef DEBUG
343 ASSERT(op->ov_magic0 == MAGIC); /* make sure it was in use */
344 ASSERT(op->ov_magic1 == MAGIC);
345#else
346 if (op->ov_magic0 != MAGIC || op->ov_magic1 != MAGIC)
347 return NULL; /* sanity */
348#endif
349#ifdef RCHECK
350 ASSERT(op->ov_rmagic == RMAGIC);
351 ASSERT(*(u_short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
352#endif
353 i = op->ov_index;
354 /*
355 ** First the malloc/copy cases
356 */
357 expensive = 0;
358 if ( i == 0xff ) {
Jack Jansenf2e51291995-01-22 16:44:49 +0000359 /* Big block. See if it has to stay big */
360 if (nbytes+OVERHEAD > MAXMALLOC) {
361 /* Yup, try to resize it first */
362 SetPtrSize((Ptr)op, nbytes+OVERHEAD);
363 if ( MemError() == 0 ) {
364#ifdef RCHECK
365 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
366 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
367#endif
368 return cp;
369 }
370 /* Nope, failed. Take the long way */
371 }
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000372 maxsize = GetPtrSize((Ptr)op);
Jack Jansenf2e51291995-01-22 16:44:49 +0000373 expensive = 1;
Jack Jansenfa4fd8e1995-01-18 13:48:31 +0000374 } else {
375 maxsize = 1 << (i+3);
376 if ( nbytes + OVERHEAD > maxsize )
377 expensive = 1;
378 else if ( i > 0 && nbytes + OVERHEAD < (maxsize/2) )
379 expensive = 1;
380 }
381 if ( expensive ) {
382 void *newp;
383
384 newp = malloc(nbytes);
385 if ( newp == NULL ) {
386 return NULL;
387 }
388 maxsize -= OVERHEAD;
389 if ( maxsize < nbytes )
390 nbytes = maxsize;
391 memcpy(newp, cp, nbytes);
392 free(cp);
393 return newp;
394 }
395 /*
396 ** Ok, we don't have to copy, it fits as-is
397 */
398#ifdef RCHECK
399 op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
400 *(u_short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
401#endif
402 return(cp);
403}
404
405
406#ifdef MSTATS
407/*
408 * mstats - print out statistics about malloc
409 *
410 * Prints two lines of numbers, one showing the length of the free list
411 * for each size category, the second showing the number of mallocs -
412 * frees for each size category.
413 */
414mstats(s)
415 char *s;
416{
417 register int i, j;
418 register union overhead *p;
419 int totfree = 0,
420 totused = 0;
421
422 fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
423 for (i = 0; i < NBUCKETS; i++) {
424 for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
425 ;
426 fprintf(stderr, " %d", j);
427 totfree += j * (1 << (i + 3));
428 }
429 fprintf(stderr, "\nused:\t");
430 for (i = 0; i < NBUCKETS; i++) {
431 fprintf(stderr, " %d", nmalloc[i]);
432 totused += nmalloc[i] * (1 << (i + 3));
433 }
434 fprintf(stderr, "\n\tTotal small in use: %d, total free: %d\n",
435 totused, totfree);
436 fprintf(stderr, "\n\tNumber of big (>%d) blocks in use: %d\n", MAXMALLOC, nmalloc[NBUCKETS]);
437}
438#endif