summaryrefslogtreecommitdiffhomepage
path: root/examples/others/sparsepp/spp_dlalloc.h
diff options
context:
space:
mode:
Diffstat (limited to 'examples/others/sparsepp/spp_dlalloc.h')
-rw-r--r--examples/others/sparsepp/spp_dlalloc.h4044
1 files changed, 0 insertions, 4044 deletions
diff --git a/examples/others/sparsepp/spp_dlalloc.h b/examples/others/sparsepp/spp_dlalloc.h
deleted file mode 100644
index f88aab7c..00000000
--- a/examples/others/sparsepp/spp_dlalloc.h
+++ /dev/null
@@ -1,4044 +0,0 @@
-#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_