From 7c68d60b6f84bc7d04c3d7bad00327e22351ce9a Mon Sep 17 00:00:00 2001 From: drh Date: Wed, 11 Oct 2000 19:28:51 +0000 Subject: [PATCH] Misc cleanup. Notes on compiling for Win95. (CVS 152) FossilOrigin-Name: 3f0f1fa1fce794d1661c845f1a63a8d744892c25 --- Makefile.in | 1 + VERSION | 2 +- contrib/dlmalloc.c | 3276 ++++++++++++++++++++++++++++++++++++++++++ manifest | 34 +- manifest.uuid | 2 +- src/dbbemem.c | 766 ++++++++++ src/main.c | 3 +- src/printf.c | 2 + src/table.c | 4 +- src/tclsqlite.c | 3 +- src/util.c | 16 +- src/vdbe.c | 3 +- src/vdbe.h | 4 +- www/changes.tcl | 6 + www/crosscompile.tcl | 64 +- 15 files changed, 4153 insertions(+), 33 deletions(-) create mode 100644 contrib/dlmalloc.c create mode 100644 src/dbbemem.c diff --git a/Makefile.in b/Makefile.in index 08bf85c7ba..d0fe73c39e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -57,6 +57,7 @@ SRC = \ $(TOP)/src/build.c \ $(TOP)/src/dbbe.c \ $(TOP)/src/dbbe.h \ + $(TOP)/src/dbbemem.c \ $(TOP)/src/delete.c \ $(TOP)/src/expr.c \ $(TOP)/src/insert.c \ diff --git a/VERSION b/VERSION index 66c4c2263e..7ee7020b38 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.9 +1.0.10 diff --git a/contrib/dlmalloc.c b/contrib/dlmalloc.c new file mode 100644 index 0000000000..333f919013 --- /dev/null +++ b/contrib/dlmalloc.c @@ -0,0 +1,3276 @@ +/* ---------- To make a malloc.h, start cutting here ------------ */ + +/* + A version of malloc/free/realloc written by Doug Lea and released to the + public domain. Send questions/comments/complaints/performance data + to dl@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.) + + 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. Unless the + #define REALLOC_ZERO_BYTES_FREES below is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + memalign(size_t alignment, size_t n); + Return a pointer to a newly allocated chunk of n bytes, aligned + in accord with the alignment argument, which must be a power of + two. + valloc(size_t n); + Equivalent to memalign(pagesize, n), where pagesize is the page + size of the system (or as near to this as can be figured out from + all the includes/defines below.) + pvalloc(size_t n); + Equivalent to valloc(minimum-page-that-holds(n)), that is, + round up n to nearest pagesize. + calloc(size_t unit, size_t quantity); + Returns a pointer to quantity * unit bytes, with all locations + set to zero. + cfree(Void_t* p); + Equivalent to free(p). + malloc_trim(size_t pad); + Release all but pad bytes of freed top-most memory back + to the system. Return 1 if successful, else 0. + malloc_usable_size(Void_t* p); + Report the number usable allocated bytes associated with allocated + chunk p. This may or may not report more bytes than were requested, + due to alignment and minimum size constraints. + malloc_stats(); + Prints brief summary statistics on stderr. + mallinfo() + Returns (by copy) a struct containing various summary statistics. + mallopt(int parameter_number, int parameter_value) + Changes one of the tunable parameters described below. Returns + 1 if successful in changing the parameter, else 0. + +* 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 + two exceptions: + 1. Because requests for zero bytes allocate non-zero space, + the worst case wastage for a request of zero bytes is 24 bytes. + 2. For requests >= mmap_threshold that are serviced via + mmap(), the worst case wastage is 8 bytes plus the remainder + from a system page (the minimal mmap unit); typically 4096 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. + + __STD_C (default: derived from C compiler defines) + Nonzero if using ANSI-standard C compiler, a C++ compiler, or + a C compiler sufficiently close to ANSI to get away with it. + 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. + REALLOC_ZERO_BYTES_FREES (default: NOT defined) + Define this if you think that realloc(p, 0) should be equivalent + to free(p). Otherwise, since malloc returns a unique pointer for + malloc(0), so does realloc(p, 0). + HAVE_MEMCPY (default: defined) + Define if you are not otherwise using ANSI STD C, but still + have memcpy and memset in your C library and want to use them. + Otherwise, simple internal versions are supplied. + USE_MEMCPY (default: 1 if HAVE_MEMCPY is defined, 0 otherwise) + Define as 1 if you want the C library versions of memset and + memcpy called in realloc and calloc (otherwise macro versions are used). + At least on some platforms, the simple macro versions usually + outperform libc versions. + HAVE_MMAP (default: defined as 1) + Define to non-zero to optionally make malloc() use mmap() to + allocate very large blocks. + HAVE_MREMAP (default: defined as 0 unless Linux libc set) + Define to non-zero to optionally make realloc() use mremap() to + reallocate very large blocks. + malloc_getpagesize (default: derived from system #includes) + Either a constant or routine call returning the system page size. + HAVE_USR_INCLUDE_MALLOC_H (default: NOT defined) + Optionally define if you are on a system with a /usr/include/malloc.h + that declares struct mallinfo. It is not at all necessary to + define this even if you do, but will ensure consistency. + 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. + INTERNAL_LINUX_C_LIB (default: NOT defined) + Defined only when compiled as part of Linux libc. + Also note that there is some odd internal name-mangling via defines + (for example, internally, `malloc' is named `mALLOc') needed + when compiling in this case. These look funny but don't otherwise + affect anything. + WIN32 (default: undefined) + Define this on MS win (95, nt) platforms to compile in sbrk emulation. + LACKS_UNISTD_H (default: undefined if not WIN32) + Define this if your system does not have a . + LACKS_SYS_PARAM_H (default: undefined if not WIN32) + Define this if your system does not have a . + MORECORE (default: sbrk) + The name of the routine to call to obtain more memory from the system. + MORECORE_FAILURE (default: -1) + The value returned upon failure of MORECORE. + MORECORE_CLEARS (default 1) + True (1) if the routine mapped to MORECORE zeroes out memory (which + holds for sbrk). + DEFAULT_TRIM_THRESHOLD + DEFAULT_TOP_PAD + DEFAULT_MMAP_THRESHOLD + DEFAULT_MMAP_MAX + Default values of tunable parameters (described in detail below) + controlling interaction with host system routines (sbrk, mmap, etc). + These values may also be changed dynamically via mallopt(). The + preset defaults are those that give best performance for typical + programs/systems. + USE_DL_PREFIX (default: undefined) + Prefix all public routines with the string 'dl'. Useful to + quickly avoid procedure declaration conflicts and linker symbol + conflicts with existing memory allocation routines. + + +*/ + + + + +/* Preliminaries */ + +#ifndef __STD_C +#ifdef __STDC__ +#define __STD_C 1 +#else +#if __cplusplus +#define __STD_C 1 +#else +#define __STD_C 0 +#endif /*__cplusplus*/ +#endif /*__STDC__*/ +#endif /*__STD_C*/ + +#ifndef Void_t +#if (__STD_C || defined(WIN32)) +#define Void_t void +#else +#define Void_t char +#endif +#endif /*Void_t*/ + +#if __STD_C +#include /* for size_t */ +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include /* needed for malloc_stats */ + + +/* + Compile-time options +*/ + + +/* + 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 -DDEBUG, 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 malloc_stats or mallinfo with DEBUG set will + attempt to check every non-mmapped allocated and free chunk in the + course of computing the summmaries. (By nature, mmapped regions + cannot be checked very much automatically.) + + Setting 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. + +*/ + +#if DEBUG +#include +#else +#define assert(x) ((void)0) +#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 size_t +#endif + +/* + REALLOC_ZERO_BYTES_FREES should be set if a call to + realloc with zero bytes should be the same as a call to free. + Some people think it should. Otherwise, since this malloc + returns a unique pointer for malloc(0), so does realloc(p, 0). +*/ + + +/* #define REALLOC_ZERO_BYTES_FREES */ + + +/* + WIN32 causes an emulation of sbrk to be compiled in + mmap-based options are not currently supported in WIN32. +*/ + +/* #define WIN32 */ +#ifdef WIN32 +#define MORECORE wsbrk +#define HAVE_MMAP 0 + +#define LACKS_UNISTD_H +#define LACKS_SYS_PARAM_H + +/* + Include 'windows.h' to get the necessary declarations for the + Microsoft Visual C++ data structures and routines used in the 'sbrk' + emulation. + + Define WIN32_LEAN_AND_MEAN so that only the essential Microsoft + Visual C++ header files are included. +*/ +#define WIN32_LEAN_AND_MEAN +#include +#endif + + +/* + HAVE_MEMCPY should be defined if you are not otherwise using + ANSI STD C, but still have memcpy and memset in your C library + and want to use them in calloc and realloc. Otherwise simple + macro versions are defined here. + + USE_MEMCPY should be defined as 1 if you actually want to + have memset and memcpy called. People report that the macro + versions are often enough faster than libc versions on many + systems that it is better to use them. + +*/ + +#define HAVE_MEMCPY + +#ifndef USE_MEMCPY +#ifdef HAVE_MEMCPY +#define USE_MEMCPY 1 +#else +#define USE_MEMCPY 0 +#endif +#endif + +#if (__STD_C || defined(HAVE_MEMCPY)) + +#if __STD_C +void* memset(void*, int, size_t); +void* memcpy(void*, const void*, size_t); +#else +#ifdef WIN32 +// On Win32 platforms, 'memset()' and 'memcpy()' are already declared in +// 'windows.h' +#else +Void_t* memset(); +Void_t* memcpy(); +#endif +#endif +#endif + +#if USE_MEMCPY + +/* 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 /* !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 + + +/* + Define HAVE_MMAP to optionally make malloc() use mmap() to + allocate very large blocks. These will be returned to the + operating system immediately after a free(). +*/ + +#ifndef HAVE_MMAP +#define HAVE_MMAP 1 +#endif + +/* + Define HAVE_MREMAP to make realloc() use mremap() to re-allocate + large blocks. This is currently only possible on Linux with + kernel versions newer than 1.3.77. +*/ + +#ifndef HAVE_MREMAP +#ifdef INTERNAL_LINUX_C_LIB +#define HAVE_MREMAP 1 +#else +#define HAVE_MREMAP 0 +#endif +#endif + +#if HAVE_MMAP + +#include +#include +#include + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + +#endif /* HAVE_MMAP */ + +/* + Access to system page size. To the extent possible, this malloc + manages memory from the system in page-size units. + + The following mechanics for getpagesize were adapted from + bsd/gnu getpagesize.h +*/ + +#ifndef LACKS_UNISTD_H +# include +#endif + +#ifndef malloc_getpagesize +# ifdef _SC_PAGESIZE /* some SVR4 systems omit an underscore */ +# ifndef _SC_PAGE_SIZE +# define _SC_PAGE_SIZE _SC_PAGESIZE +# endif +# endif +# ifdef _SC_PAGE_SIZE +# define malloc_getpagesize sysconf(_SC_PAGE_SIZE) +# else +# if defined(BSD) || defined(DGUX) || defined(HAVE_GETPAGESIZE) + extern size_t getpagesize(); +# define malloc_getpagesize getpagesize() +# else +# ifdef WIN32 +# define malloc_getpagesize (4096) /* TBD: Use 'GetSystemInfo' instead */ +# else +# ifndef LACKS_SYS_PARAM_H +# include +# endif +# ifdef EXEC_PAGESIZE +# define malloc_getpagesize EXEC_PAGESIZE +# else +# ifdef NBPG +# ifndef CLSIZE +# define malloc_getpagesize NBPG +# else +# define malloc_getpagesize (NBPG * CLSIZE) +# endif +# else +# ifdef NBPC +# define malloc_getpagesize NBPC +# else +# ifdef PAGESIZE +# define malloc_getpagesize PAGESIZE +# else +# define malloc_getpagesize (4096) /* just guess */ +# endif +# endif +# endif +# endif +# endif +# endif +# endif +#endif + + + +/* + + This version of malloc supports the standard SVID/XPG mallinfo + routine that returns a struct containing the same kind of + information you can get from malloc_stats. It should work on + any SVID/XPG compliant system that has a /usr/include/malloc.h + defining struct mallinfo. (If you'd like to install such a thing + yourself, cut out the preliminary declarations as described above + and below and save them in a malloc.h file. But there's no + compelling reason to bother to do this.) + + The main declaration needed is the mallinfo struct that is returned + (by-copy) by mallinfo(). The SVID/XPG malloinfo struct contains a + bunch of fields, most of which are not even meaningful in this + version of malloc. Some of these fields are are instead filled by + mallinfo() with other numbers that might possibly be of interest. + + HAVE_USR_INCLUDE_MALLOC_H should be set if you have a + /usr/include/malloc.h file that includes a declaration of struct + mallinfo. If so, it is included; else an SVID2/XPG2 compliant + version is declared below. These must be precisely the same for + mallinfo() to work. + +*/ + +/* #define HAVE_USR_INCLUDE_MALLOC_H */ + +#if HAVE_USR_INCLUDE_MALLOC_H +#include "/usr/include/malloc.h" +#else + +/* SVID2/XPG mallinfo structure */ + +struct mallinfo { + int arena; /* total space allocated from system */ + int ordblks; /* number of non-inuse chunks */ + int smblks; /* unused -- always zero */ + int hblks; /* number of mmapped regions */ + int hblkhd; /* total space in mmapped regions */ + int usmblks; /* unused -- always zero */ + int fsmblks; /* unused -- always zero */ + int uordblks; /* total allocated space */ + int fordblks; /* total non-inuse space */ + int keepcost; /* top-most, releasable (via malloc_trim) space */ +}; + +/* SVID2/XPG mallopt options */ + +#define M_MXFAST 1 /* UNUSED in this malloc */ +#define M_NLBLKS 2 /* UNUSED in this malloc */ +#define M_GRAIN 3 /* UNUSED in this malloc */ +#define M_KEEP 4 /* UNUSED in this malloc */ + +#endif + +/* mallopt options that actually do something */ + +#define M_TRIM_THRESHOLD -1 +#define M_TOP_PAD -2 +#define M_MMAP_THRESHOLD -3 +#define M_MMAP_MAX -4 + + + +#ifndef DEFAULT_TRIM_THRESHOLD +#define DEFAULT_TRIM_THRESHOLD (128 * 1024) +#endif + +/* + M_TRIM_THRESHOLD is the maximum amount of unused top-most memory + to keep before releasing via malloc_trim in free(). + + Automatic trimming is mainly useful in long-lived programs. + Because trimming via sbrk can be slow on some systems, and can + sometimes be wasteful (in cases where programs immediately + afterward allocate more large chunks) the value should be high + enough so that your overall system performance would improve by + releasing. + + The trim threshold and the mmap control parameters (see below) + can be traded off with one another. Trimming and mmapping are + two different ways of releasing unused memory back to the + system. Between these two, it is often possible to keep + system-level demands of a long-lived program down to a bare + minimum. For example, in one test suite of sessions measuring + the XF86 X server on Linux, using a trim threshold of 128K and a + mmap threshold of 192K led to near-minimal long term resource + consumption. + + If you are using this malloc in a long-lived program, it should + pay to experiment with these values. As a rough guide, you + might set to a value close to the average size of a process + (program) running on your system. Releasing this much memory + would allow such a process to run in memory. Generally, it's + worth it to tune for trimming rather tham memory mapping when a + program undergoes phases where several large chunks are + allocated and released in ways that can reuse each other's + storage, perhaps mixed with phases where there are no such + chunks at all. And in well-behaved long-lived programs, + controlling release of large blocks via trimming versus mapping + is usually faster. + + However, in most programs, these parameters serve mainly as + protection against the system-level effects of carrying around + massive amounts of unneeded memory. Since frequent calls to + sbrk, mmap, and munmap otherwise degrade performance, the default + parameters are set to relatively high values that serve only as + safeguards. + + The default trim value is high enough to cause trimming only in + fairly extreme (by current memory consumption standards) cases. + It must be greater than page size to have any useful effect. To + disable trimming completely, you can set to (unsigned long)(-1); + + +*/ + + +#ifndef DEFAULT_TOP_PAD +#define DEFAULT_TOP_PAD (0) +#endif + +/* + M_TOP_PAD is the amount of extra `padding' space to allocate or + retain whenever sbrk is called. It is used in two ways internally: + + * When sbrk is called to extend the top of the arena to satisfy + a new malloc request, this much padding is added to the sbrk + request. + + * When malloc_trim is called automatically from free(), + it is used as the `pad' argument. + + In both cases, the actual amount of padding is rounded + so that the end of the arena is always a system page boundary. + + The main reason for using padding is to avoid calling sbrk so + often. Having even a small pad greatly reduces the likelihood + that nearly every malloc request during program start-up (or + after trimming) will invoke sbrk, which needlessly wastes + time. + + Automatic rounding-up to page-size units is normally sufficient + to avoid measurable overhead, so the default is 0. However, in + systems where sbrk is relatively slow, it can pay to increase + this value, at the expense of carrying around more memory than + the program needs. + +*/ + + +#ifndef DEFAULT_MMAP_THRESHOLD +#define DEFAULT_MMAP_THRESHOLD (128 * 1024) +#endif + +/* + + M_MMAP_THRESHOLD is the request size threshold for using mmap() + to service a request. Requests of at least this size that cannot + be allocated using already-existing space will be serviced via mmap. + (If enough normal freed space already exists it is used instead.) + + Using mmap segregates relatively large chunks of memory so that + they can be individually obtained and released from the host + system. A request serviced through mmap is never reused by any + other request (at least not directly; the system may just so + happen to remap successive requests to the same locations). + + Segregating space in this way has the benefit that mmapped space + can ALWAYS be individually released back to the system, which + helps keep the system level memory demands of a long-lived + program low. Mapped memory can never become `locked' between + other chunks, as can happen with normally allocated chunks, which + menas that even trimming via malloc_trim would not release them. + + However, it has the disadvantages that: + + 1. The space cannot be reclaimed, consolidated, and then + used to service later requests, as happens with normal chunks. + 2. It can lead to more wastage because of mmap page alignment + requirements + 3. It causes malloc performance to be more dependent on host + system memory management support routines which may vary in + implementation quality and may impose arbitrary + limitations. Generally, servicing a request via normal + malloc steps is faster than going through a system's mmap. + + All together, these considerations should lead you to use mmap + only for relatively large requests. + + +*/ + + + +#ifndef DEFAULT_MMAP_MAX +#if HAVE_MMAP +#define DEFAULT_MMAP_MAX (64) +#else +#define DEFAULT_MMAP_MAX (0) +#endif +#endif + +/* + M_MMAP_MAX is the maximum number of requests to simultaneously + service using mmap. This parameter exists because: + + 1. Some systems have a limited number of internal tables for + use by mmap. + 2. In most systems, overreliance on mmap can degrade overall + performance. + 3. If a program allocates many large regions, it is probably + better off using normal sbrk-based allocation routines that + can reclaim and reallocate normal heap memory. Using a + small value allows transition into this mode after the + first few allocations. + + Setting to 0 disables all use of mmap. If HAVE_MMAP is not set, + the default value is 0, and attempts to set it to non-zero values + in mallopt will fail. +*/ + + + + +/* + USE_DL_PREFIX will prefix all public routines with the string 'dl'. + Useful to quickly avoid procedure declaration conflicts and linker + symbol conflicts with existing memory allocation routines. + +*/ + +/* #define USE_DL_PREFIX */ + + + + +/* + + Special defines for linux libc + + Except when compiled using these special defines for Linux libc + using weak aliases, this malloc is NOT designed to work in + multithreaded 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. A single + semaphore could be used across malloc, realloc, and free (which is + essentially the effect of the linux weak alias approach). It would + be hard to obtain finer granularity. + +*/ + + +#ifdef INTERNAL_LINUX_C_LIB + +#if __STD_C + +Void_t * __default_morecore_init (ptrdiff_t); +Void_t *(*__morecore)(ptrdiff_t) = __default_morecore_init; + +#else + +Void_t * __default_morecore_init (); +Void_t *(*__morecore)() = __default_morecore_init; + +#endif + +#define MORECORE (*__morecore) +#define MORECORE_FAILURE 0 +#define MORECORE_CLEARS 1 + +#else /* INTERNAL_LINUX_C_LIB */ + +#if __STD_C +extern Void_t* sbrk(ptrdiff_t); +#else +extern Void_t* sbrk(); +#endif + +#ifndef MORECORE +#define MORECORE sbrk +#endif + +#ifndef MORECORE_FAILURE +#define MORECORE_FAILURE -1 +#endif + +#ifndef MORECORE_CLEARS +#define MORECORE_CLEARS 1 +#endif + +#endif /* INTERNAL_LINUX_C_LIB */ + +#if defined(INTERNAL_LINUX_C_LIB) && defined(__ELF__) + +#define cALLOc __libc_calloc +#define fREe __libc_free +#define mALLOc __libc_malloc +#define mEMALIGn __libc_memalign +#define rEALLOc __libc_realloc +#define vALLOc __libc_valloc +#define pvALLOc __libc_pvalloc +#define mALLINFo __libc_mallinfo +#define mALLOPt __libc_mallopt + +#pragma weak calloc = __libc_calloc +#pragma weak free = __libc_free +#pragma weak cfree = __libc_free +#pragma weak malloc = __libc_malloc +#pragma weak memalign = __libc_memalign +#pragma weak realloc = __libc_realloc +#pragma weak valloc = __libc_valloc +#pragma weak pvalloc = __libc_pvalloc +#pragma weak mallinfo = __libc_mallinfo +#pragma weak mallopt = __libc_mallopt + +#else + +#ifdef USE_DL_PREFIX +#define cALLOc dlcalloc +#define fREe dlfree +#define mALLOc dlmalloc +#define mEMALIGn dlmemalign +#define rEALLOc dlrealloc +#define vALLOc dlvalloc +#define pvALLOc dlpvalloc +#define mALLINFo dlmallinfo +#define mALLOPt dlmallopt +#else /* USE_DL_PREFIX */ +#define cALLOc calloc +#define fREe free +#define mALLOc malloc +#define mEMALIGn memalign +#define rEALLOc realloc +#define vALLOc valloc +#define pvALLOc pvalloc +#define mALLINFo mallinfo +#define mALLOPt mallopt +#endif /* USE_DL_PREFIX */ + +#endif + +/* Public routines */ + +#if __STD_C + +Void_t* mALLOc(size_t); +void fREe(Void_t*); +Void_t* rEALLOc(Void_t*, size_t); +Void_t* mEMALIGn(size_t, size_t); +Void_t* vALLOc(size_t); +Void_t* pvALLOc(size_t); +Void_t* cALLOc(size_t, size_t); +void cfree(Void_t*); +int malloc_trim(size_t); +size_t malloc_usable_size(Void_t*); +void malloc_stats(); +int mALLOPt(int, int); +struct mallinfo mALLINFo(void); +#else +Void_t* mALLOc(); +void fREe(); +Void_t* rEALLOc(); +Void_t* mEMALIGn(); +Void_t* vALLOc(); +Void_t* pvALLOc(); +Void_t* cALLOc(); +void cfree(); +int malloc_trim(); +size_t malloc_usable_size(); +void malloc_stats(); +int mALLOPt(); +struct mallinfo mALLINFo(); +#endif + + +#ifdef __cplusplus +}; /* end of extern "C" */ +#endif + +/* ---------- To make a malloc.h, end cutting here ------------ */ + + +/* + Emulation of sbrk for WIN32 + All code within the ifdef WIN32 is untested by me. + + Thanks to Martin Fong and others for supplying this. +*/ + + +#ifdef WIN32 + +#define AlignPage(add) (((add) + (malloc_getpagesize-1)) & \ +~(malloc_getpagesize-1)) +#define AlignPage64K(add) (((add) + (0x10000 - 1)) & ~(0x10000 - 1)) + +/* resrve 64MB to insure large contiguous space */ +#define RESERVED_SIZE (1024*1024*64) +#define NEXT_SIZE (2048*1024) +#define TOP_MEMORY ((unsigned long)2*1024*1024*1024) + +struct GmListElement; +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)) +#define MALLOC_ALIGNMENT (SIZE_SZ + SIZE_SZ) +#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 : \ + (((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))) + + + + +/* + 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) + +/* check for mmap()'ed chunk */ + +#define chunk_is_mmapped(p) ((p)->size & IS_MMAPPED) + +/* 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. + +*/ + +#define NAV 128 /* number of bins */ + +typedef struct 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 */ + + +/* + Because top initially points to its own bin with initial + zero size, thus forcing extension on the first malloc request, + we avoid having any special code in malloc to check whether + it even exists yet. But we still need to in malloc_extend_top. +*/ + +#define initial_top ((mchunkptr)(bin_at(0))) + +/* Helper macro to initialize bins */ + +#define IAV(i) bin_at(i), bin_at(i) + +static mbinptr av_[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) +}; + + + +/* 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 8 bytes apart, and hold + identically sized chunks. This is exploited in malloc. +*/ + +#define MAX_SMALLBIN 63 +#define MAX_SMALLBIN_SIZE 512 +#define SMALLBIN_WIDTH 8 + +#define smallbin_index(sz) (((unsigned long)(sz)) >> 3) + +/* + 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)1 << (ix / BINBLOCKWIDTH)) +#define mark_binblock(ii) (binblocks |= idx2binblock(ii)) +#define clear_binblock(ii) (binblocks &= ~(idx2binblock(ii))) + + + + + +/* Other static bookkeeping data */ + +/* variables holding tunable values */ + +static unsigned long trim_threshold = DEFAULT_TRIM_THRESHOLD; +static unsigned long top_pad = DEFAULT_TOP_PAD; +static unsigned int n_mmaps_max = DEFAULT_MMAP_MAX; +static unsigned long mmap_threshold = DEFAULT_MMAP_THRESHOLD; + +/* The first value returned from sbrk */ +static char* sbrk_base = (char*)(-1); + +/* The maximum memory obtained from system via sbrk */ +static unsigned long max_sbrked_mem = 0; + +/* The maximum via either sbrk or mmap */ +static unsigned long max_total_mem = 0; + +/* internal working copy of mallinfo */ +static struct mallinfo current_mallinfo = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +/* The total memory obtained from system via sbrk */ +#define sbrked_mem (current_mallinfo.arena) + +/* Tracking mmaps */ + +static unsigned int n_mmaps = 0; +static unsigned int max_n_mmaps = 0; +static unsigned long mmapped_mem = 0; +static unsigned long max_mmapped_mem = 0; + + + +/* + Debugging support +*/ + +#if 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!) +*/ + +#if __STD_C +static void do_check_chunk(mchunkptr p) +#else +static void do_check_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + + /* No checkable chunk is mmapped */ + assert(!chunk_is_mmapped(p)); + + /* Check for legal address ... */ + assert((char*)p >= sbrk_base); + if (p != top) + assert((char*)p + sz <= (char*)top); + else + assert((char*)p + sz <= sbrk_base + sbrked_mem); + +} + + +#if __STD_C +static void do_check_free_chunk(mchunkptr p) +#else +static void do_check_free_chunk(p) mchunkptr p; +#endif +{ + 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); +} + +#if __STD_C +static void do_check_inuse_chunk(mchunkptr p) +#else +static void do_check_inuse_chunk(p) mchunkptr p; +#endif +{ + 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); + +} + +#if __STD_C +static void do_check_malloced_chunk(mchunkptr p, INTERNAL_SIZE_T s) +#else +static void do_check_malloced_chunk(p, s) mchunkptr p; INTERNAL_SIZE_T s; +#endif +{ + INTERNAL_SIZE_T sz = p->size & ~PREV_INUSE; + long room = 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)); + +} + + +#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) + + + + + + +/* Routines dealing with mmap(). */ + +#if HAVE_MMAP + +#if __STD_C +static mchunkptr mmap_chunk(size_t size) +#else +static mchunkptr mmap_chunk(size) size_t size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + mchunkptr p; + +#ifndef MAP_ANONYMOUS + static int fd = -1; +#endif + + if(n_mmaps >= n_mmaps_max) return 0; /* too many regions */ + + /* For mmapped chunks, the overhead is one SIZE_SZ unit larger, because + * there is no following chunk whose prev_size field could be used. + */ + size = (size + SIZE_SZ + page_mask) & ~page_mask; + +#ifdef MAP_ANONYMOUS + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#else /* !MAP_ANONYMOUS */ + if (fd < 0) + { + fd = open("/dev/zero", O_RDWR); + if(fd < 0) return 0; + } + p = (mchunkptr)mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); +#endif + + if(p == (mchunkptr)-1) return 0; + + n_mmaps++; + if (n_mmaps > max_n_mmaps) max_n_mmaps = n_mmaps; + + /* We demand that eight bytes into a page must be 8-byte aligned. */ + assert(aligned_OK(chunk2mem(p))); + + /* The offset to the start of the mmapped region is stored + * in the prev_size field of the chunk; normally it is zero, + * but that can be changed in memalign(). + */ + p->prev_size = 0; + set_head(p, size|IS_MMAPPED); + + mmapped_mem += size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#if __STD_C +static void munmap_chunk(mchunkptr p) +#else +static void munmap_chunk(p) mchunkptr p; +#endif +{ + INTERNAL_SIZE_T size = chunksize(p); + int ret; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((p->prev_size + size) & (malloc_getpagesize-1)) == 0); + + n_mmaps--; + mmapped_mem -= (size + p->prev_size); + + ret = munmap((char *)p - p->prev_size, size + p->prev_size); + + /* munmap returns non-zero on failure */ + assert(ret == 0); +} + +#if HAVE_MREMAP + +#if __STD_C +static mchunkptr mremap_chunk(mchunkptr p, size_t new_size) +#else +static mchunkptr mremap_chunk(p, new_size) mchunkptr p; size_t new_size; +#endif +{ + size_t page_mask = malloc_getpagesize - 1; + INTERNAL_SIZE_T offset = p->prev_size; + INTERNAL_SIZE_T size = chunksize(p); + char *cp; + + assert (chunk_is_mmapped(p)); + assert(! ((char*)p >= sbrk_base && (char*)p < sbrk_base + sbrked_mem)); + assert((n_mmaps > 0)); + assert(((size + offset) & (malloc_getpagesize-1)) == 0); + + /* Note the extra SIZE_SZ overhead as in mmap_chunk(). */ + new_size = (new_size + offset + SIZE_SZ + page_mask) & ~page_mask; + + cp = (char *)mremap((char *)p - offset, size + offset, new_size, 1); + + if (cp == (char *)-1) return 0; + + p = (mchunkptr)(cp + offset); + + assert(aligned_OK(chunk2mem(p))); + + assert((p->prev_size == offset)); + set_head(p, (new_size - offset)|IS_MMAPPED); + + mmapped_mem -= size + offset; + mmapped_mem += new_size; + if ((unsigned long)mmapped_mem > (unsigned long)max_mmapped_mem) + max_mmapped_mem = mmapped_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + return p; +} + +#endif /* HAVE_MREMAP */ + +#endif /* HAVE_MMAP */ + + + + +/* + Extend the top-most chunk by obtaining memory from system. + Main interface to sbrk (but see also malloc_trim). +*/ + +#if __STD_C +static void malloc_extend_top(INTERNAL_SIZE_T nb) +#else +static void malloc_extend_top(nb) INTERNAL_SIZE_T nb; +#endif +{ + char* brk; /* return value from sbrk */ + INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of sbrked space */ + INTERNAL_SIZE_T correction; /* bytes for 2nd sbrk call */ + char* new_brk; /* return of 2nd sbrk call */ + INTERNAL_SIZE_T top_size; /* new size of top chunk */ + + mchunkptr old_top = top; /* Record state of old top */ + INTERNAL_SIZE_T old_top_size = chunksize(old_top); + char* old_end = (char*)(chunk_at_offset(old_top, old_top_size)); + + /* Pad request with top_pad plus minimal overhead */ + + INTERNAL_SIZE_T sbrk_size = nb + top_pad + MINSIZE; + unsigned long pagesz = malloc_getpagesize; + + /* If not the first time through, round to preserve page boundary */ + /* Otherwise, we need to correct to a page size below anyway. */ + /* (We also correct below if an intervening foreign sbrk call.) */ + + if (sbrk_base != (char*)(-1)) + sbrk_size = (sbrk_size + (pagesz - 1)) & ~(pagesz - 1); + + brk = (char*)(MORECORE (sbrk_size)); + + /* Fail if sbrk failed or if a foreign sbrk call killed our space */ + if (brk == (char*)(MORECORE_FAILURE) || + (brk < old_end && old_top != initial_top)) + return; + + sbrked_mem += sbrk_size; + + if (brk == old_end) /* can just add bytes to current top */ + { + top_size = sbrk_size + old_top_size; + set_head(top, top_size | PREV_INUSE); + } + else + { + if (sbrk_base == (char*)(-1)) /* First time through. Record base */ + sbrk_base = brk; + else /* Someone else called sbrk(). Count those bytes as sbrked_mem. */ + sbrked_mem += brk - (char*)old_end; + + /* Guarantee alignment of first new chunk made from this space */ + front_misalign = (unsigned long)chunk2mem(brk) & MALLOC_ALIGN_MASK; + if (front_misalign > 0) + { + correction = (MALLOC_ALIGNMENT) - front_misalign; + brk += correction; + } + else + correction = 0; + + /* Guarantee the next brk will be at a page boundary */ + + correction += ((((unsigned long)(brk + sbrk_size))+(pagesz-1)) & + ~(pagesz - 1)) - ((unsigned long)(brk + sbrk_size)); + + /* Allocate correction */ + new_brk = (char*)(MORECORE (correction)); + if (new_brk == (char*)(MORECORE_FAILURE)) return; + + sbrked_mem += correction; + + top = (mchunkptr)brk; + top_size = new_brk - brk + correction; + set_head(top, top_size | PREV_INUSE); + + if (old_top != initial_top) + { + + /* There must have been an intervening foreign sbrk call. */ + /* A double fencepost is necessary to prevent consolidation */ + + /* If not enough space to do this, then user did something very wrong */ + if (old_top_size < MINSIZE) + { + set_head(top, PREV_INUSE); /* will force null return from malloc */ + return; + } + + /* Also keep size a multiple of MALLOC_ALIGNMENT */ + old_top_size = (old_top_size - 3*SIZE_SZ) & ~MALLOC_ALIGN_MASK; + set_head_size(old_top, old_top_size); + chunk_at_offset(old_top, old_top_size )->size = + SIZE_SZ|PREV_INUSE; + chunk_at_offset(old_top, old_top_size + SIZE_SZ)->size = + SIZE_SZ|PREV_INUSE; + /* If possible, release the rest. */ + if (old_top_size >= MINSIZE) + fREe(chunk2mem(old_top)); + } + } + + if ((unsigned long)sbrked_mem > (unsigned long)max_sbrked_mem) + max_sbrked_mem = sbrked_mem; + if ((unsigned long)(mmapped_mem + sbrked_mem) > (unsigned long)max_total_mem) + max_total_mem = mmapped_mem + sbrked_mem; + + /* We always land on a page boundary */ + assert(((unsigned long)((char*)top + top_size) & (pagesz - 1)) == 0); +} + + + + +/* Main public routines */ + + +/* + Malloc Algorthim: + + 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). + + 5. If the request size meets the mmap threshold and the + system supports mmap, and there are few enough currently + allocated mmapped regions, and a call to mmap succeeds, + the request is allocated via direct memory mapping. + + 6. Otherwise, the top of memory is extended by + obtaining more space from the system (normally using sbrk, + but definable to anything else via the MORECORE macro). + Memory is gathered from the system (in system page-sized + units) 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. + + + 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.) + +*/ + +#if __STD_C +Void_t* mALLOc(size_t bytes) +#else +Void_t* mALLOc(bytes) size_t bytes; +#endif +{ + 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; */ + + /* 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); + + /* Also scan the next one, since it would have a remainder < MINSIZE */ + if (victim == q) + { + q = next_bin(q); + victim = last(q); + } + if (victim != q) + { + victim_size = chunksize(victim); + unlink(victim, bck, fwd); + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + 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 = 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); + 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 = 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); + return chunk2mem(victim); + } + + clear_last_remainder; + + if (remainder_size >= 0) /* exhaust */ + { + set_inuse_bit_at_offset(victim, victim_size); + check_malloced_chunk(victim, nb); + 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 = 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); + 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); + return chunk2mem(victim); + } + + } + + bin = next_bin(bin); + + } 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 */ + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + { + +#if HAVE_MMAP + /* If big and would otherwise need to extend, try to use mmap instead */ + if ((unsigned long)nb >= (unsigned long)mmap_threshold && + (victim = mmap_chunk(nb)) != 0) + return chunk2mem(victim); +#endif + + /* Try to extend */ + malloc_extend_top(nb); + if ( (remainder_size = chunksize(top) - nb) < (long)MINSIZE) + return 0; /* 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); + return chunk2mem(victim); + +} + + + + +/* + + free() algorithm : + + cases: + + 1. free(0) has no effect. + + 2. If the chunk was allocated via mmap, it is release via munmap(). + + 3. If a returned chunk borders the current high end of memory, + it is consolidated into the top, and if the total unused + topmost memory exceeds the trim threshold, malloc_trim is + called. + + 4. Other chunks are consolidated as they arrive, and + placed in corresponding bins. (This includes the case of + consolidating with the current `last_remainder'). + +*/ + + +#if __STD_C +void fREe(Void_t* mem) +#else +void fREe(mem) Void_t* mem; +#endif +{ + 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 == 0) /* free(0) has no effect */ + return; + + p = mem2chunk(mem); + hd = p->size; + +#if HAVE_MMAP + if (hd & IS_MMAPPED) /* release mmapped memory. */ + { + munmap_chunk(p); + return; + } +#endif + + 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; + if ((unsigned long)(sz) >= (unsigned long)trim_threshold) + malloc_trim(top_pad); + return; + } + + 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); +} + + + + + +/* + + Realloc algorithm: + + Chunks that were obtained via mmap cannot be extended or shrunk + unless HAVE_MREMAP is defined, in which case mremap is used. + Otherwise, if their reallocation is for additional space, they are + copied. If for less, they are just left alone. + + Otherwise, 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. + * Extending into newly sbrked space + + Unless the #define REALLOC_ZERO_BYTES_FREES is set, realloc with a + size argument of zero (re)allocates a minimum-sized chunk. + + 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. + + +*/ + + +#if __STD_C +Void_t* rEALLOc(Void_t* oldmem, size_t bytes) +#else +Void_t* rEALLOc(oldmem, bytes) Void_t* oldmem; size_t bytes; +#endif +{ + 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 */ + Void_t* 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 */ + +#ifdef REALLOC_ZERO_BYTES_FREES + if (bytes == 0) { fREe(oldmem); return 0; } +#endif + + if ((long)bytes < 0) return 0; + + /* realloc of null is supposed to be same as malloc */ + if (oldmem == 0) return mALLOc(bytes); + + newp = oldp = mem2chunk(oldmem); + newsize = oldsize = chunksize(oldp); + + + nb = request2size(bytes); + +#if HAVE_MMAP + if (chunk_is_mmapped(oldp)) + { +#if HAVE_MREMAP + newp = mremap_chunk(oldp, nb); + if(newp) return chunk2mem(newp); +#endif + /* Note the extra SIZE_SZ overhead. */ + if(oldsize - SIZE_SZ >= nb) return oldmem; /* do nothing */ + /* Must alloc, copy, free. */ + newmem = mALLOc(bytes); + if (newmem == 0) return 0; /* propagate failure */ + MALLOC_COPY(newmem, oldmem, oldsize - 2*SIZE_SZ); + munmap_chunk(oldp); + return newmem; + } +#endif + + 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); + 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); + 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; + } + } + + /* Must allocate */ + + newmem = mALLOc (bytes); + + if (newmem == 0) /* propagate failure */ + return 0; + + /* Avoid copy if newp is next chunk after oldp. */ + /* (This can only happen when new chunk is sbrk'ed.) */ + + if ( (newp = mem2chunk(newmem)) == next_chunk(oldp)) + { + newsize += chunksize(newp); + newp = oldp; + goto split; + } + + /* Otherwise copy, free, and exit */ + MALLOC_COPY(newmem, oldmem, oldsize - SIZE_SZ); + fREe(oldmem); + return newmem; + } + + + split: /* split off extra room in old or expanded chunk */ + + if (newsize - nb >= MINSIZE) /* split off remainder */ + { + remainder = chunk_at_offset(newp, nb); + remainder_size = newsize - nb; + set_head_size(newp, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_inuse_bit_at_offset(remainder, remainder_size); + fREe(chunk2mem(remainder)); /* let free() deal with it */ + } + else + { + set_head_size(newp, newsize); + set_inuse_bit_at_offset(newp, newsize); + } + + check_inuse_chunk(newp); + return chunk2mem(newp); +} + + + + +/* + + memalign algorithm: + + 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. + + The alignment argument must be a power of two. This property is not + checked by memalign, so misuse may result in random runtime errors. + + 8-byte alignment is guaranteed by normal malloc calls, so don't + bother calling memalign with an argument of 8 or less. + + Overreliance on memalign is a sure way to fragment space. + +*/ + + +#if __STD_C +Void_t* mEMALIGn(size_t alignment, size_t bytes) +#else +Void_t* mEMALIGn(alignment, bytes) size_t alignment; size_t bytes; +#endif +{ + INTERNAL_SIZE_T nb; /* padded request size */ + char* m; /* memory returned by malloc call */ + mchunkptr p; /* corresponding chunk */ + char* brk; /* alignment point within p */ + mchunkptr newp; /* chunk to return */ + INTERNAL_SIZE_T newsize; /* its size */ + INTERNAL_SIZE_T leadsize; /* leading space befor alignment point */ + mchunkptr remainder; /* spare room at end to split off */ + long remainder_size; /* its size */ + + if ((long)bytes < 0) return 0; + + /* If need less alignment than we give anyway, just relay to malloc */ + + if (alignment <= MALLOC_ALIGNMENT) return mALLOc(bytes); + + /* Otherwise, ensure that it is at least a minimum chunk size */ + + if (alignment < MINSIZE) alignment = MINSIZE; + + /* Call malloc with worst case padding to hit alignment. */ + + nb = request2size(bytes); + m = (char*)(mALLOc(nb + alignment + MINSIZE)); + + if (m == 0) return 0; /* propagate failure */ + + p = mem2chunk(m); + + if ((((unsigned long)(m)) % alignment) == 0) /* aligned */ + { +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + return chunk2mem(p); /* nothing more to do */ +#endif + } + else /* misaligned */ + { + /* + Find an aligned spot inside chunk. + Since we need to give back leading space in a chunk of at + least MINSIZE, if the first calculation places us at + a spot with less than MINSIZE leader, we can move to the + next aligned spot -- we've allocated enough total room so that + this is always possible. + */ + + brk = (char*)mem2chunk(((unsigned long)(m + alignment - 1)) & -((signed) alignment)); + if ((long)(brk - (char*)(p)) < MINSIZE) brk = brk + alignment; + + newp = (mchunkptr)brk; + leadsize = brk - (char*)(p); + newsize = chunksize(p) - leadsize; + +#if HAVE_MMAP + if(chunk_is_mmapped(p)) + { + newp->prev_size = p->prev_size + leadsize; + set_head(newp, newsize|IS_MMAPPED); + return chunk2mem(newp); + } +#endif + + /* give back leader, use the rest */ + + set_head(newp, newsize | PREV_INUSE); + set_inuse_bit_at_offset(newp, newsize); + set_head_size(p, leadsize); + fREe(chunk2mem(p)); + p = newp; + + assert (newsize >= nb && (((unsigned long)(chunk2mem(p))) % alignment) == 0); + } + + /* Also give back spare room at the end */ + + remainder_size = chunksize(p) - nb; + + if (remainder_size >= (long)MINSIZE) + { + remainder = chunk_at_offset(p, nb); + set_head(remainder, remainder_size | PREV_INUSE); + set_head_size(p, nb); + fREe(chunk2mem(remainder)); + } + + check_inuse_chunk(p); + return chunk2mem(p); + +} + + + + +/* + valloc just invokes memalign with alignment argument equal + to the page size of the system (or as near to this as can + be figured out from all the includes/defines above.) +*/ + +#if __STD_C +Void_t* vALLOc(size_t bytes) +#else +Void_t* vALLOc(bytes) size_t bytes; +#endif +{ + return mEMALIGn (malloc_getpagesize, bytes); +} + +/* + pvalloc just invokes valloc for the nearest pagesize + that will accommodate request +*/ + + +#if __STD_C +Void_t* pvALLOc(size_t bytes) +#else +Void_t* pvALLOc(bytes) size_t bytes; +#endif +{ + size_t pagesize = malloc_getpagesize; + return mEMALIGn (pagesize, (bytes + pagesize - 1) & ~(pagesize - 1)); +} + +/* + + calloc calls malloc, then zeroes out the allocated chunk. + +*/ + +#if __STD_C +Void_t* cALLOc(size_t n, size_t elem_size) +#else +Void_t* cALLOc(n, elem_size) size_t n; size_t elem_size; +#endif +{ + mchunkptr p; + INTERNAL_SIZE_T csz; + + INTERNAL_SIZE_T sz = n * elem_size; + + + /* check if expand_top called, in which case don't need to clear */ +#if MORECORE_CLEARS + mchunkptr oldtop = top; + INTERNAL_SIZE_T oldtopsize = chunksize(top); +#endif + Void_t* mem = mALLOc (sz); + + if ((long)n < 0) return 0; + + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + + /* Two optional cases in which clearing not necessary */ + + +#if HAVE_MMAP + if (chunk_is_mmapped(p)) return mem; +#endif + + csz = chunksize(p); + +#if MORECORE_CLEARS + if (p == oldtop && csz > oldtopsize) + { + /* clear only the bytes from non-freshly-sbrked memory */ + csz = oldtopsize; + } +#endif + + MALLOC_ZERO(mem, csz - SIZE_SZ); + return mem; + } +} + +/* + + cfree just calls free. It is needed/defined on some systems + that pair it with calloc, presumably for odd historical reasons. + +*/ + +#if !defined(INTERNAL_LINUX_C_LIB) || !defined(__ELF__) +#if __STD_C +void cfree(Void_t *mem) +#else +void cfree(mem) Void_t *mem; +#endif +{ + fREe(mem); +} +#endif + + + +/* + + Malloc_trim gives memory back to the system (via negative + arguments to sbrk) if there is unused memory at the `high' end of + the malloc pool. You can call this after freeing large blocks of + memory to potentially reduce the system-level memory requirements + of a program. However, it cannot guarantee to reduce memory. Under + some allocation patterns, some large free blocks of memory will be + locked between two used chunks, so they cannot be given back to + the system. + + The `pad' argument to malloc_trim represents the amount of free + trailing space to leave untrimmed. If this argument is zero, + only the minimum amount of memory to maintain internal data + structures will be left (one page or less). Non-zero arguments + can be supplied to maintain enough trailing space to service + future expected allocations without having to re-obtain memory + from the system. + + Malloc_trim returns 1 if it actually released any memory, else 0. + +*/ + +#if __STD_C +int malloc_trim(size_t pad) +#else +int malloc_trim(pad) size_t pad; +#endif +{ + long top_size; /* Amount of top-most memory */ + long extra; /* Amount to release */ + char* current_brk; /* address returned by pre-check sbrk call */ + char* new_brk; /* address returned by negative sbrk call */ + + unsigned long pagesz = malloc_getpagesize; + + top_size = chunksize(top); + extra = ((top_size - pad - MINSIZE + (pagesz-1)) / pagesz - 1) * pagesz; + + if (extra < (long)pagesz) /* Not enough memory to release */ + return 0; + + else + { + /* Test to make sure no one else called sbrk */ + current_brk = (char*)(MORECORE (0)); + if (current_brk != (char*)(top) + top_size) + return 0; /* Apparently we don't own memory; must fail */ + + else + { + new_brk = (char*)(MORECORE (-extra)); + + if (new_brk == (char*)(MORECORE_FAILURE)) /* sbrk failed? */ + { + /* Try to figure out what we have */ + current_brk = (char*)(MORECORE (0)); + top_size = current_brk - (char*)top; + if (top_size >= (long)MINSIZE) /* if not, we are very very dead! */ + { + sbrked_mem = current_brk - sbrk_base; + set_head(top, top_size | PREV_INUSE); + } + check_chunk(top); + return 0; + } + + else + { + /* Success. Adjust top accordingly. */ + set_head(top, (top_size - extra) | PREV_INUSE); + sbrked_mem -= extra; + check_chunk(top); + return 1; + } + } + } +} + + + +/* + malloc_usable_size: + + This routine tells you how many bytes you can actually use in an + allocated chunk, which may be more than you requested (although + often not). You can use this many bytes without worrying about + overwriting other allocated objects. Not a particularly great + programming practice, but still sometimes useful. + +*/ + +#if __STD_C +size_t malloc_usable_size(Void_t* mem) +#else +size_t malloc_usable_size(mem) Void_t* mem; +#endif +{ + mchunkptr p; + if (mem == 0) + return 0; + else + { + p = mem2chunk(mem); + if(!chunk_is_mmapped(p)) + { + if (!inuse(p)) return 0; + check_inuse_chunk(p); + return chunksize(p) - SIZE_SZ; + } + return chunksize(p) - 2*SIZE_SZ; + } +} + + + + +/* Utility to update current_mallinfo for malloc_stats and mallinfo() */ + +static void malloc_update_mallinfo() +{ + int i; + mbinptr b; + mchunkptr p; +#if DEBUG + mchunkptr q; +#endif + + INTERNAL_SIZE_T avail = chunksize(top); + int navail = ((long)(avail) >= (long)MINSIZE)? 1 : 0; + + for (i = 1; i < NAV; ++i) + { + b = bin_at(i); + for (p = last(b); p != b; p = p->bk) + { +#if 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 + avail += chunksize(p); + navail++; + } + } + + current_mallinfo.ordblks = navail; + current_mallinfo.uordblks = sbrked_mem - avail; + current_mallinfo.fordblks = avail; + current_mallinfo.hblks = n_mmaps; + current_mallinfo.hblkhd = mmapped_mem; + current_mallinfo.keepcost = chunksize(top); + +} + + + +/* + + malloc_stats: + + Prints on stderr the amount of space obtain from the system (both + via sbrk and mmap), the maximum amount (which may be more than + current if malloc_trim and/or munmap got called), the maximum + number of simultaneous mmap regions used, and the current number + of bytes allocated via malloc (or realloc, etc) but not yet + freed. (Note that this is the number of bytes allocated, not the + number requested. It will be larger than the number requested + because of alignment and bookkeeping overhead.) + +*/ + +void malloc_stats() +{ + malloc_update_mallinfo(); + fprintf(stderr, "max system bytes = %10u\n", + (unsigned int)(max_total_mem)); + fprintf(stderr, "system bytes = %10u\n", + (unsigned int)(sbrked_mem + mmapped_mem)); + fprintf(stderr, "in use bytes = %10u\n", + (unsigned int)(current_mallinfo.uordblks + mmapped_mem)); +#if HAVE_MMAP + fprintf(stderr, "max mmap regions = %10u\n", + (unsigned int)max_n_mmaps); +#endif +} + +/* + mallinfo returns a copy of updated current mallinfo. +*/ + +struct mallinfo mALLINFo() +{ + malloc_update_mallinfo(); + return current_mallinfo; +} + + + + +/* + mallopt: + + mallopt is the general SVID/XPG interface to tunable parameters. + The format is to provide a (parameter-number, parameter-value) pair. + mallopt then sets the corresponding parameter to the argument + value if it can (i.e., so long as the value is meaningful), + and returns 1 if successful else 0. + + See descriptions of tunable parameters above. + +*/ + +#if __STD_C +int mALLOPt(int param_number, int value) +#else +int mALLOPt(param_number, value) int param_number; int value; +#endif +{ + switch(param_number) + { + case M_TRIM_THRESHOLD: + trim_threshold = value; return 1; + case M_TOP_PAD: + top_pad = value; return 1; + case M_MMAP_THRESHOLD: + mmap_threshold = value; return 1; + case M_MMAP_MAX: +#if HAVE_MMAP + n_mmaps_max = value; return 1; +#else + if (value != 0) return 0; else n_mmaps_max = value; return 1; +#endif + + default: + return 0; + } +} + +/* + +History: + + V2.6.6 Sun Dec 5 07:42:19 1999 Doug Lea (dl at gee) + * return null for negative arguments + * Added Several WIN32 cleanups from Martin C. Fong + * Add 'LACKS_SYS_PARAM_H' for those systems without 'sys/param.h' + (e.g. WIN32 platforms) + * Cleanup up header file inclusion for WIN32 platforms + * Cleanup code to avoid Microsoft Visual C++ compiler complaints + * Add 'USE_DL_PREFIX' to quickly allow co-existence with existing + memory allocation routines + * Set 'malloc_getpagesize' for WIN32 platforms (needs more work) + * Use 'assert' rather than 'ASSERT' in WIN32 code to conform to + usage of 'assert' in non-WIN32 code + * Improve WIN32 'sbrk()' emulation's 'findRegion()' routine to + avoid infinite loop + * Always call 'fREe()' rather than 'free()' + + V2.6.5 Wed Jun 17 15:57:31 1998 Doug Lea (dl at gee) + * Fixed ordering problem with boundary-stamping + + V2.6.3 Sun May 19 08:17:58 1996 Doug Lea (dl at gee) + * Added pvalloc, as recommended by H.J. Liu + * Added 64bit pointer support mainly from Wolfram Gloger + * Added anonymously donated WIN32 sbrk emulation + * Malloc, calloc, getpagesize: add optimizations from Raymond Nijssen + * malloc_extend_top: fix mask error that caused wastage after + foreign sbrks + * Add linux mremap support code from HJ Liu + + V2.6.2 Tue Dec 5 06:52:55 1995 Doug Lea (dl at gee) + * Integrated most documentation with the code. + * Add support for mmap, with help from + Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Use last_remainder in more cases. + * Pack bins using idea from colin@nyx10.cs.du.edu + * Use ordered bins instead of best-fit threshhold + * Eliminate block-local decls to simplify tracing and debugging. + * Support another case of realloc via move into top + * Fix error occuring when initial sbrk_base not word-aligned. + * Rely on page size for units instead of SBRK_UNIT to + avoid surprises about sbrk alignment conventions. + * Add mallinfo, mallopt. Thanks to Raymond Nijssen + (raymond@es.ele.tue.nl) for the suggestion. + * Add `pad' argument to malloc_trim and top_pad mallopt parameter. + * More precautions for cases where other routines call sbrk, + courtesy of Wolfram Gloger (Gloger@lrz.uni-muenchen.de). + * Added macros etc., allowing use in linux libc from + H.J. Lu (hjl@gnu.ai.mit.edu) + * Inverted this history list + + V2.6.1 Sat Dec 2 14:10:57 1995 Doug Lea (dl at gee) + * Re-tuned and fixed to behave more nicely with V2.6.0 changes. + * Removed all preallocation code since under current scheme + the work required to undo bad preallocations exceeds + the work saved in good cases for most test programs. + * No longer use return list or unconsolidated bins since + no scheme using them consistently outperforms those that don't + given above changes. + * Use best fit for very large chunks to prevent some worst-cases. + * Added some support for debugging + + V2.6.0 Sat Nov 4 07:05:23 1995 Doug Lea (dl at gee) + * Removed footers when chunks are in use. Thanks to + Paul Wilson (wilson@cs.texas.edu) for the suggestion. + + V2.5.4 Wed Nov 1 07:54:51 1995 Doug Lea (dl at gee) + * Added malloc_trim, with help from Wolfram Gloger + (wmglo@Dent.MED.Uni-Muenchen.DE). + + V2.5.3 Tue Apr 26 10:16:01 1994 Doug Lea (dl at g) + + V2.5.2 Tue Apr 5 16:20:40 1994 Doug Lea (dl at g) + * realloc: try to expand in both directions + * malloc: swap order of clean-bin strategy; + * realloc: only conditionally expand backwards + * Try not to scavenge used bins + * Use bin counts as a guide to preallocation + * Occasionally bin return list chunks in first scan + * Add a few optimizations from colin@nyx10.cs.du.edu + + V2.5.1 Sat Aug 14 15:40:43 1993 Doug Lea (dl at g) + * faster bin computation & slightly different binning + * merged all consolidations to one part of malloc proper + (eliminating old malloc_find_space & malloc_clean_bin) + * Scan 2 returns chunks (not just 1) + * Propagate failure in realloc if malloc returns 0 + * Add stuff to allow compilation on non-ANSI compilers + from kpv@research.att.com + + V2.5 Sat Aug 7 07:41:59 1993 Doug Lea (dl at g.oswego.edu) + * removed potential for odd address access in prev_chunk + * removed dependency on getpagesize.h + * misc cosmetics and a bit more internal documentation + * anticosmetics: mangled names in macros to evade debugger strangeness + * tested on sparc, hp-700, dec-mips, rs6000 + with gcc & native cc (hp, dec only) allowing + Detlefs & Zorn comparison study (in SIGPLAN Notices.) + + Trial version Fri Aug 28 13:14:29 1992 Doug Lea (dl at g.oswego.edu) + * Based loosely on libg++-1.2X malloc. (It retains some of the overall + structure of old version, but most details differ.) + +*/ + + diff --git a/manifest b/manifest index e80aa9a3a0..e78eb13075 100644 --- a/manifest +++ b/manifest @@ -1,33 +1,35 @@ -C Version\s1.0.9\s(CVS\s493) -D 2000-10-09T22:30:00 +C Misc\scleanup.\s\sNotes\son\scompiling\sfor\sWin95.\s(CVS\s152) +D 2000-10-11T19:28:51 F COPYRIGHT 74a8a6531a42e124df07ab5599aad63870fa0bd4 -F Makefile.in f0b70aaa6717f9454c787dc74e5504c98ae7ea18 +F Makefile.in faecea9b6419cec25030b4818c9b3f7f4163b3c1 F README 51f6a4e7408b34afa5bc1c0485f61b6a4efb6958 -F VERSION 1fe894c9b80001e9dbc3b40ef1f4f984c9b624f3 +F VERSION 0302755bd219af2c39c53cc097caccb858798223 F configure 3dc1edb9dcf60215e31ff72b447935ab62211442 x F configure.in d892ca33db7e88a055519ce2f36dcb11020e8fff +F contrib/dlmalloc.c 754f43cd3b225728017b2516a58103f4bc3f05ee F doc/lemon.html e233a3e97a779c7a87e1bc4528c664a58e49dd47 F src/build.c 4d90e9e94750ca80249fc7958c617021d8bb7a50 F src/dbbe.c 226daaf8c095ceb4aff48cad188dad90643f9867 F src/dbbe.h 6337132f904e72ecb28b07390021c241397e4cbf +F src/dbbemem.c eb3d79be7105bd80069815ee499c8e8682876378 F src/delete.c 4d491eaf61b515516749c7ed68fa3b2ee8a09065 F src/expr.c e8e350d7baa33bd9ed8701c159eaba5e912e0adb F src/insert.c f146f149ad2422a1dc3bfa7a1651a25940f98958 -F src/main.c 9a89579b40e498920f86e89878f52185457b9c2c +F src/main.c 03ca6d7601b40a3c6de95d7f8c6c817e8cca9fdf F src/parse.y 5d199034de5d29ebedb42c1c51f34db4df40cbe5 -F src/printf.c 534954b942046761e0860c30a9031fa9d803ca3c +F src/printf.c 6fc343f0c3537e2530a11ac164ce64ba171aebf8 F src/select.c d382e96c2221d08367cc87976f2b574537c9de97 F src/shell.c ef5d12129c824cb98238763e9e86ca1847e0c7bd F src/shell.tcl 27ecbd63dd88396ad16d81ab44f73e6c0ea9d20e F src/sqlite.h.in 7a11f9d131e9657d11dee5c23dfe458da91de8e2 F src/sqliteInt.h b65fdecac7281aafb4c9ff3e79ea1b5546478385 -F src/table.c 12f0165b47178b54a675d25ed373ee7e798d6ff0 -F src/tclsqlite.c 7ccccae67fb36ed60ec98282953bf5dad0f9c16f +F src/table.c f08189678c806d8a74a70c156d7c27083760e028 +F src/tclsqlite.c 44b08b47612a668caaf7c4ec32133b69d73ff78e F src/tokenize.c 097bec5843d4a0fb4509e036fee93bac080c5e73 F src/update.c 51b9ef7434b15e31096155da920302e9db0d27fc -F src/util.c 782f87af3c48c898631a2d5b7074437c899f6f13 -F src/vdbe.c c0e3d29d2e13997d5664ade19ff2c8e2212945e3 -F src/vdbe.h 6413cd0165ac62b0839fe3e077cb7c9f0b736295 +F src/util.c 811e0ad47f842c16555aaf361b26dab7221c1a6c +F src/vdbe.c b8b0bc26d6543d5d33820809122bf0e7659e4663 +F src/vdbe.h 8ca755f77eeaa93886d25188fd66129febfa16cf F src/where.c 3dfad2ffd0aa994d5eceac88852f7189c8d1d3c8 F test/all.test 0950c135cab7e60c07bd745ccfad1476211e5bd7 F test/copy.test b77a1214bd7756f2849d5c4fa6e715c0ff0c34eb @@ -64,8 +66,8 @@ F www/arch.fig 4f246003b7da23bd63b8b0af0618afb4ee3055c8 F www/arch.png 8dae0766d42ed3de9ed013c1341a5792bcf633e6 F www/arch.tcl a40380c1fe0080c43e6cc5c20ed70731511b06be F www/c_interface.tcl 03f5fe8f2da9713aa1748f7e4ce34a0c35926ac9 -F www/changes.tcl 9f98c996ce4f60d082deab9b29c2d7f7981ea680 -F www/crosscompile.tcl 19734ce7f18b16ff2ed8479412abf8aca56e1dcc +F www/changes.tcl fcd35239062ac03d6169506ae8ef19189979c8f1 +F www/crosscompile.tcl bee79c34f6c3f162ec1c6f5294e79f73651d27ee F www/fileformat.tcl cfb7fba80b7275555281ba2f256c00734bcdd1c9 F www/index.tcl b19418d506f90968deef972bf1b427d98bdf13e0 F www/lang.tcl 9192e114b19987e630a41e879585b87006eb84a1 @@ -74,7 +76,7 @@ F www/opcode.tcl cb3a1abf8b7b9be9f3a228d097d6bf8b742c2b6f F www/sqlite.tcl cb0d23d8f061a80543928755ec7775da6e4f362f F www/tclsqlite.tcl ae101d5f7c07dcc59770e2a84aae09025fab2dad F www/vdbe.tcl bcbfc33bcdd0ebad95eab31286adb9e1bc289520 -P 15340d2bb34c4d7ab629f9fa2231c7f34a248db9 -R 02bf6b83b2b17b70d381cfc976f772c2 +P ebbb9e4a660e833efddb7ffea737135fff3bf2f5 +R 0e3d2b823401c2dde691e21fb25d6652 U drh -Z 35d8208b4df3d6667977ea9f36f5d1b8 +Z faddfef9a141ecc95fe77c48bb93186c diff --git a/manifest.uuid b/manifest.uuid index 9b7d6111e7..2036262158 100644 --- a/manifest.uuid +++ b/manifest.uuid @@ -1 +1 @@ -ebbb9e4a660e833efddb7ffea737135fff3bf2f5 \ No newline at end of file +3f0f1fa1fce794d1661c845f1a63a8d744892c25 \ No newline at end of file diff --git a/src/dbbemem.c b/src/dbbemem.c new file mode 100644 index 0000000000..439230767d --- /dev/null +++ b/src/dbbemem.c @@ -0,0 +1,766 @@ +/* +** Copyright (c) 1999, 2000 D. Richard Hipp +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public +** License as published by the Free Software Foundation; either +** version 2 of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +** General Public License for more details. +** +** You should have received a copy of the GNU General Public +** License along with this library; if not, write to the +** Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +** +** Author contact information: +** drh@hwaci.com +** http://www.hwaci.com/drh/ +** +************************************************************************* +** This file contains code to implement the database backend (DBBE) +** for sqlite. The database backend is the interface between +** sqlite and the code that does the actually reading and writing +** of information to the disk. +** +** This file implements a backend that constructs a database in +** memory using hash tables. Nothing is ever read or written +** to disk. Everything is forgotten when the program exits. +** +** $Id: dbbemem.c,v 1.1 2000/10/11 19:28:52 drh Exp $ +*/ +#include "sqliteInt.h" +#include + +/* +** The following structure holds the current state of the RC4 algorithm. +** We use RC4 as a random number generator. Each call to RC4 gives +** a random 8-bit number. +** +** Nothing in this file or anywhere else in SQLite does any kind of +** encryption. The RC4 algorithm is being used as a PRNG (pseudo-random +** number generator) not as an encryption device. +*/ +struct rc4 { + int i, j; + int s[256]; +}; + +/* +** Key or data is stored as an instance of the following +*/ +typedef struct datum { + void *dptr; /* The data */ + int dsize; /* Number of bytes of data */ + void *kptr; /* The key */ + int ksize; /* Number of bytes of key */ + datum *pHash; /* Next datum with the same hash */ +} + +/* +** Information about each open database table is an instance of this +** structure. There will only be one such structure for each +** table. If the VDBE opens the same table twice (as will happen +** for a self-join, for example) then two DbbeCursor structures are +** created but there is only a single BeFile structure with an +** nRef of 2. +*/ +typedef struct BeFile BeFile; +struct BeFile { + char *zName; /* Name of the table */ + BeFile *pNext, *pPrev; /* Next and previous on list of all tables */ + BeFile *pHash; /* Next table with same hash on zName */ + int nRef; /* Number of cursor that have this file open */ + int delOnClose; /* Delete when the last cursor closes this file */ + int nRec; /* Number of entries in the hash table */ + int nHash; /* Number of slots in the hash table */ + datum **aHash; /* The hash table */ +}; + +/* +** The complete database is an instance of the following structure. +*/ +struct Dbbe { + BeFile *pOpen; /* List of open tables */ + int nTemp; /* Number of temporary files created */ + FILE **apTemp; /* Space to hold temporary file pointers */ + char **azTemp; /* Names of the temporary files */ + struct rc4 rc4; /* The random number generator */ + BeFile aHash[331]; /* Hash table of tables */ +}; + +/* +** An cursor into a database file is an instance of the following structure. +** There can only be a single BeFile structure for each disk file, but +** there can be multiple DbbeCursor structures. Each DbbeCursor represents +** a cursor pointing to a particular part of the open BeFile. The +** BeFile.nRef field hold a count of the number of DbbeCursor structures +** associated with the same disk file. +*/ +struct DbbeCursor { + Dbbe *pBe; /* The database of which this record is a part */ + BeFile *pFile; /* The database file for this table */ + datum *pRec; /* Most recently used key and data */ + int h; /* Hash of pRec */ + int needRewind; /* Next key should be the first */ + int readPending; /* The fetch hasn't actually been done yet */ +}; + +/* +** Initialize the RC4 PRNG. "seed" is a pointer to some random +** data used to initialize the PRNG. +*/ +static void rc4init(struct rc4 *p, char *seed, int seedlen){ + int i; + char k[256]; + p->j = 0; + p->i = 0; + for(i=0; i<256; i++){ + p->s[i] = i; + k[i] = seed[i%seedlen]; + } + for(i=0; i<256; i++){ + int t; + p->j = (p->j + p->s[i] + k[i]) & 0xff; + t = p->s[p->j]; + p->s[p->j] = p->s[i]; + p->s[i] = t; + } +} + +/* +** Get a single 8-bit random value from the RC4 PRNG. +*/ +static int rc4byte(struct rc4 *p){ + int t; + p->i = (p->i + 1) & 0xff; + p->j = (p->j + p->s[p->i]) & 0xff; + t = p->s[p->i]; + p->s[p->i] = p->s[p->j]; + p->s[p->j] = t; + t = p->s[p->i] + p->s[p->j]; + return t & 0xff; +} + +/* +** This routine opens a new database. For the GDBM driver +** implemented here, the database name is the name of the directory +** containing all the files of the database. +** +** If successful, a pointer to the Dbbe structure is returned. +** If there are errors, an appropriate error message is left +** in *pzErrMsg and NULL is returned. +*/ +Dbbe *sqliteDbbeOpen( + const char *zName, /* The name of the database */ + int writeFlag, /* True if we will be writing to the database */ + int createFlag, /* True to create database if it doesn't exist */ + char **pzErrMsg /* Write error messages (if any) here */ +){ + Dbbe *pNew; + time_t now; + + pNew = sqliteMalloc(sizeof(Dbbe)); + if( pNew==0 ){ + sqliteSetString(pzErrMsg, "out of memory", 0); + return 0; + } + pNew->pOpen = 0; + time(&now); + rc4init(&pNew->rc4, (char*)&now, sizeof(now)); + return pNew; +} + +/* +** Free all of the memory associated with a single BeFile structure. +** It is assumed that this BeFile structure has already been unlinked +** from its database. +*/ +static void sqliteDbbeFreeFile(BeFile *pFile){ + int i; + for(i=0; inHash; i++){ + datum *pDatum, *pNextDatum; + for(pDatum = pFile->aHash[i]; pDatum; pDatum=pNextDatum){ + pNextDatum = pDatum->pHash; + sqliteFree(pDatum->dptr); + sqliteFree(pDatum); + } + } + sqliteFree(pFile->zName); + sqliteFree(pFile->aHash); + memset(pFile, 0, sizeof(*pFile)); + sqliteFree(pFile); +} + +/* +** Completely shutdown the given database. Close all files. Free all memory. +*/ +void sqliteDbbeClose(Dbbe *pBe){ + BeFile *pFile, *pNext; + int i; + for(pFile=pBe->pOpen; pFile; pFile=pNext){ + pNext = pFile->pNext; + sqliteDbbeFreeFile(pFile); + } + for(i=0; inTemp; i++){ + if( pBe->apTemp[i]!=0 ){ + unlink(pBe->azTemp[i]); + fclose(pBe->apTemp[i]); + sqliteFree(pBe->azTemp[i]); + pBe->apTemp[i] = 0; + pBe->azTemp[i] = 0; + break; + } + } + sqliteFree(pBe->azTemp); + sqliteFree(pBe->apTemp); + memset(pBe, 0, sizeof(*pBe)); + sqliteFree(pBe); +} + +/* +** Generate a random filename with the given prefix. The new filename +** is written into zBuf[]. The calling function must insure that +** zBuf[] is big enough to hold the prefix plus 20 or so extra +** characters. +** +** Very random names are chosen so that the chance of a +** collision with an existing filename is very very small. +*/ +static void randomName(struct rc4 *pRc4, char *zBuf, char *zPrefix){ + int i, j; + static const char zRandomChars[] = "abcdefghijklmnopqrstuvwxyz0123456789"; + strcpy(zBuf, zPrefix); + j = strlen(zBuf); + for(i=0; i<15; i++){ + int c = rc4byte(pRc4) % (sizeof(zRandomChars) - 1); + zBuf[j++] = zRandomChars[c]; + } + zBuf[j] = 0; +} + +/* +** Hash a NULL-terminated string. +*/ +static int sqliteStrHash(const char *z){ + int h = 0; + while( *z ){ + h = (h<<3) ^ h ^ *(z++); + } + if( h<0 ) h = -h; + return h; +} + +/* +** Locate a file in a database +*/ +static BeFile *sqliteDbbeFindFile(Dbbe *pBe, const char *zFile){ + int h; + BeFile *pFile; + + h = sqliteStrHash(zFile) % (sizeof(pBe->aHash)/sizeof(pBe->aHash[0])); + for(pFile=pBe->aHash[h]; pFile; pFile=pFile->pHash){ + if( strcmp(pFile->zName, zFile)==0 ) break; + } + return pFile; +} + +/* +** Open a new table cursor. Write a pointer to the corresponding +** DbbeCursor structure into *ppCursr. Return an integer success +** code: +** +** SQLITE_OK It worked! +** +** SQLITE_NOMEM sqliteMalloc() failed +** +** SQLITE_PERM Attempt to access a file for which file +** access permission is denied +** +** SQLITE_BUSY Another thread or process is already using +** the corresponding file and has that file locked. +** +** SQLITE_READONLY The current thread already has this file open +** readonly but you are trying to open for writing. +** (This can happen if a SELECT callback tries to +** do an UPDATE or DELETE.) +** +** If zTable is 0 or "", then a temporary database file is created and +** a cursor to that temporary file is opened. The temporary file +** will be deleted from the disk when it is closed. +*/ +int sqliteDbbeOpenCursor( + Dbbe *pBe, /* The database the table belongs to */ + const char *zTable, /* The SQL name of the file to be opened */ + int writeable, /* True to open for writing */ + DbbeCursor **ppCursr /* Write the resulting table pointer here */ +){ + char *zFile; /* Name of the table file */ + DbbeCursor *pCursr; /* The new table cursor */ + BeFile *pFile; /* The underlying data file for this table */ + int rc = SQLITE_OK; /* Return value */ + int rw_mask; /* Permissions mask for opening a table */ + int mode; /* Mode for opening a table */ + + *ppCursr = 0; + pCursr = sqliteMalloc( sizeof(*pCursr) ); + if( pCursr==0 ) return SQLITE_NOMEM; + if( zTable ){ + zFile = sqliteStrDup(zTable); + pFile = sqliteDbbeFindFile(zFile); + }else{ + pFile = 0; + zFile = 0; + } + if( pFile==0 ){ + pFile = sqliteMalloc( sizeof(*pFile) ); + if( pFile==0 ){ + sqliteFree(zFile); + return SQLITE_NOMEM; + } + if( zFile ){ + if( !writeable || pBe->write ){ + pFile->dbf = gdbm_open(zFile, 0, rw_mask, mode, 0); + }else{ + pFile->dbf = 0; + } + }else{ + int limit; + struct rc4 *pRc4; + char zRandom[50]; + pRc4 = &pBe->rc4; + zFile = 0; + limit = 5; + while( 1 ){ + randomName(&pBe->rc4, zRandom, "_temp_table_"); + sqliteFree(zFile); + zFile = sqliteStrDup(zRandom); + if( sqliteDbbeFindFile(pBe, zFile)==0 ) break; + } + pFile->delOnClose = 1; + } + pFile->zName = zFile; + pFile->nRef = 1; + pFile->pPrev = 0; + if( pBe->pOpen ){ + pBe->pOpen->pPrev = pFile; + } + pFile->pNext = pBe->pOpen; + pBe->pOpen = pFile; + }else{ + sqliteFree(zFile); + pFile->nRef++; + } + pCursr->pBe = pBe; + pCursr->pFile = pFile; + pCursr->readPending = 0; + pCursr->needRewind = 1; + if( rc!=SQLITE_OK ){ + sqliteDbbeCloseCursor(pCursr); + *ppCursr = 0; + }else{ + *ppCursr = pCursr; + } + return rc; +} + +/* +** Unlink a file from the database +*/ +static void sqliteDbbeUnlinkFile(Dbbe *pBe, BeFile *pFile){ + int h = sqliteStrHash(pFile->zName) % + (sizeof(pBe->aHash)/sizeof(pBe->aHash[0]))); + if( pBe->aHash[h]==pFile ){ + pBe->aHash[h] = pFile->pHash; + }else{ + BeFile *pProbe; + for(pProbe=pBe->aHash[h]; pProbe; pProbe=pProbe->pHash){ + if( pProbe->pHash==pFile ){ + pProbe->pHash = pFile->pHash; + break; + } + } + } +} + +/* +** Drop a table from the database. The file that corresponds +** to this table is deleted. +*/ +void sqliteDbbeDropTable(Dbbe *pBe, const char *zTable){ + File *pFile; + + pFile = sqliteDbbeFindFile(pBe, zTable); + if( pFile ){ + sqliteDbbeUnlinkFile(pFile); + sqliteDbbeFreeFile(pFile); + } +} + +/* +** Reorganize a table to reduce search times and disk usage. +*/ +int sqliteDbbeReorganizeTable(Dbbe *pBe, const char *zTable){ + return SQLITE_OK; +} + +/* +** Close a cursor previously opened by sqliteDbbeOpenCursor(). +** +** There can be multiple cursors pointing to the same open file. +** The underlying file is not closed until all cursors have been +** closed. This routine decrements the BeFile.nref field of the +** underlying file and closes the file when nref reaches 0. +*/ +void sqliteDbbeCloseCursor(DbbeCursor *pCursr){ + BeFile *pFile; + Dbbe *pBe; + if( pCursr==0 ) return; + pFile = pCursr->pFile; + pBe = pCursr->pBe; + pFile->nRef--; + if( pFile->nRef<=0 ){ + if( pFile->pPrev ){ + pFile->pPrev->pNext = pFile->pNext; + }else{ + pBe->pOpen = pFile->pNext; + } + if( pFile->pNext ){ + pFile->pNext->pPrev = pFile->pPrev; + } + if( pFile->delOnClose ){ + sqliteDbbeUnlinkFile(pFile); + sqliteDbbeFreeFile(pFile); + } + } + memset(pCursr, 0, sizeof(*pCursr)); + sqliteFree(pCursr); +} + +/* +** Compute a hash on binary data +*/ +static int sqliteBinaryHash(int n, char *z){ + int h = 0; + while( n-- ){ + h = (h<<9) ^ (h<<3) ^ h ^ *(z++); + } + if( h<0 ) h = -h; + return h; +} + +/* +** Resize the hash table +*/ +static void sqliteDbbeRehash(BeFile *pFile, int nHash){ + int i, h; + datum *pRec, *pNextRec; + datum **aHash; + + if( nHash<1 ) return; + aHash = sqliteMalloc( sizeof(aHash[0])*nHash ); + if( aHash==0 ) return; + for(i=0; inHash; i++){ + for(pRec=pFile->aHash[i]; pRec; pRec=pNextRec){ + pNextRec = pRec->pHash; + h = sqliteBinaryHash(pRec->ksize, pRec->kptr) % nHash; + pRec->pHash = aHash[h]; + aHash[h] = pRec; + } + } + sqliteFree(pFile->aHash); + pFile->aHash = aHash; + pFile->nHash = nHash; +} + +/* +** Locate a datum in a file. Create it if it isn't already there and +** the createFlag is set. +*/ +static datum **sqliteDbbeLookup( + BeFile *pFile, /* Where to look */ + int nKey, /* The size of the key */ + char *pKey, /* The key */ + int *pH, /* Write the hash line here */ + int createFlag /* Create a new entry if this is true */ +){ + int h; + datum **ppRec = 0; + datum *pNew; + if( pFile->nHash>0 ){ + h = sqliteBinaryHash(nKey, pKey) % pFile->nHash; + ppRec = &pFile->aHash[h]; + while( *ppRec ){ + if( (**ppRec).ksize==nKey && memcpy((**ppRec).kptr, pKey, nKey)==0 ){ + if( *pH ) *pH = h; + return ppRec; + } + } + } + if( createFlag==0 ) return 0; + if( (pFile->nRec + 1) > pFile->nHash*2 ){ + int nHash = (pFile->nRec + 1)*4; + if( nHash<51 ) nHash = 51; + sqliteDbbeRehash(pFile, nHash); + if( pFile->nHash==0 ) return 0; + } + h = sqliteBinaryHash(nKey, pKey) % pFile->nHash; + pNew = sqliteMalloc( sizeof(*pNew) + nKey ); + if( pNew==0 ) return 0; + pNew->kptr = (void*)&pNew[1]; + pNew->ksize = nKey; + memcpy(pNew->kptr, pkey, nKey); + pNew->pHash = pFile->aHash[h]; + pFile->aHash[h] = pNew; + pNew->dsize = 0; + pNew->dptr = 0; + pFile->nRec++; + if( pH ) *pH = h; + return &pFile->aHash[h]; +} + +/* +** Fetch a single record from an open cursor. Return 1 on success +** and 0 on failure. +*/ +int sqliteDbbeFetch(DbbeCursor *pCursr, int nKey, char *pKey){ + datum **ppRec; + ppRec = sqliteDbbeLookup(pCursr->pFile, nKey, pKey, &pCursr->h, 0); + if( ppRec ){ + pCursr->pRec = *ppRec; + } + return pCursr->pRec!=0; +} + +/* +** Return 1 if the given key is already in the table. Return 0 +** if it is not. +*/ +int sqliteDbbeTest(DbbeCursor *pCursr, int nKey, char *pKey){ + return sqliteDbbeFetch(pCursr, nKey, pKey); +} + +/* +** Copy bytes from the current key or data into a buffer supplied by +** the calling function. Return the number of bytes copied. +*/ +int sqliteDbbeCopyKey(DbbeCursor *pCursr, int offset, int size, char *zBuf){ + int n; + datum *pRec; + if( (pRec = pCursor->pRec)==0 || offset>=pRec->ksize ) return 0; + if( offset+size>pRec->ksize ){ + n = pRec->ksize - offset; + }else{ + n = size; + } + memcpy(zBuf, pRec->kptr[offset], n); + return n; +} +int sqliteDbbeCopyData(DbbeCursor *pCursr, int offset, int size, char *zBuf){ + int n; + datum *pRec; + if( (pRec = pCursr->pRec)==0 || offset>=pRec->dsize ) return 0; + if( offset+size>pRec->dsize ){ + n = pRec->dsize - offset; + }else{ + n = size; + } + memcpy(zBuf, &pRec->dptr[offset], n); + return n; +} + +/* +** Return a pointer to bytes from the key or data. The data returned +** is ephemeral. +*/ +char *sqliteDbbeReadKey(DbbeCursor *pCursr, int offset){ + datum *pRec; + if( (pRec = pCursr->pRec)==0 || offset<0 || offset>=pRec->ksize ) return ""; + return &pRec->kptr[offset]; +} +char *sqliteDbbeReadData(DbbeCursor *pCursr, int offset){ + datum *pRec; + if( (pRec = pCursr->pRec)==0 || offset<0 || offset>=pRec->dsize ) return ""; + return &pRec->dptr[offset]; +} + +/* +** Return the total number of bytes in either data or key. +*/ +int sqliteDbbeKeyLength(DbbeCursor *pCursr){ + return pCursr->pRec ? pCursor->pRec->ksize : 0; +} +int sqliteDbbeDataLength(DbbeCursor *pCursr){ + return pCursr->pRec ? pCursor->pRec->dsize : 0; +} + +/* +** Make is so that the next call to sqliteNextKey() finds the first +** key of the table. +*/ +int sqliteDbbeRewind(DbbeCursor *pCursr){ + pCursr->needRewind = 1; + return SQLITE_OK; +} + +/* +** Read the next key from the table. Return 1 on success. Return +** 0 if there are no more keys. +*/ +int sqliteDbbeNextKey(DbbeCursor *pCursr){ + int h; + BeFile *pFile; + if( pCursr==0 || (pFile = pCursr->pFile)==0 || pFile->nHash==0 ){ + return 0; + } + if( pCursr->needRewind ){ + pCursr->pRec = 0; + pCursr->h = -1; + } + if( pCursr->pRec ){ + pCursr->pRec = pCursr->pRec->pHash; + } + if( pCursr->pRec==0 ){ + for(h=pCursr->h; hnHash && pFile->aHash[h]==0; h++){} + if( h>=pFile->nHash ){ + pCursr->h = -1; + return 0; + }else{ + pCursr->h = h; + pCursr->pRec = pFile->aHash[h]; + return 1; + } + } +} + +/* +** Get a new integer key. +*/ +int sqliteDbbeNew(DbbeCursor *pCursr){ + int iKey; + int go = 1; + int i; + struct rc4 *pRc4; + + if( pCursr->pFile==0 || pCursr->pFile->dbf==0 ) return 1; + pRc4 = &pCursr->pBe->rc4; + while( go ){ + iKey = 0; + for(i=0; i<4; i++){ + iKey = (iKey<<8) + rc4byte(pRc4); + } + if( iKey==0 ) continue; + go = sqliteDbbeLookup(pCursr->pFile, sizeof(iKey), &iKey, 0, 0)!=0; + } + return iKey; +} + +/* +** Write an entry into the table. Overwrite any prior entry with the +** same key. +*/ +int sqliteDbbePut( + DbbeCursor *pCursr, /* Write to this cursor */ + int nKey, /* Size of the key */ + char *pKey, /* The key */ + int nData, /* Size of the data */ + char *pData /* The data */ +){ + int rc; + datum **ppRec, *pRec; + if( pCursr->pFile==0 ) return SQLITE_ERROR; + ppRec = sqliteDbbeLookup(pCursr->pFile, nKey, pKey, &pCursr->h, 1); + if( ppRec==0 ) return SQLITE_NOMEM; + pRec = *ppRec; + sqliteFree(pRec->dptr); + pRec->dptr = sqliteMalloc( nData ); + if( pRec->dptr==0 ) return SQLITE_NOMEM; + memcpy(pRec->dptr, pData, nData); + pRec->dsize = nData; + return SQLITE_OK; +} + +/* +** Remove an entry from a table, if the entry exists. +*/ +int sqliteDbbeDelete(DbbeCursor *pCursr, int nKey, char *pKey){ + datum **ppRec, *pRec; + ppRec = sqliteDbbeLookcup(pCursr->pFile, nKey, pKey, 0, 0); + if( ppRec ){ + pRec = *ppRec; + *ppRec = pRec->pNext; + if( pCursr->pRec==pRec ){ + pCursr->pRec = 0; + pCursr->h = -1; + } + sqliteFree(pRec->dptr); + sqliteFree(pRec); + pCursr->pFile->nRec--; + } + return SQLITE_OK; +} + +/* +** Open a temporary file. The file should be deleted when closed. +** +** Note that we can't use the old Unix trick of opening the file +** and then immediately unlinking the file. That works great +** under Unix, but fails when we try to port to Windows. +*/ +int sqliteDbbeOpenTempFile(Dbbe *pBe, FILE **ppFile){ + char *zFile; /* Full name of the temporary file */ + char zBuf[50]; /* Base name of the temporary file */ + int i; /* Loop counter */ + int limit; /* Prevent an infinite loop */ + int rc = SQLITE_OK; /* Value returned by this function */ + + for(i=0; inTemp; i++){ + if( pBe->apTemp[i]==0 ) break; + } + if( i>=pBe->nTemp ){ + pBe->nTemp++; + pBe->apTemp = sqliteRealloc(pBe->apTemp, pBe->nTemp*sizeof(FILE*) ); + pBe->azTemp = sqliteRealloc(pBe->azTemp, pBe->nTemp*sizeof(char*) ); + } + if( pBe->apTemp==0 ){ + *ppFile = 0; + return SQLITE_NOMEM; + } + limit = 4; + zFile = 0; + do{ + randomName(&pBe->rc4, zBuf, "/_temp_file_"); + sqliteFree(zFile); + zFile = 0; + sqliteSetString(&zFile, pBe->zDir, zBuf, 0); + }while( access(zFile,0)==0 && limit-- >= 0 ); + *ppFile = pBe->apTemp[i] = fopen(zFile, "w+"); + if( pBe->apTemp[i]==0 ){ + rc = SQLITE_ERROR; + sqliteFree(zFile); + pBe->azTemp[i] = 0; + }else{ + pBe->azTemp[i] = zFile; + } + return rc; +} + +/* +** Close a temporary file opened using sqliteDbbeOpenTempFile() +*/ +void sqliteDbbeCloseTempFile(Dbbe *pBe, FILE *f){ + int i; + for(i=0; inTemp; i++){ + if( pBe->apTemp[i]==f ){ + unlink(pBe->azTemp[i]); + sqliteFree(pBe->azTemp[i]); + pBe->apTemp[i] = 0; + pBe->azTemp[i] = 0; + break; + } + } + fclose(f); +} diff --git a/src/main.c b/src/main.c index 4ee87766be..24355db728 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ ** other files are for internal use by SQLite and should not be ** accessed by users of the library. ** -** $Id: main.c,v 1.18 2000/08/22 13:40:51 drh Exp $ +** $Id: main.c,v 1.19 2000/10/11 19:28:52 drh Exp $ */ #include "sqliteInt.h" @@ -327,7 +327,6 @@ static int sqlite_default_busy_callback( const char *NotUsed, /* The name of the table that is busy */ int count /* Number of times table has been busy */ ){ - int rc; #if defined(HAVE_USLEEP) && HAVE_USLEEP int delay = 10000; int prior_delay = 0; diff --git a/src/printf.c b/src/printf.c index ed3854e44f..f554b38a18 100644 --- a/src/printf.c +++ b/src/printf.c @@ -46,7 +46,9 @@ ** + All functions are fully reentrant. ** */ +#include #include "sqliteInt.h" + /* ** Undefine COMPATIBILITY to make some slight changes in the way things ** work. I think the changes are an improvement, but they are not diff --git a/src/table.c b/src/table.c index 9a46a13d80..27b6ab7b3a 100644 --- a/src/table.c +++ b/src/table.c @@ -31,8 +31,8 @@ typedef struct TabResult { static int sqlite_get_table_cb(void *pArg, int nCol, char **argv, char **colv){ TabResult *p = (TabResult*)pArg; int need; - int i, len; - char *z, *zVal; + int i; + char *z; /* Make sure there is enough space in p->azResult to hold everything ** we need to remember from this invocation of the callback. diff --git a/src/tclsqlite.c b/src/tclsqlite.c index 0399b3f198..25f4086507 100644 --- a/src/tclsqlite.c +++ b/src/tclsqlite.c @@ -23,7 +23,7 @@ ************************************************************************* ** A TCL Interface to SQLite ** -** $Id: tclsqlite.c,v 1.10 2000/09/30 22:46:07 drh Exp $ +** $Id: tclsqlite.c,v 1.11 2000/10/11 19:28:52 drh Exp $ */ #ifndef NO_TCL /* Omit this whole file if TCL is unavailable */ @@ -138,7 +138,6 @@ static int DbBusyHandler(void *cd, const char *zTable, int nTries){ int rc; char zVal[30]; char *zCmd; - char *zResult; Tcl_DString cmd; Tcl_DStringInit(&cmd); diff --git a/src/util.c b/src/util.c index a281f13c3a..f9652bb540 100644 --- a/src/util.c +++ b/src/util.c @@ -26,15 +26,18 @@ ** This file contains functions for allocating memory, comparing ** strings, and stuff like that. ** -** $Id: util.c,v 1.15 2000/09/14 01:21:10 drh Exp $ +** $Id: util.c,v 1.16 2000/10/11 19:28:52 drh Exp $ */ #include "sqliteInt.h" #include #include +/* +** If MEMORY_DEBUG is defined, then use versions of malloc() and +** free() that track memory usage and check for buffer overruns. +*/ #ifdef MEMORY_DEBUG - /* ** Allocate new memory and set it to zero. Return NULL if ** no memory is available. @@ -164,9 +167,14 @@ char *sqliteStrNDup_(const char *z, int n, char *zFile, int line){ } return zNew; } +#endif /* MEMORY_DEBUG */ +/* +** The following versions of malloc() and free() are for use in a +** normal build. +*/ +#if !defined(MEMORY_DEBUG) -#else /* !defined(MEMORY_DEBUG) */ /* ** Allocate new memory and set it to zero. Return NULL if ** no memory is available. @@ -219,7 +227,7 @@ char *sqliteStrNDup(const char *z, int n){ } return zNew; } -#endif /* MEMORY_DEBUG */ +#endif /* !defined(MEMORY_DEBUG) */ /* ** Create a string from the 2nd and subsequent arguments (up to the diff --git a/src/vdbe.c b/src/vdbe.c index ec0ac448df..1f89782a3d 100644 --- a/src/vdbe.c +++ b/src/vdbe.c @@ -41,10 +41,11 @@ ** But other routines are also provided to help in building up ** a program instruction by instruction. ** -** $Id: vdbe.c,v 1.41 2000/09/14 01:21:10 drh Exp $ +** $Id: vdbe.c,v 1.42 2000/10/11 19:28:53 drh Exp $ */ #include "sqliteInt.h" #include +#include /* ** SQL is translated into a sequence of instructions to be diff --git a/src/vdbe.h b/src/vdbe.h index d349b5dc85..7357e4ddbe 100644 --- a/src/vdbe.h +++ b/src/vdbe.h @@ -27,7 +27,7 @@ ** or VDBE. The VDBE implements an abstract machine that runs a ** simple program to access and modify the underlying database. ** -** $Id: vdbe.h,v 1.12 2000/08/28 15:51:45 drh Exp $ +** $Id: vdbe.h,v 1.13 2000/10/11 19:28:53 drh Exp $ */ #ifndef _SQLITE_VDBE_H_ #define _SQLITE_VDBE_H_ @@ -196,6 +196,6 @@ int sqliteVdbeList(Vdbe*,sqlite_callback,void*,char**); void sqliteVdbeResolveLabel(Vdbe*, int); int sqliteVdbeCurrentAddr(Vdbe*); void sqliteVdbeTrace(Vdbe*,FILE*); - +void sqliteVdbeCompressSpace(Vdbe*,int); #endif diff --git a/www/changes.tcl b/www/changes.tcl index db4a111b4f..f0343552e9 100644 --- a/www/changes.tcl +++ b/www/changes.tcl @@ -17,6 +17,12 @@ proc chng {date desc} { puts "

    $desc

" } +chng {2000 Oct 11 (Not Released)} { +
  • Added notes on how to compile for Windows95/98.
  • +
  • Add Doug Lea's memory allocator to the distribution, for completeness.
  • +
  • Removed a few variables that were not being used. Etc.
  • +} + chng {2000 Oct 8 (1.0.9)} {
  • Added the sqlite_..._printf() interface routines.
  • Modified the sqlite shell program to use the new interface diff --git a/www/crosscompile.tcl b/www/crosscompile.tcl index edbcc1952b..fd1d1bea3c 100644 --- a/www/crosscompile.tcl +++ b/www/crosscompile.tcl @@ -1,7 +1,7 @@ # # Run this Tcl script to generate the crosscompile.html file. # -set rcsid {$Id: crosscompile.tcl,v 1.2 2000/07/31 19:16:32 drh Exp $} +set rcsid {$Id: crosscompile.tcl,v 1.3 2000/10/11 19:28:53 drh Exp $} puts { @@ -23,7 +23,9 @@ binaries. See the website for details.

    This page describes how you can use MinGW configured as a cross-compiler running under RedHat 6.0 Linux to generate a -binary for SQLite that runs under WinNT.

    +binary for SQLite that runs under WinNT. +Some additional steps (described below) +are needed to target Win95/98.

    } proc Code {body} { @@ -123,6 +125,64 @@ make
  • + + +

    Targetting Windows95/98 instead of WindowsNT

    + +

    A small amount of additional work is needed to get SQLite running +under Windows95/98. The first problem is that the LockFile() and +UnlockFile() API that the Roth GDBM port uses does not work under +Windows95. The easiest workaround is to just disable file locking +in the GDBM library. You can do so by appending a few lines of code +to the end of one of the GDBM source files and compiling the library +using a special command-line option. Replace step (4) above as +follows:

    + +
      +
    1. +Append text to the systems.h source file as follows:

      + +
      +cat >>systems.h <<\END
      +#ifdef NO_LOCKS
      +#undef  UNLOCK_FILE
      +#define UNLOCK_FILE(x)
      +#undef  READLOCK_FILE
      +#define READLOCK_FILE(x)
      +#undef  WRITELOCK_FILE
      +#define WRITELOCK_FILE(x)
      +#endif
      +END
      +
      + +

      Then manually build the GDBM library with the extra +"NO_LOCKS" define as follows:

      +
      +i386-mingw32-gcc -DWIN32=1 -DNO_LOCKS -O2 -c *.c
      +i386-mingw32-ar cr libgdbm.a *.o
      +i386-mingw32-ranlib libgdbm.a
      +cd ..
      +
      +

    2. +
    + +

    Note that the locking problem has been reported and may actually +be fixed in the Roth GDBM distribution by the time you read this. +You should probably check before you make the above changes.

    + +

    The above is all you have to do to get SQLite to work on Windows95. +But one more step is required to get it to work well. It +turns out that SQLite make heavy use of malloc() and +free() and the implementation of this functions +on Windows95 is particular poor. Large database queries will run +more than 10 times faster if you substitute a better memory allocator +such as the one by +Doug Lea. +A copy of Doug's allocator is included in the contrib +directory of the source tree. Speed improvements are also reported +on WindowsNT when alternative memory allocators are used, though +the speedup is not as dramatic as it is with WIndows95.

    + } puts {