Mercurial > ecos
diff packages/services/memalloc/common/current/src/dlmalloc.cxx @ 115:6ed91473a1cd ecos-sw-2000-08-21
Merge from eCos master repository on 2000-08-21-22:40:54-BST
| author | jlarmour |
|---|---|
| date | Fri, 25 Aug 2000 17:32:38 +0000 |
| parents | |
| children | 511f4dc167f6 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/packages/services/memalloc/common/current/src/dlmalloc.cxx @@ -0,0 +1,1627 @@ +//========================================================================== +// +// dlmalloc.cxx +// +// Port of Doug Lea's malloc implementation +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Doug Lea (dl at g.oswego.edu), jlarmour +// Contributors: +// Date: 2000-06-18 +// Purpose: Doug Lea's malloc implementation +// Description: Doug Lea's malloc has been ported to eCos. This file +// provides the implementation in a way acceptable to eCos. +// Substantial amounts of unnecessary bits (to eCos) of the +// original implementation have been removed to make the +// code more tractable. Note this may make a number of the +// comments appear to make little sense, or no longer apply! +// In particular, mmap support is removed entirely. +// Also the memory is "sbrked" all at once at the +// beginning, covering the entire memory region given at +// construction, and there can be no more afterwards. +// Usage: #include <cyg/memalloc/dlmalloc.hxx> +// +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +// DOCUMENTATION FROM ORIGINAL FILE: +// (some now irrelevant parts elided) + +//---------------------------------------------------------------------------- + +/* + A version of malloc/free/realloc written by Doug Lea and released to the + public domain. Send questions/comments/complaints/performance data + to dl at cs.oswego.edu + +* VERSION 2.6.6 Sun Mar 5 19:10:03 2000 Doug Lea (dl at gee) + + Note: There may be an updated version of this malloc obtainable at + ftp://g.oswego.edu/pub/misc/malloc.c + Check before installing! + +* Why use this malloc? + + This is not the fastest, most space-conserving, most portable, or + most tunable malloc ever written. However it is among the fastest + while also being among the most space-conserving, portable and tunable. + Consistent balance across these factors results in a good general-purpose + allocator. For a high-level description, see + http://g.oswego.edu/dl/html/malloc.html + +* Synopsis of public routines + + (Much fuller descriptions are contained in the program documentation below.) + +[ these have of course been renamed in the eCos port ]a + + malloc(size_t n); + Return a pointer to a newly allocated chunk of at least n bytes, or null + if no space is available. + free(Void_t* p); + Release the chunk of memory pointed to by p, or no effect if p is null. + realloc(Void_t* p, size_t n); + Return a pointer to a chunk of size n that contains the same data + as does chunk p up to the minimum of (n, p's size) bytes, or null + if no space is available. The returned pointer may or may not be + the same as p. If p is null, equivalent to malloc. realloc of + zero bytes calls free(p) + +* Vital statistics: + + Alignment: 8-byte + 8 byte alignment is currently hardwired into the design. This + seems to suffice for all current machines and C compilers. + + Assumed pointer representation: 4 or 8 bytes + Code for 8-byte pointers is untested by me but has worked + reliably by Wolfram Gloger, who contributed most of the + changes supporting this. + + Assumed size_t representation: 4 or 8 bytes + Note that size_t is allowed to be 4 bytes even if pointers are 8. + + Minimum overhead per allocated chunk: 4 or 8 bytes + Each malloced chunk has a hidden overhead of 4 bytes holding size + and status information. + + Minimum allocated size: 4-byte ptrs: 16 bytes (including 4 overhead) + 8-byte ptrs: 24/32 bytes (including, 4/8 overhead) + + When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte + ptrs but 4 byte size) or 24 (for 8/8) additional bytes are + needed; 4 (8) for a trailing size field + and 8 (16) bytes for free list pointers. Thus, the minimum + allocatable size is 16/24/32 bytes. + + Even a request for zero bytes (i.e., malloc(0)) returns a + pointer to something of the minimum allocatable size. + + Maximum allocated size: 4-byte size_t: 2^31 - 8 bytes + 8-byte size_t: 2^63 - 16 bytes + + It is assumed that (possibly signed) size_t bit values suffice to + represent chunk sizes. `Possibly signed' is due to the fact + that `size_t' may be defined on a system as either a signed or + an unsigned type. To be conservative, values that would appear + as negative numbers are avoided. + Requests for sizes with a negative sign bit when the request + size is treaded as a long will return null. + + Maximum overhead wastage per allocated chunk: normally 15 bytes + + Alignnment demands, plus the minimum allocatable size restriction + make the normal worst-case wastage 15 bytes (i.e., up to 15 + more bytes will be allocated than were requested in malloc), with + one exception: because requests for zero bytes allocate non-zero space, + the worst case wastage for a request of zero bytes is 24 bytes. + +* Limitations + + Here are some features that are NOT currently supported + + * No user-definable hooks for callbacks and the like. + * No automated mechanism for fully checking that all accesses + to malloced memory stay within their bounds. + * No support for compaction. + +* Synopsis of compile-time options: + + People have reported using previous versions of this malloc on all + versions of Unix, sometimes by tweaking some of the defines + below. It has been tested most extensively on Solaris and + Linux. It is also reported to work on WIN32 platforms. + People have also reported adapting this malloc for use in + stand-alone embedded systems. + + The implementation is in straight, hand-tuned ANSI C. Among other + consequences, it uses a lot of macros. Because of this, to be at + all usable, this code should be compiled using an optimizing compiler + (for example gcc -O2) that can simplify expressions and control + paths. + + CYGDBG_MEMALLOC_ALLOCATOR_DLMALLOC_DEBUG (default: NOT defined) + Define to enable debugging. Adds fairly extensive assertion-based + checking to help track down memory errors, but noticeably slows down + execution. + MALLOC_LOCK (default: NOT defined) + MALLOC_UNLOCK (default: NOT defined) + Define these to C expressions which are run to lock and unlock + the malloc data structures. Calls may be nested; that is, + MALLOC_LOCK may be called more than once before the corresponding + MALLOC_UNLOCK calls. MALLOC_LOCK must avoid waiting for a lock + that it already holds. + MALLOC_ALIGNMENT (default: NOT defined) + Define this to 16 if you need 16 byte alignment instead of 8 byte alignment + which is the normal default. + SIZE_T_SMALLER_THAN_LONG (default: NOT defined) + Define this when the platform you are compiling has + sizeof(long) > sizeof(size_t). + The option causes some extra code to be generated to handle operations + that use size_t operands and have long results. + INTERNAL_SIZE_T (default: size_t) + Define to a 32-bit type (probably `unsigned int') if you are on a + 64-bit machine, yet do not want or need to allow malloc requests of + greater than 2^31 to be handled. This saves space, especially for + very small chunks. + +*/ + +//---------------------------------------------------------------------------- + + +/* Preliminaries */ + +#include <pkgconf/memalloc.h> // configuration header +#include <pkgconf/infra.h> // CYGDBG_USE_ASSERTS +#include <cyg/infra/cyg_type.h> // types +#include <cyg/infra/cyg_ass.h> // assertions +#include <stddef.h> // for size_t +#include <cyg/memalloc/dlmalloc.hxx> + +/* + Debugging: + + Because freed chunks may be overwritten with link fields, this + malloc will often die when freed memory is overwritten by user + programs. This can be very effective (albeit in an annoying way) + in helping track down dangling pointers. + + If you compile with CYGDBG_MEMALLOC_ALLOCATOR_DLMALLOC_DEBUG enabled, a + number of assertion checks are + enabled that will catch more memory errors. You probably won't be + able to make much sense of the actual assertion errors, but they + should help you locate incorrectly overwritten memory. The + checking is fairly extensive, and will slow down execution + noticeably. Calling get_status() with DEBUG set will + attempt to check every allocated and free chunk in the + course of computing the summmaries. + + Setting CYGDBG_MEMALLOC_ALLOCATOR_DLMALLOC_DEBUG may also be helpful if you + are trying to modify this code. The assertions in the check routines + spell out in more detail the assumptions and invariants underlying + the algorithms. + +*/ + +#ifdef CYGDBG_MEMALLOC_ALLOCATOR_DLMALLOC_DEBUG +# define ASSERT(x) CYG_ASSERTC( x ) +#else +# define ASSERT(x) ((void)0) +#endif + + +/* + Define MALLOC_LOCK and MALLOC_UNLOCK to C expressions to run to + lock and unlock the malloc data structures. MALLOC_LOCK may be + called recursively. + */ + +#ifndef MALLOC_LOCK +#define MALLOC_LOCK +#endif + +#ifndef MALLOC_UNLOCK +#define MALLOC_UNLOCK +#endif + +/* + INTERNAL_SIZE_T is the word-size used for internal bookkeeping + of chunk sizes. On a 64-bit machine, you can reduce malloc + overhead by defining INTERNAL_SIZE_T to be a 32 bit `unsigned int' + at the expense of not being able to handle requests greater than + 2^31. This limitation is hardly ever a concern; you are encouraged + to set this. However, the default version is the same as size_t. +*/ + +#ifndef INTERNAL_SIZE_T +#define INTERNAL_SIZE_T Cyg_Mempool_dlmalloc_Implementation::Cyg_dlmalloc_size_t +#endif + +/* + Following is needed on implementations whereby long > size_t. + The problem is caused because the code performs subtractions of + size_t values and stores the result in long values. In the case + where long > size_t and the first value is actually less than + the second value, the resultant value is positive. For example, + (long)(x - y) where x = 0 and y is 1 ends up being 0x00000000FFFFFFFF + which is 2*31 - 1 instead of 0xFFFFFFFFFFFFFFFF. This is due to the + fact that assignment from unsigned to signed won't sign extend. +*/ + +#ifdef SIZE_T_SMALLER_THAN_LONG +#define long_sub_size_t(x, y) ( (x < y) ? -((long)(y - x)) : (x - y) ); +#else +#define long_sub_size_t(x, y) ( (long)(x - y) ) +#endif + + +#ifdef CYGIMP_MEMALLOC_ALLOCATOR_DLMALLOC_USE_MEMCPY + +#include <string.h> // memcpy, memset + +/* The following macros are only invoked with (2n+1)-multiples of + INTERNAL_SIZE_T units, with a positive integer n. This is exploited + for fast inline execution when n is small. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T mzsz = (nbytes); \ + if(mzsz <= 9*sizeof(mzsz)) { \ + INTERNAL_SIZE_T* mz = (INTERNAL_SIZE_T*) (charp); \ + if(mzsz >= 5*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 7*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; \ + if(mzsz >= 9*sizeof(mzsz)) { *mz++ = 0; \ + *mz++ = 0; }}} \ + *mz++ = 0; \ + *mz++ = 0; \ + *mz = 0; \ + } else memset((charp), 0, mzsz); \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T mcsz = (nbytes); \ + if(mcsz <= 9*sizeof(mcsz)) { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) (src); \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) (dest); \ + if(mcsz >= 5*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 7*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + if(mcsz >= 9*sizeof(mcsz)) { *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; }}} \ + *mcdst++ = *mcsrc++; \ + *mcdst++ = *mcsrc++; \ + *mcdst = *mcsrc ; \ + } else memcpy(dest, src, mcsz); \ +} while(0) + +#else /* !CYGIMP_MEMALLOC_ALLOCATOR_DLMALLOC_USE_MEMCPY */ + +/* Use Duff's device for good zeroing/copying performance. */ + +#define MALLOC_ZERO(charp, nbytes) \ +do { \ + INTERNAL_SIZE_T* mzp = (INTERNAL_SIZE_T*)(charp); \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mzp++ = 0; \ + case 7: *mzp++ = 0; \ + case 6: *mzp++ = 0; \ + case 5: *mzp++ = 0; \ + case 4: *mzp++ = 0; \ + case 3: *mzp++ = 0; \ + case 2: *mzp++ = 0; \ + case 1: *mzp++ = 0; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#define MALLOC_COPY(dest,src,nbytes) \ +do { \ + INTERNAL_SIZE_T* mcsrc = (INTERNAL_SIZE_T*) src; \ + INTERNAL_SIZE_T* mcdst = (INTERNAL_SIZE_T*) dest; \ + long mctmp = (nbytes)/sizeof(INTERNAL_SIZE_T), mcn; \ + if (mctmp < 8) mcn = 0; else { mcn = (mctmp-1)/8; mctmp %= 8; } \ + switch (mctmp) { \ + case 0: for(;;) { *mcdst++ = *mcsrc++; \ + case 7: *mcdst++ = *mcsrc++; \ + case 6: *mcdst++ = *mcsrc++; \ + case 5: *mcdst++ = *mcsrc++; \ + case 4: *mcdst++ = *mcsrc++; \ + case 3: *mcdst++ = *mcsrc++; \ + case 2: *mcdst++ = *mcsrc++; \ + case 1: *mcdst++ = *mcsrc++; if(mcn <= 0) break; mcn--; } \ + } \ +} while(0) + +#endif + + +//---------------------------------------------------------------------------- + +/* + 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 exception to all this is 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. ) + + 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. + + * `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. + +*/ + +typedef struct Cyg_Mempool_dlmalloc_Implementation::malloc_chunk* mchunkptr; + +/* 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 Cyg_Mempool_dlmalloc_Implementation::malloc_chunk)) + +/* conversion from malloc headers to user pointers, and back */ + +#define chunk2mem(p) ((cyg_uint8*)((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 + +/* Bits to mask off when extracting size */ + +#define SIZE_BITS (PREV_INUSE) + + +/* 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))) + +/* + Dealing with use bits +*/ + +/* extract p's inuse bit */ + +#define inuse(p)\ +((((mchunkptr)(((char*)(p))+((p)->size & ~PREV_INUSE)))->size) & PREV_INUSE) + +/* extract inuse bit of previous chunk */ + +#define prev_inuse(p) ((p)->size & PREV_INUSE) + +/* set/clear chunk as in use without otherwise disturbing */ + +#define set_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size |= PREV_INUSE + +#define clear_inuse(p)\ +((mchunkptr)(((char*)(p)) + ((p)->size & ~PREV_INUSE)))->size &= ~(PREV_INUSE) + +/* check/set/clear inuse bits in known places */ + +#define inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size & PREV_INUSE) + +#define set_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size |= PREV_INUSE) + +#define clear_inuse_bit_at_offset(p, s)\ + (((mchunkptr)(((char*)(p)) + (s)))->size &= ~(PREV_INUSE)) + + +/* + Dealing with size fields +*/ + +/* Get size, ignoring use bits */ + +#define chunksize(p) ((p)->size & ~(SIZE_BITS)) + +/* Set size at head, without disturbing its use bit */ + +#define set_head_size(p, s) ((p)->size = (((p)->size & PREV_INUSE) | (s))) + +/* Set size/use ignoring previous bits in header */ + +#define set_head(p, s) ((p)->size = (s)) + +/* Set size at footer (only when chunk is not in use) */ + +#define set_foot(p, s) (((mchunkptr)((char*)(p) + (s)))->prev_size = (s)) + + +//---------------------------------------------------------------------------- + +/* + Bins + + The bins, `av_' are an array of pairs of pointers serving as the + heads of (initially empty) doubly-linked lists of chunks, laid out + in a way so that each pair can be treated as if it were in a + malloc_chunk. (This way, the fd/bk offsets for linking bin heads + and chunks are the same). + + Bins for sizes < 512 bytes contain chunks of all the same size, spaced + 8 bytes apart. Larger bins are approximately logarithmically + spaced. (See the table below.) The `av_' array is never mentioned + directly in the code, but instead via bin access macros. + + Bin layout: + + 64 bins of size 8 + 32 bins of size 64 + 16 bins of size 512 + 8 bins of size 4096 + 4 bins of size 32768 + 2 bins of size 262144 + 1 bin of size what's left + + There is actually a little bit of slop in the numbers in bin_index + for the sake of speed. This makes no difference elsewhere. + + The special chunks `top' and `last_remainder' get their own bins, + (this is implemented via yet more trickery with the av_ array), + although `top' is never properly linked to its bin since it is + always handled specially. + +*/ + +typedef struct Cyg_Mempool_dlmalloc_Implementation::malloc_chunk* mbinptr; + +/* access macros */ + +#define bin_at(i) ((mbinptr)((char*)&(av_[2*(i) + 2]) - 2*SIZE_SZ)) +#define next_bin(b) ((mbinptr)((char*)(b) + 2 * sizeof(mbinptr))) +#define prev_bin(b) ((mbinptr)((char*)(b) - 2 * sizeof(mbinptr))) + +/* + The first 2 bins are never indexed. The corresponding av_ cells are instead + used for bookkeeping. This is not to save space, but to simplify + indexing, maintain locality, and avoid some initialization tests. +*/ + +#define top (bin_at(0)->fd) /* The topmost chunk */ +#define last_remainder (bin_at(1)) /* remainder from last split */ + + +/* Helper macro to initialize bins */ + +#define IAV(i) bin_at(i), bin_at(i) + +#ifndef CYGIMP_MEMALLOC_ALLOCATOR_DLMALLOC_SAFE_MULTIPLE +static mbinptr av_[CYGPRI_MEMALLOC_ALLOCATOR_DLMALLOC_NAV * 2 + 2] = { + 0, 0, + IAV(0), IAV(1), IAV(2), IAV(3), IAV(4), IAV(5), IAV(6), IAV(7), + IAV(8), IAV(9), IAV(10), IAV(11), IAV(12), IAV(13), IAV(14), IAV(15), + IAV(16), IAV(17), IAV(18), IAV(19), IAV(20), IAV(21), IAV(22), IAV(23), + IAV(24), IAV(25), IAV(26), IAV(27), IAV(28), IAV(29), IAV(30), IAV(31), + IAV(32), IAV(33), IAV(34), IAV(35), IAV(36), IAV(37), IAV(38), IAV(39), + IAV(40), IAV(41), IAV(42), IAV(43), IAV(44), IAV(45), IAV(46), IAV(47), + IAV(48), IAV(49), IAV(50), IAV(51), IAV(52), IAV(53), IAV(54), IAV(55), + IAV(56), IAV(57), IAV(58), IAV(59), IAV(60), IAV(61), IAV(62), IAV(63), + IAV(64), IAV(65), IAV(66), IAV(67), IAV(68), IAV(69), IAV(70), IAV(71), + IAV(72), IAV(73), IAV(74), IAV(75), IAV(76), IAV(77), IAV(78), IAV(79), + IAV(80), IAV(81), IAV(82), IAV(83), IAV(84), IAV(85), IAV(86), IAV(87), + IAV(88), IAV(89), IAV(90), IAV(91), IAV(92), IAV(93), IAV(94), IAV(95), + IAV(96), IAV(97), IAV(98), IAV(99), IAV(100), IAV(101), IAV(102), IAV(103), + IAV(104), IAV(105), IAV(106), IAV(107), IAV(108), IAV(109), IAV(110), IAV(111), + IAV(112), IAV(113), IAV(114), IAV(115), IAV(116), IAV(117), IAV(118), IAV(119), + IAV(120), IAV(121), IAV(122), IAV(123), IAV(124), IAV(125), IAV(126), IAV(127) +}; +#endif + +/* field-extraction macros */ + +#define first(b) ((b)->fd) +#define last(b) ((b)->bk) + +/* + Indexing into bins +*/ + +#define bin_index(sz) \ +(((((unsigned long)(sz)) >> 9) == 0) ? (((unsigned long)(sz)) >> 3): \ + ((((unsigned long)(sz)) >> 9) <= 4) ? 56 + (((unsigned long)(sz)) >> 6): \ + ((((unsigned long)(sz)) >> 9) <= 20) ? 91 + (((unsigned long)(sz)) >> 9): \ + ((((unsigned long)(sz)) >> 9) <= 84) ? 110 + (((unsigned long)(sz)) >> 12): \ + ((((unsigned long)(sz)) >> 9) <= 340) ? 119 + (((unsigned long)(sz)) >> 15): \ + ((((unsigned long)(sz)) >> 9) <= 1364) ? 124 + (((unsigned long)(sz)) >> 18): \ + 126) +/* + bins for chunks < 512 are all spaced SMALLBIN_WIDTH bytes apart, and hold + identically sized chunks. This is exploited in malloc. +*/ + +#define MAX_SMALLBIN_SIZE 512 +#define SMALLBIN_WIDTH 8 +#define SMALLBIN_WIDTH_BITS 3 +#define MAX_SMALLBIN (MAX_SMALLBIN_SIZE / SMALLBIN_WIDTH) - 1 + +#define smallbin_index(sz) (((unsigned long)(sz)) >> SMALLBIN_WIDTH_BITS) + +/* + Requests are `small' if both the corresponding and the next bin are small +*/ + +#define is_small_request(nb) (nb < MAX_SMALLBIN_SIZE - SMALLBIN_WIDTH) + +/* + To help compensate for the large number of bins, a one-level index + structure is used for bin-by-bin searching. `binblocks' is a + one-word bitvector recording whether groups of BINBLOCKWIDTH bins + have any (possibly) non-empty bins, so they can be skipped over + all at once during during traversals. The bits are NOT always + cleared as soon as all bins in a block are empty, but instead only + when all are noticed to be empty during traversal in malloc. +*/ + +#define BINBLOCKWIDTH 4 /* bins per block */ + +#define binblocks (bin_at(0)->size) /* bitvector of nonempty blocks */ + +/* bin<->block macros */ + +#define idx2binblock(ix) ((unsigned long)1 << (ix / BINBLOCKWIDTH)) +#define mark_binblock(ii) (binblocks |= idx2binblock(ii)) +#define clear_binblock(ii) (binblocks &= ~(idx2binblock(ii))) + + +//---------------------------------------------------------------------------- + +/* + Debugging support +*/ + +#ifdef CYGDBG_MEMALLOC_ALLOCATOR_DLMALLOC_DEBUG + +/* + These routines make a number of assertions about the states + of data structures that should be true at all times. If any + are not true, it's very likely that a user program has somehow + trashed memory. (It's also possible that there is a coding error + in malloc. In which case, please report it!) +*/ + +void +Cyg_Mempool_dlmalloc_Implementation::do_check_chunk( mchunkptr p ) +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + + /* Check for legal address ... */ + ASSERT((cyg_uint8 *)p >= arenabase); + if (p != top) + ASSERT((cyg_uint8 *)p + sz <= (cyg_uint8 *)top); + else + ASSERT((cyg_uint8 *)p + sz <= arenabase + arenasize); + +} // Cyg_Mempool_dlmalloc_Implementation::do_check_chunk() + + +void +Cyg_Mempool_dlmalloc_Implementation::do_check_free_chunk(mchunkptr p) +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + mchunkptr next = chunk_at_offset(p, sz); + + do_check_chunk(p); + + /* Check whether it claims to be free ... */ + ASSERT(!inuse(p)); + + /* Unless a special marker, must have OK fields */ + if ((long)sz >= (long)MINSIZE) + { + ASSERT((sz & MALLOC_ALIGN_MASK) == 0); + ASSERT(aligned_OK(chunk2mem(p))); + /* ... matching footer field */ + ASSERT(next->prev_size == sz); + /* ... and is fully consolidated */ + ASSERT(prev_inuse(p)); + ASSERT (next == top || inuse(next)); + + /* ... and has minimally sane links */ + ASSERT(p->fd->bk == p); + ASSERT(p->bk->fd == p); + } + else /* markers are always of size SIZE_SZ */ + ASSERT(sz == SIZE_SZ); +} // Cyg_Mempool_dlmalloc_Implementation::do_check_free_chunk() + +void +Cyg_Mempool_dlmalloc_Implementation::do_check_inuse_chunk(mchunkptr p) +{ + mchunkptr next = next_chunk(p); + do_check_chunk(p); + + /* Check whether it claims to be in use ... */ + ASSERT(inuse(p)); + + /* ... and is surrounded by OK chunks. + Since more things can be checked with free chunks than inuse ones, + if an inuse chunk borders them and debug is on, it's worth doing them. + */ + if (!prev_inuse(p)) + { + mchunkptr prv = prev_chunk(p); + ASSERT(next_chunk(prv) == p); + do_check_free_chunk(prv); + } + if (next == top) + { + ASSERT(prev_inuse(next)); + ASSERT(chunksize(next) >= MINSIZE); + } + else if (!inuse(next)) + do_check_free_chunk(next); + +} // Cyg_Mempool_dlmalloc_Implementation::do_check_inuse_chunk( + +void +Cyg_Mempool_dlmalloc_Implementation::do_check_malloced_chunk(mchunkptr p, + INTERNAL_SIZE_T s) +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + long room = long_sub_size_t(sz, s); + + do_check_inuse_chunk(p); + + /* Legal size ... */ + ASSERT((long)sz >= (long)MINSIZE); + ASSERT((sz & MALLOC_ALIGN_MASK) == 0); + ASSERT(room >= 0); + ASSERT(room < (long)MINSIZE); + + /* ... and alignment */ + ASSERT(aligned_OK(chunk2mem(p))); + + + /* ... and was allocated at front of an available chunk */ + ASSERT(prev_inuse(p)); + +} // Cyg_Mempool_dlmalloc_Implementation::do_check_malloced_chunk( + + +#define check_free_chunk(P) do_check_free_chunk(P) +#define check_inuse_chunk(P) do_check_inuse_chunk(P) +#define check_chunk(P) do_check_chunk(P) +#define check_malloced_chunk(P,N) do_check_malloced_chunk(P,N) +#else +#define check_free_chunk(P) +#define check_inuse_chunk(P) +#define check_chunk(P) +#define check_malloced_chunk(P,N) +#endif + + +//---------------------------------------------------------------------------- + +/* + Macro-based internal utilities +*/ + + +/* + Linking chunks in bin lists. + Call these only with variables, not arbitrary expressions, as arguments. +*/ + +/* + Place chunk p of size s in its bin, in size order, + putting it ahead of others of same size. +*/ + + +#define frontlink(P, S, IDX, BK, FD) \ +{ \ + if (S < MAX_SMALLBIN_SIZE) \ + { \ + IDX = smallbin_index(S); \ + mark_binblock(IDX); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ + else \ + { \ + IDX = bin_index(S); \ + BK = bin_at(IDX); \ + FD = BK->fd; \ + if (FD == BK) mark_binblock(IDX); \ + else \ + { \ + while (FD != BK && S < chunksize(FD)) FD = FD->fd; \ + BK = FD->bk; \ + } \ + P->bk = BK; \ + P->fd = FD; \ + FD->bk = BK->fd = P; \ + } \ +} + + +/* take a chunk off a list */ + +#define unlink(P, BK, FD) \ +{ \ + BK = P->bk; \ + FD = P->fd; \ + FD->bk = BK; \ + BK->fd = FD; \ +} \ + +/* Place p as the last remainder */ + +#define link_last_remainder(P) \ +{ \ + last_remainder->fd = last_remainder->bk = P; \ + P->fd = P->bk = last_remainder; \ +} + +/* Clear the last_remainder bin */ + +#define clear_last_remainder \ + (last_remainder->fd = last_remainder->bk = last_remainder) + + +//---------------------------------------------------------------------------- + +Cyg_Mempool_dlmalloc_Implementation::Cyg_Mempool_dlmalloc_Implementation( + cyg_uint8 *base, cyg_int32 size, + CYG_ADDRWORD /* argthru */ ) +{ + arenabase = base; + arenasize = size; + + CYG_ADDRESS front_misalign; + cyg_int32 correction; + +#ifdef CYGIMP_MEMALLOC_ALLOCATOR_DLMALLOC_SAFE_MULTIPLE + cyg_ucount16 i; + av_[0] = av_[1] = 0; + for (i=0; i < CYGPRI_MEMALLOC_ALLOCATOR_DLMALLOC_NAV; i++) { + av_[ i*2+2 ] = av_[ i*2+3 ] = bin_at(i); + } // for + +#elif defined(CYGDBG_USE_ASSERTS) + static int instances; + if ( ++instances > 1 ) + CYG_FAIL( "Multiple dlmalloc instances but " + "CYGIMP_MEMALLOC_ALLOCATOR_DLMALLOC_SAFE_MULTIPLE " + "not defined" ); +#endif + + front_misalign = (CYG_ADDRESS)chunk2mem(base) & MALLOC_ALIGN_MASK; + + if ( front_misalign > 0 ) { + correction = (MALLOC_ALIGNMENT) - front_misalign; + } else { + correction = 0; + } + + top = (mchunkptr)(base + correction); + set_head(top, arenasize | PREV_INUSE); +} + +//---------------------------------------------------------------------------- + +/* Main public routines */ + +/* + Malloc Algorithm: + + The requested size is first converted into a usable form, `nb'. + This currently means to add 4 bytes overhead plus possibly more to + obtain 8-byte alignment and/or to obtain a size of at least + MINSIZE (currently 16 bytes), the smallest allocatable size. + (All fits are considered `exact' if they are within MINSIZE bytes.) + + From there, the first successful of the following steps is taken: + + 1. The bin corresponding to the request size is scanned, and if + a chunk of exactly the right size is found, it is taken. + + 2. The most recently remaindered chunk is used if it is big + enough. This is a form of (roving) first fit, used only in + the absence of exact fits. Runs of consecutive requests use + the remainder of the chunk used for the previous such request + whenever possible. This limited use of a first-fit style + allocation strategy tends to give contiguous chunks + coextensive lifetimes, which improves locality and can reduce + fragmentation in the long run. + + 3. Other bins are scanned in increasing size order, using a + chunk big enough to fulfill the request, and splitting off + any remainder. This search is strictly by best-fit; i.e., + the smallest (with ties going to approximately the least + recently used) chunk that fits is selected. + + 4. If large enough, the chunk bordering the end of memory + (`top') is split off. (This use of `top' is in accord with + the best-fit search rule. In effect, `top' is treated as + larger (and thus less well fitting) than any other available + chunk since it can be extended to be as large as necessary + (up to system limitations). + + All allocations are made from the the `lowest' part of any found + chunk. (The implementation invariant is that prev_inuse is + always true of any allocated chunk; i.e., that each allocated + chunk borders either a previously allocated and still in-use chunk, + or the base of its memory arena.) + +*/ + +cyg_uint8 * +Cyg_Mempool_dlmalloc_Implementation::try_alloc( cyg_int32 bytes ) +{ + mchunkptr victim; /* inspected/selected chunk */ + INTERNAL_SIZE_T victim_size; /* its size */ + int idx; /* index for bin traversal */ + mbinptr bin; /* associated bin */ + mchunkptr remainder; /* remainder from a split */ + long remainder_size; /* its size */ + int remainder_index; /* its bin index */ + unsigned long block; /* block traverser bit */ + int startidx; /* first bin of a traversed block */ + mchunkptr fwd; /* misc temp for linking */ + mchunkptr bck; /* misc temp for linking */ + mbinptr q; /* misc temp */ + + INTERNAL_SIZE_T nb; + + if ((long)bytes < 0) return 0; + + nb = request2size(bytes); /* padded request size; */ + + MALLOC_LOCK; + + /* Check for exact match in a bin */ + + if (is_small_request(nb)) /* Faster version for small requests */ + { + idx = smallbin_index(nb); + + /* No traversal or size check necessary for small bins. */ + + q = bin_at(idx); + victim = last(q); + +#if MALLOC_ALIGN != 16 + /* Also scan the next one, since it would have a remainder < MINSIZE */ + if (victim == q) + { + q = next_bin(q); + victim = last(q); + } +#endif + if (victim != q) + { + victim_size = chunksize(victim); + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + MALLOC_UNLOCK; + return chunk2mem(victim); + } + + idx += 2; /* Set for bin scan below. We've already scanned 2 bins. */ + + } + else + { + idx = bin_index(nb); + bin = bin_at(idx); + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = long_sub_size_t(victim_size, nb); + + if (remainder_size >= (long)MINSIZE) /* too big */ + { + --idx; /* adjust to rescan below after checking last remainder */ + break; + } + + else if (remainder_size >= 0) /* exact fit */ + { + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + MALLOC_UNLOCK; + return chunk2mem(victim); + } + } + + ++idx; + + } + + /* Try to use the last split-off remainder */ + + if ( (victim = last_remainder->fd) != last_remainder) + { + victim_size = chunksize(victim); + remainder_size = long_sub_size_t(victim_size, nb); + + if (remainder_size >= (long)MINSIZE) /* re-split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + MALLOC_UNLOCK; + return chunk2mem(victim); + } + + clear_last_remainder; + + if (remainder_size >= 0) /* exhaust */ + { + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + MALLOC_UNLOCK; + return chunk2mem(victim); + } + + /* Else place in bin */ + + frontlink(victim, victim_size, remainder_index, bck, fwd); + } + + /* + If there are any possibly nonempty big-enough blocks, + search for best fitting chunk by scanning bins in blockwidth units. + */ + + if ( (block = idx2binblock(idx)) <= binblocks) + { + + /* Get to the first marked block */ + + if ( (block & binblocks) == 0) + { + /* force to an even block boundary */ + idx = (idx & ~(BINBLOCKWIDTH - 1)) + BINBLOCKWIDTH; + block <<= 1; + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + + /* For each possibly nonempty block ... */ + for (;;) + { + startidx = idx; /* (track incomplete blocks) */ + q = bin = bin_at(idx); + + /* For each bin in this block ... */ + do + { + /* Find and use first big enough chunk ... */ + + for (victim = last(bin); victim != bin; victim = victim->bk) + { + victim_size = chunksize(victim); + remainder_size = long_sub_size_t(victim_size, nb); + + if (remainder_size >= (long)MINSIZE) /* split */ + { + remainder = chunk_at_offset(victim, nb); + set_head(victim, nb | PREV_INUSE); + unlink(victim, bck, fwd); + link_last_remainder(remainder); + set_head(remainder, remainder_size | PREV_INUSE); + set_foot(remainder, remainder_size); + check_malloced_chunk(victim, nb); + MALLOC_UNLOCK; + return chunk2mem(victim); + } + + else if (remainder_size >= 0) /* take */ + { + set_inuse_bit_at_offset(victim, victim_size); + unlink(victim, bck, fwd); + check_malloced_chunk(victim, nb); + MALLOC_UNLOCK; + return chunk2mem(victim); + } + + } + + bin = next_bin(bin); + +#if MALLOC_ALIGN == 16 + if (idx < MAX_SMALLBIN) + { + bin = next_bin(bin); + ++idx; + } +#endif + } while ((++idx & (BINBLOCKWIDTH - 1)) != 0); + + /* Clear out the block bit. */ + + do /* Possibly backtrack to try to clear a partial block */ + { + if ((startidx & (BINBLOCKWIDTH - 1)) == 0) + { + binblocks &= ~block; + break; + } + --startidx; + q = prev_bin(q); + } while (first(q) == q); + + /* Get to the next possibly nonempty block */ + + if ( (block <<= 1) <= binblocks && (block != 0) ) + { + while ((block & binblocks) == 0) + { + idx += BINBLOCKWIDTH; + block <<= 1; + } + } + else + break; + } + } + + + /* Try to use top chunk */ + + /* Require that there be a remainder, ensuring top always exists */ + remainder_size = long_sub_size_t(chunksize(top), nb); + if (chunksize(top) < nb || remainder_size < (long)MINSIZE) + { + MALLOC_UNLOCK; + return NULL; /* propagate failure */ + } + + victim = top; + set_head(victim, nb | PREV_INUSE); + top = chunk_at_offset(victim, nb); + set_head(top, remainder_size | PREV_INUSE); + check_malloced_chunk(victim, nb); + MALLOC_UNLOCK; + return chunk2mem(victim); + +} // Cyg_Mempool_dlmalloc_Implementation::try_alloc() + +//---------------------------------------------------------------------------- + +/* + free() algorithm : + + cases: + + 1. free(NULL) has no effect. + + 2. Chunks are consolidated as they arrive, and + placed in corresponding bins. (This includes the case of + consolidating with the current `last_remainder'). +*/ + +cyg_bool +Cyg_Mempool_dlmalloc_Implementation::free( cyg_uint8 *mem, cyg_int32 ) +{ + mchunkptr p; /* chunk corresponding to mem */ + INTERNAL_SIZE_T hd; /* its head field */ + INTERNAL_SIZE_T sz; /* its size */ + int idx; /* its bin index */ + mchunkptr next; /* next contiguous chunk */ + INTERNAL_SIZE_T nextsz; /* its size */ + INTERNAL_SIZE_T prevsz; /* size of previous contiguous chunk */ + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + int islr; /* track whether merging with last_remainder */ + + if (mem == NULL) /* free(NULL) has no effect */ + return false; + + MALLOC_LOCK; + + p = mem2chunk(mem); + hd = p->size; + + check_inuse_chunk(p); + + sz = hd & ~PREV_INUSE; + next = chunk_at_offset(p, sz); + nextsz = chunksize(next); + + if (next == top) /* merge with top */ + { + sz += nextsz; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + unlink(p, bck, fwd); + } + + set_head(p, sz | PREV_INUSE); + top = p; + MALLOC_UNLOCK; + return true; + } + + set_head(next, nextsz); /* clear inuse bit */ + + islr = 0; + + if (!(hd & PREV_INUSE)) /* consolidate backward */ + { + prevsz = p->prev_size; + p = chunk_at_offset(p, -((long) prevsz)); + sz += prevsz; + + if (p->fd == last_remainder) /* keep as last_remainder */ + islr = 1; + else + unlink(p, bck, fwd); + } + + if (!(inuse_bit_at_offset(next, nextsz))) /* consolidate forward */ + { + sz += nextsz; + + if (!islr && next->fd == last_remainder) /* re-insert last_remainder */ + { + islr = 1; + link_last_remainder(p); + } + else + unlink(next, bck, fwd); + } + + + set_head(p, sz | PREV_INUSE); + set_foot(p, sz); + if (!islr) + frontlink(p, sz, idx, bck, fwd); + + MALLOC_UNLOCK; + + return true; +} // Cyg_Mempool_dlmalloc_Implementation::free() + +//---------------------------------------------------------------------------- + +// resize existing allocation, if oldsize is non-NULL, previous +// allocation size is placed into it. If previous size not available, +// it is set to 0. NB previous allocation size may have been rounded up. +// Occasionally the allocation can be adjusted *backwards* as well as, +// or instead of forwards, therefore the address of the resized +// allocation is returned, or NULL if no resizing was possible. +// Note that this differs from ::realloc() in that no attempt is +// made to call malloc() if resizing is not possible - that is left +// to higher layers. The data is copied from old to new though. +// The effects of alloc_ptr==NULL or newsize==0 are undefined + + +// DOCUMENTATION FROM ORIGINAL FILE: +// (some now irrelevant parts elided) +/* + Realloc algorithm: + + If the reallocation is for additional space, and the + chunk can be extended, it is, else a malloc-copy-free sequence is + taken. There are several different ways that a chunk could be + extended. All are tried: + + * Extending forward into following adjacent free chunk. + * Shifting backwards, joining preceding adjacent space + * Both shifting backwards and extending forward. + + If the reallocation is for less space, and the new request is for + a `small' (<512 bytes) size, then the newly unused space is lopped + off and freed. + + The old unix realloc convention of allowing the last-free'd chunk + to be used as an argument to realloc is no longer supported. + I don't know of any programs still relying on this feature, + and allowing it would also allow too many other incorrect + usages of realloc to be sensible. +*/ + +cyg_uint8 * +Cyg_Mempool_dlmalloc_Implementation::resize_alloc( cyg_uint8 *oldmem, + cyg_int32 bytes, + cyg_int32 *poldsize ) +{ + + INTERNAL_SIZE_T nb; /* padded request size */ + + mchunkptr oldp; /* chunk corresponding to oldmem */ + INTERNAL_SIZE_T oldsize; /* its size */ + + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + cyg_uint8* newmem; /* corresponding user mem */ + + mchunkptr next; /* next contiguous chunk after oldp */ + INTERNAL_SIZE_T nextsize; /* its size */ + + mchunkptr prev; /* previous contiguous chunk before oldp */ + INTERNAL_SIZE_T prevsize; /* its size */ + + mchunkptr remainder; /* holds split off extra space from newp */ + INTERNAL_SIZE_T remainder_size; /* its size */ + + mchunkptr bck; /* misc temp for linking */ + mchunkptr fwd; /* misc temp for linking */ + + MALLOC_LOCK; + + newp = oldp = mem2chunk(oldmem); + newsize = oldsize = chunksize(oldp); + + if (NULL != poldsize) + *poldsize = oldsize - SIZE_SZ; + + nb = request2size(bytes); + + check_inuse_chunk(oldp); + + if ((long)(oldsize) < (long)(nb)) + { + + /* Try expanding forward */ + + next = chunk_at_offset(oldp, oldsize); + if (next == top || !inuse(next)) + { + nextsize = chunksize(next); + + /* Forward into top only if a remainder */ + if (next == top) + { + if ((long)(nextsize + newsize) >= (long)(nb + MINSIZE)) + { + newsize += nextsize; + top = chunk_at_offset(oldp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(oldp, nb); + MALLOC_UNLOCK; + return chunk2mem(oldp); + } + } + + /* Forward into next chunk */ + else if (((long)(nextsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + newsize += nextsize; + goto split; + } + } + else + { + next = 0; + nextsize = 0; + } + + /* Try shifting backwards. */ + + if (!prev_inuse(oldp)) + { + prev = prev_chunk(oldp); + prevsize = chunksize(prev); + + /* try forward + backward first to save a later consolidation */ + + if (next != 0) + { + /* into top */ + if (next == top) + { + if ((long)(nextsize + prevsize + newsize) >= (long)(nb + MINSIZE)) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize + nextsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + top = chunk_at_offset(newp, nb); + set_head(top, (newsize - nb) | PREV_INUSE); + set_head_size(newp, nb); + MALLOC_UNLOCK; + return newmem; + } + } + + /* into next chunk */ + else if (((long)(nextsize + prevsize + newsize) >= (long)(nb))) + { + unlink(next, bck, fwd); + unlink(prev, bck, fwd); + newp = prev; + newsize += nextsize + prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + /* backward only */ + if (prev != 0 && (long)(prevsize + newsize) >= (long)nb) + { + unlink(prev, bck, fwd); + newp = prev; + newsize += prevsize; + newmem = chunk2mem(newp); + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + goto split; + } + } + + // couldn't resize the allocation any direction, so return failure + MALLOC_UNLOCK; + return NULL; + } + + + split: /* split off extra room in old or expanded chunk */ + + remainder_size = long_sub_size_t(newsize, nb); + + if (remainder_size >= (long)MINSIZE) /* split off remainder */ + { + remainder = chunk_at_offset(newp, nb); + set_head_size(newp, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_inuse_bit_at_offset(remainder, remainder_size); + /* let free() deal with it */ + Cyg_Mempool_dlmalloc_Implementation::free( chunk2mem(remainder) ); + } + else + { + set_head_size(newp, newsize); + set_inuse_bit_at_offset(newp, newsize); + } + + check_inuse_chunk(newp); + MALLOC_UNLOCK; + return chunk2mem(newp); + +} // Cyg_Mempool_dlmalloc_Implementation::resize_alloc() + +//---------------------------------------------------------------------------- + +// Get memory pool status +// flags is a bitmask of requested fields to fill in. The flags are +// defined in common.hxx +void +Cyg_Mempool_dlmalloc_Implementation::get_status( + cyg_mempool_status_flag_t flags, + Cyg_Mempool_Status &status ) +{ + if (0 != (flags&(CYG_MEMPOOL_STAT_FREEBLOCKS|CYG_MEMPOOL_STAT_TOTALFREE| + CYG_MEMPOOL_STAT_TOTALALLOCATED|CYG_MEMPOOL_STAT_MAXFREE))) + { + int i; + mbinptr b; + mchunkptr p; + cyg_int32 chunksizep; + cyg_int32 maxfree; +#ifdef CYGDBG_MEMALLOC_ALLOCATOR_DLMALLOC_DEBUG + mchunkptr q; +#endif + + INTERNAL_SIZE_T avail = chunksize(top); + int navail = ((long)(avail) >= (long)MINSIZE)? 1 : 0; + maxfree = avail; + + for (i = 1; i < CYGPRI_MEMALLOC_ALLOCATOR_DLMALLOC_NAV; ++i) { + b = bin_at(i); + for (p = last(b); p != b; p = p->bk) { +#ifdef CYGDBG_MEMALLOC_ALLOCATOR_DLMALLOC_DEBUG + check_free_chunk(p); + for (q = next_chunk(p); + (q < top) && inuse(q) && + (long)(chunksize(q)) >= (long)MINSIZE; + q = next_chunk(q)) + check_inuse_chunk(q); +#endif + chunksizep = chunksize(p); + avail += chunksizep; + if ( chunksizep > maxfree ) + maxfree = chunksizep; + navail++; + } + } + + if ( 0 != (flags & CYG_MEMPOOL_STAT_TOTALALLOCATED) ) + status.totalallocated = arenasize - avail; + // as quick or quicker to just set most of these, rather than + // test flag first + status.totalfree = avail; + status.freeblocks = navail; + status.maxfree = maxfree; + } // if + + // as quick or quicker to just set most of these, rather than + // test flag first + status.arenabase = status.origbase = arenabase; + status.arenasize = status.origsize = arenasize; + status.maxoverhead = MINSIZE + MALLOC_ALIGNMENT; + +} // Cyg_Mempool_dlmalloc_Implementation::get_status() + + +//---------------------------------------------------------------------------- + +// EOF dlmalloc.cxx
