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