1995-05-30 05:05:38 +00:00
|
|
|
|
#define inline
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
/*
|
1993-07-30 20:16:53 +00:00
|
|
|
|
Copyright (C) 1989 Free Software Foundation
|
|
|
|
|
written by Doug Lea (dl@oswego.edu)
|
|
|
|
|
|
|
|
|
|
This file is part of GNU CC.
|
|
|
|
|
|
|
|
|
|
GNU CC is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY. No author or distributor
|
|
|
|
|
accepts responsibility to anyone for the consequences of using it
|
|
|
|
|
or for whether it serves any particular purpose or works at all,
|
|
|
|
|
unless he says so in writing. Refer to the GNU CC General Public
|
|
|
|
|
License for full details.
|
|
|
|
|
|
|
|
|
|
Everyone is granted permission to copy, modify and redistribute
|
|
|
|
|
GNU CC, but only under the conditions described in the
|
|
|
|
|
GNU CC General Public License. A copy of this license is
|
|
|
|
|
supposed to have been given to you along with GNU CC so you
|
|
|
|
|
can know your rights and responsibilities. It should be in a
|
|
|
|
|
file named COPYING. Among other things, the copyright notice
|
1995-05-30 05:05:38 +00:00
|
|
|
|
and this notice must be preserved on all copies.
|
1993-07-30 20:16:53 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef NO_LIBGXX_MALLOC /* ignore whole file otherwise */
|
|
|
|
|
|
|
|
|
|
/* compile with -DMALLOC_STATS to collect statistics */
|
|
|
|
|
/* collecting statistics slows down malloc by at least 15% */
|
|
|
|
|
|
|
|
|
|
#ifdef MALLOC_STATS
|
|
|
|
|
#define UPDATE_STATS(ARGS) {ARGS;}
|
|
|
|
|
#else
|
|
|
|
|
#define UPDATE_STATS(ARGS)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/* History
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tue Jan 16 04:54:27 1990 Doug Lea (dl at g.oswego.edu)
|
|
|
|
|
|
|
|
|
|
version 1 released in libg++
|
|
|
|
|
|
|
|
|
|
Sun Jan 21 05:52:47 1990 Doug Lea (dl at g.oswego.edu)
|
|
|
|
|
|
|
|
|
|
bins are now own struct for, sanity.
|
|
|
|
|
|
|
|
|
|
new victim search strategy: scan up and consolidate.
|
|
|
|
|
Both faster and less fragmentation.
|
|
|
|
|
|
|
|
|
|
refined when to scan bins for consolidation, via consollink, etc.
|
|
|
|
|
|
|
|
|
|
realloc: always try to expand chunk, avoiding some fragmentation.
|
|
|
|
|
|
|
|
|
|
changed a few inlines into macros
|
|
|
|
|
|
|
|
|
|
hardwired SBRK_UNIT to 4096 for uniformity across systems
|
|
|
|
|
|
|
|
|
|
Tue Mar 20 14:18:23 1990 Doug Lea (dl at g.oswego.edu)
|
|
|
|
|
|
|
|
|
|
calloc and cfree now correctly parameterized.
|
|
|
|
|
|
|
|
|
|
Sun Apr 1 10:00:48 1990 Doug Lea (dl at g.oswego.edu)
|
|
|
|
|
|
|
|
|
|
added memalign and valloc.
|
|
|
|
|
|
|
|
|
|
Sun Jun 24 05:46:48 1990 Doug Lea (dl at g.oswego.edu)
|
|
|
|
|
|
|
|
|
|
#include gepagesize.h only ifndef sun
|
|
|
|
|
cache pagesize after first call
|
|
|
|
|
|
|
|
|
|
Wed Jul 25 08:35:19 1990 Doug Lea (dl at g.oswego.edu)
|
|
|
|
|
|
|
|
|
|
No longer rely on a `designated victim':
|
|
|
|
|
|
|
|
|
|
1. It sometimes caused splits of large chunks
|
|
|
|
|
when smaller ones would do, leading to
|
|
|
|
|
bad worst-case fragmentation.
|
|
|
|
|
|
|
|
|
|
2. Scanning through the av array fast anyway,
|
|
|
|
|
so the overhead isn't worth it.
|
|
|
|
|
|
|
|
|
|
To compensate, several other minor changes:
|
|
|
|
|
|
|
|
|
|
1. Unusable chunks are checked for consolidation during
|
|
|
|
|
searches inside bins, better distributing chunks
|
|
|
|
|
across bins.
|
|
|
|
|
|
|
|
|
|
2. Chunks are returned when found in malloc_find_space,
|
|
|
|
|
rather than finishing cleaning everything up, to
|
|
|
|
|
avoid wasted iterations due to (1).
|
|
|
|
|
*/
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
/*
|
1993-07-30 20:16:53 +00:00
|
|
|
|
A version of malloc/free/realloc tuned for C++ applications.
|
|
|
|
|
|
|
|
|
|
Here's what you probably want to know first:
|
|
|
|
|
|
|
|
|
|
In various tests, this appears to be about as fast as,
|
|
|
|
|
and usually substantially less memory-wasteful than BSD/GNUemacs malloc.
|
|
|
|
|
|
|
|
|
|
Generally, it is slower (by perhaps 20%) than bsd-style malloc
|
1995-05-30 05:05:38 +00:00
|
|
|
|
only when bsd malloc would waste a great deal of space in
|
1993-07-30 20:16:53 +00:00
|
|
|
|
fragmented blocks, which this malloc recovers; or when, by
|
|
|
|
|
chance or design, nearly all requests are near the bsd malloc
|
|
|
|
|
power-of-2 allocation bin boundaries, and as many chunks are
|
1995-05-30 05:05:38 +00:00
|
|
|
|
used as are allocated.
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
It uses more space than bsd malloc only when, again by chance
|
|
|
|
|
or design, only bsdmalloc bin-sized requests are malloced, or when
|
|
|
|
|
little dynamic space is malloced, since this malloc may grab larger
|
|
|
|
|
chunks from the system at a time than bsd.
|
|
|
|
|
|
|
|
|
|
In other words, this malloc seems generally superior to bsd
|
|
|
|
|
except perhaps for programs that are specially tuned to
|
|
|
|
|
deal with bsdmalloc's characteristics. But even here, the
|
|
|
|
|
performance differences are slight.
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
|
|
|
|
This malloc, like any other, is a compromised design.
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Chunks of memory are maintained using a `boundary tag' method as
|
|
|
|
|
described in e.g., Knuth or Standish. This means that the size of
|
|
|
|
|
the chunk is stored both in the front of the chunk and at the end.
|
|
|
|
|
This makes consolidating fragmented chunks into bigger chunks very fast.
|
|
|
|
|
The size field is also used to hold bits representing whether a
|
|
|
|
|
chunk is free or in use.
|
|
|
|
|
|
|
|
|
|
Malloced chunks have space overhead of 8 bytes: The preceding
|
|
|
|
|
and trailing size fields. When they are freed, the list pointer
|
|
|
|
|
fields are also needed.
|
|
|
|
|
|
|
|
|
|
Available chunks are kept in doubly linked lists. The lists are
|
|
|
|
|
maintained in an array of bins using a power-of-two method, except
|
|
|
|
|
that instead of 32 bins (one for each 1 << i), there are 128: each
|
1995-05-30 05:05:38 +00:00
|
|
|
|
power of two is split in quarters. The use of very fine bin sizes
|
1993-07-30 20:16:53 +00:00
|
|
|
|
closely approximates the use of one bin per actually used size,
|
|
|
|
|
without necessitating the overhead of locating such bins. It is
|
|
|
|
|
especially desirable in common C++ applications where large numbers
|
|
|
|
|
of identically-sized blocks are malloced/freed in some dynamic
|
|
|
|
|
manner, and then later are all freed. The finer bin sizes make
|
|
|
|
|
finding blocks fast, with little wasted overallocation. The
|
|
|
|
|
consolidation methods ensure that once the collection of blocks is
|
|
|
|
|
no longer useful, fragments are gathered into bigger chunks awaiting new
|
|
|
|
|
roles.
|
|
|
|
|
|
|
|
|
|
The bins av[i] serve as heads of the lists. Bins contain a dummy
|
|
|
|
|
header for the chunk lists, and a `dirty' field used to indicate
|
|
|
|
|
whether the list may need to be scanned for consolidation.
|
|
|
|
|
|
|
|
|
|
On allocation, the bin corresponding to the request size is
|
|
|
|
|
scanned, and if there is a chunk with size >= requested, it
|
|
|
|
|
is split, if too big, and used. Chunks on the list which are
|
|
|
|
|
too small are examined for consolidation during this traversal.
|
|
|
|
|
|
|
|
|
|
If no chunk exists in the list bigger bins are scanned in search of
|
|
|
|
|
a victim.
|
|
|
|
|
|
|
|
|
|
If no victim can be found, then smaller bins are examined for
|
|
|
|
|
consolidation in order to construct a victim.
|
|
|
|
|
|
|
|
|
|
Finally, if consolidation fails to come up with a usable chunk,
|
|
|
|
|
more space is obtained from the system.
|
|
|
|
|
|
|
|
|
|
After a split, the remainder is placed on
|
|
|
|
|
the back of the appropriate bin list. (All freed chunks are placed
|
|
|
|
|
on fronts of lists. All remaindered or consolidated chunks are
|
|
|
|
|
placed on the rear. Correspondingly, searching within a bin
|
|
|
|
|
starts at the front, but finding victims is from the back. All
|
1995-05-30 05:05:38 +00:00
|
|
|
|
of this approximates the effect of having 2 kinds of lists per
|
|
|
|
|
bin: returned chunks vs unallocated chunks, but without the overhead
|
1993-07-30 20:16:53 +00:00
|
|
|
|
of maintaining 2 lists.)
|
|
|
|
|
|
|
|
|
|
Deallocation (free) consists only of placing the chunk on
|
|
|
|
|
a list.
|
|
|
|
|
|
|
|
|
|
Reallocation proceeds in the usual way. If a chunk can be extended,
|
|
|
|
|
it is, else a malloc-copy-free sequence is taken.
|
|
|
|
|
|
|
|
|
|
memalign requests more than enough space from malloc, finds a
|
|
|
|
|
spot within that chunk that meets the alignment request, and
|
|
|
|
|
then possibly frees the leading and trailing space. Overreliance
|
|
|
|
|
on memalign is a sure way to fragment space.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Some other implementation matters:
|
|
|
|
|
|
|
|
|
|
8 byte alignment is currently hardwired into the design. Calling
|
|
|
|
|
memalign will return a chunk that is both 8-byte aligned, and
|
|
|
|
|
meets the requested alignment.
|
|
|
|
|
|
|
|
|
|
The basic overhead of a used chunk is 8 bytes: 4 at the front and
|
|
|
|
|
4 at the end.
|
|
|
|
|
|
|
|
|
|
When a chunk is free, 8 additional bytes are needed for free list
|
|
|
|
|
pointers. Thus, the minimum allocatable size is 16 bytes.
|
|
|
|
|
|
|
|
|
|
The existence of front and back overhead permits some reasonably
|
|
|
|
|
effective fence-bashing checks: The front and back fields must
|
|
|
|
|
be identical. This is checked only within free() and realloc().
|
|
|
|
|
The checks are fast enough to be made non-optional.
|
|
|
|
|
|
|
|
|
|
The overwriting of parts of freed memory with the freelist pointers
|
1995-05-30 05:05:38 +00:00
|
|
|
|
can also be very effective (albeit in an annoying way) in helping
|
1993-07-30 20:16:53 +00:00
|
|
|
|
users track down dangling pointers.
|
|
|
|
|
|
|
|
|
|
User overwriting of freed space will often result in crashes
|
|
|
|
|
within malloc or free.
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
These routines are also tuned to C++ in that free(0) is a noop and
|
1995-05-30 05:05:38 +00:00
|
|
|
|
a failed malloc automatically calls (*new_handler)().
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
malloc(0) returns a pointer to something of the minimum allocatable size.
|
|
|
|
|
|
|
|
|
|
Additional memory is gathered from the system (via sbrk) in a
|
|
|
|
|
way that allows chunks obtained across different sbrk calls to
|
|
|
|
|
be consolidated, but does not require contiguous memory: Thus,
|
|
|
|
|
it should be safe to intersperse mallocs with other sbrk calls.
|
|
|
|
|
|
|
|
|
|
This malloc is NOT designed to work in multiprocessing applications.
|
|
|
|
|
No semaphores or other concurrency control are provided to ensure
|
|
|
|
|
that multiple malloc or free calls don't run at the same time,
|
|
|
|
|
which could be disasterous.
|
|
|
|
|
|
|
|
|
|
VERY heavy use of inlines is made, for clarity. If this malloc
|
|
|
|
|
is ported via a compiler without inlining capabilities, all
|
|
|
|
|
inlines should be transformed into macros -- making them non-inline
|
|
|
|
|
makes malloc at least twice as slow.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* preliminaries */
|
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#else
|
|
|
|
|
#include "//usr/include/stdio.h" /* needed for error reporting */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
|
extern "C" {
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef USG
|
|
|
|
|
extern void* memset(void*, int, int);
|
|
|
|
|
extern void* memcpy(void*, const void*, int);
|
|
|
|
|
/*inline void bzero(void* s, int l) { memset(s, 0, l); }*/
|
|
|
|
|
#else
|
|
|
|
|
/*extern void bzero(void*, unsigned int);*/
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*extern void bcopy(void*, void*, unsigned int);*/
|
|
|
|
|
|
|
|
|
|
extern void* sbrk(unsigned int);
|
|
|
|
|
|
|
|
|
|
/* Put this in instead of commmented out stuff above. */
|
|
|
|
|
#define bcopy(s,d,n) memcpy((d),(s),(n))
|
|
|
|
|
#define bcmp(s1,s2,n) memcmp((s1),(s2),(n))
|
|
|
|
|
#define bzero(s,n) memset((s),0,(n))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
extern volatile void abort();
|
|
|
|
|
#else
|
|
|
|
|
extern void abort();
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
|
}; /* end of extern "C" */
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* A good multiple to call sbrk with */
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
#define SBRK_UNIT 4096
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* how to die on detected error */
|
|
|
|
|
|
|
|
|
|
#ifdef __GNUC__
|
|
|
|
|
static volatile void malloc_user_error()
|
|
|
|
|
#else
|
|
|
|
|
static void malloc_user_error()
|
|
|
|
|
#endif
|
|
|
|
|
{
|
|
|
|
|
fputs("malloc/free/realloc: clobbered space detected\n", stderr); abort();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Basic overhead for each malloc'ed chunk */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct malloc_chunk
|
|
|
|
|
{
|
|
|
|
|
unsigned int size; /* Size in bytes, including overhead. */
|
|
|
|
|
/* Or'ed with INUSE if in use. */
|
|
|
|
|
|
|
|
|
|
struct malloc_chunk* fd; /* double links -- used only if free. */
|
|
|
|
|
struct malloc_chunk* bk;
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct malloc_chunk* mchunkptr;
|
|
|
|
|
|
|
|
|
|
struct malloc_bin
|
|
|
|
|
{
|
|
|
|
|
struct malloc_chunk hd; /* dummy list header */
|
|
|
|
|
unsigned int dirty; /* True if maybe consolidatable */
|
|
|
|
|
/* Wasting a word here makes */
|
|
|
|
|
/* sizeof(bin) a power of 2, */
|
|
|
|
|
/* which makes size2bin() faster */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
typedef struct malloc_bin* mbinptr;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* sizes, alignments */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define SIZE_SZ (sizeof(unsigned int))
|
|
|
|
|
#define MALLOC_MIN_OVERHEAD (SIZE_SZ + SIZE_SZ)
|
|
|
|
|
#define MALLOC_ALIGN_MASK (MALLOC_MIN_OVERHEAD - 1)
|
|
|
|
|
|
|
|
|
|
#define MINSIZE (sizeof(struct malloc_chunk) + SIZE_SZ) /* MUST == 16! */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* pad request bytes into a usable size */
|
|
|
|
|
|
|
|
|
|
static inline unsigned int request2size(unsigned int request)
|
|
|
|
|
{
|
1995-05-30 05:05:38 +00:00
|
|
|
|
return (request == 0) ? MINSIZE :
|
|
|
|
|
((request + MALLOC_MIN_OVERHEAD + MALLOC_ALIGN_MASK)
|
1993-07-30 20:16:53 +00:00
|
|
|
|
& ~(MALLOC_ALIGN_MASK));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
static inline int aligned_OK(void* m)
|
1993-07-30 20:16:53 +00:00
|
|
|
|
{
|
|
|
|
|
return ((unsigned int)(m) & (MALLOC_ALIGN_MASK)) == 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* size field or'd with INUSE when in use */
|
|
|
|
|
#define INUSE 0x1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* the bins, initialized to have null double linked lists */
|
|
|
|
|
|
|
|
|
|
#define MAXBIN 120 /* 1 more than needed for 32 bit addresses */
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
#define FIRSTBIN (&(av[0]))
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
static struct malloc_bin av[MAXBIN] =
|
1993-07-30 20:16:53 +00:00
|
|
|
|
{
|
|
|
|
|
{ { 0, &(av[0].hd), &(av[0].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[1].hd), &(av[1].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[2].hd), &(av[2].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[3].hd), &(av[3].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[4].hd), &(av[4].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[5].hd), &(av[5].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[6].hd), &(av[6].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[7].hd), &(av[7].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[8].hd), &(av[8].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[9].hd), &(av[9].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[10].hd), &(av[10].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[11].hd), &(av[11].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[12].hd), &(av[12].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[13].hd), &(av[13].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[14].hd), &(av[14].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[15].hd), &(av[15].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[16].hd), &(av[16].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[17].hd), &(av[17].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[18].hd), &(av[18].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[19].hd), &(av[19].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[20].hd), &(av[20].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[21].hd), &(av[21].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[22].hd), &(av[22].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[23].hd), &(av[23].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[24].hd), &(av[24].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[25].hd), &(av[25].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[26].hd), &(av[26].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[27].hd), &(av[27].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[28].hd), &(av[28].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[29].hd), &(av[29].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[30].hd), &(av[30].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[31].hd), &(av[31].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[32].hd), &(av[32].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[33].hd), &(av[33].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[34].hd), &(av[34].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[35].hd), &(av[35].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[36].hd), &(av[36].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[37].hd), &(av[37].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[38].hd), &(av[38].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[39].hd), &(av[39].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[40].hd), &(av[40].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[41].hd), &(av[41].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[42].hd), &(av[42].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[43].hd), &(av[43].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[44].hd), &(av[44].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[45].hd), &(av[45].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[46].hd), &(av[46].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[47].hd), &(av[47].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[48].hd), &(av[48].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[49].hd), &(av[49].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[50].hd), &(av[50].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[51].hd), &(av[51].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[52].hd), &(av[52].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[53].hd), &(av[53].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[54].hd), &(av[54].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[55].hd), &(av[55].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[56].hd), &(av[56].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[57].hd), &(av[57].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[58].hd), &(av[58].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[59].hd), &(av[59].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[60].hd), &(av[60].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[61].hd), &(av[61].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[62].hd), &(av[62].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[63].hd), &(av[63].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[64].hd), &(av[64].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[65].hd), &(av[65].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[66].hd), &(av[66].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[67].hd), &(av[67].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[68].hd), &(av[68].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[69].hd), &(av[69].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[70].hd), &(av[70].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[71].hd), &(av[71].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[72].hd), &(av[72].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[73].hd), &(av[73].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[74].hd), &(av[74].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[75].hd), &(av[75].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[76].hd), &(av[76].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[77].hd), &(av[77].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[78].hd), &(av[78].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[79].hd), &(av[79].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[80].hd), &(av[80].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[81].hd), &(av[81].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[82].hd), &(av[82].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[83].hd), &(av[83].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[84].hd), &(av[84].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[85].hd), &(av[85].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[86].hd), &(av[86].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[87].hd), &(av[87].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[88].hd), &(av[88].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[89].hd), &(av[89].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[90].hd), &(av[90].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[91].hd), &(av[91].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[92].hd), &(av[92].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[93].hd), &(av[93].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[94].hd), &(av[94].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[95].hd), &(av[95].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[96].hd), &(av[96].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[97].hd), &(av[97].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[98].hd), &(av[98].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[99].hd), &(av[99].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[100].hd), &(av[100].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[101].hd), &(av[101].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[102].hd), &(av[102].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[103].hd), &(av[103].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[104].hd), &(av[104].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[105].hd), &(av[105].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[106].hd), &(av[106].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[107].hd), &(av[107].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[108].hd), &(av[108].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[109].hd), &(av[109].hd) }, 0 },
|
|
|
|
|
|
|
|
|
|
{ { 0, &(av[110].hd), &(av[110].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[111].hd), &(av[111].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[112].hd), &(av[112].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[113].hd), &(av[113].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[114].hd), &(av[114].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[115].hd), &(av[115].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[116].hd), &(av[116].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[117].hd), &(av[117].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[118].hd), &(av[118].hd) }, 0 },
|
|
|
|
|
{ { 0, &(av[119].hd), &(av[119].hd) }, 0 }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
indexing into bins
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
static inline mbinptr size2bin(unsigned int sz)
|
|
|
|
|
{
|
|
|
|
|
mbinptr b = av;
|
|
|
|
|
while (sz >= (MINSIZE * 2)) { b += 4; sz >>= 1; } /* find power of 2 */
|
|
|
|
|
b += (sz - MINSIZE) >> 2; /* find quadrant */
|
|
|
|
|
return b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* counts maintained if MALLOC_STATS defined */
|
|
|
|
|
|
|
|
|
|
#ifdef MALLOC_STATS
|
|
|
|
|
|
|
|
|
|
static unsigned int sbrked_mem;
|
|
|
|
|
static unsigned int requested_mem;
|
|
|
|
|
static unsigned int malloced_mem;
|
|
|
|
|
static unsigned int freed_mem;
|
|
|
|
|
static unsigned int max_used_mem;
|
|
|
|
|
|
|
|
|
|
static unsigned int n_sbrks;
|
|
|
|
|
static unsigned int n_mallocs;
|
|
|
|
|
static unsigned int n_frees;
|
|
|
|
|
static unsigned int n_reallocs;
|
|
|
|
|
static unsigned int n_reallocs_with_copy;
|
|
|
|
|
static unsigned int n_avail;
|
|
|
|
|
static unsigned int max_inuse;
|
|
|
|
|
|
|
|
|
|
static unsigned int n_malloc_chunks;
|
|
|
|
|
static unsigned int n_malloc_bins;
|
|
|
|
|
|
|
|
|
|
static unsigned int n_split;
|
|
|
|
|
static unsigned int n_consol;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void do_malloc_stats(const mchunkptr p)
|
|
|
|
|
{
|
|
|
|
|
++n_mallocs;
|
|
|
|
|
if ((n_mallocs-n_frees) > max_inuse)
|
|
|
|
|
max_inuse = n_mallocs - n_frees;
|
|
|
|
|
malloced_mem += (p->size & ~(INUSE));
|
|
|
|
|
if (malloced_mem - freed_mem > max_used_mem)
|
|
|
|
|
max_used_mem = malloced_mem - freed_mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void do_free_stats(const mchunkptr p)
|
|
|
|
|
{
|
|
|
|
|
++n_frees;
|
|
|
|
|
freed_mem += (p->size & ~(INUSE));
|
1995-05-30 05:05:38 +00:00
|
|
|
|
}
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Utilities needed below for memalign */
|
|
|
|
|
/* This is redundant with libg++ support, but not if used stand-alone */
|
|
|
|
|
|
|
|
|
|
static unsigned int gcd(unsigned int a, unsigned int b)
|
|
|
|
|
{
|
|
|
|
|
unsigned int tmp;
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
if (b > a)
|
|
|
|
|
{
|
|
|
|
|
tmp = a; a = b; b = tmp;
|
|
|
|
|
}
|
|
|
|
|
for(;;)
|
|
|
|
|
{
|
|
|
|
|
if (b == 0)
|
|
|
|
|
return a;
|
|
|
|
|
else if (b == 1)
|
|
|
|
|
return b;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
tmp = b;
|
|
|
|
|
b = a % b;
|
|
|
|
|
a = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static inline unsigned int lcm(unsigned int x, unsigned int y)
|
|
|
|
|
{
|
|
|
|
|
return x / gcd(x, y) * y;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* maintaining INUSE via size field */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define inuse(p) ((p)->size & INUSE)
|
|
|
|
|
#define set_inuse(p) ((p)->size |= INUSE)
|
|
|
|
|
#define clear_inuse(b) ((p)->size &= ~INUSE)
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
/* operations on malloc_chunk addresses */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* return ptr to next physical malloc_chunk */
|
|
|
|
|
|
|
|
|
|
#define next_chunk(p) ((mchunkptr)((char*)(p) + (p)->size))
|
|
|
|
|
|
|
|
|
|
/* return ptr to previous physical malloc_chunk */
|
|
|
|
|
|
|
|
|
|
#define prev_chunk(p) ((mchunkptr)((char*)(p)-((((int*)(p))[-1]) & ~(INUSE))))
|
|
|
|
|
|
|
|
|
|
/* place size at front and back of chunk */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static inline void set_size(mchunkptr p, unsigned int sz)
|
|
|
|
|
{
|
|
|
|
|
p->size = *((int*)((char*)(p) + sz - SIZE_SZ)) = sz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* conversion from malloc headers to user pointers, and back */
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
static inline void* chunk2mem(mchunkptr p)
|
|
|
|
|
{
|
1993-07-30 20:16:53 +00:00
|
|
|
|
void *mem;
|
|
|
|
|
set_inuse(p);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
mem = (void*)((char*)(p) + SIZE_SZ);
|
1993-07-30 20:16:53 +00:00
|
|
|
|
return mem;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* xxxx my own */
|
1995-05-30 05:05:38 +00:00
|
|
|
|
mchunkptr sanity_check(void* mem)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ);
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
/* a quick sanity check */
|
|
|
|
|
unsigned int sz = p->size & ~(INUSE);
|
|
|
|
|
if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ)))
|
|
|
|
|
malloc_user_error();
|
|
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
static inline mchunkptr mem2chunk(void* mem)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ);
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
/* a quick sanity check */
|
|
|
|
|
unsigned int sz = p->size & ~(INUSE);
|
|
|
|
|
if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ)))
|
|
|
|
|
malloc_user_error();
|
|
|
|
|
|
|
|
|
|
p->size = sz; /* clears INUSE */
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* maintaining bins & pointers */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* maximum bin actually used */
|
|
|
|
|
|
|
|
|
|
static mbinptr malloc_maxbin = FIRSTBIN;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* operations on lists inside bins */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* take a chunk off a list */
|
|
|
|
|
|
|
|
|
|
static inline void unlink(mchunkptr p)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr b = p->bk;
|
|
|
|
|
mchunkptr f = p->fd;
|
|
|
|
|
|
|
|
|
|
f->bk = b; b->fd = f;
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS (--n_avail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* split a chunk and place on the back of a list */
|
|
|
|
|
|
|
|
|
|
static inline void split(mchunkptr p, unsigned int offset)
|
|
|
|
|
{
|
|
|
|
|
unsigned int room = p->size - offset;
|
|
|
|
|
if (room >= MINSIZE)
|
|
|
|
|
{
|
|
|
|
|
mbinptr bn = size2bin(room); /* new bin */
|
|
|
|
|
mchunkptr h = &(bn->hd); /* its head */
|
|
|
|
|
mchunkptr b = h->bk; /* old back element */
|
|
|
|
|
mchunkptr t = (mchunkptr)((char*)(p) + offset); /* remaindered chunk */
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
/* set size */
|
|
|
|
|
t->size = *((int*)((char*)(t) + room - SIZE_SZ)) = room;
|
|
|
|
|
|
|
|
|
|
/* link up */
|
|
|
|
|
t->bk = b; t->fd = h; h->bk = b->fd = t;
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
/* adjust maxbin (h == b means was empty) */
|
1995-05-30 05:05:38 +00:00
|
|
|
|
if (h == b && bn > malloc_maxbin) malloc_maxbin = bn;
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
/* adjust size of chunk to be returned */
|
|
|
|
|
p->size = *((int*)((char*)(p) + offset - SIZE_SZ)) = offset;
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS ((++n_split, ++n_avail));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* place a consolidated chunk on the back of a list */
|
|
|
|
|
/* like above, except no split */
|
|
|
|
|
|
|
|
|
|
static inline void consollink(mchunkptr p)
|
|
|
|
|
{
|
|
|
|
|
mbinptr bn = size2bin(p->size);
|
|
|
|
|
mchunkptr h = &(bn->hd);
|
|
|
|
|
mchunkptr b = h->bk;
|
|
|
|
|
|
|
|
|
|
p->bk = b; p->fd = h; h->bk = b->fd = p;
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
if (h == b && bn > malloc_maxbin) malloc_maxbin = bn;
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
UPDATE_STATS(++n_avail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* place a freed chunk on the front of a list */
|
|
|
|
|
|
|
|
|
|
static inline void frontlink(mchunkptr p)
|
|
|
|
|
{
|
|
|
|
|
mbinptr bn = size2bin(p->size);
|
|
|
|
|
mchunkptr h = &(bn->hd);
|
|
|
|
|
mchunkptr f = h->fd;
|
|
|
|
|
|
|
|
|
|
p->bk = h; p->fd = f; f->bk = h->fd = p;
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
if (h == f && bn > malloc_maxbin) malloc_maxbin = bn;
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
bn->dirty = 1;
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS(++n_avail);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Dealing with sbrk */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* To link consecutive sbrk regions when possible */
|
|
|
|
|
|
|
|
|
|
static int* last_sbrk_end;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* who to call when sbrk returns failure */
|
|
|
|
|
|
|
|
|
|
#ifndef NO_NEW_HANDLER
|
|
|
|
|
typedef volatile void (*vfp)();
|
|
|
|
|
#ifdef __cplusplus
|
|
|
|
|
extern "C" vfp __new_handler;
|
|
|
|
|
#else
|
|
|
|
|
extern vfp __new_handler;
|
|
|
|
|
#endif
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static mchunkptr malloc_from_sys(unsigned nb)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr p;
|
|
|
|
|
unsigned int sbrk_size;
|
|
|
|
|
int* ip;
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
/* Minimally, we need to pad with enough space */
|
|
|
|
|
/* to place dummy size/use fields to ends if needed */
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
sbrk_size = ((nb + SBRK_UNIT - 1 + SIZE_SZ + SIZE_SZ)
|
1993-07-30 20:16:53 +00:00
|
|
|
|
/ SBRK_UNIT) * SBRK_UNIT;
|
|
|
|
|
|
|
|
|
|
ip = (int*)(sbrk(sbrk_size));
|
|
|
|
|
if ((char*)ip == (char*)(-1)) /* sbrk returns -1 on failure */
|
|
|
|
|
{
|
|
|
|
|
#ifndef NO_NEW_HANDLER
|
|
|
|
|
(*__new_handler) ();
|
|
|
|
|
#endif
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS ((++n_sbrks, sbrked_mem += sbrk_size));
|
|
|
|
|
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
if (last_sbrk_end != &ip[-1])
|
|
|
|
|
{
|
1993-07-30 20:16:53 +00:00
|
|
|
|
/* It's either first time through or someone else called sbrk. */
|
|
|
|
|
/* Arrange end-markers at front & back */
|
|
|
|
|
|
|
|
|
|
/* Shouldn't be necessary, but better to be safe */
|
|
|
|
|
while (!aligned_OK(ip)) { ++ip; sbrk_size -= SIZE_SZ; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Mark the front as in use to prevent merging. */
|
|
|
|
|
/* Note we can get away with only 1 word, not MINSIZE overhead here */
|
|
|
|
|
|
|
|
|
|
*ip++ = SIZE_SZ | INUSE;
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
p = (mchunkptr)ip;
|
1995-05-30 05:05:38 +00:00
|
|
|
|
set_size(p,sbrk_size - (SIZE_SZ + SIZE_SZ));
|
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
}
|
1995-05-30 05:05:38 +00:00
|
|
|
|
else
|
1993-07-30 20:16:53 +00:00
|
|
|
|
{
|
1995-05-30 05:05:38 +00:00
|
|
|
|
mchunkptr l;
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
/* We can safely make the header start at end of prev sbrked chunk. */
|
|
|
|
|
/* We will still have space left at the end from a previous call */
|
|
|
|
|
/* to place the end marker, below */
|
|
|
|
|
|
|
|
|
|
p = (mchunkptr)(last_sbrk_end);
|
|
|
|
|
set_size(p, sbrk_size);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Even better, maybe we can merge with last fragment: */
|
|
|
|
|
|
|
|
|
|
l = prev_chunk(p);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
if (!inuse(l))
|
1993-07-30 20:16:53 +00:00
|
|
|
|
{
|
|
|
|
|
unlink(l);
|
|
|
|
|
set_size(l, p->size + l->size);
|
|
|
|
|
p = l;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* mark the end of sbrked space as in use to prevent merging */
|
|
|
|
|
|
|
|
|
|
last_sbrk_end = (int*)((char*)p + p->size);
|
|
|
|
|
*last_sbrk_end = SIZE_SZ | INUSE;
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS((++n_avail, ++n_malloc_chunks));
|
|
|
|
|
|
|
|
|
|
/* make it safe to unlink in malloc */
|
|
|
|
|
UPDATE_STATS(++n_avail);
|
|
|
|
|
p->fd = p->bk = p;
|
|
|
|
|
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Consolidate dirty bins. */
|
|
|
|
|
/* Stop if found a chunk big enough to satisfy current malloc request */
|
|
|
|
|
|
|
|
|
|
/* (It requires much less bookkeeping to consolidate entire bins */
|
|
|
|
|
/* at once than to keep records of which chunks might be */
|
|
|
|
|
/* consolidatable. So long as the lists are short, which we */
|
|
|
|
|
/* try to ensure via small bin ranges, there is little wasted effort.) */
|
|
|
|
|
|
|
|
|
|
static mchunkptr malloc_find_space(unsigned int nb)
|
|
|
|
|
{
|
|
|
|
|
mbinptr b;
|
|
|
|
|
|
|
|
|
|
/* first, re-adjust max used bin */
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
while (malloc_maxbin >= FIRSTBIN &&
|
1993-07-30 20:16:53 +00:00
|
|
|
|
malloc_maxbin->hd.bk == &(malloc_maxbin->hd))
|
|
|
|
|
{
|
|
|
|
|
malloc_maxbin->dirty = 0;
|
|
|
|
|
--malloc_maxbin;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (b = malloc_maxbin; b >= FIRSTBIN; --b)
|
|
|
|
|
{
|
|
|
|
|
UPDATE_STATS(++n_malloc_bins);
|
|
|
|
|
|
|
|
|
|
if (b->dirty)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr h = &(b->hd); /* head of list */
|
|
|
|
|
mchunkptr p = h->fd; /* chunk traverser */
|
|
|
|
|
|
|
|
|
|
while (p != h)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr nextp = p->fd; /* save, in case of relinks */
|
|
|
|
|
int consolidated = 0; /* only unlink/relink if consolidated */
|
|
|
|
|
|
|
|
|
|
mchunkptr t;
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS(++n_malloc_chunks);
|
|
|
|
|
|
|
|
|
|
while (!inuse(t = prev_chunk(p))) /* consolidate backward */
|
|
|
|
|
{
|
|
|
|
|
if (!consolidated) { consolidated = 1; unlink(p); }
|
|
|
|
|
if (t == nextp) nextp = t->fd;
|
|
|
|
|
unlink(t);
|
|
|
|
|
set_size(t, t->size + p->size);
|
|
|
|
|
p = t;
|
|
|
|
|
UPDATE_STATS (++n_consol);
|
|
|
|
|
}
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
while (!inuse(t = next_chunk(p))) /* consolidate forward */
|
|
|
|
|
{
|
|
|
|
|
if (!consolidated) { consolidated = 1; unlink(p); }
|
|
|
|
|
if (t == nextp) nextp = t->fd;
|
|
|
|
|
unlink(t);
|
|
|
|
|
set_size(p, p->size + t->size);
|
|
|
|
|
UPDATE_STATS (++n_consol);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (consolidated)
|
|
|
|
|
{
|
|
|
|
|
if (p->size >= nb)
|
|
|
|
|
{
|
|
|
|
|
/* make it safe to unlink in malloc */
|
|
|
|
|
UPDATE_STATS(++n_avail);
|
|
|
|
|
p->fd = p->bk = p;
|
|
|
|
|
return p;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
consollink(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p = nextp;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b->dirty = 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* nothing available - sbrk some more */
|
|
|
|
|
|
|
|
|
|
return malloc_from_sys(nb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Finally, the user-level functions */
|
|
|
|
|
|
|
|
|
|
void* malloc(unsigned int bytes)
|
|
|
|
|
{
|
|
|
|
|
unsigned int nb = request2size(bytes); /* padded request size */
|
|
|
|
|
mbinptr b = size2bin(nb); /* corresponding bin */
|
|
|
|
|
mchunkptr hd = &(b->hd); /* head of its list */
|
|
|
|
|
mchunkptr p = hd->fd; /* chunk traverser */
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS((requested_mem+=bytes, ++n_malloc_bins));
|
|
|
|
|
|
|
|
|
|
/* Try a (near) exact match in own bin */
|
|
|
|
|
/* clean out unusable but consolidatable chunks in bin while traversing */
|
|
|
|
|
|
|
|
|
|
while (p != hd)
|
|
|
|
|
{
|
|
|
|
|
UPDATE_STATS(++n_malloc_chunks);
|
|
|
|
|
if (p->size >= nb)
|
|
|
|
|
goto found;
|
|
|
|
|
else /* try to consolidate; same code as malloc_find_space */
|
|
|
|
|
{
|
|
|
|
|
mchunkptr nextp = p->fd; /* save, in case of relinks */
|
|
|
|
|
int consolidated = 0; /* only unlink/relink if consolidated */
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
mchunkptr t;
|
|
|
|
|
|
|
|
|
|
while (!inuse(t = prev_chunk(p))) /* consolidate backward */
|
|
|
|
|
{
|
|
|
|
|
if (!consolidated) { consolidated = 1; unlink(p); }
|
|
|
|
|
if (t == nextp) nextp = t->fd;
|
|
|
|
|
unlink(t);
|
|
|
|
|
set_size(t, t->size + p->size);
|
|
|
|
|
p = t;
|
|
|
|
|
UPDATE_STATS (++n_consol);
|
|
|
|
|
}
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
while (!inuse(t = next_chunk(p))) /* consolidate forward */
|
|
|
|
|
{
|
|
|
|
|
if (!consolidated) { consolidated = 1; unlink(p); }
|
|
|
|
|
if (t == nextp) nextp = t->fd;
|
|
|
|
|
unlink(t);
|
|
|
|
|
set_size(p, p->size + t->size);
|
|
|
|
|
UPDATE_STATS (++n_consol);
|
|
|
|
|
}
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
if (consolidated)
|
|
|
|
|
{
|
|
|
|
|
if (p->size >= nb)
|
|
|
|
|
{
|
|
|
|
|
/* make it safe to unlink again below */
|
|
|
|
|
UPDATE_STATS(++n_avail);
|
|
|
|
|
p->fd = p->bk = p;
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
consollink(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p = nextp;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b->dirty = 0; /* true if got here */
|
|
|
|
|
|
|
|
|
|
/* Scan bigger bins for a victim */
|
|
|
|
|
|
|
|
|
|
while (++b <= malloc_maxbin)
|
|
|
|
|
{
|
|
|
|
|
UPDATE_STATS(++n_malloc_bins);
|
|
|
|
|
if ((p = b->hd.bk) != &(b->hd)) /* no need to check size */
|
|
|
|
|
goto found;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Consolidate or sbrk */
|
|
|
|
|
|
|
|
|
|
p = malloc_find_space(nb);
|
|
|
|
|
|
|
|
|
|
if (p == 0) return 0; /* allocation failure */
|
|
|
|
|
|
|
|
|
|
found: /* Use what we found */
|
|
|
|
|
|
|
|
|
|
unlink(p);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
split(p, nb);
|
1993-07-30 20:16:53 +00:00
|
|
|
|
UPDATE_STATS(do_malloc_stats(p));
|
|
|
|
|
return chunk2mem(p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void free(void* mem)
|
|
|
|
|
{
|
|
|
|
|
if (mem != 0)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr p = mem2chunk(mem);
|
|
|
|
|
UPDATE_STATS(do_free_stats(p));
|
|
|
|
|
frontlink(p);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void* calloc(unsigned int n, unsigned int elem_size)
|
|
|
|
|
{
|
|
|
|
|
unsigned int sz = n * elem_size;
|
|
|
|
|
void* p = malloc(sz);
|
|
|
|
|
bzero(p, sz);
|
|
|
|
|
return p;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* This is here for compatibility with older systems */
|
|
|
|
|
void cfree(void *mem)
|
|
|
|
|
{
|
|
|
|
|
free(mem);
|
|
|
|
|
}
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
unsigned int malloc_usable_size(void* mem)
|
|
|
|
|
{
|
|
|
|
|
if (mem == 0)
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
{
|
1995-05-30 05:05:38 +00:00
|
|
|
|
mchunkptr p = (mchunkptr)((char*)(mem) - SIZE_SZ);
|
1993-07-30 20:16:53 +00:00
|
|
|
|
unsigned int sz = p->size & ~(INUSE);
|
|
|
|
|
if (p->size == sz || sz != *((int*)((char*)(p) + sz - SIZE_SZ)))
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
return sz - MALLOC_MIN_OVERHEAD;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void* realloc(void* mem, unsigned int bytes)
|
|
|
|
|
{
|
1995-05-30 05:05:38 +00:00
|
|
|
|
if (mem == 0)
|
1993-07-30 20:16:53 +00:00
|
|
|
|
return malloc(bytes);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unsigned int nb = request2size(bytes);
|
|
|
|
|
mchunkptr p = mem2chunk(mem);
|
|
|
|
|
unsigned int oldsize = p->size;
|
|
|
|
|
int room;
|
|
|
|
|
mchunkptr nxt;
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS((++n_reallocs, requested_mem += bytes-oldsize));
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
/* try to expand (even if already big enough), to clean up chunk */
|
|
|
|
|
|
|
|
|
|
while (!inuse(nxt = next_chunk(p)))
|
|
|
|
|
{
|
|
|
|
|
UPDATE_STATS ((malloced_mem += nxt->size, ++n_consol));
|
|
|
|
|
unlink(nxt);
|
|
|
|
|
set_size(p, p->size + nxt->size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
room = p->size - nb;
|
|
|
|
|
if (room >= 0)
|
|
|
|
|
{
|
|
|
|
|
split(p, nb);
|
|
|
|
|
UPDATE_STATS(malloced_mem -= room);
|
|
|
|
|
return chunk2mem(p);
|
|
|
|
|
}
|
|
|
|
|
else /* do the obvious */
|
|
|
|
|
{
|
|
|
|
|
void* newmem;
|
|
|
|
|
set_inuse(p); /* don't let malloc consolidate us yet! */
|
|
|
|
|
newmem = malloc(nb);
|
|
|
|
|
bcopy(mem, newmem, oldsize - SIZE_SZ);
|
|
|
|
|
free(mem);
|
|
|
|
|
UPDATE_STATS(++n_reallocs_with_copy);
|
|
|
|
|
return newmem;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* return a pointer to space with at least the alignment requested */
|
|
|
|
|
|
|
|
|
|
void* memalign(unsigned int alignment, unsigned int bytes)
|
|
|
|
|
{
|
|
|
|
|
mchunkptr p;
|
|
|
|
|
unsigned int nb = request2size(bytes);
|
|
|
|
|
|
|
|
|
|
/* find an alignment that both we and the user can live with: */
|
|
|
|
|
/* least common multiple guarantees mutual happiness */
|
|
|
|
|
unsigned int align = lcm(alignment, MALLOC_MIN_OVERHEAD);
|
|
|
|
|
unsigned int mask = align - 1;
|
|
|
|
|
|
|
|
|
|
/* call malloc with worst case padding to hit alignment; */
|
|
|
|
|
/* we will give back extra */
|
|
|
|
|
|
|
|
|
|
unsigned int req = nb + align + MINSIZE;
|
|
|
|
|
void* m = malloc(req);
|
|
|
|
|
|
|
|
|
|
if (m == 0) return m;
|
|
|
|
|
|
|
|
|
|
p = mem2chunk(m);
|
|
|
|
|
|
|
|
|
|
/* keep statistics on track */
|
|
|
|
|
|
|
|
|
|
UPDATE_STATS(--n_mallocs);
|
|
|
|
|
UPDATE_STATS(malloced_mem -= p->size);
|
|
|
|
|
UPDATE_STATS(requested_mem -= req);
|
|
|
|
|
UPDATE_STATS(requested_mem += bytes);
|
|
|
|
|
|
|
|
|
|
if (((int)(m) & (mask)) != 0) /* misaligned */
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
/* find an aligned spot inside chunk */
|
|
|
|
|
|
|
|
|
|
mchunkptr ap = (mchunkptr)(( ((int)(m) + mask) & -align) - SIZE_SZ);
|
|
|
|
|
|
|
|
|
|
unsigned int gap = (unsigned int)(ap) - (unsigned int)(p);
|
|
|
|
|
unsigned int room;
|
|
|
|
|
|
|
|
|
|
/* we need to give back leading space in a chunk of at least MINSIZE */
|
|
|
|
|
|
|
|
|
|
if (gap < MINSIZE)
|
|
|
|
|
{
|
|
|
|
|
/* This works since align >= MINSIZE */
|
|
|
|
|
/* and we've malloc'd enough total room */
|
|
|
|
|
|
|
|
|
|
ap = (mchunkptr)( (int)(ap) + align );
|
1995-05-30 05:05:38 +00:00
|
|
|
|
gap += align;
|
1993-07-30 20:16:53 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (gap + nb > p->size) /* can't happen unless chunk sizes corrupted */
|
|
|
|
|
malloc_user_error();
|
|
|
|
|
|
|
|
|
|
room = p->size - gap;
|
|
|
|
|
|
|
|
|
|
/* give back leader */
|
|
|
|
|
set_size(p, gap);
|
|
|
|
|
consollink(p);
|
|
|
|
|
|
|
|
|
|
/* use the rest */
|
|
|
|
|
p = ap;
|
|
|
|
|
set_size(p, room);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* also give back spare room at the end */
|
|
|
|
|
|
1995-05-30 05:05:38 +00:00
|
|
|
|
split(p, nb);
|
1993-07-30 20:16:53 +00:00
|
|
|
|
UPDATE_STATS(do_malloc_stats(p));
|
|
|
|
|
return chunk2mem(p);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifndef sun
|
|
|
|
|
#include "getpagesize.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
static unsigned int malloc_pagesize = 0;
|
|
|
|
|
|
|
|
|
|
void* valloc(unsigned int bytes)
|
|
|
|
|
{
|
|
|
|
|
if (malloc_pagesize == 0) malloc_pagesize = getpagesize();
|
|
|
|
|
return memalign (malloc_pagesize, bytes);
|
|
|
|
|
}
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
|
|
|
|
|
void malloc_stats()
|
|
|
|
|
{
|
|
|
|
|
#ifndef MALLOC_STATS
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
int i;
|
|
|
|
|
mchunkptr p;
|
|
|
|
|
double nm = (double)(n_mallocs + n_reallocs);
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "\nmalloc statistics\n\n");
|
|
|
|
|
|
|
|
|
|
if (n_mallocs != 0)
|
|
|
|
|
fprintf(stderr, "requests = %10u total size = %10u\tave = %10u\n",
|
|
|
|
|
n_mallocs, requested_mem, requested_mem/n_mallocs);
|
|
|
|
|
|
|
|
|
|
if (n_mallocs != 0)
|
|
|
|
|
fprintf(stderr, "mallocs = %10u total size = %10u\tave = %10u\n",
|
|
|
|
|
n_mallocs, malloced_mem, malloced_mem/n_mallocs);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
if (n_frees != 0)
|
|
|
|
|
fprintf(stderr, "frees = %10u total size = %10u\tave = %10u\n",
|
|
|
|
|
n_frees, freed_mem, freed_mem/n_frees);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
if (n_mallocs-n_frees != 0)
|
|
|
|
|
fprintf(stderr, "in use = %10u total size = %10u\tave = %10u\n",
|
1995-05-30 05:05:38 +00:00
|
|
|
|
n_mallocs-n_frees, malloced_mem-freed_mem,
|
1993-07-30 20:16:53 +00:00
|
|
|
|
(malloced_mem-freed_mem) / (n_mallocs-n_frees));
|
|
|
|
|
|
|
|
|
|
if (max_inuse != 0)
|
|
|
|
|
fprintf(stderr, "max in use= %10u total size = %10u\tave = %10u\n",
|
|
|
|
|
max_inuse, max_used_mem, max_used_mem / max_inuse);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
if (n_avail != 0)
|
|
|
|
|
fprintf(stderr, "available = %10u total size = %10u\tave = %10u\n",
|
1995-05-30 05:05:38 +00:00
|
|
|
|
n_avail, sbrked_mem - (malloced_mem-freed_mem),
|
1993-07-30 20:16:53 +00:00
|
|
|
|
(sbrked_mem - (malloced_mem-freed_mem)) / n_avail);
|
|
|
|
|
|
|
|
|
|
if (n_sbrks != 0)
|
|
|
|
|
fprintf(stderr, "sbrks = %10u total size = %10u\tave = %10u\n\n",
|
|
|
|
|
n_sbrks, sbrked_mem, sbrked_mem/ n_sbrks);
|
|
|
|
|
|
|
|
|
|
if (n_reallocs != 0)
|
|
|
|
|
fprintf(stderr, "reallocs = %10u with copy = %10u\n\n",
|
|
|
|
|
n_reallocs, n_reallocs_with_copy);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nm != 0)
|
|
|
|
|
{
|
1995-05-30 05:05:38 +00:00
|
|
|
|
fprintf(stderr, "chunks scanned per malloc = %6.3f\n",
|
1993-07-30 20:16:53 +00:00
|
|
|
|
n_malloc_chunks / nm);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
fprintf(stderr, "bins scanned per malloc = %6.3f\n",
|
1993-07-30 20:16:53 +00:00
|
|
|
|
n_malloc_bins / nm);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
fprintf(stderr, "splits per malloc = %6.3f\n",
|
1993-07-30 20:16:53 +00:00
|
|
|
|
n_split / nm);
|
1995-05-30 05:05:38 +00:00
|
|
|
|
fprintf(stderr, "consolidations per malloc = %6.3f\n",
|
1993-07-30 20:16:53 +00:00
|
|
|
|
n_consol / nm);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "\nfree chunks:\n");
|
|
|
|
|
for (i = 0; i < MAXBIN; ++i)
|
|
|
|
|
{
|
|
|
|
|
p = av[i].hd.fd;
|
|
|
|
|
if (p != &(av[i].hd))
|
|
|
|
|
{
|
|
|
|
|
unsigned int count = 1;
|
|
|
|
|
unsigned int sz = p->size;
|
|
|
|
|
for (p = p->fd; p != &(av[i].hd); p = p->fd)
|
|
|
|
|
{
|
|
|
|
|
if (p->size == sz)
|
|
|
|
|
++count;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fprintf(stderr, "\tsize = %10u count = %5u\n", sz, count);
|
|
|
|
|
count = 1;
|
|
|
|
|
sz = p->size;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fprintf(stderr, "\tsize = %10u count = %5u\n", sz, count);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif /* MALLOC_STATS */
|
1995-05-30 05:05:38 +00:00
|
|
|
|
|
1993-07-30 20:16:53 +00:00
|
|
|
|
#endif /* NO_LIBGXX_MALLOC */
|
|
|
|
|
|
|
|
|
|
|