📄 dlmalloc-merged.c
字号:
typedef struct GmListElement GmListElement;
struct GmListElement
{
GmListElement* next;
void* base;
};
static GmListElement* head = 0;
static unsigned int gNextAddress = 0;
static unsigned int gAddressBase = 0;
static unsigned int gAllocatedSize = 0;
static
GmListElement* makeGmListElement (void* bas)
{
GmListElement* this;
this = (GmListElement*)(void*)LocalAlloc (0, sizeof (GmListElement));
assert (this);
if (this)
{
this->base = bas;
this->next = head;
head = this;
}
return this;
}
void gcleanup ()
{
BOOL rval;
assert ( (head == NULL) || (head->base == (void*)gAddressBase));
if (gAddressBase && (gNextAddress - gAddressBase))
{
rval = VirtualFree ((void*)gAddressBase,
gNextAddress - gAddressBase,
MEM_DECOMMIT);
assert (rval);
}
while (head)
{
GmListElement* next = head->next;
rval = VirtualFree (head->base, 0, MEM_RELEASE);
assert (rval);
LocalFree (head);
head = next;
}
}
static
void* findRegion (void* start_address, unsigned long size)
{
MEMORY_BASIC_INFORMATION info;
if (size >= TOP_MEMORY) return NULL;
while ((unsigned long)start_address + size < TOP_MEMORY)
{
VirtualQuery (start_address, &info, sizeof (info));
if ((info.State == MEM_FREE) && (info.RegionSize >= size))
return start_address;
else
{
// Requested region is not available so see if the
// next region is available. Set 'start_address'
// to the next region and call 'VirtualQuery()'
// again.
start_address = (char*)info.BaseAddress + info.RegionSize;
// Make sure we start looking for the next region
// on the *next* 64K boundary. Otherwise, even if
// the new region is free according to
// 'VirtualQuery()', the subsequent call to
// 'VirtualAlloc()' (which follows the call to
// this routine in 'wsbrk()') will round *down*
// the requested address to a 64K boundary which
// we already know is an address in the
// unavailable region. Thus, the subsequent call
// to 'VirtualAlloc()' will fail and bring us back
// here, causing us to go into an infinite loop.
start_address =
(void *) AlignPage64K((unsigned long) start_address);
}
}
return NULL;
}
void* wsbrk (long size)
{
void* tmp;
if (size > 0)
{
if (gAddressBase == 0)
{
gAllocatedSize = max (RESERVED_SIZE, AlignPage (size));
gNextAddress = gAddressBase =
(unsigned int)VirtualAlloc (NULL, gAllocatedSize,
MEM_RESERVE, PAGE_NOACCESS);
} else if (AlignPage (gNextAddress + size) > (gAddressBase +
gAllocatedSize))
{
long new_size = max (NEXT_SIZE, AlignPage (size));
void* new_address = (void*)(gAddressBase+gAllocatedSize);
do
{
new_address = findRegion (new_address, new_size);
if (new_address == 0)
return (void*)-1;
gAddressBase = gNextAddress =
(unsigned int)VirtualAlloc (new_address, new_size,
MEM_RESERVE, PAGE_NOACCESS);
// repeat in case of race condition
// The region that we found has been snagged
// by another thread
}
while (gAddressBase == 0);
assert (new_address == (void*)gAddressBase);
gAllocatedSize = new_size;
if (!makeGmListElement ((void*)gAddressBase))
return (void*)-1;
}
if ((size + gNextAddress) > AlignPage (gNextAddress))
{
void* res;
res = VirtualAlloc ((void*)AlignPage (gNextAddress),
(size + gNextAddress -
AlignPage (gNextAddress)),
MEM_COMMIT, PAGE_READWRITE);
if (res == 0)
return (void*)-1;
}
tmp = (void*)gNextAddress;
gNextAddress = (unsigned int)tmp + size;
return tmp;
}
else if (size < 0)
{
unsigned int alignedGoal = AlignPage (gNextAddress + size);
/* Trim by releasing the virtual memory */
if (alignedGoal >= gAddressBase)
{
VirtualFree ((void*)alignedGoal, gNextAddress - alignedGoal,
MEM_DECOMMIT);
gNextAddress = gNextAddress + size;
return (void*)gNextAddress;
}
else
{
VirtualFree ((void*)gAddressBase, gNextAddress - gAddressBase,
MEM_DECOMMIT);
gNextAddress = gAddressBase;
return (void*)-1;
}
}
else
{
return (void*)gNextAddress;
}
}
#endif
/*
Type declarations
*/
struct malloc_chunk
{
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
};
typedef struct malloc_chunk* mchunkptr;
/*
malloc_chunk details:
(The following includes lightly edited explanations by Colin Plumb.)
Chunks of memory are maintained using a `boundary tag' method as
described in e.g., Knuth or Standish. (See the paper by Paul
Wilson ftp://ftp.cs.utexas.edu/pub/garbage/allocsrv.ps for a
survey of such techniques.) Sizes of free chunks are stored both
in the front of each chunk and at the end. This makes
consolidating fragmented chunks into bigger chunks very fast. The
size fields also hold bits representing whether chunks are free or
in use.
An allocated chunk looks like this:
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if allocated | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_space() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Where "chunk" is the front of the chunk for the purpose of most of
the malloc code, but "mem" is the pointer that is returned to the
user. "Nextchunk" is the beginning of the next contiguous chunk.
Chunks always begin on even word boundries, so the mem portion
(which is returned to the user) is also on an even word boundary, and
thus double-word aligned.
Free chunks are stored in circular doubly-linked lists, and look like this:
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`head:' | Size of chunk, in bytes |P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Forward pointer to next chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Back pointer to previous chunk in list |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Unused space (may be 0 bytes long) .
. .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
`foot:' | Size of chunk, in bytes |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
The P (PREV_INUSE) bit, stored in the unused low-order bit of the
chunk size (which is always a multiple of two words), is an in-use
bit for the *previous* chunk. If that bit is *clear*, then the
word before the current chunk size contains the previous chunk
size, and can be used to find the front of the previous chunk.
(The very first chunk allocated always has this bit set,
preventing access to non-existent (or non-owned) memory.)
Note that the `foot' of the current chunk is actually represented
as the prev_size of the NEXT chunk. (This makes it easier to
deal with alignments etc).
The two exceptions to all this are
1. The special chunk `top', which doesn't bother using the
trailing size field since there is no
next contiguous chunk that would have to index off it. (After
initialization, `top' is forced to always exist. If it would
become less than MINSIZE bytes long, it is replenished via
malloc_extend_top.)
2. Chunks allocated via mmap, which have the second-lowest-order
bit (IS_MMAPPED) set in their size fields. Because they are
never merged or traversed from any other chunk, they have no
foot size or inuse information.
Available chunks are kept in any of several places (all declared below):
* `av': An array of chunks serving as bin headers for consolidated
chunks. Each bin is doubly linked. The bins are approximately
proportionally (log) spaced. There are a lot of these bins
(128). This may look excessive, but works very well in
practice. All procedures maintain the invariant that no
consolidated chunk physically borders another one. Chunks in
bins are kept in size order, with ties going to the
approximately least recently used chunk.
The chunks in each bin are maintained in decreasing sorted order by
size. This is irrelevant for the small bins, which all contain
the same-sized chunks, but facilitates best-fit allocation for
larger chunks. (These lists are just sequential. Keeping them in
order almost never requires enough traversal to warrant using
fancier ordered data structures.) Chunks of the same size are
linked with the most recently freed at the front, and allocations
are taken from the back. This results in LRU or FIFO allocation
order, which tends to give each chunk an equal opportunity to be
consolidated with adjacent freed chunks, resulting in larger free
chunks and less fragmentation.
* `top': The top-most available chunk (i.e., the one bordering the
end of available memory) is treated specially. It is never
included in any bin, is used only if no other chunk is
available, and is released back to the system if it is very
large (see M_TRIM_THRESHOLD).
* `last_remainder': A bin holding only the remainder of the
most recently split (non-top) chunk. This bin is checked
before other non-fitting chunks, so as to provide better
locality for runs of sequentially allocated chunks.
* Implicitly, through the host system's memory mapping tables.
If supported, requests greater than a threshold are usually
serviced via calls to mmap, and then later released via munmap.
*/
/* sizes, alignments */
#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))
#ifndef MALLOC_ALIGNMENT
#define MALLOC_ALIGN 8
#define MALLOC_ALIGNMENT (SIZE_SZ + SIZE_SZ)
#else
#define MALLOC_ALIGN MALLOC_ALIGNMENT
#endif
#define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
#define MINSIZE (sizeof(struct malloc_chunk))
/* conversion from malloc headers to user pointers, and back */
#define chunk2mem(p) ((Void_t*)((char*)(p) + 2*SIZE_SZ))
#define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*SIZE_SZ))
/* pad request bytes into a usable size */
#define request2size(req) \ (((long)((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) < \ (long)(MINSIZE + MALLOC_ALIGN_MASK)) ? ((MINSIZE + MALLOC_ALIGN_MASK) & ~(MALLOC_ALIGN_MASK)) : \ (((req) + (SIZE_SZ + MALLOC_ALIGN_MASK)) & ~(MALLOC_ALIGN_MASK)))
/* Check if m has acceptable alignment */
#define aligned_OK(m) (((unsigned long)((m)) & (MALLOC_ALIGN_MASK)) == 0)
/*
Physical chunk operations
*/
/* size field is or'ed with PREV_INUSE when previous adjacent chunk in use */
#define PREV_INUSE 0x1
/* size field is or'ed with IS_MMAPPED if the chunk was obtained with mmap() */
#define IS_MMAPPED 0x2
/* Bits to mask off when extracting size */
#define SIZE_BITS (PREV_INUSE|IS_MMAPPED)
/* Ptr to next physical malloc_chunk. */
#define next_chunk(p) ((mchunkptr)( ((char*)(p)) + ((p)->size & ~PREV_INUSE) ))
/* Ptr to previous physical malloc_chunk */
#define prev_chunk(p)\ ((mchunkptr)( ((char*)(p)) - ((p)->prev_size) ))
/* Treat space at ptr + offset as a chunk */
#define chunk_at_offset(p, s) ((mchunkptr)(((char*)(p)) + (s)))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -