2 * Attempt at a memory allocator for the Mac, Jack Jansen, CWI, 1995-1997.
4 * Code adapted from BSD malloc, which is:
6 * Copyright (c) 1983 Regents of the University of California.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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.
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
38 #if defined(LIBC_SCCS) && !defined(lint)
39 /*static char *sccsid = "from: @(#)malloc.c 5.11 (Berkeley) 2/23/91";*/
40 static char *rcsid
= "$Id$";
41 #endif /* LIBC_SCCS and not lint */
44 * malloc.c (Caltech) 2/21/82
45 * Chris Kingsley, kingsley@cit-20. Modified by Jack Jansen, CWI.
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.
51 * Blocks over a certain size are directly allocated by calling NewPtr.
55 #ifdef USE_MALLOC_DEBUG
56 /* You may also selectively enable some of these (but some are interdependent) */
62 #endif /* USE_MALLOC_DEBUG */
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.
69 /* #define USE_CACHE_ALIGNED 8 /* The alignment (in 4-byte words) */
71 typedef unsigned char u_char
;
72 typedef unsigned long u_long
;
73 typedef unsigned int u_int
;
74 typedef unsigned short u_short
;
75 typedef u_long caddr_t
;
85 static void morecore();
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.
98 union overhead
*ov_next
; /* when free */
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 */
105 u_short ovu_rmagic
; /* range magic number */
106 u_long ovu_size
; /* actual block size */
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
114 #ifdef USE_CACHE_ALIGNED
115 struct cachealigner
{
116 u_long ovalign
[USE_CACHE_ALIGNED
];
118 #endif /* USE_CACHE_ALIGN */
121 #define MAGIC 0xef /* magic # on accounting info */
122 #define RMAGIC 0x5555 /* magic # on range info */
125 #define RSLOP sizeof (u_short)
130 #define OVERHEAD (sizeof(union overhead) + RSLOP)
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.
138 #define MAXMALLOC (1<<(NBUCKETS+2))
139 static union overhead
*nextf
[NBUCKETS
];
143 * nmalloc[i] is the difference between the number of mallocs and frees
144 * for a given block size.
146 static u_int nmalloc
[NBUCKETS
+1];
150 #if defined(DEBUG) || defined(RCHECK) || defined(DEBUG2)
151 #define ASSERT(p) if (!(p)) botch(# p)
157 fprintf(stderr
, "\r\nmalloc assertion botched: %s\r\n", s
);
158 (void) fflush(stderr
); /* just in case user buffered it */
169 register union overhead
*op
;
170 register long bucket
;
171 register unsigned amt
;
174 ** First the simple case: we simple allocate big blocks directly
176 if ( nbytes
+ OVERHEAD
>= MAXMALLOC
) {
177 op
= (union overhead
*)NewPtr(nbytes
+OVERHEAD
);
180 op
->ov_magic0
= op
->ov_magic1
= MAGIC
;
187 * Record allocated size of block and
188 * bound space with magic numbers.
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
;
194 return (void *)(op
+1);
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.
202 amt
= 8; /* size of first bucket */
205 amt
= 16; /* size of first bucket */
208 while (nbytes
+ OVERHEAD
> amt
) {
215 ASSERT( bucket
< NBUCKETS
);
218 * If nothing in hash bucket right now,
219 * request more memory from the system.
221 if ((op
= nextf
[bucket
]) == NULL
) {
223 if ((op
= nextf
[bucket
]) == NULL
)
226 /* remove from linked list */
227 nextf
[bucket
] = op
->ov_next
;
228 op
->ov_magic0
= op
->ov_magic1
= MAGIC
;
229 op
->ov_index
= bucket
;
235 * Record allocated size of block and
236 * bound space with magic numbers.
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
;
243 memset((char *)(op
+1), 0x41, nbytes
);
245 return ((char *)(op
+ 1));
249 * Allocate more memory to the indicated bucket.
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 */
261 * sbrk_size <= 0 only for big, FLUFFY, requests (about
262 * 2^30 bytes on a VAX, I think) or for a negative arg.
264 sz
= 1 << (bucket
+ 3);
271 ASSERT(nblks
*sz
== amt
);
273 #ifdef USE_CACHE_ALIGNED
274 op
= (union overhead
*)NewPtr(amt
+4*USE_CACHE_ALIGNED
);
276 op
= (union overhead
*)NewPtr(amt
);
281 #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 */
287 * Add new memory allocated to that on
288 * free list for this hash bucket.
291 while (--nblks
> 0) {
292 op
->ov_next
= (union overhead
*)((caddr_t
)op
+ sz
);
293 op
= (union overhead
*)((caddr_t
)op
+ sz
);
295 op
->ov_next
= (union overhead
*)NULL
;
303 register union overhead
*op
;
307 op
= (union overhead
*)((caddr_t
)cp
- sizeof (union overhead
));
309 ASSERT(op
->ov_magic0
== MAGIC
); /* make sure it was in use */
310 ASSERT(op
->ov_magic1
== MAGIC
);
312 if (op
->ov_magic0
!= MAGIC
|| op
->ov_magic1
!= MAGIC
)
316 ASSERT(op
->ov_rmagic
== RMAGIC
);
317 ASSERT(*(u_short
*)((caddr_t
)(op
+ 1) + op
->ov_size
) == RMAGIC
);
320 memset(cp
, 43, op
->ov_size
);
323 if ( size
== 0xff ) {
330 ASSERT(size
< NBUCKETS
);
331 op
->ov_next
= nextf
[size
]; /* also clobbers ov_magic */
349 return (malloc(nbytes
));
350 op
= (union overhead
*)((caddr_t
)cp
- sizeof (union overhead
));
352 ASSERT(op
->ov_magic0
== MAGIC
); /* make sure it was in use */
353 ASSERT(op
->ov_magic1
== MAGIC
);
355 if (op
->ov_magic0
!= MAGIC
|| op
->ov_magic1
!= MAGIC
)
356 return NULL
; /* sanity */
359 ASSERT(op
->ov_rmagic
== RMAGIC
);
360 ASSERT(*(u_short
*)((caddr_t
)(op
+ 1) + op
->ov_size
) == RMAGIC
);
364 ** First the malloc/copy cases
368 /* 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 ) {
374 op
->ov_size
= (nbytes
+ RSLOP
- 1) & ~(RSLOP
- 1);
375 *(u_short
*)((caddr_t
)(op
+ 1) + op
->ov_size
) = RMAGIC
;
379 /* Nope, failed. Take the long way */
381 maxsize
= GetPtrSize((Ptr
)op
);
384 maxsize
= 1 << (i
+3);
385 if ( nbytes
+ OVERHEAD
> maxsize
)
387 else if ( i
> 0 && nbytes
+ OVERHEAD
< (maxsize
/2) )
393 newp
= malloc(nbytes
);
394 if ( newp
== NULL
) {
398 if ( maxsize
< nbytes
)
400 memcpy(newp
, cp
, nbytes
);
405 ** Ok, we don't have to copy, it fits as-is
408 op
->ov_size
= (nbytes
+ RSLOP
- 1) & ~(RSLOP
- 1);
409 *(u_short
*)((caddr_t
)(op
+ 1) + op
->ov_size
) = RMAGIC
;
417 * mstats - print out statistics about malloc
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.
427 register union overhead
*p
;
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
++)
435 fprintf(stderr
, " %d", j
);
436 totfree
+= j
* (1 << (i
+ 3));
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));
443 fprintf(stderr
, "\n\tTotal small in use: %d, total free: %d\n",
445 fprintf(stderr
, "\n\tNumber of big (>%d) blocks in use: %d\n", MAXMALLOC
, nmalloc
[NBUCKETS
]);