summaryrefslogtreecommitdiffhomepage
path: root/benchmarks/others/sparsepp/spp_dlalloc.h
diff options
context:
space:
mode:
Diffstat (limited to 'benchmarks/others/sparsepp/spp_dlalloc.h')
-rw-r--r--benchmarks/others/sparsepp/spp_dlalloc.h4044
1 files changed, 4044 insertions, 0 deletions
diff --git a/benchmarks/others/sparsepp/spp_dlalloc.h b/benchmarks/others/sparsepp/spp_dlalloc.h
new file mode 100644
index 00000000..f88aab7c
--- /dev/null
+++ b/benchmarks/others/sparsepp/spp_dlalloc.h
@@ -0,0 +1,4044 @@
+#ifndef spp_dlalloc__h_
+#define spp_dlalloc__h_
+
+/* This is a C++ allocator created from Doug Lea's dlmalloc
+ (Version 2.8.6 Wed Aug 29 06:57:58 2012)
+ see: http://g.oswego.edu/dl/html/malloc.html
+*/
+
+#include "spp_utils.h"
+#include "spp_smartptr.h"
+
+
+#ifndef SPP_FORCEINLINE
+ #if defined(__GNUC__)
+ #define SPP_FORCEINLINE __inline __attribute__ ((always_inline))
+ #elif defined(_MSC_VER)
+ #define SPP_FORCEINLINE __forceinline
+ #else
+ #define SPP_FORCEINLINE inline
+ #endif
+#endif
+
+
+#ifndef SPP_IMPL
+ #define SPP_IMPL SPP_FORCEINLINE
+#endif
+
+#ifndef SPP_API
+ #define SPP_API static
+#endif
+
+
+namespace spp
+{
+ // ---------------------- allocator internal API -----------------------
+ typedef void* mspace;
+
+ /*
+ create_mspace creates and returns a new independent space with the
+ given initial capacity, or, if 0, the default granularity size. It
+ returns null if there is no system memory available to create the
+ space. If argument locked is non-zero, the space uses a separate
+ lock to control access. The capacity of the space will grow
+ dynamically as needed to service mspace_malloc requests. You can
+ control the sizes of incremental increases of this space by
+ compiling with a different SPP_DEFAULT_GRANULARITY or dynamically
+ setting with mallopt(M_GRANULARITY, value).
+ */
+ SPP_API mspace create_mspace(size_t capacity, int locked);
+ SPP_API size_t destroy_mspace(mspace msp);
+ SPP_API void* mspace_malloc(mspace msp, size_t bytes);
+ SPP_API void mspace_free(mspace msp, void* mem);
+ SPP_API void* mspace_realloc(mspace msp, void* mem, size_t newsize);
+
+#if 0
+ SPP_API mspace create_mspace_with_base(void* base, size_t capacity, int locked);
+ SPP_API int mspace_track_large_chunks(mspace msp, int enable);
+ SPP_API void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size);
+ SPP_API void* mspace_memalign(mspace msp, size_t alignment, size_t bytes);
+ SPP_API void** mspace_independent_calloc(mspace msp, size_t n_elements,
+ size_t elem_size, void* chunks[]);
+ SPP_API void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+ size_t sizes[], void* chunks[]);
+ SPP_API size_t mspace_footprint(mspace msp);
+ SPP_API size_t mspace_max_footprint(mspace msp);
+ SPP_API size_t mspace_usable_size(const void* mem);
+ SPP_API int mspace_trim(mspace msp, size_t pad);
+ SPP_API int mspace_mallopt(int, int);
+#endif
+
+ // -----------------------------------------------------------
+ // -----------------------------------------------------------
+ struct MSpace : public spp_rc
+ {
+ MSpace() :
+ _sp(create_mspace(0, 0))
+ {}
+
+ ~MSpace()
+ {
+ destroy_mspace(_sp);
+ }
+
+ mspace _sp;
+ };
+
+ // -----------------------------------------------------------
+ // -----------------------------------------------------------
+ template<class T>
+ class spp_allocator
+ {
+ public:
+ typedef T value_type;
+ typedef T* pointer;
+ typedef ptrdiff_t difference_type;
+ typedef const T* const_pointer;
+ typedef size_t size_type;
+
+ MSpace *getSpace() const { return _space.get(); }
+
+ spp_allocator() : _space(new MSpace) {}
+
+ template<class U>
+ spp_allocator(const spp_allocator<U> &o) : _space(o.getSpace()) {}
+
+ template<class U>
+ spp_allocator& operator=(const spp_allocator<U> &o)
+ {
+ if (&o != this)
+ _space = o.getSpace();
+ return *this;
+ }
+
+ void swap(spp_allocator &o)
+ {
+ std::swap(_space, o._space);
+ }
+
+ pointer allocate(size_t n, const_pointer /* unused */ = 0)
+ {
+ pointer res = static_cast<pointer>(mspace_malloc(_space->_sp, n * sizeof(T)));
+ if (!res)
+ throw std::bad_alloc();
+ return res;
+ }
+
+ void deallocate(pointer p, size_t /* unused */)
+ {
+ mspace_free(_space->_sp, p);
+ }
+
+ pointer reallocate(pointer p, size_t new_size)
+ {
+ pointer res = static_cast<pointer>(mspace_realloc(_space->_sp, p, new_size * sizeof(T)));
+ if (!res)
+ throw std::bad_alloc();
+ return res;
+ }
+
+ pointer reallocate(pointer p, size_type /* old_size */, size_t new_size)
+ {
+ return reallocate(p, new_size);
+ }
+
+ size_type max_size() const
+ {
+ return static_cast<size_type>(-1) / sizeof(value_type);
+ }
+
+ void construct(pointer p, const value_type& val)
+ {
+ new (p) value_type(val);
+ }
+
+ void destroy(pointer p) { p->~value_type(); }
+
+ template<class U>
+ struct rebind
+ {
+ // rebind to libc_allocator because we want to use malloc_inspect_all in destructive_iterator
+ // to reduce peak memory usage (we don't want <group_items> mixed with value_type when
+ // we traverse the allocated memory).
+ typedef spp::spp_allocator<U> other;
+ };
+
+ mspace space() const { return _space->_sp; }
+
+ // check if we can clear the whole allocator memory at once => works only if the allocator
+ // is not be shared. If can_clear() returns true, we expect that the next allocator call
+ // will be clear() - not allocate() or deallocate()
+ bool can_clear()
+ {
+ assert(!_space_to_clear);
+ _space_to_clear.reset();
+ _space_to_clear.swap(_space);
+ if (_space_to_clear->count() == 1)
+ return true;
+ else
+ _space_to_clear.swap(_space);
+ return false;
+ }
+
+ void clear()
+ {
+ assert(!_space && !!_space_to_clear);
+ _space_to_clear.reset();
+ _space = new MSpace;
+ }
+
+ private:
+ spp_sptr<MSpace> _space;
+ spp_sptr<MSpace> _space_to_clear;
+ };
+}
+
+
+// allocators are "equal" whenever memory allocated with one can be deallocated with the other
+template<class T>
+inline bool operator==(const spp_::spp_allocator<T> &a, const spp_::spp_allocator<T> &b)
+{
+ return a.space() == b.space();
+}
+
+template<class T>
+inline bool operator!=(const spp_::spp_allocator<T> &a, const spp_::spp_allocator<T> &b)
+{
+ return !(a == b);
+}
+
+namespace std
+{
+ template <class T>
+ inline void swap(spp_::spp_allocator<T> &a, spp_::spp_allocator<T> &b)
+ {
+ a.swap(b);
+ }
+}
+
+#if !defined(SPP_EXCLUDE_IMPLEMENTATION)
+
+#ifndef WIN32
+ #ifdef _WIN32
+ #define WIN32 1
+ #endif
+ #ifdef _WIN32_WCE
+ #define SPP_LACKS_FCNTL_H
+ #define WIN32 1
+ #endif
+#endif
+
+#ifdef WIN32
+ #define WIN32_LEAN_AND_MEAN
+ #include <windows.h>
+ #include <tchar.h>
+ #define SPP_HAVE_MMAP 1
+ #define SPP_LACKS_UNISTD_H
+ #define SPP_LACKS_SYS_PARAM_H
+ #define SPP_LACKS_SYS_MMAN_H
+ #define SPP_LACKS_STRING_H
+ #define SPP_LACKS_STRINGS_H
+ #define SPP_LACKS_SYS_TYPES_H
+ #define SPP_LACKS_ERRNO_H
+ #define SPP_LACKS_SCHED_H
+ #ifndef SPP_MALLOC_FAILURE_ACTION
+ #define SPP_MALLOC_FAILURE_ACTION
+ #endif
+ #ifndef SPP_MMAP_CLEARS
+ #ifdef _WIN32_WCE /* WINCE reportedly does not clear */
+ #define SPP_MMAP_CLEARS 0
+ #else
+ #define SPP_MMAP_CLEARS 1
+ #endif
+ #endif
+#endif
+
+#if defined(DARWIN) || defined(_DARWIN)
+ #define SPP_HAVE_MMAP 1
+ /* OSX allocators provide 16 byte alignment */
+ #ifndef SPP_MALLOC_ALIGNMENT
+ #define SPP_MALLOC_ALIGNMENT ((size_t)16U)
+ #endif
+#endif
+
+#ifndef SPP_LACKS_SYS_TYPES_H
+ #include <sys/types.h> /* For size_t */
+#endif
+
+#ifndef SPP_MALLOC_ALIGNMENT
+ #define SPP_MALLOC_ALIGNMENT ((size_t)(2 * sizeof(void *)))
+#endif
+
+/* ------------------- size_t and alignment properties -------------------- */
+static const size_t spp_max_size_t = ~(size_t)0;
+static const size_t spp_size_t_bitsize = sizeof(size_t) << 3;
+static const size_t spp_half_max_size_t = spp_max_size_t / 2U;
+static const size_t spp_chunk_align_mask = SPP_MALLOC_ALIGNMENT - 1;
+
+#if defined(SPP_DEBUG) || !defined(NDEBUG)
+static bool spp_is_aligned(void *p) { return ((size_t)p & spp_chunk_align_mask) == 0; }
+#endif
+
+// the number of bytes to offset an address to align it
+static size_t align_offset(void *p)
+{
+ return (((size_t)p & spp_chunk_align_mask) == 0) ? 0 :
+ ((SPP_MALLOC_ALIGNMENT - ((size_t)p & spp_chunk_align_mask)) & spp_chunk_align_mask);
+}
+
+
+#ifndef SPP_FOOTERS
+ #define SPP_FOOTERS 0
+#endif
+
+#ifndef SPP_ABORT
+ #define SPP_ABORT abort()
+#endif
+
+#ifndef SPP_ABORT_ON_ASSERT_FAILURE
+ #define SPP_ABORT_ON_ASSERT_FAILURE 1
+#endif
+
+#ifndef SPP_PROCEED_ON_ERROR
+ #define SPP_PROCEED_ON_ERROR 0
+#endif
+
+#ifndef SPP_INSECURE
+ #define SPP_INSECURE 0
+#endif
+
+#ifndef SPP_MALLOC_INSPECT_ALL
+ #define SPP_MALLOC_INSPECT_ALL 0
+#endif
+
+#ifndef SPP_HAVE_MMAP
+ #define SPP_HAVE_MMAP 1
+#endif
+
+#ifndef SPP_MMAP_CLEARS
+ #define SPP_MMAP_CLEARS 1
+#endif
+
+#ifndef SPP_HAVE_MREMAP
+ #ifdef linux
+ #define SPP_HAVE_MREMAP 1
+ #ifndef _GNU_SOURCE
+ #define _GNU_SOURCE /* Turns on mremap() definition */
+ #endif
+ #else
+ #define SPP_HAVE_MREMAP 0
+ #endif
+#endif
+
+#ifndef SPP_MALLOC_FAILURE_ACTION
+ // ENOMEM = 12
+ #define SPP_MALLOC_FAILURE_ACTION errno = 12
+#endif
+
+
+#ifndef SPP_DEFAULT_GRANULARITY
+ #if defined(WIN32)
+ #define SPP_DEFAULT_GRANULARITY (0) /* 0 means to compute in init_mparams */
+ #else
+ #define SPP_DEFAULT_GRANULARITY ((size_t)64U * (size_t)1024U)
+ #endif
+#endif
+
+#ifndef SPP_DEFAULT_TRIM_THRESHOLD
+ #define SPP_DEFAULT_TRIM_THRESHOLD ((size_t)2U * (size_t)1024U * (size_t)1024U)
+#endif
+
+#ifndef SPP_DEFAULT_MMAP_THRESHOLD
+ #if SPP_HAVE_MMAP
+ #define SPP_DEFAULT_MMAP_THRESHOLD ((size_t)256U * (size_t)1024U)
+ #else
+ #define SPP_DEFAULT_MMAP_THRESHOLD spp_max_size_t
+ #endif
+#endif
+
+#ifndef SPP_MAX_RELEASE_CHECK_RATE
+ #if SPP_HAVE_MMAP
+ #define SPP_MAX_RELEASE_CHECK_RATE 4095
+ #else
+ #define SPP_MAX_RELEASE_CHECK_RATE spp_max_size_t
+ #endif
+#endif
+
+#ifndef SPP_USE_BUILTIN_FFS
+ #define SPP_USE_BUILTIN_FFS 0
+#endif
+
+#ifndef SPP_USE_DEV_RANDOM
+ #define SPP_USE_DEV_RANDOM 0
+#endif
+
+#ifndef SPP_NO_SEGMENT_TRAVERSAL
+ #define SPP_NO_SEGMENT_TRAVERSAL 0
+#endif
+
+
+
+/*------------------------------ internal #includes ---------------------- */
+
+#ifdef _MSC_VER
+ #pragma warning( disable : 4146 ) /* no "unsigned" warnings */
+#endif
+#ifndef SPP_LACKS_ERRNO_H
+ #include <errno.h> /* for SPP_MALLOC_FAILURE_ACTION */
+#endif
+
+#ifdef SPP_DEBUG
+ #if SPP_ABORT_ON_ASSERT_FAILURE
+ #undef assert
+ #define assert(x) if(!(x)) SPP_ABORT
+ #else
+ #include <assert.h>
+ #endif
+#else
+ #ifndef assert
+ #define assert(x)
+ #endif
+ #define SPP_DEBUG 0
+#endif
+
+#if !defined(WIN32) && !defined(SPP_LACKS_TIME_H)
+ #include <time.h> /* for magic initialization */
+#endif
+
+#ifndef SPP_LACKS_STDLIB_H
+ #include <stdlib.h> /* for abort() */
+#endif
+
+#ifndef SPP_LACKS_STRING_H
+ #include <string.h> /* for memset etc */
+#endif
+
+#if SPP_USE_BUILTIN_FFS
+ #ifndef SPP_LACKS_STRINGS_H
+ #include <strings.h> /* for ffs */
+ #endif
+#endif
+
+#if SPP_HAVE_MMAP
+ #ifndef SPP_LACKS_SYS_MMAN_H
+ /* On some versions of linux, mremap decl in mman.h needs __USE_GNU set */
+ #if (defined(linux) && !defined(__USE_GNU))
+ #define __USE_GNU 1
+ #include <sys/mman.h> /* for mmap */
+ #undef __USE_GNU
+ #else
+ #include <sys/mman.h> /* for mmap */
+ #endif
+ #endif
+ #ifndef SPP_LACKS_FCNTL_H
+ #include <fcntl.h>
+ #endif
+#endif
+
+#ifndef SPP_LACKS_UNISTD_H
+ #include <unistd.h> /* for sbrk, sysconf */
+#else
+ #if !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
+ extern void* sbrk(ptrdiff_t);
+ #endif
+#endif
+
+#include <new>
+
+namespace spp
+{
+
+/* Declarations for bit scanning on win32 */
+#if defined(_MSC_VER) && _MSC_VER>=1300
+ #ifndef BitScanForward /* Try to avoid pulling in WinNT.h */
+ extern "C" {
+ unsigned char _BitScanForward(unsigned long *index, unsigned long mask);
+ unsigned char _BitScanReverse(unsigned long *index, unsigned long mask);
+ }
+
+ #define BitScanForward _BitScanForward
+ #define BitScanReverse _BitScanReverse
+ #pragma intrinsic(_BitScanForward)
+ #pragma intrinsic(_BitScanReverse)
+ #endif /* BitScanForward */
+#endif /* defined(_MSC_VER) && _MSC_VER>=1300 */
+
+#ifndef WIN32
+ #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 /* use supplied emulation of getpagesize */
+ #define malloc_getpagesize getpagesize()
+ #else
+ #ifndef SPP_LACKS_SYS_PARAM_H
+ #include <sys/param.h>
+ #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 /* just guess */
+ #define malloc_getpagesize ((size_t)4096U)
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+ #endif
+#endif
+
+/* -------------------------- MMAP preliminaries ------------------------- */
+
+/*
+ If SPP_HAVE_MORECORE or SPP_HAVE_MMAP are false, we just define calls and
+ checks to fail so compiler optimizer can delete code rather than
+ using so many "#if"s.
+*/
+
+
+/* MMAP must return mfail on failure */
+static void *mfail = (void*)spp_max_size_t;
+static char *cmfail = (char*)mfail;
+
+#if SPP_HAVE_MMAP
+
+#ifndef WIN32
+ #define SPP_MUNMAP_DEFAULT(a, s) munmap((a), (s))
+ #define SPP_MMAP_PROT (PROT_READ | PROT_WRITE)
+ #if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
+ #define MAP_ANONYMOUS MAP_ANON
+ #endif
+
+ #ifdef MAP_ANONYMOUS
+ #define SPP_MMAP_FLAGS (MAP_PRIVATE | MAP_ANONYMOUS)
+ #define SPP_MMAP_DEFAULT(s) mmap(0, (s), SPP_MMAP_PROT, SPP_MMAP_FLAGS, -1, 0)
+ #else /* MAP_ANONYMOUS */
+ /*
+ Nearly all versions of mmap support MAP_ANONYMOUS, so the following
+ is unlikely to be needed, but is supplied just in case.
+ */
+ #define SPP_MMAP_FLAGS (MAP_PRIVATE)
+ static int dev_zero_fd = -1; /* Cached file descriptor for /dev/zero. */
+ void SPP_MMAP_DEFAULT(size_t s)
+ {
+ if (dev_zero_fd < 0)
+ dev_zero_fd = open("/dev/zero", O_RDWR);
+ mmap(0, s, SPP_MMAP_PROT, SPP_MMAP_FLAGS, dev_zero_fd, 0);
+ }
+ #endif /* MAP_ANONYMOUS */
+
+ #define SPP_DIRECT_MMAP_DEFAULT(s) SPP_MMAP_DEFAULT(s)
+
+#else /* WIN32 */
+
+ /* Win32 MMAP via VirtualAlloc */
+ static SPP_FORCEINLINE void* win32mmap(size_t size)
+ {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
+ return (ptr != 0) ? ptr : mfail;
+ }
+
+ /* For direct MMAP, use MEM_TOP_DOWN to minimize interference */
+ static SPP_FORCEINLINE void* win32direct_mmap(size_t size)
+ {
+ void* ptr = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT | MEM_TOP_DOWN,
+ PAGE_READWRITE);
+ return (ptr != 0) ? ptr : mfail;
+ }
+
+ /* This function supports releasing coalesed segments */
+ static SPP_FORCEINLINE int win32munmap(void* ptr, size_t size)
+ {
+ MEMORY_BASIC_INFORMATION minfo;
+ char* cptr = (char*)ptr;
+ while (size)
+ {
+ if (VirtualQuery(cptr, &minfo, sizeof(minfo)) == 0)
+ return -1;
+ if (minfo.BaseAddress != cptr || minfo.AllocationBase != cptr ||
+ minfo.State != MEM_COMMIT || minfo.RegionSize > size)
+ return -1;
+ if (VirtualFree(cptr, 0, MEM_RELEASE) == 0)
+ return -1;
+ cptr += minfo.RegionSize;
+ size -= minfo.RegionSize;
+ }
+ return 0;
+ }
+
+ #define SPP_MMAP_DEFAULT(s) win32mmap(s)
+ #define SPP_MUNMAP_DEFAULT(a, s) win32munmap((a), (s))
+ #define SPP_DIRECT_MMAP_DEFAULT(s) win32direct_mmap(s)
+#endif /* WIN32 */
+#endif /* SPP_HAVE_MMAP */
+
+#if SPP_HAVE_MREMAP
+ #ifndef WIN32
+ #define SPP_MREMAP_DEFAULT(addr, osz, nsz, mv) mremap((addr), (osz), (nsz), (mv))
+ #endif
+#endif
+
+/**
+ * Define SPP_CALL_MMAP/SPP_CALL_MUNMAP/SPP_CALL_DIRECT_MMAP
+ */
+#if SPP_HAVE_MMAP
+ #define USE_MMAP_BIT 1
+
+ #ifdef SPP_MMAP
+ #define SPP_CALL_MMAP(s) SPP_MMAP(s)
+ #else
+ #define SPP_CALL_MMAP(s) SPP_MMAP_DEFAULT(s)
+ #endif
+
+ #ifdef SPP_MUNMAP
+ #define SPP_CALL_MUNMAP(a, s) SPP_MUNMAP((a), (s))
+ #else
+ #define SPP_CALL_MUNMAP(a, s) SPP_MUNMAP_DEFAULT((a), (s))
+ #endif
+
+ #ifdef SPP_DIRECT_MMAP
+ #define SPP_CALL_DIRECT_MMAP(s) SPP_DIRECT_MMAP(s)
+ #else
+ #define SPP_CALL_DIRECT_MMAP(s) SPP_DIRECT_MMAP_DEFAULT(s)
+ #endif
+
+#else /* SPP_HAVE_MMAP */
+ #define USE_MMAP_BIT 0
+
+ #define SPP_MMAP(s) mfail
+ #define SPP_MUNMAP(a, s) (-1)
+ #define SPP_DIRECT_MMAP(s) mfail
+ #define SPP_CALL_DIRECT_MMAP(s) SPP_DIRECT_MMAP(s)
+ #define SPP_CALL_MMAP(s) SPP_MMAP(s)
+ #define SPP_CALL_MUNMAP(a, s) SPP_MUNMAP((a), (s))
+#endif
+
+/**
+ * Define SPP_CALL_MREMAP
+ */
+#if SPP_HAVE_MMAP && SPP_HAVE_MREMAP
+ #ifdef MREMAP
+ #define SPP_CALL_MREMAP(addr, osz, nsz, mv) MREMAP((addr), (osz), (nsz), (mv))
+ #else
+ #define SPP_CALL_MREMAP(addr, osz, nsz, mv) SPP_MREMAP_DEFAULT((addr), (osz), (nsz), (mv))
+ #endif
+#else
+ #define SPP_CALL_MREMAP(addr, osz, nsz, mv) mfail
+#endif
+
+/* mstate bit set if continguous morecore disabled or failed */
+static const unsigned USE_NONCONTIGUOUS_BIT = 4U;
+
+/* segment bit set in create_mspace_with_base */
+static const unsigned EXTERN_BIT = 8U;
+
+
+/* --------------------------- flags ------------------------ */
+
+static const unsigned PINUSE_BIT = 1;
+static const unsigned CINUSE_BIT = 2;
+static const unsigned FLAG4_BIT = 4;
+static const unsigned INUSE_BITS = (PINUSE_BIT | CINUSE_BIT);
+static const unsigned FLAG_BITS = (PINUSE_BIT | CINUSE_BIT | FLAG4_BIT);
+
+/* ------------------- Chunks sizes and alignments ----------------------- */
+
+#if SPP_FOOTERS
+ static const unsigned CHUNK_OVERHEAD = 2 * sizeof(size_t);
+#else
+ static const unsigned CHUNK_OVERHEAD = sizeof(size_t);
+#endif
+
+/* MMapped chunks need a second word of overhead ... */
+static const unsigned SPP_MMAP_CHUNK_OVERHEAD = 2 * sizeof(size_t);
+
+/* ... and additional padding for fake next-chunk at foot */
+static const unsigned SPP_MMAP_FOOT_PAD = 4 * sizeof(size_t);
+
+// ===============================================================================
+struct malloc_chunk_header
+{
+ void set_size_and_pinuse_of_free_chunk(size_t s)
+ {
+ _head = s | PINUSE_BIT;
+ set_foot(s);
+ }
+
+ void set_foot(size_t s)
+ {
+ ((malloc_chunk_header *)((char*)this + s))->_prev_foot = s;
+ }
+
+ // extraction of fields from head words
+ bool cinuse() const { return !!(_head & CINUSE_BIT); }
+ bool pinuse() const { return !!(_head & PINUSE_BIT); }
+ bool flag4inuse() const { return !!(_head & FLAG4_BIT); }
+ bool is_inuse() const { return (_head & INUSE_BITS) != PINUSE_BIT; }
+ bool is_mmapped() const { return (_head & INUSE_BITS) == 0; }
+
+ size_t chunksize() const { return _head & ~(FLAG_BITS); }
+
+ void clear_pinuse() { _head &= ~PINUSE_BIT; }
+ void set_flag4() { _head |= FLAG4_BIT; }
+ void clear_flag4() { _head &= ~FLAG4_BIT; }
+
+ // Treat space at ptr +/- offset as a chunk
+ malloc_chunk_header * chunk_plus_offset(size_t s)
+ {
+ return (malloc_chunk_header *)((char*)this + s);
+ }
+ malloc_chunk_header * chunk_minus_offset(size_t s)
+ {
+ return (malloc_chunk_header *)((char*)this - s);
+ }
+
+ // Ptr to next or previous physical malloc_chunk.
+ malloc_chunk_header * next_chunk()
+ {
+ return (malloc_chunk_header *)((char*)this + (_head & ~FLAG_BITS));
+ }
+ malloc_chunk_header * prev_chunk()
+ {
+ return (malloc_chunk_header *)((char*)this - (_prev_foot));
+ }
+
+ // extract next chunk's pinuse bit
+ size_t next_pinuse() { return next_chunk()->_head & PINUSE_BIT; }
+
+ size_t _prev_foot; // Size of previous chunk (if free).
+ size_t _head; // Size and inuse bits.
+};
+
+// ===============================================================================
+struct malloc_chunk : public malloc_chunk_header
+{
+ // Set size, pinuse bit, foot, and clear next pinuse
+ void set_free_with_pinuse(size_t s, malloc_chunk* n)
+ {
+ n->clear_pinuse();
+ set_size_and_pinuse_of_free_chunk(s);
+ }
+
+ // Get the internal overhead associated with chunk p
+ size_t overhead_for() { return is_mmapped() ? SPP_MMAP_CHUNK_OVERHEAD : CHUNK_OVERHEAD; }
+
+ // Return true if malloced space is not necessarily cleared
+ bool calloc_must_clear()
+ {
+#if SPP_MMAP_CLEARS
+ return !is_mmapped();
+#else
+ return true;
+#endif
+ }
+
+ struct malloc_chunk* _fd; // double links -- used only if free.
+ struct malloc_chunk* _bk;
+};
+
+static const unsigned MCHUNK_SIZE = sizeof(malloc_chunk);
+
+/* The smallest size we can malloc is an aligned minimal chunk */
+static const unsigned MIN_CHUNK_SIZE = (MCHUNK_SIZE + spp_chunk_align_mask) & ~spp_chunk_align_mask;
+
+typedef malloc_chunk mchunk;
+typedef malloc_chunk* mchunkptr;
+typedef malloc_chunk_header *hchunkptr;
+typedef malloc_chunk* sbinptr; // The type of bins of chunks
+typedef unsigned int bindex_t; // Described below
+typedef unsigned int binmap_t; // Described below
+typedef unsigned int flag_t; // The type of various bit flag sets
+
+// conversion from malloc headers to user pointers, and back
+static SPP_FORCEINLINE void *chunk2mem(const void *p) { return (void *)((char *)p + 2 * sizeof(size_t)); }
+static SPP_FORCEINLINE mchunkptr mem2chunk(const void *mem) { return (mchunkptr)((char *)mem - 2 * sizeof(size_t)); }
+
+// chunk associated with aligned address A
+static SPP_FORCEINLINE mchunkptr align_as_chunk(char *A) { return (mchunkptr)(A + align_offset(chunk2mem(A))); }
+
+// Bounds on request (not chunk) sizes.
+static const unsigned MAX_REQUEST = (-MIN_CHUNK_SIZE) << 2;
+static const unsigned MIN_REQUEST = MIN_CHUNK_SIZE - CHUNK_OVERHEAD - 1;
+
+// pad request bytes into a usable size
+static SPP_FORCEINLINE size_t pad_request(size_t req)
+{
+ return (req + CHUNK_OVERHEAD + spp_chunk_align_mask) & ~spp_chunk_align_mask;
+}
+
+// pad request, checking for minimum (but not maximum)
+static SPP_FORCEINLINE size_t request2size(size_t req)
+{
+ return req < MIN_REQUEST ? MIN_CHUNK_SIZE : pad_request(req);
+}
+
+
+/* ------------------ Operations on head and foot fields ----------------- */
+
+/*
+ The head field of a chunk is or'ed with PINUSE_BIT when previous
+ adjacent chunk in use, and or'ed with CINUSE_BIT if this chunk is in
+ use, unless mmapped, in which case both bits are cleared.
+
+ FLAG4_BIT is not used by this malloc, but might be useful in extensions.
+*/
+
+// Head value for fenceposts
+static const unsigned FENCEPOST_HEAD = INUSE_BITS | sizeof(size_t);
+
+
+/* ---------------------- Overlaid data structures ----------------------- */
+
+/*
+ When chunks are not in use, they are treated as nodes of either
+ lists or trees.
+
+ "Small" 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 |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Larger chunks are kept in a form of bitwise digital trees (aka
+ tries) keyed on chunksizes. Because malloc_tree_chunks are only for
+ free chunks greater than 256 bytes, their size doesn't impose any
+ constraints on user chunk sizes. Each node looks like:
+
+ chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Size of previous chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `head:' | Size of chunk, in bytes |P|
+ mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Forward pointer to next chunk of same size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Back pointer to previous chunk of same size |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to left child (child[0]) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to right child (child[1]) |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Pointer to parent |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | bin index of this chunk |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ | Unused space .
+ . |
+nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ `foot:' | Size of chunk, in bytes |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ Each tree holding treenodes is a tree of unique chunk sizes. Chunks
+ of the same size are arranged in a circularly-linked list, with only
+ the oldest chunk (the next to be used, in our FIFO ordering)
+ actually in the tree. (Tree members are distinguished by a non-null
+ parent pointer.) If a chunk with the same size an an existing node
+ is inserted, it is linked off the existing node using pointers that
+ work in the same way as fd/bk pointers of small chunks.
+
+ Each tree contains a power of 2 sized range of chunk sizes (the
+ smallest is 0x100 <= x < 0x180), which is is divided in half at each
+ tree level, with the chunks in the smaller half of the range (0x100
+ <= x < 0x140 for the top nose) in the left subtree and the larger
+ half (0x140 <= x < 0x180) in the right subtree. This is, of course,
+ done by inspecting individual bits.
+
+ Using these rules, each node's left subtree contains all smaller
+ sizes than its right subtree. However, the node at the root of each
+ subtree has no particular ordering relationship to either. (The
+ dividing line between the subtree sizes is based on trie relation.)
+ If we remove the last chunk of a given size from the interior of the
+ tree, we need to replace it with a leaf node. The tree ordering
+ rules permit a node to be replaced by any leaf below it.
+
+ The smallest chunk in a tree (a common operation in a best-fit
+ allocator) can be found by walking a path to the leftmost leaf in
+ the tree. Unlike a usual binary tree, where we follow left child
+ pointers until we reach a null, here we follow the right child
+ pointer any time the left one is null, until we reach a leaf with
+ both child pointers null. The smallest chunk in the tree will be
+ somewhere along that path.
+
+ The worst case number of steps to add, find, or remove a node is
+ bounded by the number of bits differentiating chunks within
+ bins. Under current bin calculations, this ranges from 6 up to 21
+ (for 32 bit sizes) or up to 53 (for 64 bit sizes). The typical case
+ is of course much better.
+*/
+
+// ===============================================================================
+struct malloc_tree_chunk : public malloc_chunk_header
+{
+ malloc_tree_chunk *leftmost_child()
+ {
+ return _child[0] ? _child[0] : _child[1];
+ }
+
+
+ malloc_tree_chunk* _fd;
+ malloc_tree_chunk* _bk;
+
+ malloc_tree_chunk* _child[2];
+ malloc_tree_chunk* _parent;
+ bindex_t _index;
+};
+
+typedef malloc_tree_chunk tchunk;
+typedef malloc_tree_chunk* tchunkptr;
+typedef malloc_tree_chunk* tbinptr; // The type of bins of trees
+
+/* ----------------------------- Segments -------------------------------- */
+
+/*
+ Each malloc space may include non-contiguous segments, held in a
+ list headed by an embedded malloc_segment record representing the
+ top-most space. Segments also include flags holding properties of
+ the space. Large chunks that are directly allocated by mmap are not
+ included in this list. They are instead independently created and
+ destroyed without otherwise keeping track of them.
+
+ Segment management mainly comes into play for spaces allocated by
+ MMAP. Any call to MMAP might or might not return memory that is
+ adjacent to an existing segment. MORECORE normally contiguously
+ extends the current space, so this space is almost always adjacent,
+ which is simpler and faster to deal with. (This is why MORECORE is
+ used preferentially to MMAP when both are available -- see
+ sys_alloc.) When allocating using MMAP, we don't use any of the
+ hinting mechanisms (inconsistently) supported in various
+ implementations of unix mmap, or distinguish reserving from
+ committing memory. Instead, we just ask for space, and exploit
+ contiguity when we get it. It is probably possible to do
+ better than this on some systems, but no general scheme seems
+ to be significantly better.
+
+ Management entails a simpler variant of the consolidation scheme
+ used for chunks to reduce fragmentation -- new adjacent memory is
+ normally prepended or appended to an existing segment. However,
+ there are limitations compared to chunk consolidation that mostly
+ reflect the fact that segment processing is relatively infrequent
+ (occurring only when getting memory from system) and that we
+ don't expect to have huge numbers of segments:
+
+ * Segments are not indexed, so traversal requires linear scans. (It
+ would be possible to index these, but is not worth the extra
+ overhead and complexity for most programs on most platforms.)
+ * New segments are only appended to old ones when holding top-most
+ memory; if they cannot be prepended to others, they are held in
+ different segments.
+
+ Except for the top-most segment of an mstate, each segment record
+ is kept at the tail of its segment. Segments are added by pushing
+ segment records onto the list headed by &mstate.seg for the
+ containing mstate.
+
+ Segment flags control allocation/merge/deallocation policies:
+ * If EXTERN_BIT set, then we did not allocate this segment,
+ and so should not try to deallocate or merge with others.
+ (This currently holds only for the initial segment passed
+ into create_mspace_with_base.)
+ * If USE_MMAP_BIT set, the segment may be merged with
+ other surrounding mmapped segments and trimmed/de-allocated
+ using munmap.
+ * If neither bit is set, then the segment was obtained using
+ MORECORE so can be merged with surrounding MORECORE'd segments
+ and deallocated/trimmed using MORECORE with negative arguments.
+*/
+
+// ===============================================================================
+struct malloc_segment
+{
+ bool is_mmapped_segment() { return !!(_sflags & USE_MMAP_BIT); }
+ bool is_extern_segment() { return !!(_sflags & EXTERN_BIT); }
+
+ char* _base; // base address
+ size_t _size; // allocated size
+ malloc_segment* _next; // ptr to next segment
+ flag_t _sflags; // mmap and extern flag
+};
+
+typedef malloc_segment msegment;
+typedef malloc_segment* msegmentptr;
+
+/* ------------- Malloc_params ------------------- */
+
+/*
+ malloc_params holds global properties, including those that can be
+ dynamically set using mallopt. There is a single instance, mparams,
+ initialized in init_mparams. Note that the non-zeroness of "magic"
+ also serves as an initialization flag.
+*/
+
+// ===============================================================================
+struct malloc_params
+{
+ malloc_params() : _magic(0) {}
+
+ void ensure_initialization()
+ {
+ if (!_magic)
+ _init();
+ }
+
+ SPP_IMPL int change(int param_number, int value);
+
+ size_t page_align(size_t sz)
+ {
+ return (sz + (_page_size - 1)) & ~(_page_size - 1);
+ }
+
+ size_t granularity_align(size_t sz)
+ {
+ return (sz + (_granularity - 1)) & ~(_granularity - 1);
+ }
+
+ bool is_page_aligned(char *S)
+ {
+ return ((size_t)S & (_page_size - 1)) == 0;
+ }
+
+ SPP_IMPL int _init();
+
+ size_t _magic;
+ size_t _page_size;
+ size_t _granularity;
+ size_t _mmap_threshold;
+ size_t _trim_threshold;
+ flag_t _default_mflags;
+};
+
+static malloc_params mparams;
+
+/* ---------------------------- malloc_state ----------------------------- */
+
+/*
+ A malloc_state holds all of the bookkeeping for a space.
+ The main fields are:
+
+ Top
+ The topmost chunk of the currently active segment. Its size is
+ cached in topsize. The actual size of topmost space is
+ topsize+TOP_FOOT_SIZE, which includes space reserved for adding
+ fenceposts and segment records if necessary when getting more
+ space from the system. The size at which to autotrim top is
+ cached from mparams in trim_check, except that it is disabled if
+ an autotrim fails.
+
+ Designated victim (dv)
+ This is the preferred chunk for servicing small requests that
+ don't have exact fits. It is normally the chunk split off most
+ recently to service another small request. Its size is cached in
+ dvsize. The link fields of this chunk are not maintained since it
+ is not kept in a bin.
+
+ SmallBins
+ An array of bin headers for free chunks. These bins hold chunks
+ with sizes less than MIN_LARGE_SIZE bytes. Each bin contains
+ chunks of all the same size, spaced 8 bytes apart. To simplify
+ use in double-linked lists, each bin header acts as a malloc_chunk
+ pointing to the real first node, if it exists (else pointing to
+ itself). This avoids special-casing for headers. But to avoid
+ waste, we allocate only the fd/bk pointers of bins, and then use
+ repositioning tricks to treat these as the fields of a chunk.
+
+ TreeBins
+ Treebins are pointers to the roots of trees holding a range of
+ sizes. There are 2 equally spaced treebins for each power of two
+ from TREE_SHIFT to TREE_SHIFT+16. The last bin holds anything
+ larger.
+
+ Bin maps
+ There is one bit map for small bins ("smallmap") and one for
+ treebins ("treemap). Each bin sets its bit when non-empty, and
+ clears the bit when empty. Bit operations are then used to avoid
+ bin-by-bin searching -- nearly all "search" is done without ever
+ looking at bins that won't be selected. The bit maps
+ conservatively use 32 bits per map word, even if on 64bit system.
+ For a good description of some of the bit-based techniques used
+ here, see Henry S. Warren Jr's book "Hacker's Delight" (and
+ supplement at http://hackersdelight.org/). Many of these are
+ intended to reduce the branchiness of paths through malloc etc, as
+ well as to reduce the number of memory locations read or written.
+
+ Segments
+ A list of segments headed by an embedded malloc_segment record
+ representing the initial space.
+
+ Address check support
+ The least_addr field is the least address ever obtained from
+ MORECORE or MMAP. Attempted frees and reallocs of any address less
+ than this are trapped (unless SPP_INSECURE is defined).
+
+ Magic tag
+ A cross-check field that should always hold same value as mparams._magic.
+
+ Max allowed footprint
+ The maximum allowed bytes to allocate from system (zero means no limit)
+
+ Flags
+ Bits recording whether to use MMAP, locks, or contiguous MORECORE
+
+ Statistics
+ Each space keeps track of current and maximum system memory
+ obtained via MORECORE or MMAP.
+
+ Trim support
+ Fields holding the amount of unused topmost memory that should trigger
+ trimming, and a counter to force periodic scanning to release unused
+ non-topmost segments.
+
+ Extension support
+ A void* pointer and a size_t field that can be used to help implement
+ extensions to this malloc.
+*/
+
+
+// ================================================================================
+class malloc_state
+{
+public:
+ /* ----------------------- _malloc, _free, etc... --- */
+ SPP_FORCEINLINE void* _malloc(size_t bytes);
+ SPP_FORCEINLINE void _free(mchunkptr p);
+
+
+ /* ------------------------ Relays to internal calls to malloc/free from realloc, memalign etc */
+ void *internal_malloc(size_t b) { return mspace_malloc(this, b); }
+ void internal_free(void *mem) { mspace_free(this, mem); }
+
+ /* ------------------------ ----------------------- */
+
+ SPP_IMPL void init_top(mchunkptr p, size_t psize);
+ SPP_IMPL void init_bins();
+ SPP_IMPL void init(char* tbase, size_t tsize);
+
+ /* ------------------------ System alloc/dealloc -------------------------- */
+ SPP_IMPL void* sys_alloc(size_t nb);
+ SPP_IMPL size_t release_unused_segments();
+ SPP_IMPL int sys_trim(size_t pad);
+ SPP_IMPL void dispose_chunk(mchunkptr p, size_t psize);
+
+ /* ----------------------- Internal support for realloc, memalign, etc --- */
+ SPP_IMPL mchunkptr try_realloc_chunk(mchunkptr p, size_t nb, int can_move);
+ SPP_IMPL void* internal_memalign(size_t alignment, size_t bytes);
+ SPP_IMPL void** ialloc(size_t n_elements, size_t* sizes, int opts, void* chunks[]);
+ SPP_IMPL size_t internal_bulk_free(void* array[], size_t nelem);
+ SPP_IMPL void internal_inspect_all(void(*handler)(void *start, void *end,
+ size_t used_bytes, void* callback_arg),
+ void* arg);
+
+ /* -------------------------- system alloc setup (Operations on mflags) ----- */
+ bool use_lock() const { return false; }
+ void enable_lock() {}
+ void set_lock(int) {}
+ void disable_lock() {}
+
+ bool use_mmap() const { return !!(_mflags & USE_MMAP_BIT); }
+ void enable_mmap() { _mflags |= USE_MMAP_BIT; }
+
+#if SPP_HAVE_MMAP
+ void disable_mmap() { _mflags &= ~USE_MMAP_BIT; }
+#else
+ void disable_mmap() {}
+#endif
+
+ /* ----------------------- Runtime Check Support ------------------------- */
+
+ /*
+ For security, the main invariant is that malloc/free/etc never
+ writes to a static address other than malloc_state, unless static
+ malloc_state itself has been corrupted, which cannot occur via
+ malloc (because of these checks). In essence this means that we
+ believe all pointers, sizes, maps etc held in malloc_state, but
+ check all of those linked or offsetted from other embedded data
+ structures. These checks are interspersed with main code in a way
+ that tends to minimize their run-time cost.
+
+ When SPP_FOOTERS is defined, in addition to range checking, we also
+ verify footer fields of inuse chunks, which can be used guarantee
+ that the mstate controlling malloc/free is intact. This is a
+ streamlined version of the approach described by William Robertson
+ et al in "Run-time Detection of Heap-based Overflows" LISA'03
+ http://www.usenix.org/events/lisa03/tech/robertson.html The footer
+ of an inuse chunk holds the xor of its mstate and a random seed,
+ that is checked upon calls to free() and realloc(). This is
+ (probabalistically) unguessable from outside the program, but can be
+ computed by any code successfully malloc'ing any chunk, so does not
+ itself provide protection against code that has already broken
+ security through some other means. Unlike Robertson et al, we
+ always dynamically check addresses of all offset chunks (previous,
+ next, etc). This turns out to be cheaper than relying on hashes.
+ */
+
+
+#if !SPP_INSECURE
+ // Check if address a is at least as high as any from MORECORE or MMAP
+ bool ok_address(void *a) const { return (char *)a >= _least_addr; }
+
+ // Check if address of next chunk n is higher than base chunk p
+ static bool ok_next(void *p, void *n) { return p < n; }
+
+ // Check if p has inuse status
+ static bool ok_inuse(mchunkptr p) { return p->is_inuse(); }
+
+ // Check if p has its pinuse bit on
+ static bool ok_pinuse(mchunkptr p) { return p->pinuse(); }
+
+ // Check if (alleged) mstate m has expected magic field
+ bool ok_magic() const { return _magic == mparams._magic; }
+
+ // In gcc, use __builtin_expect to minimize impact of checks
+ #if defined(__GNUC__) && __GNUC__ >= 3
+ static bool rtcheck(bool e) { return __builtin_expect(e, 1); }
+ #else
+ static bool rtcheck(bool e) { return e; }
+ #endif
+#else
+ static bool ok_address(void *) { return true; }
+ static bool ok_next(void *, void *) { return true; }
+ static bool ok_inuse(mchunkptr) { return true; }
+ static bool ok_pinuse(mchunkptr) { return true; }
+ static bool ok_magic() { return true; }
+ static bool rtcheck(bool) { return true; }
+#endif
+
+ bool is_initialized() const { return _top != 0; }
+
+ bool use_noncontiguous() const { return !!(_mflags & USE_NONCONTIGUOUS_BIT); }
+ void disable_contiguous() { _mflags |= USE_NONCONTIGUOUS_BIT; }
+
+ // Return segment holding given address
+ msegmentptr segment_holding(char* addr) const
+ {
+ msegmentptr sp = (msegmentptr)&_seg;
+ for (;;)
+ {
+ if (addr >= sp->_base && addr < sp->_base + sp->_size)
+ return sp;
+ if ((sp = sp->_next) == 0)
+ return 0;
+ }
+ }
+
+ // Return true if segment contains a segment link
+ int has_segment_link(msegmentptr ss) const
+ {
+ msegmentptr sp = (msegmentptr)&_seg;
+ for (;;)
+ {
+ if ((char*)sp >= ss->_base && (char*)sp < ss->_base + ss->_size)
+ return 1;
+ if ((sp = sp->_next) == 0)
+ return 0;
+ }
+ }
+
+ bool should_trim(size_t s) const { return s > _trim_check; }
+
+ /* -------------------------- Debugging setup ---------------------------- */
+
+#if ! SPP_DEBUG
+ void check_free_chunk(mchunkptr) {}
+ void check_inuse_chunk(mchunkptr) {}
+ void check_malloced_chunk(void*, size_t) {}
+ void check_mmapped_chunk(mchunkptr) {}
+ void check_malloc_state() {}
+ void check_top_chunk(mchunkptr) {}
+#else /* SPP_DEBUG */
+ void check_free_chunk(mchunkptr p) { do_check_free_chunk(p); }
+ void check_inuse_chunk(mchunkptr p) { do_check_inuse_chunk(p); }
+ void check_malloced_chunk(void* p, size_t s) { do_check_malloced_chunk(p, s); }
+ void check_mmapped_chunk(mchunkptr p) { do_check_mmapped_chunk(p); }
+ void check_malloc_state() { do_check_malloc_state(); }
+ void check_top_chunk(mchunkptr p) { do_check_top_chunk(p); }
+
+ void do_check_any_chunk(mchunkptr p) const;
+ void do_check_top_chunk(mchunkptr p) const;
+ void do_check_mmapped_chunk(mchunkptr p) const;
+ void do_check_inuse_chunk(mchunkptr p) const;
+ void do_check_free_chunk(mchunkptr p) const;
+ void do_check_malloced_chunk(void* mem, size_t s) const;
+ void do_check_tree(tchunkptr t);
+ void do_check_treebin(bindex_t i);
+ void do_check_smallbin(bindex_t i);
+ void do_check_malloc_state();
+ int bin_find(mchunkptr x);
+ size_t traverse_and_check();
+#endif
+
+private:
+
+ /* ---------------------------- Indexing Bins ---------------------------- */
+
+ static bool is_small(size_t s) { return (s >> SMALLBIN_SHIFT) < NSMALLBINS; }
+ static bindex_t small_index(size_t s) { return (bindex_t)(s >> SMALLBIN_SHIFT); }
+ static size_t small_index2size(size_t i) { return i << SMALLBIN_SHIFT; }
+ static bindex_t MIN_SMALL_INDEX() { return small_index(MIN_CHUNK_SIZE); }
+
+ // assign tree index for size S to variable I. Use x86 asm if possible
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+ SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S)
+ {
+ unsigned int X = S >> TREEBIN_SHIFT;
+ if (X == 0)
+ return 0;
+ else if (X > 0xFFFF)
+ return NTREEBINS - 1;
+
+ unsigned int K = (unsigned) sizeof(X) * __CHAR_BIT__ - 1 - (unsigned) __builtin_clz(X);
+ return (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1)));
+ }
+
+#elif defined (__INTEL_COMPILER)
+ SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S)
+ {
+ size_t X = S >> TREEBIN_SHIFT;
+ if (X == 0)
+ return 0;
+ else if (X > 0xFFFF)
+ return NTREEBINS - 1;
+
+ unsigned int K = _bit_scan_reverse(X);
+ return (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1)));
+ }
+
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+ SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S)
+ {
+ size_t X = S >> TREEBIN_SHIFT;
+ if (X == 0)
+ return 0;
+ else if (X > 0xFFFF)
+ return NTREEBINS - 1;
+
+ unsigned int K;
+ _BitScanReverse((DWORD *) &K, (DWORD) X);
+ return (bindex_t)((K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1)));
+ }
+
+#else // GNUC
+ SPP_FORCEINLINE static bindex_t compute_tree_index(size_t S)
+ {
+ size_t X = S >> TREEBIN_SHIFT;
+ if (X == 0)
+ return 0;
+ else if (X > 0xFFFF)
+ return NTREEBINS - 1;
+
+ unsigned int Y = (unsigned int)X;
+ unsigned int N = ((Y - 0x100) >> 16) & 8;
+ unsigned int K = (((Y <<= N) - 0x1000) >> 16) & 4;
+ N += K;
+ N += K = (((Y <<= K) - 0x4000) >> 16) & 2;
+ K = 14 - N + ((Y <<= K) >> 15);
+ return (K << 1) + ((S >> (K + (TREEBIN_SHIFT - 1)) & 1));
+ }
+#endif
+
+ // Shift placing maximum resolved bit in a treebin at i as sign bit
+ static bindex_t leftshift_for_tree_index(bindex_t i)
+ {
+ return (i == NTREEBINS - 1) ? 0 :
+ ((spp_size_t_bitsize - 1) - ((i >> 1) + TREEBIN_SHIFT - 2));
+ }
+
+ // The size of the smallest chunk held in bin with index i
+ static bindex_t minsize_for_tree_index(bindex_t i)
+ {
+ return ((size_t)1 << ((i >> 1) + TREEBIN_SHIFT)) |
+ (((size_t)(i & 1)) << ((i >> 1) + TREEBIN_SHIFT - 1));
+ }
+
+
+ // ----------- isolate the least set bit of a bitmap
+ static binmap_t least_bit(binmap_t x) { return x & -x; }
+
+ // ----------- mask with all bits to left of least bit of x on
+ static binmap_t left_bits(binmap_t x) { return (x << 1) | -(x << 1); }
+
+ // index corresponding to given bit. Use x86 asm if possible
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+ static bindex_t compute_bit2idx(binmap_t X)
+ {
+ unsigned int J;
+ J = __builtin_ctz(X);
+ return (bindex_t)J;
+ }
+
+#elif defined (__INTEL_COMPILER)
+ static bindex_t compute_bit2idx(binmap_t X)
+ {
+ unsigned int J;
+ J = _bit_scan_forward(X);
+ return (bindex_t)J;
+ }
+
+#elif defined(_MSC_VER) && _MSC_VER>=1300
+ static bindex_t compute_bit2idx(binmap_t X)
+ {
+ unsigned int J;
+ _BitScanForward((DWORD *) &J, X);
+ return (bindex_t)J;
+ }
+
+#elif SPP_USE_BUILTIN_FFS
+ static bindex_t compute_bit2idx(binmap_t X) { return ffs(X) - 1; }
+
+#else
+ static bindex_t compute_bit2idx(binmap_t X)
+ {
+ unsigned int Y = X - 1;
+ unsigned int K = Y >> (16 - 4) & 16;
+ unsigned int N = K; Y >>= K;
+ N += K = Y >> (8 - 3) & 8; Y >>= K;
+ N += K = Y >> (4 - 2) & 4; Y >>= K;
+ N += K = Y >> (2 - 1) & 2; Y >>= K;
+ N += K = Y >> (1 - 0) & 1; Y >>= K;
+ return (bindex_t)(N + Y);
+ }
+#endif
+
+ /* ------------------------ Set up inuse chunks with or without footers ---*/
+#if !SPP_FOOTERS
+ void mark_inuse_foot(malloc_chunk_header *, size_t) {}
+#else
+ //Set foot of inuse chunk to be xor of mstate and seed
+ void mark_inuse_foot(malloc_chunk_header *p, size_t s)
+ {
+ (((mchunkptr)((char*)p + s))->prev_foot = (size_t)this ^ mparams._magic);
+ }
+#endif
+
+ void set_inuse(malloc_chunk_header *p, size_t s)
+ {
+ p->_head = (p->_head & PINUSE_BIT) | s | CINUSE_BIT;
+ ((mchunkptr)(((char*)p) + s))->_head |= PINUSE_BIT;
+ mark_inuse_foot(p, s);
+ }
+
+ void set_inuse_and_pinuse(malloc_chunk_header *p, size_t s)
+ {
+ p->_head = s | PINUSE_BIT | CINUSE_BIT;
+ ((mchunkptr)(((char*)p) + s))->_head |= PINUSE_BIT;
+ mark_inuse_foot(p, s);
+ }
+
+ void set_size_and_pinuse_of_inuse_chunk(malloc_chunk_header *p, size_t s)
+ {
+ p->_head = s | PINUSE_BIT | CINUSE_BIT;
+ mark_inuse_foot(p, s);
+ }
+
+ /* ------------------------ Addressing by index. See about smallbin repositioning --- */
+ sbinptr smallbin_at(bindex_t i) const { return (sbinptr)((char*)&_smallbins[i << 1]); }
+ tbinptr* treebin_at(bindex_t i) { return &_treebins[i]; }
+
+ /* ----------------------- bit corresponding to given index ---------*/
+ static binmap_t idx2bit(bindex_t i) { return ((binmap_t)1 << i); }
+
+ // --------------- Mark/Clear bits with given index
+ void mark_smallmap(bindex_t i) { _smallmap |= idx2bit(i); }
+ void clear_smallmap(bindex_t i) { _smallmap &= ~idx2bit(i); }
+ binmap_t smallmap_is_marked(bindex_t i) const { return _smallmap & idx2bit(i); }
+
+ void mark_treemap(bindex_t i) { _treemap |= idx2bit(i); }
+ void clear_treemap(bindex_t i) { _treemap &= ~idx2bit(i); }
+ binmap_t treemap_is_marked(bindex_t i) const { return _treemap & idx2bit(i); }
+
+ /* ------------------------ ----------------------- */
+ SPP_FORCEINLINE void insert_small_chunk(mchunkptr P, size_t S);
+ SPP_FORCEINLINE void unlink_small_chunk(mchunkptr P, size_t S);
+ SPP_FORCEINLINE void unlink_first_small_chunk(mchunkptr B, mchunkptr P, bindex_t I);
+ SPP_FORCEINLINE void replace_dv(mchunkptr P, size_t S);
+
+ /* ------------------------- Operations on trees ------------------------- */
+ SPP_FORCEINLINE void insert_large_chunk(tchunkptr X, size_t S);
+ SPP_FORCEINLINE void unlink_large_chunk(tchunkptr X);
+
+ /* ------------------------ Relays to large vs small bin operations */
+ SPP_FORCEINLINE void insert_chunk(mchunkptr P, size_t S);
+ SPP_FORCEINLINE void unlink_chunk(mchunkptr P, size_t S);
+
+ /* ----------------------- Direct-mmapping chunks ----------------------- */
+ SPP_IMPL void* mmap_alloc(size_t nb);
+ SPP_IMPL mchunkptr mmap_resize(mchunkptr oldp, size_t nb, int flags);
+
+ SPP_IMPL void reset_on_error();
+ SPP_IMPL void* prepend_alloc(char* newbase, char* oldbase, size_t nb);
+ SPP_IMPL void add_segment(char* tbase, size_t tsize, flag_t mmapped);
+
+ /* ------------------------ malloc --------------------------- */
+ SPP_IMPL void* tmalloc_large(size_t nb);
+ SPP_IMPL void* tmalloc_small(size_t nb);
+
+ /* ------------------------Bin types, widths and sizes -------- */
+ static const size_t NSMALLBINS = 32;
+ static const size_t NTREEBINS = 32;
+ static const size_t SMALLBIN_SHIFT = 3;
+ static const size_t SMALLBIN_WIDTH = 1 << SMALLBIN_SHIFT;
+ static const size_t TREEBIN_SHIFT = 8;
+ static const size_t MIN_LARGE_SIZE = 1 << TREEBIN_SHIFT;
+ static const size_t MAX_SMALL_SIZE = (MIN_LARGE_SIZE - 1);
+ static const size_t MAX_SMALL_REQUEST = (MAX_SMALL_SIZE - spp_chunk_align_mask - CHUNK_OVERHEAD);
+
+ /* ------------------------ data members --------------------------- */
+ binmap_t _smallmap;
+ binmap_t _treemap;
+ size_t _dvsize;
+ size_t _topsize;
+ char* _least_addr;
+ mchunkptr _dv;
+ mchunkptr _top;
+ size_t _trim_check;
+ size_t _release_checks;
+ size_t _magic;
+ mchunkptr _smallbins[(NSMALLBINS + 1) * 2];
+ tbinptr _treebins[NTREEBINS];
+public:
+ size_t _footprint;
+ size_t _max_footprint;
+ size_t _footprint_limit; // zero means no limit
+ flag_t _mflags;
+
+ msegment _seg;
+
+private:
+ void* _extp; // Unused but available for extensions
+ size_t _exts;
+};
+
+typedef malloc_state* mstate;
+
+/* ------------- end malloc_state ------------------- */
+
+#if SPP_FOOTERS
+static malloc_state* get_mstate_for(malloc_chunk_header *p)
+{
+ return (malloc_state*)(((mchunkptr)((char*)(p) +
+ (p->chunksize())))->prev_foot ^ mparams._magic);
+}
+#endif
+
+/* -------------------------- system alloc setup ------------------------- */
+
+
+
+// For mmap, use granularity alignment on windows, else page-align
+#ifdef WIN32
+ #define mmap_align(S) mparams.granularity_align(S)
+#else
+ #define mmap_align(S) mparams.page_align(S)
+#endif
+
+// True if segment S holds address A
+static bool segment_holds(msegmentptr S, mchunkptr A)
+{
+ return (char*)A >= S->_base && (char*)A < S->_base + S->_size;
+}
+
+/*
+ top_foot_size is padding at the end of a segment, including space
+ that may be needed to place segment records and fenceposts when new
+ noncontiguous segments are added.
+*/
+static SPP_FORCEINLINE size_t top_foot_size()
+{
+ return align_offset(chunk2mem((void *)0)) +
+ pad_request(sizeof(struct malloc_segment)) +
+ MIN_CHUNK_SIZE;
+}
+
+
+// For sys_alloc, enough padding to ensure can malloc request on success
+static SPP_FORCEINLINE size_t sys_alloc_padding()
+{
+ return top_foot_size() + SPP_MALLOC_ALIGNMENT;
+}
+
+
+#define SPP_USAGE_ERROR_ACTION(m,p) SPP_ABORT
+
+/* ---------------------------- setting mparams -------------------------- */
+
+// Initialize mparams
+int malloc_params::_init()
+{
+#ifdef NEED_GLOBAL_LOCK_INIT
+ if (malloc_global_mutex_status <= 0)
+ init_malloc_global_mutex();
+#endif
+
+ if (_magic == 0)
+ {
+ size_t magic;
+ size_t psize;
+ size_t gsize;
+
+#ifndef WIN32
+ psize = malloc_getpagesize;
+ gsize = ((SPP_DEFAULT_GRANULARITY != 0) ? SPP_DEFAULT_GRANULARITY : psize);
+#else
+ {
+ SYSTEM_INFO system_info;
+ GetSystemInfo(&system_info);
+ psize = system_info.dwPageSize;
+ gsize = ((SPP_DEFAULT_GRANULARITY != 0) ?
+ SPP_DEFAULT_GRANULARITY : system_info.dwAllocationGranularity);
+ }
+#endif
+
+ /* Sanity-check configuration:
+ size_t must be unsigned and as wide as pointer type.
+ ints must be at least 4 bytes.
+ alignment must be at least 8.
+ Alignment, min chunk size, and page size must all be powers of 2.
+ */
+ if ((sizeof(size_t) != sizeof(char*)) ||
+ (spp_max_size_t < MIN_CHUNK_SIZE) ||
+ (sizeof(int) < 4) ||
+ (SPP_MALLOC_ALIGNMENT < (size_t)8U) ||
+ ((SPP_MALLOC_ALIGNMENT & (SPP_MALLOC_ALIGNMENT - 1)) != 0) ||
+ ((MCHUNK_SIZE & (MCHUNK_SIZE - 1)) != 0) ||
+ ((gsize & (gsize - 1)) != 0) ||
+ ((psize & (psize - 1)) != 0))
+ SPP_ABORT;
+ _granularity = gsize;
+ _page_size = psize;
+ _mmap_threshold = SPP_DEFAULT_MMAP_THRESHOLD;
+ _trim_threshold = SPP_DEFAULT_TRIM_THRESHOLD;
+ _default_mflags = USE_MMAP_BIT | USE_NONCONTIGUOUS_BIT;
+
+ {
+#if SPP_USE_DEV_RANDOM
+ int fd;
+ unsigned char buf[sizeof(size_t)];
+ // Try to use /dev/urandom, else fall back on using time
+ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0 &&
+ read(fd, buf, sizeof(buf)) == sizeof(buf))
+ {
+ magic = *((size_t *) buf);
+ close(fd);
+ }
+ else
+#endif
+ {
+#ifdef WIN32
+ magic = (size_t)(GetTickCount() ^ (size_t)0x55555555U);
+#elif defined(SPP_LACKS_TIME_H)
+ magic = (size_t)&magic ^ (size_t)0x55555555U;
+#else
+ magic = (size_t)(time(0) ^ (size_t)0x55555555U);
+#endif
+ }
+ magic |= (size_t)8U; // ensure nonzero
+ magic &= ~(size_t)7U; // improve chances of fault for bad values
+ // Until memory modes commonly available, use volatile-write
+ (*(volatile size_t *)(&(_magic))) = magic;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ mallopt tuning options. SVID/XPG defines four standard parameter
+ numbers for mallopt, normally defined in malloc.h. None of these
+ are used in this malloc, so setting them has no effect. But this
+ malloc does support the following options.
+*/
+static const int m_trim_threshold = -1;
+static const int m_granularity = -2;
+static const int m_mmap_threshold = -3;
+
+// support for mallopt
+int malloc_params::change(int param_number, int value)
+{
+ size_t val;
+ ensure_initialization();
+ val = (value == -1) ? spp_max_size_t : (size_t)value;
+
+ switch (param_number)
+ {
+ case m_trim_threshold:
+ _trim_threshold = val;
+ return 1;
+
+ case m_granularity:
+ if (val >= _page_size && ((val & (val - 1)) == 0))
+ {
+ _granularity = val;
+ return 1;
+ }
+ else
+ return 0;
+
+ case m_mmap_threshold:
+ _mmap_threshold = val;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+#if SPP_DEBUG
+/* ------------------------- Debugging Support --------------------------- */
+
+// Check properties of any chunk, whether free, inuse, mmapped etc
+void malloc_state::do_check_any_chunk(mchunkptr p) const
+{
+ assert((spp_is_aligned(chunk2mem(p))) || (p->_head == FENCEPOST_HEAD));
+ assert(ok_address(p));
+}
+
+// Check properties of top chunk
+void malloc_state::do_check_top_chunk(mchunkptr p) const
+{
+ msegmentptr sp = segment_holding((char*)p);
+ size_t sz = p->_head & ~INUSE_BITS; // third-lowest bit can be set!
+ assert(sp != 0);
+ assert((spp_is_aligned(chunk2mem(p))) || (p->_head == FENCEPOST_HEAD));
+ assert(ok_address(p));
+ assert(sz == _topsize);
+ assert(sz > 0);
+ assert(sz == ((sp->_base + sp->_size) - (char*)p) - top_foot_size());
+ assert(p->pinuse());
+ assert(!p->chunk_plus_offset(sz)->pinuse());
+}
+
+// Check properties of (inuse) mmapped chunks
+void malloc_state::do_check_mmapped_chunk(mchunkptr p) const
+{
+ size_t sz = p->chunksize();
+ size_t len = (sz + (p->_prev_foot) + SPP_MMAP_FOOT_PAD);
+ assert(p->is_mmapped());
+ assert(use_mmap());
+ assert((spp_is_aligned(chunk2mem(p))) || (p->_head == FENCEPOST_HEAD));
+ assert(ok_address(p));
+ assert(!is_small(sz));
+ assert((len & (mparams._page_size - 1)) == 0);
+ assert(p->chunk_plus_offset(sz)->_head == FENCEPOST_HEAD);
+ assert(p->chunk_plus_offset(sz + sizeof(size_t))->_head == 0);
+}
+
+// Check properties of inuse chunks
+void malloc_state::do_check_inuse_chunk(mchunkptr p) const
+{
+ do_check_any_chunk(p);
+ assert(p->is_inuse());
+ assert(p->next_pinuse());
+ // If not pinuse and not mmapped, previous chunk has OK offset
+ assert(p->is_mmapped() || p->pinuse() || (mchunkptr)p->prev_chunk()->next_chunk() == p);
+ if (p->is_mmapped())
+ do_check_mmapped_chunk(p);
+}
+
+// Check properties of free chunks
+void malloc_state::do_check_free_chunk(mchunkptr p) const
+{
+ size_t sz = p->chunksize();
+ mchunkptr next = (mchunkptr)p->chunk_plus_offset(sz);
+ do_check_any_chunk(p);
+ assert(!p->is_inuse());
+ assert(!p->next_pinuse());
+ assert(!p->is_mmapped());
+ if (p != _dv && p != _top)
+ {
+ if (sz >= MIN_CHUNK_SIZE)
+ {
+ assert((sz & spp_chunk_align_mask) == 0);
+ assert(spp_is_aligned(chunk2mem(p)));
+ assert(next->_prev_foot == sz);
+ assert(p->pinuse());
+ assert(next == _top || next->is_inuse());
+ assert(p->_fd->_bk == p);
+ assert(p->_bk->_fd == p);
+ }
+ else // markers are always of size sizeof(size_t)
+ assert(sz == sizeof(size_t));
+ }
+}
+
+// Check properties of malloced chunks at the point they are malloced
+void malloc_state::do_check_malloced_chunk(void* mem, size_t s) const
+{
+ if (mem != 0)
+ {
+ mchunkptr p = mem2chunk(mem);
+ size_t sz = p->_head & ~INUSE_BITS;
+ do_check_inuse_chunk(p);
+ assert((sz & spp_chunk_align_mask) == 0);
+ assert(sz >= MIN_CHUNK_SIZE);
+ assert(sz >= s);
+ // unless mmapped, size is less than MIN_CHUNK_SIZE more than request
+ assert(p->is_mmapped() || sz < (s + MIN_CHUNK_SIZE));
+ }
+}
+
+// Check a tree and its subtrees.
+void malloc_state::do_check_tree(tchunkptr t)
+{
+ tchunkptr head = 0;
+ tchunkptr u = t;
+ bindex_t tindex = t->_index;
+ size_t tsize = t->chunksize();
+ bindex_t idx = compute_tree_index(tsize);
+ assert(tindex == idx);
+ assert(tsize >= MIN_LARGE_SIZE);
+ assert(tsize >= minsize_for_tree_index(idx));
+ assert((idx == NTREEBINS - 1) || (tsize < minsize_for_tree_index((idx + 1))));
+
+ do
+ {
+ // traverse through chain of same-sized nodes
+ do_check_any_chunk((mchunkptr)u);
+ assert(u->_index == tindex);
+ assert(u->chunksize() == tsize);
+ assert(!u->is_inuse());
+ assert(!u->next_pinuse());
+ assert(u->_fd->_bk == u);
+ assert(u->_bk->_fd == u);
+ if (u->_parent == 0)
+ {
+ assert(u->_child[0] == 0);
+ assert(u->_child[1] == 0);
+ }
+ else
+ {
+ assert(head == 0); // only one node on chain has parent
+ head = u;
+ assert(u->_parent != u);
+ assert(u->_parent->_child[0] == u ||
+ u->_parent->_child[1] == u ||
+ *((tbinptr*)(u->_parent)) == u);
+ if (u->_child[0] != 0)
+ {
+ assert(u->_child[0]->_parent == u);
+ assert(u->_child[0] != u);
+ do_check_tree(u->_child[0]);
+ }
+ if (u->_child[1] != 0)
+ {
+ assert(u->_child[1]->_parent == u);
+ assert(u->_child[1] != u);
+ do_check_tree(u->_child[1]);
+ }
+ if (u->_child[0] != 0 && u->_child[1] != 0)
+ assert(u->_child[0]->chunksize() < u->_child[1]->chunksize());
+ }
+ u = u->_fd;
+ }
+ while (u != t);
+ assert(head != 0);
+}
+
+// Check all the chunks in a treebin.
+void malloc_state::do_check_treebin(bindex_t i)
+{
+ tbinptr* tb = (tbinptr*)treebin_at(i);
+ tchunkptr t = *tb;
+ int empty = (_treemap & (1U << i)) == 0;
+ if (t == 0)
+ assert(empty);
+ if (!empty)
+ do_check_tree(t);
+}
+
+// Check all the chunks in a smallbin.
+void malloc_state::do_check_smallbin(bindex_t i)
+{
+ sbinptr b = smallbin_at(i);
+ mchunkptr p = b->_bk;
+ unsigned int empty = (_smallmap & (1U << i)) == 0;
+ if (p == b)
+ assert(empty);
+ if (!empty)
+ {
+ for (; p != b; p = p->_bk)
+ {
+ size_t size = p->chunksize();
+ mchunkptr q;
+ // each chunk claims to be free
+ do_check_free_chunk(p);
+ // chunk belongs in bin
+ assert(small_index(size) == i);
+ assert(p->_bk == b || p->_bk->chunksize() == p->chunksize());
+ // chunk is followed by an inuse chunk
+ q = (mchunkptr)p->next_chunk();
+ if (q->_head != FENCEPOST_HEAD)
+ do_check_inuse_chunk(q);
+ }
+ }
+}
+
+// Find x in a bin. Used in other check functions.
+int malloc_state::bin_find(mchunkptr x)
+{
+ size_t size = x->chunksize();
+ if (is_small(size))
+ {
+ bindex_t sidx = small_index(size);
+ sbinptr b = smallbin_at(sidx);
+ if (smallmap_is_marked(sidx))
+ {
+ mchunkptr p = b;
+ do
+ {
+ if (p == x)
+ return 1;
+ }
+ while ((p = p->_fd) != b);
+ }
+ }
+ else
+ {
+ bindex_t tidx = compute_tree_index(size);
+ if (treemap_is_marked(tidx))
+ {
+ tchunkptr t = *treebin_at(tidx);
+ size_t sizebits = size << leftshift_for_tree_index(tidx);
+ while (t != 0 && t->chunksize() != size)
+ {
+ t = t->_child[(sizebits >> (spp_size_t_bitsize - 1)) & 1];
+ sizebits <<= 1;
+ }
+ if (t != 0)
+ {
+ tchunkptr u = t;
+ do
+ {
+ if (u == (tchunkptr)x)
+ return 1;
+ }
+ while ((u = u->_fd) != t);
+ }
+ }
+ }
+ return 0;
+}
+
+// Traverse each chunk and check it; return total
+size_t malloc_state::traverse_and_check()
+{
+ size_t sum = 0;
+ if (is_initialized())
+ {
+ msegmentptr s = (msegmentptr)&_seg;
+ sum += _topsize + top_foot_size();
+ while (s != 0)
+ {
+ mchunkptr q = align_as_chunk(s->_base);
+ mchunkptr lastq = 0;
+ assert(q->pinuse());
+ while (segment_holds(s, q) &&
+ q != _top && q->_head != FENCEPOST_HEAD)
+ {
+ sum += q->chunksize();
+ if (q->is_inuse())
+ {
+ assert(!bin_find(q));
+ do_check_inuse_chunk(q);
+ }
+ else
+ {
+ assert(q == _dv || bin_find(q));
+ assert(lastq == 0 || lastq->is_inuse()); // Not 2 consecutive free
+ do_check_free_chunk(q);
+ }
+ lastq = q;
+ q = (mchunkptr)q->next_chunk();
+ }
+ s = s->_next;
+ }
+ }
+ return sum;
+}
+
+
+// Check all properties of malloc_state.
+void malloc_state::do_check_malloc_state()
+{
+ bindex_t i;
+ size_t total;
+ // check bins
+ for (i = 0; i < NSMALLBINS; ++i)
+ do_check_smallbin(i);
+ for (i = 0; i < NTREEBINS; ++i)
+ do_check_treebin(i);
+
+ if (_dvsize != 0)
+ {
+ // check dv chunk
+ do_check_any_chunk(_dv);
+ assert(_dvsize == _dv->chunksize());
+ assert(_dvsize >= MIN_CHUNK_SIZE);
+ assert(bin_find(_dv) == 0);
+ }
+
+ if (_top != 0)
+ {
+ // check top chunk
+ do_check_top_chunk(_top);
+ //assert(topsize == top->chunksize()); redundant
+ assert(_topsize > 0);
+ assert(bin_find(_top) == 0);
+ }
+
+ total = traverse_and_check();
+ assert(total <= _footprint);
+ assert(_footprint <= _max_footprint);
+}
+#endif // SPP_DEBUG
+
+/* ----------------------- Operations on smallbins ----------------------- */
+
+/*
+ Various forms of linking and unlinking are defined as macros. Even
+ the ones for trees, which are very long but have very short typical
+ paths. This is ugly but reduces reliance on inlining support of
+ compilers.
+*/
+
+// Link a free chunk into a smallbin
+void malloc_state::insert_small_chunk(mchunkptr p, size_t s)
+{
+ bindex_t I = small_index(s);
+ mchunkptr B = smallbin_at(I);
+ mchunkptr F = B;
+ assert(s >= MIN_CHUNK_SIZE);
+ if (!smallmap_is_marked(I))
+ mark_smallmap(I);
+ else if (rtcheck(ok_address(B->_fd)))
+ F = B->_fd;
+ else
+ SPP_ABORT;
+ B->_fd = p;
+ F->_bk = p;
+ p->_fd = F;
+ p->_bk = B;
+}
+
+// Unlink a chunk from a smallbin
+void malloc_state::unlink_small_chunk(mchunkptr p, size_t s)
+{
+ mchunkptr F = p->_fd;
+ mchunkptr B = p->_bk;
+ bindex_t I = small_index(s);
+ assert(p != B);
+ assert(p != F);
+ assert(p->chunksize() == small_index2size(I));
+ if (rtcheck(F == smallbin_at(I) || (ok_address(F) && F->_bk == p)))
+ {
+ if (B == F)
+ clear_smallmap(I);
+ else if (rtcheck(B == smallbin_at(I) ||
+ (ok_address(B) && B->_fd == p)))
+ {
+ F->_bk = B;
+ B->_fd = F;
+ }
+ else
+ SPP_ABORT;
+ }
+ else
+ SPP_ABORT;
+}
+
+// Unlink the first chunk from a smallbin
+void malloc_state::unlink_first_small_chunk(mchunkptr B, mchunkptr p, bindex_t I)
+{
+ mchunkptr F = p->_fd;
+ assert(p != B);
+ assert(p != F);
+ assert(p->chunksize() == small_index2size(I));
+ if (B == F)
+ clear_smallmap(I);
+ else if (rtcheck(ok_address(F) && F->_bk == p))
+ {
+ F->_bk = B;
+ B->_fd = F;
+ }
+ else
+ SPP_ABORT;
+}
+
+// Replace dv node, binning the old one
+// Used only when dvsize known to be small
+void malloc_state::replace_dv(mchunkptr p, size_t s)
+{
+ size_t DVS = _dvsize;
+ assert(is_small(DVS));
+ if (DVS != 0)
+ {
+ mchunkptr DV = _dv;
+ insert_small_chunk(DV, DVS);
+ }
+ _dvsize = s;
+ _dv = p;
+}
+
+/* ------------------------- Operations on trees ------------------------- */
+
+// Insert chunk into tree
+void malloc_state::insert_large_chunk(tchunkptr X, size_t s)
+{
+ tbinptr* H;
+ bindex_t I = compute_tree_index(s);
+ H = treebin_at(I);
+ X->_index = I;
+ X->_child[0] = X->_child[1] = 0;
+ if (!treemap_is_marked(I))
+ {
+ mark_treemap(I);
+ *H = X;
+ X->_parent = (tchunkptr)H;
+ X->_fd = X->_bk = X;
+ }
+ else
+ {
+ tchunkptr T = *H;
+ size_t K = s << leftshift_for_tree_index(I);
+ for (;;)
+ {
+ if (T->chunksize() != s)
+ {
+ tchunkptr* C = &(T->_child[(K >> (spp_size_t_bitsize - 1)) & 1]);
+ K <<= 1;
+ if (*C != 0)
+ T = *C;
+ else if (rtcheck(ok_address(C)))
+ {
+ *C = X;
+ X->_parent = T;
+ X->_fd = X->_bk = X;
+ break;
+ }
+ else
+ {
+ SPP_ABORT;
+ break;
+ }
+ }
+ else
+ {
+ tchunkptr F = T->_fd;
+ if (rtcheck(ok_address(T) && ok_address(F)))
+ {
+ T->_fd = F->_bk = X;
+ X->_fd = F;
+ X->_bk = T;
+ X->_parent = 0;
+ break;
+ }
+ else
+ {
+ SPP_ABORT;
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*
+ Unlink steps:
+
+ 1. If x is a chained node, unlink it from its same-sized fd/bk links
+ and choose its bk node as its replacement.
+ 2. If x was the last node of its size, but not a leaf node, it must
+ be replaced with a leaf node (not merely one with an open left or
+ right), to make sure that lefts and rights of descendents
+ correspond properly to bit masks. We use the rightmost descendent
+ of x. We could use any other leaf, but this is easy to locate and
+ tends to counteract removal of leftmosts elsewhere, and so keeps
+ paths shorter than minimally guaranteed. This doesn't loop much
+ because on average a node in a tree is near the bottom.
+ 3. If x is the base of a chain (i.e., has parent links) relink
+ x's parent and children to x's replacement (or null if none).
+*/
+
+void malloc_state::unlink_large_chunk(tchunkptr X)
+{
+ tchunkptr XP = X->_parent;
+ tchunkptr R;
+ if (X->_bk != X)
+ {
+ tchunkptr F = X->_fd;
+ R = X->_bk;
+ if (rtcheck(ok_address(F) && F->_bk == X && R->_fd == X))
+ {
+ F->_bk = R;
+ R->_fd = F;
+ }
+ else
+ SPP_ABORT;
+ }
+ else
+ {
+ tchunkptr* RP;
+ if (((R = *(RP = &(X->_child[1]))) != 0) ||
+ ((R = *(RP = &(X->_child[0]))) != 0))
+ {
+ tchunkptr* CP;
+ while ((*(CP = &(R->_child[1])) != 0) ||
+ (*(CP = &(R->_child[0])) != 0))
+ R = *(RP = CP);
+ if (rtcheck(ok_address(RP)))
+ *RP = 0;
+ else
+ SPP_ABORT;
+ }
+ }
+ if (XP != 0)
+ {
+ tbinptr* H = treebin_at(X->_index);
+ if (X == *H)
+ {
+ if ((*H = R) == 0)
+ clear_treemap(X->_index);
+ }
+ else if (rtcheck(ok_address(XP)))
+ {
+ if (XP->_child[0] == X)
+ XP->_child[0] = R;
+ else
+ XP->_child[1] = R;
+ }
+ else
+ SPP_ABORT;
+ if (R != 0)
+ {
+ if (rtcheck(ok_address(R)))
+ {
+ tchunkptr C0, C1;
+ R->_parent = XP;
+ if ((C0 = X->_child[0]) != 0)
+ {
+ if (rtcheck(ok_address(C0)))
+ {
+ R->_child[0] = C0;
+ C0->_parent = R;
+ }
+ else
+ SPP_ABORT;
+ }
+ if ((C1 = X->_child[1]) != 0)
+ {
+ if (rtcheck(ok_address(C1)))
+ {
+ R->_child[1] = C1;
+ C1->_parent = R;
+ }
+ else
+ SPP_ABORT;
+ }
+ }
+ else
+ SPP_ABORT;
+ }
+ }
+}
+
+// Relays to large vs small bin operations
+
+void malloc_state::insert_chunk(mchunkptr p, size_t s)
+{
+ if (is_small(s))
+ insert_small_chunk(p, s);
+ else
+ {
+ tchunkptr tp = (tchunkptr)(p);
+ insert_large_chunk(tp, s);
+ }
+}
+
+void malloc_state::unlink_chunk(mchunkptr p, size_t s)
+{
+ if (is_small(s))
+ unlink_small_chunk(p, s);
+ else
+ {
+ tchunkptr tp = (tchunkptr)(p);
+ unlink_large_chunk(tp);
+ }
+}
+
+
+/* ----------------------- Direct-mmapping chunks ----------------------- */
+
+/*
+ Directly mmapped chunks are set up with an offset to the start of
+ the mmapped region stored in the prev_foot field of the chunk. This
+ allows reconstruction of the required argument to MUNMAP when freed,
+ and also allows adjustment of the returned chunk to meet alignment
+ requirements (especially in memalign).
+*/
+
+// Malloc using mmap
+void* malloc_state::mmap_alloc(size_t nb)
+{
+ size_t mmsize = mmap_align(nb + 6 * sizeof(size_t) + spp_chunk_align_mask);
+ if (_footprint_limit != 0)
+ {
+ size_t fp = _footprint + mmsize;
+ if (fp <= _footprint || fp > _footprint_limit)
+ return 0;
+ }
+ if (mmsize > nb)
+ {
+ // Check for wrap around 0
+ char* mm = (char*)(SPP_CALL_DIRECT_MMAP(mmsize));
+ if (mm != cmfail)
+ {
+ size_t offset = align_offset(chunk2mem(mm));
+ size_t psize = mmsize - offset - SPP_MMAP_FOOT_PAD;
+ mchunkptr p = (mchunkptr)(mm + offset);
+ p->_prev_foot = offset;
+ p->_head = psize;
+ mark_inuse_foot(p, psize);
+ p->chunk_plus_offset(psize)->_head = FENCEPOST_HEAD;
+ p->chunk_plus_offset(psize + sizeof(size_t))->_head = 0;
+
+ if (_least_addr == 0 || mm < _least_addr)
+ _least_addr = mm;
+ if ((_footprint += mmsize) > _max_footprint)
+ _max_footprint = _footprint;
+ assert(spp_is_aligned(chunk2mem(p)));
+ check_mmapped_chunk(p);
+ return chunk2mem(p);
+ }
+ }
+ return 0;
+}
+
+// Realloc using mmap
+mchunkptr malloc_state::mmap_resize(mchunkptr oldp, size_t nb, int flags)
+{
+ size_t oldsize = oldp->chunksize();
+ (void)flags; // placate people compiling -Wunused
+ if (is_small(nb)) // Can't shrink mmap regions below small size
+ return 0;
+
+ // Keep old chunk if big enough but not too big
+ if (oldsize >= nb + sizeof(size_t) &&
+ (oldsize - nb) <= (mparams._granularity << 1))
+ return oldp;
+ else
+ {
+ size_t offset = oldp->_prev_foot;
+ size_t oldmmsize = oldsize + offset + SPP_MMAP_FOOT_PAD;
+ size_t newmmsize = mmap_align(nb + 6 * sizeof(size_t) + spp_chunk_align_mask);
+ char* cp = (char*)SPP_CALL_MREMAP((char*)oldp - offset,
+ oldmmsize, newmmsize, flags);
+ if (cp != cmfail)
+ {
+ mchunkptr newp = (mchunkptr)(cp + offset);
+ size_t psize = newmmsize - offset - SPP_MMAP_FOOT_PAD;
+ newp->_head = psize;
+ mark_inuse_foot(newp, psize);
+ newp->chunk_plus_offset(psize)->_head = FENCEPOST_HEAD;
+ newp->chunk_plus_offset(psize + sizeof(size_t))->_head = 0;
+
+ if (cp < _least_addr)
+ _least_addr = cp;
+ if ((_footprint += newmmsize - oldmmsize) > _max_footprint)
+ _max_footprint = _footprint;
+ check_mmapped_chunk(newp);
+ return newp;
+ }
+ }
+ return 0;
+}
+
+
+/* -------------------------- mspace management -------------------------- */
+
+// Initialize top chunk and its size
+void malloc_state::init_top(mchunkptr p, size_t psize)
+{
+ // Ensure alignment
+ size_t offset = align_offset(chunk2mem(p));
+ p = (mchunkptr)((char*)p + offset);
+ psize -= offset;
+
+ _top = p;
+ _topsize = psize;
+ p->_head = psize | PINUSE_BIT;
+ // set size of fake trailing chunk holding overhead space only once
+ p->chunk_plus_offset(psize)->_head = top_foot_size();
+ _trim_check = mparams._trim_threshold; // reset on each update
+}
+
+// Initialize bins for a new mstate that is otherwise zeroed out
+void malloc_state::init_bins()
+{
+ // Establish circular links for smallbins
+ bindex_t i;
+ for (i = 0; i < NSMALLBINS; ++i)
+ {
+ sbinptr bin = smallbin_at(i);
+ bin->_fd = bin->_bk = bin;
+ }
+}
+
+#if SPP_PROCEED_ON_ERROR
+
+// default corruption action
+void malloc_state::reset_on_error()
+{
+ int i;
+ ++malloc_corruption_error_count;
+ // Reinitialize fields to forget about all memory
+ _smallmap = _treemap = 0;
+ _dvsize = _topsize = 0;
+ _seg._base = 0;
+ _seg._size = 0;
+ _seg._next = 0;
+ _top = _dv = 0;
+ for (i = 0; i < NTREEBINS; ++i)
+ *treebin_at(i) = 0;
+ init_bins();
+}
+#endif
+
+/* Allocate chunk and prepend remainder with chunk in successor base. */
+void* malloc_state::prepend_alloc(char* newbase, char* oldbase, size_t nb)
+{
+ mchunkptr p = align_as_chunk(newbase);
+ mchunkptr oldfirst = align_as_chunk(oldbase);
+ size_t psize = (char*)oldfirst - (char*)p;
+ mchunkptr q = (mchunkptr)p->chunk_plus_offset(nb);
+ size_t qsize = psize - nb;
+ set_size_and_pinuse_of_inuse_chunk(p, nb);
+
+ assert((char*)oldfirst > (char*)q);
+ assert(oldfirst->pinuse());
+ assert(qsize >= MIN_CHUNK_SIZE);
+
+ // consolidate remainder with first chunk of old base
+ if (oldfirst == _top)
+ {
+ size_t tsize = _topsize += qsize;
+ _top = q;
+ q->_head = tsize | PINUSE_BIT;
+ check_top_chunk(q);
+ }
+ else if (oldfirst == _dv)
+ {
+ size_t dsize = _dvsize += qsize;
+ _dv = q;
+ q->set_size_and_pinuse_of_free_chunk(dsize);
+ }
+ else
+ {
+ if (!oldfirst->is_inuse())
+ {
+ size_t nsize = oldfirst->chunksize();
+ unlink_chunk(oldfirst, nsize);
+ oldfirst = (mchunkptr)oldfirst->chunk_plus_offset(nsize);
+ qsize += nsize;
+ }
+ q->set_free_with_pinuse(qsize, oldfirst);
+ insert_chunk(q, qsize);
+ check_free_chunk(q);
+ }
+
+ check_malloced_chunk(chunk2mem(p), nb);
+ return chunk2mem(p);
+}
+
+// Add a segment to hold a new noncontiguous region
+void malloc_state::add_segment(char* tbase, size_t tsize, flag_t mmapped)
+{
+ // Determine locations and sizes of segment, fenceposts, old top
+ char* old_top = (char*)_top;
+ msegmentptr oldsp = segment_holding(old_top);
+ char* old_end = oldsp->_base + oldsp->_size;
+ size_t ssize = pad_request(sizeof(struct malloc_segment));
+ char* rawsp = old_end - (ssize + 4 * sizeof(size_t) + spp_chunk_align_mask);
+ size_t offset = align_offset(chunk2mem(rawsp));
+ char* asp = rawsp + offset;
+ char* csp = (asp < (old_top + MIN_CHUNK_SIZE)) ? old_top : asp;
+ mchunkptr sp = (mchunkptr)csp;
+ msegmentptr ss = (msegmentptr)(chunk2mem(sp));
+ mchunkptr tnext = (mchunkptr)sp->chunk_plus_offset(ssize);
+ mchunkptr p = tnext;
+ int nfences = 0;
+
+ // reset top to new space
+ init_top((mchunkptr)tbase, tsize - top_foot_size());
+
+ // Set up segment record
+ assert(spp_is_aligned(ss));
+ set_size_and_pinuse_of_inuse_chunk(sp, ssize);
+ *ss = _seg; // Push current record
+ _seg._base = tbase;
+ _seg._size = tsize;
+ _seg._sflags = mmapped;
+ _seg._next = ss;
+
+ // Insert trailing fenceposts
+ for (;;)
+ {
+ mchunkptr nextp = (mchunkptr)p->chunk_plus_offset(sizeof(size_t));
+ p->_head = FENCEPOST_HEAD;
+ ++nfences;
+ if ((char*)(&(nextp->_head)) < old_end)
+ p = nextp;
+ else
+ break;
+ }
+ assert(nfences >= 2);
+
+ // Insert the rest of old top into a bin as an ordinary free chunk
+ if (csp != old_top)
+ {
+ mchunkptr q = (mchunkptr)old_top;
+ size_t psize = csp - old_top;
+ mchunkptr tn = (mchunkptr)q->chunk_plus_offset(psize);
+ q->set_free_with_pinuse(psize, tn);
+ insert_chunk(q, psize);
+ }
+
+ check_top_chunk(_top);
+}
+
+/* -------------------------- System allocation -------------------------- */
+
+// Get memory from system using MMAP
+void* malloc_state::sys_alloc(size_t nb)
+{
+ char* tbase = cmfail;
+ size_t tsize = 0;
+ flag_t mmap_flag = 0;
+ size_t asize; // allocation size
+
+ mparams.ensure_initialization();
+
+ // Directly map large chunks, but only if already initialized
+ if (use_mmap() && nb >= mparams._mmap_threshold && _topsize != 0)
+ {
+ void* mem = mmap_alloc(nb);
+ if (mem != 0)
+ return mem;
+ }
+
+ asize = mparams.granularity_align(nb + sys_alloc_padding());
+ if (asize <= nb)
+ return 0; // wraparound
+ if (_footprint_limit != 0)
+ {
+ size_t fp = _footprint + asize;
+ if (fp <= _footprint || fp > _footprint_limit)
+ return 0;
+ }
+
+ /*
+ Try getting memory with a call to MMAP new space (disabled if not SPP_HAVE_MMAP).
+ We need to request enough bytes from system to ensure
+ we can malloc nb bytes upon success, so pad with enough space for
+ top_foot, plus alignment-pad to make sure we don't lose bytes if
+ not on boundary, and round this up to a granularity unit.
+ */
+
+ if (SPP_HAVE_MMAP && tbase == cmfail)
+ {
+ // Try MMAP
+ char* mp = (char*)(SPP_CALL_MMAP(asize));
+ if (mp != cmfail)
+ {
+ tbase = mp;
+ tsize = asize;
+ mmap_flag = USE_MMAP_BIT;
+ }
+ }
+
+ if (tbase != cmfail)
+ {
+
+ if ((_footprint += tsize) > _max_footprint)
+ _max_footprint = _footprint;
+
+ if (!is_initialized())
+ {
+ // first-time initialization
+ if (_least_addr == 0 || tbase < _least_addr)
+ _least_addr = tbase;
+ _seg._base = tbase;
+ _seg._size = tsize;
+ _seg._sflags = mmap_flag;
+ _magic = mparams._magic;
+ _release_checks = SPP_MAX_RELEASE_CHECK_RATE;
+ init_bins();
+
+ // Offset top by embedded malloc_state
+ mchunkptr mn = (mchunkptr)mem2chunk(this)->next_chunk();
+ init_top(mn, (size_t)((tbase + tsize) - (char*)mn) - top_foot_size());
+ }
+
+ else
+ {
+ // Try to merge with an existing segment
+ msegmentptr sp = &_seg;
+ // Only consider most recent segment if traversal suppressed
+ while (sp != 0 && tbase != sp->_base + sp->_size)
+ sp = (SPP_NO_SEGMENT_TRAVERSAL) ? 0 : sp->_next;
+ if (sp != 0 &&
+ !sp->is_extern_segment() &&
+ (sp->_sflags & USE_MMAP_BIT) == mmap_flag &&
+ segment_holds(sp, _top))
+ {
+ // append
+ sp->_size += tsize;
+ init_top(_top, _topsize + tsize);
+ }
+ else
+ {
+ if (tbase < _least_addr)
+ _least_addr = tbase;
+ sp = &_seg;
+ while (sp != 0 && sp->_base != tbase + tsize)
+ sp = (SPP_NO_SEGMENT_TRAVERSAL) ? 0 : sp->_next;
+ if (sp != 0 &&
+ !sp->is_extern_segment() &&
+ (sp->_sflags & USE_MMAP_BIT) == mmap_flag)
+ {
+ char* oldbase = sp->_base;
+ sp->_base = tbase;
+ sp->_size += tsize;
+ return prepend_alloc(tbase, oldbase, nb);
+ }
+ else
+ add_segment(tbase, tsize, mmap_flag);
+ }
+ }
+
+ if (nb < _topsize)
+ {
+ // Allocate from new or extended top space
+ size_t rsize = _topsize -= nb;
+ mchunkptr p = _top;
+ mchunkptr r = _top = (mchunkptr)p->chunk_plus_offset(nb);
+ r->_head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(p, nb);
+ check_top_chunk(_top);
+ check_malloced_chunk(chunk2mem(p), nb);
+ return chunk2mem(p);
+ }
+ }
+
+ SPP_MALLOC_FAILURE_ACTION;
+ return 0;
+}
+
+/* ----------------------- system deallocation -------------------------- */
+
+// Unmap and unlink any mmapped segments that don't contain used chunks
+size_t malloc_state::release_unused_segments()
+{
+ size_t released = 0;
+ int nsegs = 0;
+ msegmentptr pred = &_seg;
+ msegmentptr sp = pred->_next;
+ while (sp != 0)
+ {
+ char* base = sp->_base;
+ size_t size = sp->_size;
+ msegmentptr next = sp->_next;
+ ++nsegs;
+ if (sp->is_mmapped_segment() && !sp->is_extern_segment())
+ {
+ mchunkptr p = align_as_chunk(base);
+ size_t psize = p->chunksize();
+ // Can unmap if first chunk holds entire segment and not pinned
+ if (!p->is_inuse() && (char*)p + psize >= base + size - top_foot_size())
+ {
+ tchunkptr tp = (tchunkptr)p;
+ assert(segment_holds(sp, p));
+ if (p == _dv)
+ {
+ _dv = 0;
+ _dvsize = 0;
+ }
+ else
+ unlink_large_chunk(tp);
+ if (SPP_CALL_MUNMAP(base, size) == 0)
+ {
+ released += size;
+ _footprint -= size;
+ // unlink obsoleted record
+ sp = pred;
+ sp->_next = next;
+ }
+ else
+ {
+ // back out if cannot unmap
+ insert_large_chunk(tp, psize);
+ }
+ }
+ }
+ if (SPP_NO_SEGMENT_TRAVERSAL) // scan only first segment
+ break;
+ pred = sp;
+ sp = next;
+ }
+ // Reset check counter
+ _release_checks = (((size_t) nsegs > (size_t) SPP_MAX_RELEASE_CHECK_RATE) ?
+ (size_t) nsegs : (size_t) SPP_MAX_RELEASE_CHECK_RATE);
+ return released;
+}
+
+int malloc_state::sys_trim(size_t pad)
+{
+ size_t released = 0;
+ mparams.ensure_initialization();
+ if (pad < MAX_REQUEST && is_initialized())
+ {
+ pad += top_foot_size(); // ensure enough room for segment overhead
+
+ if (_topsize > pad)
+ {
+ // Shrink top space in _granularity - size units, keeping at least one
+ size_t unit = mparams._granularity;
+ size_t extra = ((_topsize - pad + (unit - 1)) / unit -
+ 1) * unit;
+ msegmentptr sp = segment_holding((char*)_top);
+
+ if (!sp->is_extern_segment())
+ {
+ if (sp->is_mmapped_segment())
+ {
+ if (SPP_HAVE_MMAP &&
+ sp->_size >= extra &&
+ !has_segment_link(sp))
+ {
+ // can't shrink if pinned
+ size_t newsize = sp->_size - extra;
+ (void)newsize; // placate people compiling -Wunused-variable
+ // Prefer mremap, fall back to munmap
+ if ((SPP_CALL_MREMAP(sp->_base, sp->_size, newsize, 0) != mfail) ||
+ (SPP_CALL_MUNMAP(sp->_base + newsize, extra) == 0))
+ released = extra;
+ }
+ }
+ }
+
+ if (released != 0)
+ {
+ sp->_size -= released;
+ _footprint -= released;
+ init_top(_top, _topsize - released);
+ check_top_chunk(_top);
+ }
+ }
+
+ // Unmap any unused mmapped segments
+ if (SPP_HAVE_MMAP)
+ released += release_unused_segments();
+
+ // On failure, disable autotrim to avoid repeated failed future calls
+ if (released == 0 && _topsize > _trim_check)
+ _trim_check = spp_max_size_t;
+ }
+
+ return (released != 0) ? 1 : 0;
+}
+
+/* Consolidate and bin a chunk. Differs from exported versions
+ of free mainly in that the chunk need not be marked as inuse.
+*/
+void malloc_state::dispose_chunk(mchunkptr p, size_t psize)
+{
+ mchunkptr next = (mchunkptr)p->chunk_plus_offset(psize);
+ if (!p->pinuse())
+ {
+ mchunkptr prev;
+ size_t prevsize = p->_prev_foot;
+ if (p->is_mmapped())
+ {
+ psize += prevsize + SPP_MMAP_FOOT_PAD;
+ if (SPP_CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+ _footprint -= psize;
+ return;
+ }
+ prev = (mchunkptr)p->chunk_minus_offset(prevsize);
+ psize += prevsize;
+ p = prev;
+ if (rtcheck(ok_address(prev)))
+ {
+ // consolidate backward
+ if (p != _dv)
+ unlink_chunk(p, prevsize);
+ else if ((next->_head & INUSE_BITS) == INUSE_BITS)
+ {
+ _dvsize = psize;
+ p->set_free_with_pinuse(psize, next);
+ return;
+ }
+ }
+ else
+ {
+ SPP_ABORT;
+ return;
+ }
+ }
+ if (rtcheck(ok_address(next)))
+ {
+ if (!next->cinuse())
+ {
+ // consolidate forward
+ if (next == _top)
+ {
+ size_t tsize = _topsize += psize;
+ _top = p;
+ p->_head = tsize | PINUSE_BIT;
+ if (p == _dv)
+ {
+ _dv = 0;
+ _dvsize = 0;
+ }
+ return;
+ }
+ else if (next == _dv)
+ {
+ size_t dsize = _dvsize += psize;
+ _dv = p;
+ p->set_size_and_pinuse_of_free_chunk(dsize);
+ return;
+ }
+ else
+ {
+ size_t nsize = next->chunksize();
+ psize += nsize;
+ unlink_chunk(next, nsize);
+ p->set_size_and_pinuse_of_free_chunk(psize);
+ if (p == _dv)
+ {
+ _dvsize = psize;
+ return;
+ }
+ }
+ }
+ else
+ p->set_free_with_pinuse(psize, next);
+ insert_chunk(p, psize);
+ }
+ else
+ SPP_ABORT;
+}
+
+/* ---------------------------- malloc --------------------------- */
+
+// allocate a large request from the best fitting chunk in a treebin
+void* malloc_state::tmalloc_large(size_t nb)
+{
+ tchunkptr v = 0;
+ size_t rsize = -nb; // Unsigned negation
+ tchunkptr t;
+ bindex_t idx = compute_tree_index(nb);
+ if ((t = *treebin_at(idx)) != 0)
+ {
+ // Traverse tree for this bin looking for node with size == nb
+ size_t sizebits = nb << leftshift_for_tree_index(idx);
+ tchunkptr rst = 0; // The deepest untaken right subtree
+ for (;;)
+ {
+ tchunkptr rt;
+ size_t trem = t->chunksize() - nb;
+ if (trem < rsize)
+ {
+ v = t;
+ if ((rsize = trem) == 0)
+ break;
+ }
+ rt = t->_child[1];
+ t = t->_child[(sizebits >> (spp_size_t_bitsize - 1)) & 1];
+ if (rt != 0 && rt != t)
+ rst = rt;
+ if (t == 0)
+ {
+ t = rst; // set t to least subtree holding sizes > nb
+ break;
+ }
+ sizebits <<= 1;
+ }
+ }
+ if (t == 0 && v == 0)
+ {
+ // set t to root of next non-empty treebin
+ binmap_t leftbits = left_bits(idx2bit(idx)) & _treemap;
+ if (leftbits != 0)
+ {
+ binmap_t leastbit = least_bit(leftbits);
+ bindex_t i = compute_bit2idx(leastbit);
+ t = *treebin_at(i);
+ }
+ }
+
+ while (t != 0)
+ {
+ // find smallest of tree or subtree
+ size_t trem = t->chunksize() - nb;
+ if (trem < rsize)
+ {
+ rsize = trem;
+ v = t;
+ }
+ t = t->leftmost_child();
+ }
+
+ // If dv is a better fit, return 0 so malloc will use it
+ if (v != 0 && rsize < (size_t)(_dvsize - nb))
+ {
+ if (rtcheck(ok_address(v)))
+ {
+ // split
+ mchunkptr r = (mchunkptr)v->chunk_plus_offset(nb);
+ assert(v->chunksize() == rsize + nb);
+ if (rtcheck(ok_next(v, r)))
+ {
+ unlink_large_chunk(v);
+ if (rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(v, (rsize + nb));
+ else
+ {
+ set_size_and_pinuse_of_inuse_chunk(v, nb);
+ r->set_size_and_pinuse_of_free_chunk(rsize);
+ insert_chunk(r, rsize);
+ }
+ return chunk2mem(v);
+ }
+ }
+ SPP_ABORT;
+ }
+ return 0;
+}
+
+// allocate a small request from the best fitting chunk in a treebin
+void* malloc_state::tmalloc_small(size_t nb)
+{
+ tchunkptr t, v;
+ size_t rsize;
+ binmap_t leastbit = least_bit(_treemap);
+ bindex_t i = compute_bit2idx(leastbit);
+ v = t = *treebin_at(i);
+ rsize = t->chunksize() - nb;
+
+ while ((t = t->leftmost_child()) != 0)
+ {
+ size_t trem = t->chunksize() - nb;
+ if (trem < rsize)
+ {
+ rsize = trem;
+ v = t;
+ }
+ }
+
+ if (rtcheck(ok_address(v)))
+ {
+ mchunkptr r = (mchunkptr)v->chunk_plus_offset(nb);
+ assert(v->chunksize() == rsize + nb);
+ if (rtcheck(ok_next(v, r)))
+ {
+ unlink_large_chunk(v);
+ if (rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(v, (rsize + nb));
+ else
+ {
+ set_size_and_pinuse_of_inuse_chunk(v, nb);
+ r->set_size_and_pinuse_of_free_chunk(rsize);
+ replace_dv(r, rsize);
+ }
+ return chunk2mem(v);
+ }
+ }
+
+ SPP_ABORT;
+ return 0;
+}
+
+/* ---------------------------- malloc --------------------------- */
+
+void* malloc_state::_malloc(size_t bytes)
+{
+ if (1)
+ {
+ void* mem;
+ size_t nb;
+ if (bytes <= MAX_SMALL_REQUEST)
+ {
+ bindex_t idx;
+ binmap_t smallbits;
+ nb = (bytes < MIN_REQUEST) ? MIN_CHUNK_SIZE : pad_request(bytes);
+ idx = small_index(nb);
+ smallbits = _smallmap >> idx;
+
+ if ((smallbits & 0x3U) != 0)
+ {
+ // Remainderless fit to a smallbin.
+ mchunkptr b, p;
+ idx += ~smallbits & 1; // Uses next bin if idx empty
+ b = smallbin_at(idx);
+ p = b->_fd;
+ assert(p->chunksize() == small_index2size(idx));
+ unlink_first_small_chunk(b, p, idx);
+ set_inuse_and_pinuse(p, small_index2size(idx));
+ mem = chunk2mem(p);
+ check_malloced_chunk(mem, nb);
+ goto postaction;
+ }
+
+ else if (nb > _dvsize)
+ {
+ if (smallbits != 0)
+ {
+ // Use chunk in next nonempty smallbin
+ mchunkptr b, p, r;
+ size_t rsize;
+ binmap_t leftbits = (smallbits << idx) & left_bits(malloc_state::idx2bit(idx));
+ binmap_t leastbit = least_bit(leftbits);
+ bindex_t i = compute_bit2idx(leastbit);
+ b = smallbin_at(i);
+ p = b->_fd;
+ assert(p->chunksize() == small_index2size(i));
+ unlink_first_small_chunk(b, p, i);
+ rsize = small_index2size(i) - nb;
+ // Fit here cannot be remainderless if 4byte sizes
+ if (sizeof(size_t) != 4 && rsize < MIN_CHUNK_SIZE)
+ set_inuse_and_pinuse(p, small_index2size(i));
+ else
+ {
+ set_size_and_pinuse_of_inuse_chunk(p, nb);
+ r = (mchunkptr)p->chunk_plus_offset(nb);
+ r->set_size_and_pinuse_of_free_chunk(rsize);
+ replace_dv(r, rsize);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(mem, nb);
+ goto postaction;
+ }
+
+ else if (_treemap != 0 && (mem = tmalloc_small(nb)) != 0)
+ {
+ check_malloced_chunk(mem, nb);
+ goto postaction;
+ }
+ }
+ }
+ else if (bytes >= MAX_REQUEST)
+ nb = spp_max_size_t; // Too big to allocate. Force failure (in sys alloc)
+ else
+ {
+ nb = pad_request(bytes);
+ if (_treemap != 0 && (mem = tmalloc_large(nb)) != 0)
+ {
+ check_malloced_chunk(mem, nb);
+ goto postaction;
+ }
+ }
+
+ if (nb <= _dvsize)
+ {
+ size_t rsize = _dvsize - nb;
+ mchunkptr p = _dv;
+ if (rsize >= MIN_CHUNK_SIZE)
+ {
+ // split dv
+ mchunkptr r = _dv = (mchunkptr)p->chunk_plus_offset(nb);
+ _dvsize = rsize;
+ r->set_size_and_pinuse_of_free_chunk(rsize);
+ set_size_and_pinuse_of_inuse_chunk(p, nb);
+ }
+ else // exhaust dv
+ {
+ size_t dvs = _dvsize;
+ _dvsize = 0;
+ _dv = 0;
+ set_inuse_and_pinuse(p, dvs);
+ }
+ mem = chunk2mem(p);
+ check_malloced_chunk(mem, nb);
+ goto postaction;
+ }
+
+ else if (nb < _topsize)
+ {
+ // Split top
+ size_t rsize = _topsize -= nb;
+ mchunkptr p = _top;
+ mchunkptr r = _top = (mchunkptr)p->chunk_plus_offset(nb);
+ r->_head = rsize | PINUSE_BIT;
+ set_size_and_pinuse_of_inuse_chunk(p, nb);
+ mem = chunk2mem(p);
+ check_top_chunk(_top);
+ check_malloced_chunk(mem, nb);
+ goto postaction;
+ }
+
+ mem = sys_alloc(nb);
+
+postaction:
+ return mem;
+ }
+
+ return 0;
+}
+
+/* ---------------------------- free --------------------------- */
+
+void malloc_state::_free(mchunkptr p)
+{
+ if (1)
+ {
+ check_inuse_chunk(p);
+ if (rtcheck(ok_address(p) && ok_inuse(p)))
+ {
+ size_t psize = p->chunksize();
+ mchunkptr next = (mchunkptr)p->chunk_plus_offset(psize);
+ if (!p->pinuse())
+ {
+ size_t prevsize = p->_prev_foot;
+ if (p->is_mmapped())
+ {
+ psize += prevsize + SPP_MMAP_FOOT_PAD;
+ if (SPP_CALL_MUNMAP((char*)p - prevsize, psize) == 0)
+ _footprint -= psize;
+ goto postaction;
+ }
+ else
+ {
+ mchunkptr prev = (mchunkptr)p->chunk_minus_offset(prevsize);
+ psize += prevsize;
+ p = prev;
+ if (rtcheck(ok_address(prev)))
+ {
+ // consolidate backward
+ if (p != _dv)
+ unlink_chunk(p, prevsize);
+ else if ((next->_head & INUSE_BITS) == INUSE_BITS)
+ {
+ _dvsize = psize;
+ p->set_free_with_pinuse(psize, next);
+ goto postaction;
+ }
+ }
+ else
+ goto erroraction;
+ }
+ }
+
+ if (rtcheck(ok_next(p, next) && ok_pinuse(next)))
+ {
+ if (!next->cinuse())
+ {
+ // consolidate forward
+ if (next == _top)
+ {
+ size_t tsize = _topsize += psize;
+ _top = p;
+ p->_head = tsize | PINUSE_BIT;
+ if (p == _dv)
+ {
+ _dv = 0;
+ _dvsize = 0;
+ }
+ if (should_trim(tsize))
+ sys_trim(0);
+ goto postaction;
+ }
+ else if (next == _dv)
+ {
+ size_t dsize = _dvsize += psize;
+ _dv = p;
+ p->set_size_and_pinuse_of_free_chunk(dsize);
+ goto postaction;
+ }
+ else
+ {
+ size_t nsize = next->chunksize();
+ psize += nsize;
+ unlink_chunk(next, nsize);
+ p->set_size_and_pinuse_of_free_chunk(psize);
+ if (p == _dv)
+ {
+ _dvsize = psize;
+ goto postaction;
+ }
+ }
+ }
+ else
+ p->set_free_with_pinuse(psize, next);
+
+ if (is_small(psize))
+ {
+ insert_small_chunk(p, psize);
+ check_free_chunk(p);
+ }
+ else
+ {
+ tchunkptr tp = (tchunkptr)p;
+ insert_large_chunk(tp, psize);
+ check_free_chunk(p);
+ if (--_release_checks == 0)
+ release_unused_segments();
+ }
+ goto postaction;
+ }
+ }
+erroraction:
+ SPP_USAGE_ERROR_ACTION(this, p);
+postaction:
+ ;
+ }
+}
+
+/* ------------ Internal support for realloc, memalign, etc -------------- */
+
+// Try to realloc; only in-place unless can_move true
+mchunkptr malloc_state::try_realloc_chunk(mchunkptr p, size_t nb, int can_move)
+{
+ mchunkptr newp = 0;
+ size_t oldsize = p->chunksize();
+ mchunkptr next = (mchunkptr)p->chunk_plus_offset(oldsize);
+ if (rtcheck(ok_address(p) && ok_inuse(p) &&
+ ok_next(p, next) && ok_pinuse(next)))
+ {
+ if (p->is_mmapped())
+ newp = mmap_resize(p, nb, can_move);
+ else if (oldsize >= nb)
+ {
+ // already big enough
+ size_t rsize = oldsize - nb;
+ if (rsize >= MIN_CHUNK_SIZE)
+ {
+ // split off remainder
+ mchunkptr r = (mchunkptr)p->chunk_plus_offset(nb);
+ set_inuse(p, nb);
+ set_inuse(r, rsize);
+ dispose_chunk(r, rsize);
+ }
+ newp = p;
+ }
+ else if (next == _top)
+ {
+ // extend into top
+ if (oldsize + _topsize > nb)
+ {
+ size_t newsize = oldsize + _topsize;
+ size_t newtopsize = newsize - nb;
+ mchunkptr newtop = (mchunkptr)p->chunk_plus_offset(nb);
+ set_inuse(p, nb);
+ newtop->_head = newtopsize | PINUSE_BIT;
+ _top = newtop;
+ _topsize = newtopsize;
+ newp = p;
+ }
+ }
+ else if (next == _dv)
+ {
+ // extend into dv
+ size_t dvs = _dvsize;
+ if (oldsize + dvs >= nb)
+ {
+ size_t dsize = oldsize + dvs - nb;
+ if (dsize >= MIN_CHUNK_SIZE)
+ {
+ mchunkptr r = (mchunkptr)p->chunk_plus_offset(nb);
+ mchunkptr n = (mchunkptr)r->chunk_plus_offset(dsize);
+ set_inuse(p, nb);
+ r->set_size_and_pinuse_of_free_chunk(dsize);
+ n->clear_pinuse();
+ _dvsize = dsize;
+ _dv = r;
+ }
+ else
+ {
+ // exhaust dv
+ size_t newsize = oldsize + dvs;
+ set_inuse(p, newsize);
+ _dvsize = 0;
+ _dv = 0;
+ }
+ newp = p;
+ }
+ }
+ else if (!next->cinuse())
+ {
+ // extend into next free chunk
+ size_t nextsize = next->chunksize();
+ if (oldsize + nextsize >= nb)
+ {
+ size_t rsize = oldsize + nextsize - nb;
+ unlink_chunk(next, nextsize);
+ if (rsize < MIN_CHUNK_SIZE)
+ {
+ size_t newsize = oldsize + nextsize;
+ set_inuse(p, newsize);
+ }
+ else
+ {
+ mchunkptr r = (mchunkptr)p->chunk_plus_offset(nb);
+ set_inuse(p, nb);
+ set_inuse(r, rsize);
+ dispose_chunk(r, rsize);
+ }
+ newp = p;
+ }
+ }
+ }
+ else
+ SPP_USAGE_ERROR_ACTION(m, chunk2mem(p));
+ return newp;
+}
+
+void* malloc_state::internal_memalign(size_t alignment, size_t bytes)
+{
+ void* mem = 0;
+ if (alignment < MIN_CHUNK_SIZE) // must be at least a minimum chunk size
+ alignment = MIN_CHUNK_SIZE;
+ if ((alignment & (alignment - 1)) != 0)
+ {
+ // Ensure a power of 2
+ size_t a = SPP_MALLOC_ALIGNMENT << 1;
+ while (a < alignment)
+ a <<= 1;
+ alignment = a;
+ }
+ if (bytes >= MAX_REQUEST - alignment)
+ SPP_MALLOC_FAILURE_ACTION;
+ else
+ {
+ size_t nb = request2size(bytes);
+ size_t req = nb + alignment + MIN_CHUNK_SIZE - CHUNK_OVERHEAD;
+ mem = internal_malloc(req);
+ if (mem != 0)
+ {
+ mchunkptr p = mem2chunk(mem);
+ if ((((size_t)(mem)) & (alignment - 1)) != 0)
+ {
+ // misaligned
+ /*
+ Find an aligned spot inside chunk. Since we need to give
+ back leading space in a chunk of at least MIN_CHUNK_SIZE, if
+ the first calculation places us at a spot with less than
+ MIN_CHUNK_SIZE leader, we can move to the next aligned spot.
+ We've allocated enough total room so that this is always
+ possible.
+ */
+ char* br = (char*)mem2chunk((void *)(((size_t)((char*)mem + alignment - 1)) &
+ -alignment));
+ char* pos = ((size_t)(br - (char*)(p)) >= MIN_CHUNK_SIZE) ?
+ br : br + alignment;
+ mchunkptr newp = (mchunkptr)pos;
+ size_t leadsize = pos - (char*)(p);
+ size_t newsize = p->chunksize() - leadsize;
+
+ if (p->is_mmapped())
+ {
+ // For mmapped chunks, just adjust offset
+ newp->_prev_foot = p->_prev_foot + leadsize;
+ newp->_head = newsize;
+ }
+ else
+ {
+ // Otherwise, give back leader, use the rest
+ set_inuse(newp, newsize);
+ set_inuse(p, leadsize);
+ dispose_chunk(p, leadsize);
+ }
+ p = newp;
+ }
+
+ // Give back spare room at the end
+ if (!p->is_mmapped())
+ {
+ size_t size = p->chunksize();
+ if (size > nb + MIN_CHUNK_SIZE)
+ {
+ size_t remainder_size = size - nb;
+ mchunkptr remainder = (mchunkptr)p->chunk_plus_offset(nb);
+ set_inuse(p, nb);
+ set_inuse(remainder, remainder_size);
+ dispose_chunk(remainder, remainder_size);
+ }
+ }
+
+ mem = chunk2mem(p);
+ assert(p->chunksize() >= nb);
+ assert(((size_t)mem & (alignment - 1)) == 0);
+ check_inuse_chunk(p);
+ }
+ }
+ return mem;
+}
+
+/*
+ Common support for independent_X routines, handling
+ all of the combinations that can result.
+ The opts arg has:
+ bit 0 set if all elements are same size (using sizes[0])
+ bit 1 set if elements should be zeroed
+*/
+void** malloc_state::ialloc(size_t n_elements, size_t* sizes, int opts,
+ void* chunks[])
+{
+
+ size_t element_size; // chunksize of each element, if all same
+ size_t contents_size; // total size of elements
+ size_t array_size; // request size of pointer array
+ void* mem; // malloced aggregate space
+ mchunkptr p; // corresponding chunk
+ size_t remainder_size; // remaining bytes while splitting
+ void** marray; // either "chunks" or malloced ptr array
+ mchunkptr array_chunk; // chunk for malloced ptr array
+ flag_t was_enabled; // to disable mmap
+ size_t size;
+ size_t i;
+
+ mparams.ensure_initialization();
+ // compute array length, if needed
+ if (chunks != 0)
+ {
+ if (n_elements == 0)
+ return chunks; // nothing to do
+ marray = chunks;
+ array_size = 0;
+ }
+ else
+ {
+ // if empty req, must still return chunk representing empty array
+ if (n_elements == 0)
+ return (void**)internal_malloc(0);
+ marray = 0;
+ array_size = request2size(n_elements * (sizeof(void*)));
+ }
+
+ // compute total element size
+ if (opts & 0x1)
+ {
+ // all-same-size
+ element_size = request2size(*sizes);
+ contents_size = n_elements * element_size;
+ }
+ else
+ {
+ // add up all the sizes
+ element_size = 0;
+ contents_size = 0;
+ for (i = 0; i != n_elements; ++i)
+ contents_size += request2size(sizes[i]);
+ }
+
+ size = contents_size + array_size;
+
+ /*
+ Allocate the aggregate chunk. First disable direct-mmapping so
+ malloc won't use it, since we would not be able to later
+ free/realloc space internal to a segregated mmap region.
+ */
+ was_enabled = use_mmap();
+ disable_mmap();
+ mem = internal_malloc(size - CHUNK_OVERHEAD);
+ if (was_enabled)
+ enable_mmap();
+ if (mem == 0)
+ return 0;
+
+ p = mem2chunk(mem);
+ remainder_size = p->chunksize();
+
+ assert(!p->is_mmapped());
+
+ if (opts & 0x2)
+ {
+ // optionally clear the elements
+ memset((size_t*)mem, 0, remainder_size - sizeof(size_t) - array_size);
+ }
+
+ // If not provided, allocate the pointer array as final part of chunk
+ if (marray == 0)
+ {
+ size_t array_chunk_size;
+ array_chunk = (mchunkptr)p->chunk_plus_offset(contents_size);
+ array_chunk_size = remainder_size - contents_size;
+ marray = (void**)(chunk2mem(array_chunk));
+ set_size_and_pinuse_of_inuse_chunk(array_chunk, array_chunk_size);
+ remainder_size = contents_size;
+ }
+
+ // split out elements
+ for (i = 0; ; ++i)
+ {
+ marray[i] = chunk2mem(p);
+ if (i != n_elements - 1)
+ {
+ if (element_size != 0)
+ size = element_size;
+ else
+ size = request2size(sizes[i]);
+ remainder_size -= size;
+ set_size_and_pinuse_of_inuse_chunk(p, size);
+ p = (mchunkptr)p->chunk_plus_offset(size);
+ }
+ else
+ {
+ // the final element absorbs any overallocation slop
+ set_size_and_pinuse_of_inuse_chunk(p, remainder_size);
+ break;
+ }
+ }
+
+#if SPP_DEBUG
+ if (marray != chunks)
+ {
+ // final element must have exactly exhausted chunk
+ if (element_size != 0)
+ assert(remainder_size == element_size);
+ else
+ assert(remainder_size == request2size(sizes[i]));
+ check_inuse_chunk(mem2chunk(marray));
+ }
+ for (i = 0; i != n_elements; ++i)
+ check_inuse_chunk(mem2chunk(marray[i]));
+
+#endif
+
+ return marray;
+}
+
+/* Try to free all pointers in the given array.
+ Note: this could be made faster, by delaying consolidation,
+ at the price of disabling some user integrity checks, We
+ still optimize some consolidations by combining adjacent
+ chunks before freeing, which will occur often if allocated
+ with ialloc or the array is sorted.
+*/
+size_t malloc_state::internal_bulk_free(void* array[], size_t nelem)
+{
+ size_t unfreed = 0;
+ if (1)
+ {
+ void** a;
+ void** fence = &(array[nelem]);
+ for (a = array; a != fence; ++a)
+ {
+ void* mem = *a;
+ if (mem != 0)
+ {
+ mchunkptr p = mem2chunk(mem);
+ size_t psize = p->chunksize();
+#if SPP_FOOTERS
+ if (get_mstate_for(p) != m)
+ {
+ ++unfreed;
+ continue;
+ }
+#endif
+ check_inuse_chunk(p);
+ *a = 0;
+ if (rtcheck(ok_address(p) && ok_inuse(p)))
+ {
+ void ** b = a + 1; // try to merge with next chunk
+ mchunkptr next = (mchunkptr)p->next_chunk();
+ if (b != fence && *b == chunk2mem(next))
+ {
+ size_t newsize = next->chunksize() + psize;
+ set_inuse(p, newsize);
+ *b = chunk2mem(p);
+ }
+ else
+ dispose_chunk(p, psize);
+ }
+ else
+ {
+ SPP_ABORT;
+ break;
+ }
+ }
+ }
+ if (should_trim(_topsize))
+ sys_trim(0);
+ }
+ return unfreed;
+}
+
+void malloc_state::init(char* tbase, size_t tsize)
+{
+ _seg._base = _least_addr = tbase;
+ _seg._size = _footprint = _max_footprint = tsize;
+ _magic = mparams._magic;
+ _release_checks = SPP_MAX_RELEASE_CHECK_RATE;
+ _mflags = mparams._default_mflags;
+ _extp = 0;
+ _exts = 0;
+ disable_contiguous();
+ init_bins();
+ mchunkptr mn = (mchunkptr)mem2chunk(this)->next_chunk();
+ init_top(mn, (size_t)((tbase + tsize) - (char*)mn) - top_foot_size());
+ check_top_chunk(_top);
+}
+
+/* Traversal */
+#if SPP_MALLOC_INSPECT_ALL
+void malloc_state::internal_inspect_all(void(*handler)(void *start, void *end,
+ size_t used_bytes,
+ void* callback_arg),
+ void* arg)
+{
+ if (is_initialized())
+ {
+ mchunkptr top = top;
+ msegmentptr s;
+ for (s = &seg; s != 0; s = s->next)
+ {
+ mchunkptr q = align_as_chunk(s->base);
+ while (segment_holds(s, q) && q->head != FENCEPOST_HEAD)
+ {
+ mchunkptr next = (mchunkptr)q->next_chunk();
+ size_t sz = q->chunksize();
+ size_t used;
+ void* start;
+ if (q->is_inuse())
+ {
+ used = sz - CHUNK_OVERHEAD; // must not be mmapped
+ start = chunk2mem(q);
+ }
+ else
+ {
+ used = 0;
+ if (is_small(sz))
+ {
+ // offset by possible bookkeeping
+ start = (void*)((char*)q + sizeof(struct malloc_chunk));
+ }
+ else
+ start = (void*)((char*)q + sizeof(struct malloc_tree_chunk));
+ }
+ if (start < (void*)next) // skip if all space is bookkeeping
+ handler(start, next, used, arg);
+ if (q == top)
+ break;
+ q = next;
+ }
+ }
+ }
+}
+#endif // SPP_MALLOC_INSPECT_ALL
+
+
+
+/* ----------------------------- user mspaces ---------------------------- */
+
+static mstate init_user_mstate(char* tbase, size_t tsize)
+{
+ size_t msize = pad_request(sizeof(malloc_state));
+ mchunkptr msp = align_as_chunk(tbase);
+ mstate m = (mstate)(chunk2mem(msp));
+ memset(m, 0, msize);
+ msp->_head = (msize | INUSE_BITS);
+ m->init(tbase, tsize);
+ return m;
+}
+
+SPP_API mspace create_mspace(size_t capacity, int locked)
+{
+ mstate m = 0;
+ size_t msize;
+ mparams.ensure_initialization();
+ msize = pad_request(sizeof(malloc_state));
+ if (capacity < (size_t) - (msize + top_foot_size() + mparams._page_size))
+ {
+ size_t rs = ((capacity == 0) ? mparams._granularity :
+ (capacity + top_foot_size() + msize));
+ size_t tsize = mparams.granularity_align(rs);
+ char* tbase = (char*)(SPP_CALL_MMAP(tsize));
+ if (tbase != cmfail)
+ {
+ m = init_user_mstate(tbase, tsize);
+ m->_seg._sflags = USE_MMAP_BIT;
+ m->set_lock(locked);
+ }
+ }
+ return (mspace)m;
+}
+
+SPP_API size_t destroy_mspace(mspace msp)
+{
+ size_t freed = 0;
+ mstate ms = (mstate)msp;
+ if (ms->ok_magic())
+ {
+ msegmentptr sp = &ms->_seg;
+ while (sp != 0)
+ {
+ char* base = sp->_base;
+ size_t size = sp->_size;
+ flag_t flag = sp->_sflags;
+ (void)base; // placate people compiling -Wunused-variable
+ sp = sp->_next;
+ if ((flag & USE_MMAP_BIT) && !(flag & EXTERN_BIT) &&
+ SPP_CALL_MUNMAP(base, size) == 0)
+ freed += size;
+ }
+ }
+ else
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return freed;
+}
+
+/* ---------------------------- mspace versions of malloc/calloc/free routines -------------------- */
+SPP_API void* mspace_malloc(mspace msp, size_t bytes)
+{
+ mstate ms = (mstate)msp;
+ if (!ms->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return 0;
+ }
+ return ms->_malloc(bytes);
+}
+
+SPP_API void mspace_free(mspace msp, void* mem)
+{
+ if (mem != 0)
+ {
+ mchunkptr p = mem2chunk(mem);
+#if SPP_FOOTERS
+ mstate fm = get_mstate_for(p);
+ (void)msp; // placate people compiling -Wunused
+#else
+ mstate fm = (mstate)msp;
+#endif
+ if (!fm->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(fm, p);
+ return;
+ }
+ fm->_free(p);
+ }
+}
+
+SPP_API inline void* mspace_calloc(mspace msp, size_t n_elements, size_t elem_size)
+{
+ void* mem;
+ size_t req = 0;
+ mstate ms = (mstate)msp;
+ if (!ms->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return 0;
+ }
+ if (n_elements != 0)
+ {
+ req = n_elements * elem_size;
+ if (((n_elements | elem_size) & ~(size_t)0xffff) &&
+ (req / n_elements != elem_size))
+ req = spp_max_size_t; // force downstream failure on overflow
+ }
+ mem = ms->internal_malloc(req);
+ if (mem != 0 && mem2chunk(mem)->calloc_must_clear())
+ memset(mem, 0, req);
+ return mem;
+}
+
+SPP_API inline void* mspace_realloc(mspace msp, void* oldmem, size_t bytes)
+{
+ void* mem = 0;
+ if (oldmem == 0)
+ mem = mspace_malloc(msp, bytes);
+ else if (bytes >= MAX_REQUEST)
+ SPP_MALLOC_FAILURE_ACTION;
+#ifdef REALLOC_ZERO_BYTES_FREES
+ else if (bytes == 0)
+ mspace_free(msp, oldmem);
+#endif
+ else
+ {
+ size_t nb = request2size(bytes);
+ mchunkptr oldp = mem2chunk(oldmem);
+#if ! SPP_FOOTERS
+ mstate m = (mstate)msp;
+#else
+ mstate m = get_mstate_for(oldp);
+ if (!m->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(m, oldmem);
+ return 0;
+ }
+#endif
+ if (1)
+ {
+ mchunkptr newp = m->try_realloc_chunk(oldp, nb, 1);
+ if (newp != 0)
+ {
+ m->check_inuse_chunk(newp);
+ mem = chunk2mem(newp);
+ }
+ else
+ {
+ mem = mspace_malloc(m, bytes);
+ if (mem != 0)
+ {
+ size_t oc = oldp->chunksize() - oldp->overhead_for();
+ memcpy(mem, oldmem, (oc < bytes) ? oc : bytes);
+ mspace_free(m, oldmem);
+ }
+ }
+ }
+ }
+ return mem;
+}
+
+#if 0
+
+SPP_API mspace create_mspace_with_base(void* base, size_t capacity, int locked)
+{
+ mstate m = 0;
+ size_t msize;
+ mparams.ensure_initialization();
+ msize = pad_request(sizeof(malloc_state));
+ if (capacity > msize + top_foot_size() &&
+ capacity < (size_t) - (msize + top_foot_size() + mparams._page_size))
+ {
+ m = init_user_mstate((char*)base, capacity);
+ m->_seg._sflags = EXTERN_BIT;
+ m->set_lock(locked);
+ }
+ return (mspace)m;
+}
+
+SPP_API int mspace_track_large_chunks(mspace msp, int enable)
+{
+ int ret = 0;
+ mstate ms = (mstate)msp;
+ if (1)
+ {
+ if (!ms->use_mmap())
+ ret = 1;
+ if (!enable)
+ ms->enable_mmap();
+ else
+ ms->disable_mmap();
+ }
+ return ret;
+}
+
+SPP_API void* mspace_realloc_in_place(mspace msp, void* oldmem, size_t bytes)
+{
+ void* mem = 0;
+ if (oldmem != 0)
+ {
+ if (bytes >= MAX_REQUEST)
+ SPP_MALLOC_FAILURE_ACTION;
+ else
+ {
+ size_t nb = request2size(bytes);
+ mchunkptr oldp = mem2chunk(oldmem);
+#if ! SPP_FOOTERS
+ mstate m = (mstate)msp;
+#else
+ mstate m = get_mstate_for(oldp);
+ (void)msp; // placate people compiling -Wunused
+ if (!m->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(m, oldmem);
+ return 0;
+ }
+#endif
+ if (1)
+ {
+ mchunkptr newp = m->try_realloc_chunk(oldp, nb, 0);
+ if (newp == oldp)
+ {
+ m->check_inuse_chunk(newp);
+ mem = oldmem;
+ }
+ }
+ }
+ }
+ return mem;
+}
+
+SPP_API void* mspace_memalign(mspace msp, size_t alignment, size_t bytes)
+{
+ mstate ms = (mstate)msp;
+ if (!ms->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return 0;
+ }
+ if (alignment <= SPP_MALLOC_ALIGNMENT)
+ return mspace_malloc(msp, bytes);
+ return ms->internal_memalign(alignment, bytes);
+}
+
+SPP_API void** mspace_independent_calloc(mspace msp, size_t n_elements,
+ size_t elem_size, void* chunks[])
+{
+ size_t sz = elem_size; // serves as 1-element array
+ mstate ms = (mstate)msp;
+ if (!ms->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return 0;
+ }
+ return ms->ialloc(n_elements, &sz, 3, chunks);
+}
+
+SPP_API void** mspace_independent_comalloc(mspace msp, size_t n_elements,
+ size_t sizes[], void* chunks[])
+{
+ mstate ms = (mstate)msp;
+ if (!ms->ok_magic())
+ {
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return 0;
+ }
+ return ms->ialloc(n_elements, sizes, 0, chunks);
+}
+
+#endif
+
+SPP_API inline size_t mspace_bulk_free(mspace msp, void* array[], size_t nelem)
+{
+ return ((mstate)msp)->internal_bulk_free(array, nelem);
+}
+
+#if SPP_MALLOC_INSPECT_ALL
+SPP_API void mspace_inspect_all(mspace msp,
+ void(*handler)(void *start,
+ void *end,
+ size_t used_bytes,
+ void* callback_arg),
+ void* arg)
+{
+ mstate ms = (mstate)msp;
+ if (ms->ok_magic())
+ internal_inspect_all(ms, handler, arg);
+ else
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+}
+#endif
+
+SPP_API inline int mspace_trim(mspace msp, size_t pad)
+{
+ int result = 0;
+ mstate ms = (mstate)msp;
+ if (ms->ok_magic())
+ result = ms->sys_trim(pad);
+ else
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return result;
+}
+
+SPP_API inline size_t mspace_footprint(mspace msp)
+{
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ms->ok_magic())
+ result = ms->_footprint;
+ else
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return result;
+}
+
+SPP_API inline size_t mspace_max_footprint(mspace msp)
+{
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ms->ok_magic())
+ result = ms->_max_footprint;
+ else
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return result;
+}
+
+SPP_API inline size_t mspace_footprint_limit(mspace msp)
+{
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ms->ok_magic())
+ {
+ size_t maf = ms->_footprint_limit;
+ result = (maf == 0) ? spp_max_size_t : maf;
+ }
+ else
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return result;
+}
+
+SPP_API inline size_t mspace_set_footprint_limit(mspace msp, size_t bytes)
+{
+ size_t result = 0;
+ mstate ms = (mstate)msp;
+ if (ms->ok_magic())
+ {
+ if (bytes == 0)
+ result = mparams.granularity_align(1); // Use minimal size
+ if (bytes == spp_max_size_t)
+ result = 0; // disable
+ else
+ result = mparams.granularity_align(bytes);
+ ms->_footprint_limit = result;
+ }
+ else
+ SPP_USAGE_ERROR_ACTION(ms, ms);
+ return result;
+}
+
+SPP_API inline size_t mspace_usable_size(const void* mem)
+{
+ if (mem != 0)
+ {
+ mchunkptr p = mem2chunk(mem);
+ if (p->is_inuse())
+ return p->chunksize() - p->overhead_for();
+ }
+ return 0;
+}
+
+SPP_API inline int mspace_mallopt(int param_number, int value)
+{
+ return mparams.change(param_number, value);
+}
+
+} // spp_ namespace
+
+
+#endif // SPP_EXCLUDE_IMPLEMENTATION
+
+#endif // spp_dlalloc__h_