#define	JEMALLOC_CHUNK_C_
#include "jemalloc/internal/jemalloc_internal.h"

/******************************************************************************/
/* Data. */

size_t	opt_lg_chunk = LG_CHUNK_DEFAULT;
#ifdef JEMALLOC_SWAP
bool	opt_overcommit = true;
#endif

#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
malloc_mutex_t	chunks_mtx;
chunk_stats_t	stats_chunks;
#endif

#ifdef JEMALLOC_IVSALLOC
rtree_t		*chunks_rtree;
#endif

/* Various chunk-related settings. */
size_t		chunksize;
size_t		chunksize_mask; /* (chunksize - 1). */
size_t		chunk_npages;
size_t		map_bias;
size_t		arena_maxclass; /* Max size class for arenas. */

/******************************************************************************/

/*
 * If the caller specifies (*zero == false), it is still possible to receive
 * zeroed memory, in which case *zero is toggled to true.  arena_chunk_alloc()
 * takes advantage of this to avoid demanding zeroed chunks, but taking
 * advantage of them if they are returned.
 */
void *
chunk_alloc(size_t size, bool base, bool *zero)
{
	void *ret;

	assert(size != 0);
	assert((size & chunksize_mask) == 0);

#ifdef JEMALLOC_SWAP
	if (swap_enabled) {
		ret = chunk_alloc_swap(size, zero);
		if (ret != NULL)
			goto RETURN;
	}

	if (swap_enabled == false || opt_overcommit) {
#endif
#ifdef JEMALLOC_DSS
		ret = chunk_alloc_dss(size, zero);
		if (ret != NULL)
			goto RETURN;
#endif
		ret = chunk_alloc_mmap(size);
		if (ret != NULL) {
			*zero = true;
			goto RETURN;
		}
#ifdef JEMALLOC_SWAP
	}
#endif

	/* All strategies for allocation failed. */
	ret = NULL;
RETURN:
#ifdef JEMALLOC_IVSALLOC
	if (base == false && ret != NULL) {
		if (rtree_set(chunks_rtree, (uintptr_t)ret, ret)) {
			chunk_dealloc(ret, size, true);
			return (NULL);
		}
	}
#endif
#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
	if (ret != NULL) {
#  ifdef JEMALLOC_PROF
		bool gdump;
#  endif
		malloc_mutex_lock(&chunks_mtx);
#  ifdef JEMALLOC_STATS
		stats_chunks.nchunks += (size / chunksize);
#  endif
		stats_chunks.curchunks += (size / chunksize);
		if (stats_chunks.curchunks > stats_chunks.highchunks) {
			stats_chunks.highchunks = stats_chunks.curchunks;
#  ifdef JEMALLOC_PROF
			gdump = true;
#  endif
		}
#  ifdef JEMALLOC_PROF
		else
			gdump = false;
#  endif
		malloc_mutex_unlock(&chunks_mtx);
#  ifdef JEMALLOC_PROF
		if (opt_prof && opt_prof_gdump && gdump)
			prof_gdump();
#  endif
	}
#endif

	assert(CHUNK_ADDR2BASE(ret) == ret);
	return (ret);
}

void
chunk_dealloc(void *chunk, size_t size, bool unmap)
{

	assert(chunk != NULL);
	assert(CHUNK_ADDR2BASE(chunk) == chunk);
	assert(size != 0);
	assert((size & chunksize_mask) == 0);

#ifdef JEMALLOC_IVSALLOC
	rtree_set(chunks_rtree, (uintptr_t)chunk, NULL);
#endif
#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
	malloc_mutex_lock(&chunks_mtx);
	stats_chunks.curchunks -= (size / chunksize);
	malloc_mutex_unlock(&chunks_mtx);
#endif

	if (unmap) {
#ifdef JEMALLOC_SWAP
		if (swap_enabled && chunk_dealloc_swap(chunk, size) == false)
			return;
#endif
#ifdef JEMALLOC_DSS
		if (chunk_dealloc_dss(chunk, size) == false)
			return;
#endif
		chunk_dealloc_mmap(chunk, size);
	}
}

bool
chunk_boot(void)
{

	/* Set variables according to the value of opt_lg_chunk. */
	chunksize = (ZU(1) << opt_lg_chunk);
	assert(chunksize >= PAGE_SIZE);
	chunksize_mask = chunksize - 1;
	chunk_npages = (chunksize >> PAGE_SHIFT);

#if (defined(JEMALLOC_STATS) || defined(JEMALLOC_PROF))
	if (malloc_mutex_init(&chunks_mtx))
		return (true);
	memset(&stats_chunks, 0, sizeof(chunk_stats_t));
#endif
#ifdef JEMALLOC_SWAP
	if (chunk_swap_boot())
		return (true);
#endif
	if (chunk_mmap_boot())
		return (true);
#ifdef JEMALLOC_DSS
	if (chunk_dss_boot())
		return (true);
#endif
#ifdef JEMALLOC_IVSALLOC
	chunks_rtree = rtree_new((ZU(1) << (LG_SIZEOF_PTR+3)) - opt_lg_chunk);
	if (chunks_rtree == NULL)
		return (true);
#endif

	return (false);
}
