////////////////////////////////////////////////////////////////////////////// // // (C) Copyright Ion Gaztanaga 2007-2015. Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) // // See http://www.boost.org/libs/container for documentation. // ////////////////////////////////////////////////////////////////////////////// #include #include "errno.h" //dlmalloc bug EINVAL is used in posix_memalign without checking LACKS_ERRNO_H #include "limits.h" //CHAR_BIT #ifdef BOOST_CONTAINER_DLMALLOC_FOOTERS #define FOOTERS 1 #endif #define USE_LOCKS 1 #define MSPACES 1 #define NO_MALLINFO 1 #define NO_MALLOC_STATS 1 //disable sbrk as it's deprecated in some systems and weakens ASLR #define HAVE_MORECORE 0 #if !defined(NDEBUG) #if !defined(DEBUG) #define DEBUG 1 #define DL_DEBUG_DEFINED #endif #endif #define USE_DL_PREFIX #ifdef __GNUC__ #define FORCEINLINE inline #endif #include "dlmalloc_2_8_6.c" #ifdef _MSC_VER #pragma warning (push) #pragma warning (disable : 4127) #pragma warning (disable : 4267) #pragma warning (disable : 4127) #pragma warning (disable : 4702) #pragma warning (disable : 4390) /*empty controlled statement found; is this the intent?*/ #pragma warning (disable : 4251 4231 4660) /*dll warnings*/ #endif #define DL_SIZE_IMPL(p) (chunksize(mem2chunk(p)) - overhead_for(mem2chunk(p))) static size_t s_allocated_memory; /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// // // SLIGHTLY MODIFIED DLMALLOC FUNCTIONS // /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// //This function is equal to mspace_free //replacing PREACTION with 0 and POSTACTION with nothing static void mspace_free_lockless(mspace msp, void* mem) { if (mem != 0) { mchunkptr p = mem2chunk(mem); #if FOOTERS mstate fm = get_mstate_for(p); msp = msp; /* placate people compiling -Wunused */ #else /* FOOTERS */ mstate fm = (mstate)msp; #endif /* FOOTERS */ if (!ok_magic(fm)) { USAGE_ERROR_ACTION(fm, p); return; } if (!0){//PREACTION(fm)) { check_inuse_chunk(fm, p); if (RTCHECK(ok_address(fm, p) && ok_inuse(p))) { size_t psize = chunksize(p); mchunkptr next = chunk_plus_offset(p, psize); if (!pinuse(p)) { size_t prevsize = p->prev_foot; if (is_mmapped(p)) { psize += prevsize + MMAP_FOOT_PAD; if (CALL_MUNMAP((char*)p - prevsize, psize) == 0) fm->footprint -= psize; goto postaction; } else { mchunkptr prev = chunk_minus_offset(p, prevsize); psize += prevsize; p = prev; if (RTCHECK(ok_address(fm, prev))) { /* consolidate backward */ if (p != fm->dv) { unlink_chunk(fm, p, prevsize); } else if ((next->head & INUSE_BITS) == INUSE_BITS) { fm->dvsize = psize; set_free_with_pinuse(p, psize, next); goto postaction; } } else goto erroraction; } } if (RTCHECK(ok_next(p, next) && ok_pinuse(next))) { if (!cinuse(next)) { /* consolidate forward */ if (next == fm->top) { size_t tsize = fm->topsize += psize; fm->top = p; p->head = tsize | PINUSE_BIT; if (p == fm->dv) { fm->dv = 0; fm->dvsize = 0; } if (should_trim(fm, tsize)) sys_trim(fm, 0); goto postaction; } else if (next == fm->dv) { size_t dsize = fm->dvsize += psize; fm->dv = p; set_size_and_pinuse_of_free_chunk(p, dsize); goto postaction; } else { size_t nsize = chunksize(next); psize += nsize; unlink_chunk(fm, next, nsize); set_size_and_pinuse_of_free_chunk(p, psize); if (p == fm->dv) { fm->dvsize = psize; goto postaction; } } } else set_free_with_pinuse(p, psize, next); if (is_small(psize)) { insert_small_chunk(fm, p, psize); check_free_chunk(fm, p); } else { tchunkptr tp = (tchunkptr)p; insert_large_chunk(fm, tp, psize); check_free_chunk(fm, p); if (--fm->release_checks == 0) release_unused_segments(fm); } goto postaction; } } erroraction: USAGE_ERROR_ACTION(fm, p); postaction: ;//POSTACTION(fm); } } } //This function is equal to mspace_malloc //replacing PREACTION with 0 and POSTACTION with nothing void* mspace_malloc_lockless(mspace msp, size_t bytes) { mstate ms = (mstate)msp; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (!0){//PREACTION(ms)) { 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 = ms->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(ms, idx); p = b->fd; assert(chunksize(p) == small_index2size(idx)); unlink_first_small_chunk(ms, b, p, idx); set_inuse_and_pinuse(ms, p, small_index2size(idx)); mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb > ms->dvsize) { if (smallbits != 0) { /* Use chunk in next nonempty smallbin */ mchunkptr b, p, r; size_t rsize; bindex_t i; binmap_t leftbits = (smallbits << idx) & left_bits(idx2bit(idx)); binmap_t leastbit = least_bit(leftbits); compute_bit2idx(leastbit, i); b = smallbin_at(ms, i); p = b->fd; assert(chunksize(p) == small_index2size(i)); unlink_first_small_chunk(ms, b, p, i); rsize = small_index2size(i) - nb; /* Fit here cannot be remainderless if 4byte sizes */ if (SIZE_T_SIZE != 4 && rsize < MIN_CHUNK_SIZE) set_inuse_and_pinuse(ms, p, small_index2size(i)); else { set_size_and_pinuse_of_inuse_chunk(ms, p, nb); r = chunk_plus_offset(p, nb); set_size_and_pinuse_of_free_chunk(r, rsize); replace_dv(ms, r, rsize); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (ms->treemap != 0 && (mem = tmalloc_small(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } } else if (bytes >= MAX_REQUEST) nb = MAX_SIZE_T; /* Too big to allocate. Force failure (in sys alloc) */ else { nb = pad_request(bytes); if (ms->treemap != 0 && (mem = tmalloc_large(ms, nb)) != 0) { check_malloced_chunk(ms, mem, nb); goto postaction; } } if (nb <= ms->dvsize) { size_t rsize = ms->dvsize - nb; mchunkptr p = ms->dv; if (rsize >= MIN_CHUNK_SIZE) { /* split dv */ mchunkptr r = ms->dv = chunk_plus_offset(p, nb); ms->dvsize = rsize; set_size_and_pinuse_of_free_chunk(r, rsize); set_size_and_pinuse_of_inuse_chunk(ms, p, nb); } else { /* exhaust dv */ size_t dvs = ms->dvsize; ms->dvsize = 0; ms->dv = 0; set_inuse_and_pinuse(ms, p, dvs); } mem = chunk2mem(p); check_malloced_chunk(ms, mem, nb); goto postaction; } else if (nb < ms->topsize) { /* Split top */ size_t rsize = ms->topsize -= nb; mchunkptr p = ms->top; mchunkptr r = ms->top = chunk_plus_offset(p, nb); r->head = rsize | PINUSE_BIT; set_size_and_pinuse_of_inuse_chunk(ms, p, nb); mem = chunk2mem(p); check_top_chunk(ms, ms->top); check_malloced_chunk(ms, mem, nb); goto postaction; } mem = sys_alloc(ms, nb); postaction: ;//POSTACTION(ms); return mem; } return 0; } //This function is equal to try_realloc_chunk but handling //minimum and desired bytes static mchunkptr try_realloc_chunk_with_min(mstate m, mchunkptr p, size_t min_nb, size_t des_nb, int can_move) { mchunkptr newp = 0; size_t oldsize = chunksize(p); mchunkptr next = chunk_plus_offset(p, oldsize); if (RTCHECK(ok_address(m, p) && ok_inuse(p) && ok_next(p, next) && ok_pinuse(next))) { if (is_mmapped(p)) { newp = mmap_resize(m, p, des_nb, can_move); if(!newp) //mmap does not return how many bytes we could reallocate, so go the minimum newp = mmap_resize(m, p, min_nb, can_move); } else if (oldsize >= min_nb) { /* already big enough */ size_t nb = oldsize >= des_nb ? des_nb : oldsize; size_t rsize = oldsize - nb; if (rsize >= MIN_CHUNK_SIZE) { /* split off remainder */ mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); } newp = p; } else if (next == m->top) { /* extend into top */ if (oldsize + m->topsize > min_nb) { size_t nb = (oldsize + m->topsize) > des_nb ? des_nb : (oldsize + m->topsize - MALLOC_ALIGNMENT); size_t newsize = oldsize + m->topsize; size_t newtopsize = newsize - nb; mchunkptr newtop = chunk_plus_offset(p, nb); set_inuse(m, p, nb); newtop->head = newtopsize |PINUSE_BIT; m->top = newtop; m->topsize = newtopsize; newp = p; } } else if (next == m->dv) { /* extend into dv */ size_t dvs = m->dvsize; if (oldsize + dvs >= min_nb) { size_t nb = (oldsize + dvs) >= des_nb ? des_nb : (oldsize + dvs); size_t dsize = oldsize + dvs - nb; if (dsize >= MIN_CHUNK_SIZE) { mchunkptr r = chunk_plus_offset(p, nb); mchunkptr n = chunk_plus_offset(r, dsize); set_inuse(m, p, nb); set_size_and_pinuse_of_free_chunk(r, dsize); clear_pinuse(n); m->dvsize = dsize; m->dv = r; } else { /* exhaust dv */ size_t newsize = oldsize + dvs; set_inuse(m, p, newsize); m->dvsize = 0; m->dv = 0; } newp = p; } } else if (!cinuse(next)) { /* extend into next free chunk */ size_t nextsize = chunksize(next); if (oldsize + nextsize >= min_nb) { size_t nb = (oldsize + nextsize) >= des_nb ? des_nb : (oldsize + nextsize); size_t rsize = oldsize + nextsize - nb; unlink_chunk(m, next, nextsize); if (rsize < MIN_CHUNK_SIZE) { size_t newsize = oldsize + nextsize; set_inuse(m, p, newsize); } else { mchunkptr r = chunk_plus_offset(p, nb); set_inuse(m, p, nb); set_inuse(m, r, rsize); dispose_chunk(m, r, rsize); } newp = p; } } } else { USAGE_ERROR_ACTION(m, chunk2mem(p)); } return newp; } /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// // // NEW FUNCTIONS BASED ON DLMALLOC INTERNALS // /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////// #define GET_TRUNCATED_SIZE(ORIG_SIZE, ROUNDTO) ((ORIG_SIZE)/(ROUNDTO)*(ROUNDTO)) #define GET_ROUNDED_SIZE(ORIG_SIZE, ROUNDTO) ((((ORIG_SIZE)-1)/(ROUNDTO)+1)*(ROUNDTO)) #define GET_TRUNCATED_PO2_SIZE(ORIG_SIZE, ROUNDTO) ((ORIG_SIZE) & (~(ROUNDTO-1))) #define GET_ROUNDED_PO2_SIZE(ORIG_SIZE, ROUNDTO) (((ORIG_SIZE - 1) & (~(ROUNDTO-1))) + ROUNDTO) /* Greatest common divisor and least common multiple gcd is an algorithm that calculates the greatest common divisor of two integers, using Euclid's algorithm. Pre: A > 0 && B > 0 Recommended: A > B*/ #define CALCULATE_GCD(A, B, OUT)\ {\ size_t a = A;\ size_t b = B;\ do\ {\ size_t tmp = b;\ b = a % b;\ a = tmp;\ } while (b != 0);\ \ OUT = a;\ } /* lcm is an algorithm that calculates the least common multiple of two integers. Pre: A > 0 && B > 0 Recommended: A > B*/ #define CALCULATE_LCM(A, B, OUT)\ {\ CALCULATE_GCD(A, B, OUT);\ OUT = (A / OUT)*B;\ } static int calculate_lcm_and_needs_backwards_lcmed (size_t backwards_multiple, size_t received_size, size_t size_to_achieve, size_t *plcm, size_t *pneeds_backwards_lcmed) { /* Now calculate lcm */ size_t max = backwards_multiple; size_t min = MALLOC_ALIGNMENT; size_t needs_backwards; size_t needs_backwards_lcmed; size_t lcm; size_t current_forward; /*Swap if necessary*/ if(max < min){ size_t tmp = min; min = max; max = tmp; } /*Check if it's power of two*/ if((backwards_multiple & (backwards_multiple-1)) == 0){ if(0 != (size_to_achieve & ((backwards_multiple-1)))){ USAGE_ERROR_ACTION(m, oldp); return 0; } lcm = max; /*If we want to use minbytes data to get a buffer between maxbytes and minbytes if maxbytes can't be achieved, calculate the biggest of all possibilities*/ current_forward = GET_TRUNCATED_PO2_SIZE(received_size, backwards_multiple); needs_backwards = size_to_achieve - current_forward; assert((needs_backwards % backwards_multiple) == 0); needs_backwards_lcmed = GET_ROUNDED_PO2_SIZE(needs_backwards, lcm); *plcm = lcm; *pneeds_backwards_lcmed = needs_backwards_lcmed; return 1; } /*Check if it's multiple of alignment*/ else if((backwards_multiple & (MALLOC_ALIGNMENT - 1u)) == 0){ lcm = backwards_multiple; current_forward = GET_TRUNCATED_SIZE(received_size, backwards_multiple); //No need to round needs_backwards because backwards_multiple == lcm needs_backwards_lcmed = needs_backwards = size_to_achieve - current_forward; assert((needs_backwards_lcmed & (MALLOC_ALIGNMENT - 1u)) == 0); *plcm = lcm; *pneeds_backwards_lcmed = needs_backwards_lcmed; return 1; } /*Check if it's multiple of the half of the alignmment*/ else if((backwards_multiple & ((MALLOC_ALIGNMENT/2u) - 1u)) == 0){ lcm = backwards_multiple*2u; current_forward = GET_TRUNCATED_SIZE(received_size, backwards_multiple); needs_backwards_lcmed = needs_backwards = size_to_achieve - current_forward; if(0 != (needs_backwards_lcmed & (MALLOC_ALIGNMENT-1))) //while(0 != (needs_backwards_lcmed & (MALLOC_ALIGNMENT-1))) needs_backwards_lcmed += backwards_multiple; assert((needs_backwards_lcmed % lcm) == 0); *plcm = lcm; *pneeds_backwards_lcmed = needs_backwards_lcmed; return 1; } /*Check if it's multiple of the quarter of the alignmment*/ else if((backwards_multiple & ((MALLOC_ALIGNMENT/4u) - 1u)) == 0){ size_t remainder; lcm = backwards_multiple*4u; current_forward = GET_TRUNCATED_SIZE(received_size, backwards_multiple); needs_backwards_lcmed = needs_backwards = size_to_achieve - current_forward; //while(0 != (needs_backwards_lcmed & (MALLOC_ALIGNMENT-1))) //needs_backwards_lcmed += backwards_multiple; if(0 != (remainder = ((needs_backwards_lcmed & (MALLOC_ALIGNMENT-1))>>(MALLOC_ALIGNMENT/8u)))){ if(backwards_multiple & MALLOC_ALIGNMENT/2u){ needs_backwards_lcmed += (remainder)*backwards_multiple; } else{ needs_backwards_lcmed += (4-remainder)*backwards_multiple; } } assert((needs_backwards_lcmed % lcm) == 0); *plcm = lcm; *pneeds_backwards_lcmed = needs_backwards_lcmed; return 1; } else{ CALCULATE_LCM(max, min, lcm); /*If we want to use minbytes data to get a buffer between maxbytes and minbytes if maxbytes can't be achieved, calculate the biggest of all possibilities*/ current_forward = GET_TRUNCATED_SIZE(received_size, backwards_multiple); needs_backwards = size_to_achieve - current_forward; assert((needs_backwards % backwards_multiple) == 0); needs_backwards_lcmed = GET_ROUNDED_SIZE(needs_backwards, lcm); *plcm = lcm; *pneeds_backwards_lcmed = needs_backwards_lcmed; return 1; } } static void *internal_grow_both_sides (mstate m ,allocation_type command ,void *oldmem ,size_t minbytes ,size_t maxbytes ,size_t *received_size ,size_t backwards_multiple ,int only_preferred_backwards) { mchunkptr oldp = mem2chunk(oldmem); size_t oldsize = chunksize(oldp); *received_size = oldsize - overhead_for(oldp); if(minbytes <= *received_size) return oldmem; if (RTCHECK(ok_address(m, oldp) && ok_inuse(oldp))) { if(command & BOOST_CONTAINER_EXPAND_FWD){ if(try_realloc_chunk_with_min(m, oldp, request2size(minbytes), request2size(maxbytes), 0)){ check_inuse_chunk(m, oldp); *received_size = DL_SIZE_IMPL(oldmem); s_allocated_memory += chunksize(oldp) - oldsize; return oldmem; } } else{ *received_size = DL_SIZE_IMPL(oldmem); if(*received_size >= maxbytes) return oldmem; } /* Should we check this? if(backwards_multiple && (0 != (minbytes % backwards_multiple) && 0 != (maxbytes % backwards_multiple)) ){ USAGE_ERROR_ACTION(m, oldp); return 0; } */ /* We reach here only if forward expansion fails */ if(!(command & BOOST_CONTAINER_EXPAND_BWD) || pinuse(oldp)){ return 0; } { size_t prevsize = oldp->prev_foot; if ((prevsize & USE_MMAP_BIT) != 0){ /*Return failure the previous chunk was mmapped. mremap does not allow expanding to a fixed address (MREMAP_MAYMOVE) without copying (MREMAP_MAYMOVE must be also set).*/ return 0; } else { mchunkptr prev = chunk_minus_offset(oldp, prevsize); size_t dsize = oldsize + prevsize; size_t needs_backwards_lcmed; size_t lcm; /* Let's calculate the number of extra bytes of data before the current block's begin. The value is a multiple of backwards_multiple and the alignment*/ if(!calculate_lcm_and_needs_backwards_lcmed ( backwards_multiple, *received_size , only_preferred_backwards ? maxbytes : minbytes , &lcm, &needs_backwards_lcmed) || !RTCHECK(ok_address(m, prev))){ USAGE_ERROR_ACTION(m, oldp); return 0; } /* Check if previous block has enough size */ else if(prevsize < needs_backwards_lcmed){ /* preferred size? */ return 0; } /* Now take all next space. This must succeed, as we've previously calculated the correct size */ if(command & BOOST_CONTAINER_EXPAND_FWD){ if(!try_realloc_chunk_with_min(m, oldp, request2size(*received_size), request2size(*received_size), 0)){ assert(0); } check_inuse_chunk(m, oldp); *received_size = DL_SIZE_IMPL(oldmem); s_allocated_memory += chunksize(oldp) - oldsize; oldsize = chunksize(oldp); dsize = oldsize + prevsize; } /* We need a minimum size to split the previous one */ if(prevsize >= (needs_backwards_lcmed + MIN_CHUNK_SIZE)){ mchunkptr r = chunk_minus_offset(oldp, needs_backwards_lcmed); size_t rsize = oldsize + needs_backwards_lcmed; size_t newprevsize = dsize - rsize; int prev_was_dv = prev == m->dv; assert(newprevsize >= MIN_CHUNK_SIZE); if (prev_was_dv) { m->dvsize = newprevsize; } else{/* if ((next->head & INUSE_BITS) == INUSE_BITS) { */ unlink_chunk(m, prev, prevsize); insert_chunk(m, prev, newprevsize); } set_size_and_pinuse_of_free_chunk(prev, newprevsize); clear_pinuse(r); set_inuse(m, r, rsize); check_malloced_chunk(m, chunk2mem(r), rsize); *received_size = chunksize(r) - overhead_for(r); s_allocated_memory += chunksize(r) - oldsize; return chunk2mem(r); } /* Check if there is no place to create a new block and the whole new block is multiple of the backwards expansion multiple */ else if(prevsize >= needs_backwards_lcmed && !(prevsize % lcm)) { /* Just merge the whole previous block */ /* prevsize is multiple of lcm (and backwards_multiple)*/ *received_size += prevsize; if (prev != m->dv) { unlink_chunk(m, prev, prevsize); } else{ m->dvsize = 0; m->dv = 0; } set_inuse(m, prev, dsize); check_malloced_chunk(m, chunk2mem(prev), dsize); s_allocated_memory += chunksize(prev) - oldsize; return chunk2mem(prev); } else{ /* Previous block was big enough but there is no room to create an empty block and taking the whole block does not fulfill alignment requirements */ return 0; } } } } else{ USAGE_ERROR_ACTION(m, oldmem); return 0; } return 0; } /* This is similar to mmap_resize but: * Only to shrink * It takes min and max sizes * Takes additional 'do_commit' argument to obtain the final size before doing the real shrink operation. */ static int internal_mmap_shrink_in_place(mstate m, mchunkptr oldp, size_t nbmin, size_t nbmax, size_t *received_size, int do_commit) { size_t oldsize = chunksize(oldp); *received_size = oldsize; #if HAVE_MREMAP if (is_small(nbmax)) /* Can't shrink mmap regions below small size */ return 0; { size_t effective_min = nbmin > MIN_LARGE_SIZE ? nbmin : MIN_LARGE_SIZE; /* Keep old chunk if big enough but not too big */ if (oldsize >= effective_min + SIZE_T_SIZE && (oldsize - effective_min) <= (mparams.granularity << 1)) return 0; /* Now calculate new sizes */ { size_t offset = oldp->prev_foot; size_t oldmmsize = oldsize + offset + MMAP_FOOT_PAD; size_t newmmsize = mmap_align(effective_min + SIX_SIZE_T_SIZES + CHUNK_ALIGN_MASK); *received_size = newmmsize; if(!do_commit){ const int flags = 0; /* placate people compiling -Wunused */ char* cp = (char*)CALL_MREMAP((char*)oldp - offset, oldmmsize, newmmsize, flags); /*This must always succeed */ if(!cp){ USAGE_ERROR_ACTION(m, m); return 0; } { mchunkptr newp = (mchunkptr)(cp + offset); size_t psize = newmmsize - offset - MMAP_FOOT_PAD; newp->head = psize; mark_inuse_foot(m, newp, psize); chunk_plus_offset(newp, psize)->head = FENCEPOST_HEAD; chunk_plus_offset(newp, psize+SIZE_T_SIZE)->head = 0; if (cp < m->least_addr) m->least_addr = cp; if ((m->footprint += newmmsize - oldmmsize) > m->max_footprint) m->max_footprint = m->footprint; check_mmapped_chunk(m, newp); } } } return 1; } #else //#if HAVE_MREMAP (void)m; (void)oldp; (void)nbmin; (void)nbmax; (void)received_size; (void)do_commit; return 0; #endif //#if HAVE_MREMAP } static int internal_shrink(mstate m, void* oldmem, size_t minbytes, size_t maxbytes, size_t *received_size, int do_commit) { *received_size = chunksize(mem2chunk(oldmem)) - overhead_for(mem2chunk(oldmem)); if (minbytes >= MAX_REQUEST || maxbytes >= MAX_REQUEST) { MALLOC_FAILURE_ACTION; return 0; } else if(minbytes < MIN_REQUEST){ minbytes = MIN_REQUEST; } if (minbytes > maxbytes) { return 0; } { mchunkptr oldp = mem2chunk(oldmem); size_t oldsize = chunksize(oldp); mchunkptr next = chunk_plus_offset(oldp, oldsize); void* extra = 0; /* Try to either shrink or extend into top. Else malloc-copy-free*/ if (RTCHECK(ok_address(m, oldp) && ok_inuse(oldp) && ok_next(oldp, next) && ok_pinuse(next))) { size_t nbmin = request2size(minbytes); size_t nbmax = request2size(maxbytes); if (nbmin > oldsize){ /* Return error if old size is too small */ } else if (is_mmapped(oldp)){ return internal_mmap_shrink_in_place(m, oldp, nbmin, nbmax, received_size, do_commit); } else{ // nbmin <= oldsize /* already big enough*/ size_t nb = nbmin; size_t rsize = oldsize - nb; if (rsize >= MIN_CHUNK_SIZE) { if(do_commit){ mchunkptr remainder = chunk_plus_offset(oldp, nb); set_inuse(m, oldp, nb); set_inuse(m, remainder, rsize); s_allocated_memory -= rsize; extra = chunk2mem(remainder); mspace_free_lockless(m, extra); check_inuse_chunk(m, oldp); } *received_size = nb - overhead_for(oldp); return 1; } } } else { USAGE_ERROR_ACTION(m, oldmem); } return 0; } } #define INTERNAL_MULTIALLOC_DEFAULT_CONTIGUOUS_MEM 4096 #define SQRT_MAX_SIZE_T (((size_t)-1)>>(sizeof(size_t)*CHAR_BIT/2)) static int internal_node_multialloc (mstate m, size_t n_elements, size_t element_size, size_t contiguous_elements, boost_cont_memchain *pchain) { void* mem; /* malloced aggregate space */ mchunkptr p; /* corresponding chunk */ size_t remainder_size; /* remaining bytes while splitting */ flag_t was_enabled; /* to disable mmap */ size_t elements_per_segment = 0; size_t element_req_size = request2size(element_size); boost_cont_memchain_it prev_last_it = BOOST_CONTAINER_MEMCHAIN_LAST_IT(pchain); /*Error if wrong element_size parameter */ if (!element_size || /*OR Error if n_elements less than contiguous_elements */ ((contiguous_elements + 1) > (BOOST_CONTAINER_DL_MULTIALLOC_DEFAULT_CONTIGUOUS + 1) && n_elements < contiguous_elements) || /* OR Error if integer overflow */ (SQRT_MAX_SIZE_T < (element_req_size | contiguous_elements) && (MAX_SIZE_T / element_req_size) < contiguous_elements)) { return 0; } switch (contiguous_elements) { case BOOST_CONTAINER_DL_MULTIALLOC_DEFAULT_CONTIGUOUS: { /* Default contiguous, just check that we can store at least one element */ elements_per_segment = INTERNAL_MULTIALLOC_DEFAULT_CONTIGUOUS_MEM / element_req_size; elements_per_segment += (size_t)(!elements_per_segment); } break; case BOOST_CONTAINER_DL_MULTIALLOC_ALL_CONTIGUOUS: /* All elements should be allocated in a single call */ elements_per_segment = n_elements; break; default: /* Allocate in chunks of "contiguous_elements" */ elements_per_segment = contiguous_elements; } { size_t i; size_t next_i; /* 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(m); disable_mmap(m); for (i = 0; i != n_elements; i = next_i) { size_t accum_size; size_t n_elements_left = n_elements - i; next_i = i + ((n_elements_left < elements_per_segment) ? n_elements_left : elements_per_segment); accum_size = element_req_size * (next_i - i); mem = mspace_malloc_lockless(m, accum_size - CHUNK_OVERHEAD); if (mem == 0) { BOOST_CONTAINER_MEMIT_NEXT(prev_last_it); while (i) { void *addr = BOOST_CONTAINER_MEMIT_ADDR(prev_last_it); --i; BOOST_CONTAINER_MEMIT_NEXT(prev_last_it); s_allocated_memory -= chunksize(mem2chunk(addr)); mspace_free_lockless(m, addr); } if (was_enabled) enable_mmap(m); return 0; } p = mem2chunk(mem); remainder_size = chunksize(p); s_allocated_memory += remainder_size; assert(!is_mmapped(p)); { /* split out elements */ //void *mem_orig = mem; //boost_cont_memchain_it last_it = BOOST_CONTAINER_MEMCHAIN_LAST_IT(pchain); size_t num_elements = next_i - i; size_t num_loops = num_elements - 1; remainder_size -= element_req_size * num_loops; while (num_loops) { --num_loops; //void **mem_prev = ((void**)mem); set_size_and_pinuse_of_inuse_chunk(m, p, element_req_size); BOOST_CONTAINER_MEMCHAIN_PUSH_BACK(pchain, mem); p = chunk_plus_offset(p, element_req_size); mem = chunk2mem(p); //*mem_prev = mem; } set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); BOOST_CONTAINER_MEMCHAIN_PUSH_BACK(pchain, mem); //BOOST_CONTAINER_MEMCHAIN_INCORPORATE_AFTER(pchain, last_it, mem_orig, mem, num_elements); } } if (was_enabled) enable_mmap(m); } return 1; } #define BOOST_CONTAINER_DLMALLOC_SIMPLE_MULTIDEALLOC #ifndef BOOST_CONTAINER_DLMALLOC_SIMPLE_MULTIDEALLOC #define BOOST_ALLOC_PLUS_MEMCHAIN_MEM_JUMP_NEXT(THISMEM, NEXTMEM) \ *((void**)(THISMEM)) = *((void**)((NEXTMEM))) //This function is based on internal_bulk_free //replacing iteration over array[] with boost_cont_memchain. //Instead of returning the unallocated nodes, returns a chain of non-deallocated nodes. //After forward merging, backwards merging is also tried static void internal_multialloc_free(mstate m, boost_cont_memchain *pchain) { #if FOOTERS boost_cont_memchain ret_chain; BOOST_CONTAINER_MEMCHAIN_INIT(&ret_chain); #endif if (!PREACTION(m)) { boost_cont_memchain_it a_it = BOOST_CONTAINER_MEMCHAIN_BEGIN_IT(pchain); while (!BOOST_CONTAINER_MEMCHAIN_IS_END_IT(pchain, a_it)) { /* Iterate though all memory holded by the chain */ void* a_mem = BOOST_CONTAINER_MEMIT_ADDR(a_it); mchunkptr a_p = mem2chunk(a_mem); size_t psize = chunksize(a_p); #if FOOTERS if (get_mstate_for(a_p) != m) { BOOST_CONTAINER_MEMIT_NEXT(a_it); BOOST_CONTAINER_MEMCHAIN_PUSH_BACK(&ret_chain, a_mem); continue; } #endif check_inuse_chunk(m, a_p); if (RTCHECK(ok_address(m, a_p) && ok_inuse(a_p))) { while (1) { /* Internal loop to speed up forward and backward merging (avoids some redundant checks) */ boost_cont_memchain_it b_it = a_it; BOOST_CONTAINER_MEMIT_NEXT(b_it); if (!BOOST_CONTAINER_MEMCHAIN_IS_END_IT(pchain, b_it)) { void *b_mem = BOOST_CONTAINER_MEMIT_ADDR(b_it); mchunkptr b_p = mem2chunk(b_mem); if (b_p == next_chunk(a_p)) { /* b chunk is contiguous and next so b's size can be added to a */ psize += chunksize(b_p); set_inuse(m, a_p, psize); BOOST_ALLOC_PLUS_MEMCHAIN_MEM_JUMP_NEXT(a_mem, b_mem); continue; } if (RTCHECK(ok_address(m, b_p) && ok_inuse(b_p))) { /* b chunk is contiguous and previous so a's size can be added to b */ if (a_p == next_chunk(b_p)) { psize += chunksize(b_p); set_inuse(m, b_p, psize); a_it = b_it; a_p = b_p; a_mem = b_mem; continue; } } } /* Normal deallocation starts again in the outer loop */ a_it = b_it; s_allocated_memory -= psize; dispose_chunk(m, a_p, psize); break; } } else { CORRUPTION_ERROR_ACTION(m); break; } } if (should_trim(m, m->topsize)) sys_trim(m, 0); POSTACTION(m); } #if FOOTERS { boost_cont_memchain_it last_pchain = BOOST_CONTAINER_MEMCHAIN_LAST_IT(pchain); BOOST_CONTAINER_MEMCHAIN_INIT(pchain); BOOST_CONTAINER_MEMCHAIN_INCORPORATE_AFTER (pchain , last_pchain , BOOST_CONTAINER_MEMCHAIN_FIRSTMEM(&ret_chain) , BOOST_CONTAINER_MEMCHAIN_LASTMEM(&ret_chain) , BOOST_CONTAINER_MEMCHAIN_SIZE(&ret_chain) ); } #endif } #else //BOOST_CONTAINER_DLMALLOC_SIMPLE_MULTIDEALLOC //This function is based on internal_bulk_free //replacing iteration over array[] with boost_cont_memchain. //Instead of returning the unallocated nodes, returns a chain of non-deallocated nodes. //After forward merging, backwards merging is also tried static void internal_multialloc_free(mstate m, boost_cont_memchain *pchain) { if (!PREACTION(m)) { boost_cont_memchain_it a_it = BOOST_CONTAINER_MEMCHAIN_BEGIN_IT(pchain); while (!BOOST_CONTAINER_MEMCHAIN_IS_END_IT(pchain, a_it)) { /* Iterate though all memory holded by the chain */ void* a_mem = BOOST_CONTAINER_MEMIT_ADDR(a_it); BOOST_CONTAINER_MEMIT_NEXT(a_it); s_allocated_memory -= chunksize(mem2chunk(a_mem)); mspace_free_lockless(m, a_mem); } POSTACTION(m); } } #endif //BOOST_CONTAINER_DLMALLOC_SIMPLE_MULTIDEALLOC static int internal_multialloc_arrays (mstate m, size_t n_elements, const size_t* sizes, size_t element_size, size_t contiguous_elements, boost_cont_memchain *pchain) { void* mem; /* malloced aggregate space */ mchunkptr p; /* corresponding chunk */ size_t remainder_size; /* remaining bytes while splitting */ flag_t was_enabled; /* to disable mmap */ size_t size; size_t boost_cont_multialloc_segmented_malloc_size; size_t max_size; /* Check overflow */ if(!element_size){ return 0; } max_size = MAX_REQUEST/element_size; /* Different sizes*/ switch(contiguous_elements){ case BOOST_CONTAINER_DL_MULTIALLOC_DEFAULT_CONTIGUOUS: /* Use default contiguous mem */ boost_cont_multialloc_segmented_malloc_size = INTERNAL_MULTIALLOC_DEFAULT_CONTIGUOUS_MEM; break; case BOOST_CONTAINER_DL_MULTIALLOC_ALL_CONTIGUOUS: boost_cont_multialloc_segmented_malloc_size = MAX_REQUEST + CHUNK_OVERHEAD; break; default: if(max_size < contiguous_elements){ return 0; } else{ /* The suggested buffer is just the the element count by the size */ boost_cont_multialloc_segmented_malloc_size = element_size*contiguous_elements; } } { size_t i; size_t next_i; /* 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(m); disable_mmap(m); for(i = 0, next_i = 0; i != n_elements; i = next_i) { int error = 0; size_t accum_size; for(accum_size = 0; next_i != n_elements; ++next_i){ size_t cur_array_size = sizes[next_i]; if(max_size < cur_array_size){ error = 1; break; } else{ size_t reqsize = request2size(cur_array_size*element_size); if(((boost_cont_multialloc_segmented_malloc_size - CHUNK_OVERHEAD) - accum_size) < reqsize){ if(!accum_size){ accum_size += reqsize; ++next_i; } break; } accum_size += reqsize; } } mem = error ? 0 : mspace_malloc_lockless(m, accum_size - CHUNK_OVERHEAD); if (mem == 0){ boost_cont_memchain_it it = BOOST_CONTAINER_MEMCHAIN_BEGIN_IT(pchain); while(i--){ void *addr = BOOST_CONTAINER_MEMIT_ADDR(it); BOOST_CONTAINER_MEMIT_NEXT(it); s_allocated_memory -= chunksize(mem2chunk(addr)); mspace_free_lockless(m, addr); } if (was_enabled) enable_mmap(m); return 0; } p = mem2chunk(mem); remainder_size = chunksize(p); s_allocated_memory += remainder_size; assert(!is_mmapped(p)); { /* split out elements */ void *mem_orig = mem; boost_cont_memchain_it last_it = BOOST_CONTAINER_MEMCHAIN_LAST_IT(pchain); size_t num_elements = next_i-i; for(++i; i != next_i; ++i) { void **mem_prev = ((void**)mem); size = request2size(sizes[i]*element_size); remainder_size -= size; set_size_and_pinuse_of_inuse_chunk(m, p, size); p = chunk_plus_offset(p, size); mem = chunk2mem(p); *mem_prev = mem; } set_size_and_pinuse_of_inuse_chunk(m, p, remainder_size); BOOST_CONTAINER_MEMCHAIN_INCORPORATE_AFTER(pchain, last_it, mem_orig, mem, num_elements); } } if (was_enabled) enable_mmap(m); } return 1; } int boost_cont_multialloc_arrays (size_t n_elements, const size_t *sizes, size_t element_size, size_t contiguous_elements, boost_cont_memchain *pchain) { int ret = 0; mstate ms = (mstate)gm; ensure_initialization(); if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); } else if (!PREACTION(ms)) { ret = internal_multialloc_arrays(ms, n_elements, sizes, element_size, contiguous_elements, pchain); POSTACTION(ms); } return ret; } /*Doug Lea malloc extensions*/ static boost_cont_malloc_stats_t get_malloc_stats(mstate m) { boost_cont_malloc_stats_t ret = { 0, 0, 0 }; ensure_initialization(); if (!PREACTION(m)) { size_t maxfp = 0; size_t fp = 0; size_t used = 0; check_malloc_state(m); if (is_initialized(m)) { msegmentptr s = &m->seg; maxfp = m->max_footprint; fp = m->footprint; used = fp - (m->topsize + TOP_FOOT_SIZE); while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { if (!cinuse(q)) used -= chunksize(q); q = next_chunk(q); } s = s->next; } } ret.max_system_bytes = maxfp; ret.system_bytes = fp; ret.in_use_bytes = used; POSTACTION(m); } return ret; } size_t boost_cont_size(const void *p) { return DL_SIZE_IMPL(p); } void* boost_cont_malloc(size_t bytes) { size_t received_bytes; ensure_initialization(); return boost_cont_allocation_command (BOOST_CONTAINER_ALLOCATE_NEW, 1, bytes, bytes, &received_bytes, 0).first; } void boost_cont_free(void* mem) { mstate ms = (mstate)gm; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); } else if (!PREACTION(ms)) { if(mem) s_allocated_memory -= chunksize(mem2chunk(mem)); mspace_free_lockless(ms, mem); POSTACTION(ms); } } void* boost_cont_memalign(size_t bytes, size_t alignment) { void *addr; ensure_initialization(); addr = mspace_memalign(gm, alignment, bytes); if(addr){ s_allocated_memory += chunksize(mem2chunk(addr)); } return addr; } int boost_cont_multialloc_nodes (size_t n_elements, size_t elem_size, size_t contiguous_elements, boost_cont_memchain *pchain) { int ret = 0; mstate ms = (mstate)gm; ensure_initialization(); if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); } else if (!PREACTION(ms)) { ret = internal_node_multialloc(ms, n_elements, elem_size, contiguous_elements, pchain); POSTACTION(ms); } return ret; } size_t boost_cont_footprint() { return ((mstate)gm)->footprint; } size_t boost_cont_allocated_memory() { size_t alloc_mem = 0; mstate m = (mstate)gm; ensure_initialization(); if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); } if (!PREACTION(m)) { check_malloc_state(m); if (is_initialized(m)) { size_t nfree = SIZE_T_ONE; /* top always free */ size_t mfree = m->topsize + TOP_FOOT_SIZE; size_t sum = mfree; msegmentptr s = &m->seg; while (s != 0) { mchunkptr q = align_as_chunk(s->base); while (segment_holds(s, q) && q != m->top && q->head != FENCEPOST_HEAD) { size_t sz = chunksize(q); sum += sz; if (!is_inuse(q)) { mfree += sz; ++nfree; } q = next_chunk(q); } s = s->next; } { size_t uordblks = m->footprint - mfree; if(nfree) alloc_mem = (size_t)(uordblks - (nfree-1)*TOP_FOOT_SIZE); else alloc_mem = uordblks; } } POSTACTION(m); } return alloc_mem; } size_t boost_cont_chunksize(const void *p) { return chunksize(mem2chunk(p)); } int boost_cont_all_deallocated() { return !s_allocated_memory; } boost_cont_malloc_stats_t boost_cont_malloc_stats() { mstate ms = (mstate)gm; if (ok_magic(ms)) { return get_malloc_stats(ms); } else { boost_cont_malloc_stats_t r = { 0, 0, 0 }; USAGE_ERROR_ACTION(ms,ms); return r; } } size_t boost_cont_in_use_memory() { return s_allocated_memory; } int boost_cont_trim(size_t pad) { ensure_initialization(); return dlmalloc_trim(pad); } int boost_cont_grow (void* oldmem, size_t minbytes, size_t maxbytes, size_t *received) { mstate ms = (mstate)gm; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (!PREACTION(ms)) { mchunkptr p = mem2chunk(oldmem); size_t oldsize = chunksize(p); p = try_realloc_chunk_with_min(ms, p, request2size(minbytes), request2size(maxbytes), 0); POSTACTION(ms); if(p){ check_inuse_chunk(ms, p); *received = DL_SIZE_IMPL(oldmem); s_allocated_memory += chunksize(p) - oldsize; } return 0 != p; } return 0; } int boost_cont_shrink (void* oldmem, size_t minbytes, size_t maxbytes, size_t *received, int do_commit) { mstate ms = (mstate)gm; if (!ok_magic(ms)) { USAGE_ERROR_ACTION(ms,ms); return 0; } if (!PREACTION(ms)) { int ret = internal_shrink(ms, oldmem, minbytes, maxbytes, received, do_commit); POSTACTION(ms); return 0 != ret; } return 0; } void* boost_cont_alloc (size_t minbytes, size_t preferred_bytes, size_t *received_bytes) { //ensure_initialization provided by boost_cont_allocation_command return boost_cont_allocation_command (BOOST_CONTAINER_ALLOCATE_NEW, 1, minbytes, preferred_bytes, received_bytes, 0).first; } void boost_cont_multidealloc(boost_cont_memchain *pchain) { mstate ms = (mstate)gm; if (!ok_magic(ms)) { (void)ms; USAGE_ERROR_ACTION(ms,ms); } internal_multialloc_free(ms, pchain); } int boost_cont_malloc_check() { #ifdef DEBUG mstate ms = (mstate)gm; ensure_initialization(); if (!ok_magic(ms)) { (void)ms; USAGE_ERROR_ACTION(ms,ms); return 0; } check_malloc_state(ms); return 1; #else return 1; #endif } boost_cont_command_ret_t boost_cont_allocation_command (allocation_type command, size_t sizeof_object, size_t limit_size , size_t preferred_size, size_t *received_size, void *reuse_ptr) { boost_cont_command_ret_t ret = { 0, 0 }; ensure_initialization(); if(command & (BOOST_CONTAINER_SHRINK_IN_PLACE | BOOST_CONTAINER_TRY_SHRINK_IN_PLACE)){ int success = boost_cont_shrink( reuse_ptr, preferred_size, limit_size , received_size, (command & BOOST_CONTAINER_SHRINK_IN_PLACE)); ret.first = success ? reuse_ptr : 0; return ret; } *received_size = 0; if(limit_size > preferred_size) return ret; { mstate ms = (mstate)gm; /*Expand in place*/ if (!PREACTION(ms)) { #if FOOTERS if(reuse_ptr){ mstate m = get_mstate_for(mem2chunk(reuse_ptr)); if (!ok_magic(m)) { USAGE_ERROR_ACTION(m, reuse_ptr); return ret; } } #endif if(reuse_ptr && (command & (BOOST_CONTAINER_EXPAND_FWD | BOOST_CONTAINER_EXPAND_BWD))){ void *r = internal_grow_both_sides ( ms, command, reuse_ptr, limit_size , preferred_size, received_size, sizeof_object, 1); if(r){ ret.first = r; ret.second = 1; goto postaction; } } if(command & BOOST_CONTAINER_ALLOCATE_NEW){ void *addr = mspace_malloc_lockless(ms, preferred_size); if(!addr) addr = mspace_malloc_lockless(ms, limit_size); if(addr){ s_allocated_memory += chunksize(mem2chunk(addr)); *received_size = DL_SIZE_IMPL(addr); } ret.first = addr; ret.second = 0; if(addr){ goto postaction; } } //Now try to expand both sides with min size if(reuse_ptr && (command & (BOOST_CONTAINER_EXPAND_FWD | BOOST_CONTAINER_EXPAND_BWD))){ void *r = internal_grow_both_sides ( ms, command, reuse_ptr, limit_size , preferred_size, received_size, sizeof_object, 0); if(r){ ret.first = r; ret.second = 1; goto postaction; } } postaction: POSTACTION(ms); } } return ret; } int boost_cont_mallopt(int param_number, int value) { return change_mparam(param_number, value); } void *boost_cont_sync_create() { void *p = boost_cont_malloc(sizeof(MLOCK_T)); if(p){ if(0 != INITIAL_LOCK((MLOCK_T*)p)){ boost_cont_free(p); p = 0; } } return p; } void boost_cont_sync_destroy(void *sync) { if(sync){ (void)DESTROY_LOCK((MLOCK_T*)sync); boost_cont_free(sync); } } int boost_cont_sync_lock(void *sync) { return 0 == (ACQUIRE_LOCK((MLOCK_T*)sync)); } void boost_cont_sync_unlock(void *sync) { RELEASE_LOCK((MLOCK_T*)sync); } int boost_cont_global_sync_lock() { int ret; ensure_initialization(); ret = ACQUIRE_MALLOC_GLOBAL_LOCK(); return 0 == ret; } void boost_cont_global_sync_unlock() { RELEASE_MALLOC_GLOBAL_LOCK() } //#ifdef DL_DEBUG_DEFINED // #undef DEBUG //#endif #ifdef _MSC_VER #pragma warning (pop) #endif