932 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			932 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
    Copyright (c) 2016-2018 Chung, Hyung-Hwan. All rights reserved.
 | 
						|
 | 
						|
    Redistribution and use in source and binary forms, with or without
 | 
						|
    modification, are permitted provided that the following conditions
 | 
						|
    are met:
 | 
						|
    1. Redistributions of source code must retain the above copyright
 | 
						|
       notice, this list of conditions and the following disclaimer.
 | 
						|
    2. Redistributions in binary form must reproduce the above copyright
 | 
						|
       notice, this list of conditions and the following disclaimer in the
 | 
						|
       documentation and/or other materials provided with the distribution.
 | 
						|
 | 
						|
    THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 | 
						|
    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 | 
						|
    OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 | 
						|
    IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 | 
						|
    INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 | 
						|
    NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | 
						|
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | 
						|
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | 
						|
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 | 
						|
    THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | 
						|
 */
 | 
						|
 | 
						|
#include <hak-xma.h>
 | 
						|
#include "hak-prv.h"
 | 
						|
#include <assert.h> /* TODO: replace assert() with HAK_ASSERT() or something */
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
 * in the following run, movaps tries to write to the address 0x7fffea722f78.
 | 
						|
 * since the instruction deals with 16-byte aligned data only, it triggered
 | 
						|
 * the general protection error.
 | 
						|
 *
 | 
						|
$ gdb ~/xxx/bin/xxx
 | 
						|
Program received signal SIGSEGV, Segmentation fault.
 | 
						|
0x000000000042156a in set_global (rtx=rtx@entry=0x7fffea718ff8, idx=idx@entry=2,
 | 
						|
    var=var@entry=0x0, val=val@entry=0x1, assign=assign@entry=0) at ../../../lib/run.c:358
 | 
						|
358				rtx->gbl.fnr = lv;
 | 
						|
(gdb) print &rtx->gbl.fnr
 | 
						|
$1 = (xxx_int_t *) 0x7fffea722f78
 | 
						|
(gdb) disp /i 0x42156a
 | 
						|
1: x/i 0x42156a
 | 
						|
=> 0x42156a <set_global+874>:	movaps %xmm2,0x9f80(%rbp)
 | 
						|
*/
 | 
						|
 | 
						|
/* set ALIGN to twice the pointer size to prevent unaligned memory access by
 | 
						|
 * instructions dealing with data larger than the system word size. e.g. movaps on x86_64 */
 | 
						|
#define ALIGN (HAK_SIZEOF_VOID_P * 2) /* this must be a power of 2 */
 | 
						|
 | 
						|
#define MBLKHDRSIZE (HAK_SIZEOF(hak_xma_mblk_t))
 | 
						|
#define MINALLOCSIZE (HAK_SIZEOF(hak_xma_fblk_t) - HAK_SIZEOF(hak_xma_mblk_t))
 | 
						|
#define FBLKMINSIZE (MBLKHDRSIZE + MINALLOCSIZE) /* or HAK_SIZEOF(hak_xma_fblk_t) - need space for the free links when the block is freeed */
 | 
						|
 | 
						|
/* NOTE: you must ensure that FBLKMINSIZE is equal to ALIGN or multiples of ALIGN */
 | 
						|
 | 
						|
#define SYS_TO_USR(b) ((hak_uint8_t*)(b) + MBLKHDRSIZE)
 | 
						|
#define USR_TO_SYS(b) ((hak_uint8_t*)(b) - MBLKHDRSIZE)
 | 
						|
 | 
						|
/*
 | 
						|
 * the xfree array is divided into three region
 | 
						|
 * 0 ....................... FIXED ......................... XFIMAX-1 ... XFIMAX
 | 
						|
 * |  small fixed-size chains |     large  chains                | huge chain |
 | 
						|
 */
 | 
						|
#define FIXED HAK_XMA_FIXED
 | 
						|
#define XFIMAX(xma) (HAK_COUNTOF(xma->xfree)-1)
 | 
						|
 | 
						|
 | 
						|
#define fblk_free(b) (((hak_xma_fblk_t*)(b))->free)
 | 
						|
#define fblk_size(b) (((hak_xma_fblk_t*)(b))->size)
 | 
						|
#define fblk_prev_size(b) (((hak_xma_fblk_t*)(b))->prev_size)
 | 
						|
#if 0
 | 
						|
#define mblk_free(b) (((hak_xma_mblk_t*)(b))->free)
 | 
						|
#define mblk_size(b) (((hak_xma_mblk_t*)(b))->size)
 | 
						|
#define mblk_prev_size(b) (((hak_xma_mblk_t*)(b))->prev_size)
 | 
						|
#else
 | 
						|
/* Let mblk_free(), mblk_size(), mblk_prev_size() be an alias to
 | 
						|
 * fblk_free(), fblk_size(), fblk_prev_size() to follow strict aliasing rule.
 | 
						|
 * if gcc/clang is used, specifying __attribute__((__may_alias__)) to hak_xma_mblk_t
 | 
						|
 * and hak_xma_fblk_t would also work. */
 | 
						|
#define mblk_free(b) fblk_free(b)
 | 
						|
#define mblk_size(b) fblk_size(b)
 | 
						|
#define mblk_prev_size(b) fblk_prev_size(b)
 | 
						|
#endif
 | 
						|
#define next_mblk(b) ((hak_xma_mblk_t*)((hak_uint8_t*)b + MBLKHDRSIZE + mblk_size(b)))
 | 
						|
#define prev_mblk(b) ((hak_xma_mblk_t*)((hak_uint8_t*)b - (MBLKHDRSIZE + mblk_prev_size(b))))
 | 
						|
 | 
						|
struct hak_xma_mblk_t
 | 
						|
{
 | 
						|
	hak_oow_t prev_size;
 | 
						|
 | 
						|
	/* the block size is shifted by 1 bit and the maximum value is
 | 
						|
	 * offset by 1 bit because of the 'free' bit-field.
 | 
						|
	 * i could keep 'size' without shifting with bit manipulation
 | 
						|
	 * because the actual size is aligned and the last bit will
 | 
						|
	 * never be 1. i don't think there is a practical use case where
 | 
						|
	 * you need to allocate a huge chunk covering the entire
 | 
						|
	 * address space of your machine. */
 | 
						|
	hak_oow_t free: 1;
 | 
						|
	hak_oow_t size: HAK_XMA_SIZE_BITS; /**< block size */
 | 
						|
};
 | 
						|
 | 
						|
struct hak_xma_fblk_t
 | 
						|
{
 | 
						|
	hak_oow_t prev_size;
 | 
						|
	hak_oow_t free: 1;
 | 
						|
	hak_oow_t size: HAK_XMA_SIZE_BITS;/**< block size */
 | 
						|
 | 
						|
	/* the fields are must be identical to the fields of hak_xma_mblk_t */
 | 
						|
	/* the two fields below are used only if the block is free */
 | 
						|
	hak_xma_fblk_t* free_prev; /**< link to the previous free block */
 | 
						|
	hak_xma_fblk_t* free_next; /**< link to the next free block */
 | 
						|
};
 | 
						|
 | 
						|
/*#define VERIFY*/
 | 
						|
#if defined(VERIFY)
 | 
						|
static void DBG_VERIFY (hak_xma_t* xma, const char* desc)
 | 
						|
{
 | 
						|
	hak_xma_mblk_t* tmp, * next;
 | 
						|
	hak_oow_t cnt;
 | 
						|
	hak_oow_t fsum, asum;
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	hak_oow_t isum;
 | 
						|
#endif
 | 
						|
 | 
						|
	for (tmp = (hak_xma_mblk_t*)xma->start, cnt = 0, fsum = 0, asum = 0; (hak_uint8_t*)tmp < xma->end; tmp = next, cnt++)
 | 
						|
	{
 | 
						|
		next = next_mblk(tmp);
 | 
						|
 | 
						|
		if ((hak_uint8_t*)tmp == xma->start)
 | 
						|
		{
 | 
						|
			assert (tmp->prev_size == 0);
 | 
						|
		}
 | 
						|
		if ((hak_uint8_t*)next < xma->end)
 | 
						|
		{
 | 
						|
			assert (next->prev_size == tmp->size);
 | 
						|
		}
 | 
						|
 | 
						|
		if (tmp->free) fsum += tmp->size;
 | 
						|
		else asum += tmp->size;
 | 
						|
	}
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	isum = (xma->stat.nfree + xma->stat.nused) * MBLKHDRSIZE;
 | 
						|
	assert (asum == xma->stat.alloc);
 | 
						|
	assert (fsum == xma->stat.avail);
 | 
						|
	assert (isum == xma->stat.total - (xma->stat.alloc + xma->stat.avail));
 | 
						|
	assert (asum + fsum + isum == xma->stat.total);
 | 
						|
#endif
 | 
						|
}
 | 
						|
#else
 | 
						|
#define DBG_VERIFY(xma, desc)
 | 
						|
#endif
 | 
						|
 | 
						|
static HAK_INLINE hak_oow_t szlog2 (hak_oow_t n)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * 2**x = n;
 | 
						|
	 * x = log2(n);
 | 
						|
	 * -------------------------------------------
 | 
						|
	 * 	unsigned int x = 0;
 | 
						|
	 * 	while ((n >> x) > 1) ++x;
 | 
						|
	 * 	return x;
 | 
						|
	 */
 | 
						|
 | 
						|
#define BITS (HAK_SIZEOF_OOW_T * 8)
 | 
						|
	int x = BITS - 1;
 | 
						|
 | 
						|
#if HAK_SIZEOF_OOW_T >= 128
 | 
						|
#	error hak_oow_t too large. unsupported platform
 | 
						|
#endif
 | 
						|
 | 
						|
#if HAK_SIZEOF_OOW_T >= 64
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-128))) == 0) { x -= 256; n <<= 256; }
 | 
						|
#endif
 | 
						|
#if HAK_SIZEOF_OOW_T >= 32
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-128))) == 0) { x -= 128; n <<= 128; }
 | 
						|
#endif
 | 
						|
#if HAK_SIZEOF_OOW_T >= 16
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-64))) == 0) { x -= 64; n <<= 64; }
 | 
						|
#endif
 | 
						|
#if HAK_SIZEOF_OOW_T >= 8
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-32))) == 0) { x -= 32; n <<= 32; }
 | 
						|
#endif
 | 
						|
#if HAK_SIZEOF_OOW_T >= 4
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-16))) == 0) { x -= 16; n <<= 16; }
 | 
						|
#endif
 | 
						|
#if HAK_SIZEOF_OOW_T >= 2
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-8))) == 0) { x -= 8; n <<= 8; }
 | 
						|
#endif
 | 
						|
#if HAK_SIZEOF_OOW_T >= 1
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-4))) == 0) { x -= 4; n <<= 4; }
 | 
						|
#endif
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-2))) == 0) { x -= 2; n <<= 2; }
 | 
						|
	if ((n & (~(hak_oow_t)0 << (BITS-1))) == 0) { x -= 1; }
 | 
						|
 | 
						|
	return x;
 | 
						|
#undef BITS
 | 
						|
}
 | 
						|
 | 
						|
static HAK_INLINE hak_oow_t getxfi (hak_xma_t* xma, hak_oow_t size)
 | 
						|
{
 | 
						|
	hak_oow_t xfi = ((size) / ALIGN) - 1;
 | 
						|
	if (xfi >= FIXED) xfi = szlog2(size) - (xma)->bdec + FIXED;
 | 
						|
	if (xfi > XFIMAX(xma)) xfi = XFIMAX(xma);
 | 
						|
	return xfi;
 | 
						|
}
 | 
						|
 | 
						|
hak_xma_t* hak_xma_open (hak_mmgr_t* mmgr, hak_oow_t xtnsize, void* zoneptr, hak_oow_t zonesize)
 | 
						|
{
 | 
						|
	hak_xma_t* xma;
 | 
						|
 | 
						|
	xma = (hak_xma_t*)HAK_MMGR_ALLOC(mmgr, HAK_SIZEOF(*xma) + xtnsize);
 | 
						|
	if (HAK_UNLIKELY(!xma)) return HAK_NULL;
 | 
						|
 | 
						|
	if (hak_xma_init(xma, mmgr, zoneptr, zonesize) <= -1)
 | 
						|
	{
 | 
						|
		HAK_MMGR_FREE (mmgr, xma);
 | 
						|
		return HAK_NULL;
 | 
						|
	}
 | 
						|
 | 
						|
	HAK_MEMSET(xma + 1, 0, xtnsize);
 | 
						|
	return xma;
 | 
						|
}
 | 
						|
 | 
						|
void hak_xma_close(hak_xma_t* xma)
 | 
						|
{
 | 
						|
	hak_xma_fini (xma);
 | 
						|
	HAK_MMGR_FREE (xma->_mmgr, xma);
 | 
						|
}
 | 
						|
 | 
						|
int hak_xma_init (hak_xma_t* xma, hak_mmgr_t* mmgr, void* zoneptr, hak_oow_t zonesize)
 | 
						|
{
 | 
						|
	hak_xma_fblk_t* first;
 | 
						|
	hak_oow_t xfi;
 | 
						|
	int internal = 0;
 | 
						|
 | 
						|
	if (!zoneptr)
 | 
						|
	{
 | 
						|
		/* round 'zonesize' to be the multiples of ALIGN */
 | 
						|
		zonesize = HAK_ALIGN_POW2(zonesize, ALIGN);
 | 
						|
 | 
						|
		/* adjust 'zonesize' to be large enough to hold a single smallest block */
 | 
						|
		if (zonesize < FBLKMINSIZE) zonesize = FBLKMINSIZE;
 | 
						|
 | 
						|
		zoneptr = HAK_MMGR_ALLOC(mmgr, zonesize);
 | 
						|
		if (HAK_UNLIKELY(!zoneptr)) return -1;
 | 
						|
 | 
						|
		internal = 1; /* internally created. must be freed upon hak_xma_fini() */
 | 
						|
	}
 | 
						|
	else if (zonesize < FBLKMINSIZE)
 | 
						|
	{
 | 
						|
		/* the zone size is too small for an externally allocated zone. */
 | 
						|
/* TODO: difference error code from memory allocation failure.. this is not really memory shortage */
 | 
						|
		return -1;
 | 
						|
	}
 | 
						|
 | 
						|
	first = (hak_xma_fblk_t*)zoneptr;
 | 
						|
 | 
						|
	/* initialize the header part of the free chunk. the entire zone is a single free block */
 | 
						|
	first->prev_size = 0;
 | 
						|
	first->free = 1;
 | 
						|
	first->size = zonesize - MBLKHDRSIZE; /* size excluding the block header */
 | 
						|
	first->free_prev = HAK_NULL;
 | 
						|
	first->free_next = HAK_NULL;
 | 
						|
 | 
						|
	HAK_MEMSET(xma, 0, HAK_SIZEOF(*xma));
 | 
						|
	xma->_mmgr = mmgr;
 | 
						|
	xma->bdec = szlog2(FIXED * ALIGN); /* precalculate the decrement value */
 | 
						|
 | 
						|
	/* at this point, the 'free' chunk is a only block available */
 | 
						|
 | 
						|
	/* get the free block index */
 | 
						|
	xfi = getxfi(xma, first->size);
 | 
						|
	/* locate it into an apporopriate slot */
 | 
						|
	xma->xfree[xfi] = first;
 | 
						|
	/* let it be the head, which is natural with only a block */
 | 
						|
	xma->start = (hak_uint8_t*)first;
 | 
						|
	xma->end = xma->start + zonesize;
 | 
						|
	xma->internal = internal;
 | 
						|
 | 
						|
	/* initialize some statistical variables */
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	xma->stat.total = zonesize;
 | 
						|
	xma->stat.alloc = 0;
 | 
						|
	xma->stat.avail = zonesize - MBLKHDRSIZE;
 | 
						|
	xma->stat.nfree = 1;
 | 
						|
	xma->stat.nused = 0;
 | 
						|
	xma->stat.alloc_hwmark = 0;
 | 
						|
 | 
						|
	xma->stat.nallocops = 0;
 | 
						|
	xma->stat.nallocgoodops = 0;
 | 
						|
	xma->stat.nallocbadops = 0;
 | 
						|
	xma->stat.nreallocops = 0;
 | 
						|
	xma->stat.nreallocgoodops = 0;
 | 
						|
	xma->stat.nreallocbadops = 0;
 | 
						|
	xma->stat.nfreeops = 0;
 | 
						|
#endif
 | 
						|
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
void hak_xma_fini (hak_xma_t* xma)
 | 
						|
{
 | 
						|
	/* the head must point to the free chunk allocated in init().
 | 
						|
	 * let's deallocate it */
 | 
						|
	if (xma->internal) HAK_MMGR_FREE (xma->_mmgr, xma->start);
 | 
						|
	xma->start = HAK_NULL;
 | 
						|
	xma->end = HAK_NULL;
 | 
						|
}
 | 
						|
 | 
						|
static HAK_INLINE void attach_to_freelist (hak_xma_t* xma, hak_xma_fblk_t* b)
 | 
						|
{
 | 
						|
	/*
 | 
						|
	 * attach a block to a free list
 | 
						|
	 */
 | 
						|
 | 
						|
	/* get the free list index for the block size */
 | 
						|
	hak_oow_t xfi = getxfi(xma, b->size);
 | 
						|
 | 
						|
	/* let it be the head of the free list doubly-linked */
 | 
						|
	b->free_prev = HAK_NULL;
 | 
						|
	b->free_next = xma->xfree[xfi];
 | 
						|
	if (xma->xfree[xfi]) xma->xfree[xfi]->free_prev = b;
 | 
						|
	xma->xfree[xfi] = b;
 | 
						|
}
 | 
						|
 | 
						|
static HAK_INLINE void detach_from_freelist (hak_xma_t* xma, hak_xma_fblk_t* b)
 | 
						|
{
 | 
						|
	/* detach a block from a free list */
 | 
						|
	hak_xma_fblk_t* p, * n;
 | 
						|
 | 
						|
	/* alias the previous and the next with short variable names */
 | 
						|
	p = b->free_prev;
 | 
						|
	n = b->free_next;
 | 
						|
 | 
						|
	if (p)
 | 
						|
	{
 | 
						|
		/* the previous item exists. let its 'next' pointer point to
 | 
						|
		 * the block's next item. */
 | 
						|
		p->free_next = n;
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* the previous item does not exist. the block is the first
 | 
						|
		 * item in the free list. */
 | 
						|
		hak_oow_t xfi = getxfi(xma, b->size);
 | 
						|
		assert (b == xma->xfree[xfi]);
 | 
						|
		/* let's update the free list head */
 | 
						|
		xma->xfree[xfi] = n;
 | 
						|
	}
 | 
						|
 | 
						|
	/* let the 'prev' pointer of the block's next item point to the
 | 
						|
	 * block's previous item */
 | 
						|
	if (n) n->free_prev = p;
 | 
						|
}
 | 
						|
 | 
						|
static hak_xma_fblk_t* alloc_from_freelist (hak_xma_t* xma, hak_oow_t xfi, hak_oow_t size)
 | 
						|
{
 | 
						|
	hak_xma_fblk_t* cand;
 | 
						|
 | 
						|
	for (cand = xma->xfree[xfi]; cand; cand = cand->free_next)
 | 
						|
	{
 | 
						|
		if (cand->size >= size)
 | 
						|
		{
 | 
						|
			hak_oow_t rem;
 | 
						|
 | 
						|
			detach_from_freelist(xma, cand);
 | 
						|
 | 
						|
			rem = cand->size - size;
 | 
						|
			if (rem >= FBLKMINSIZE)
 | 
						|
			{
 | 
						|
				hak_xma_mblk_t* y, * z;
 | 
						|
 | 
						|
				/* the remaining part is large enough to hold
 | 
						|
				 * another block. let's split it
 | 
						|
				 */
 | 
						|
 | 
						|
				/* shrink the size of the 'cand' block */
 | 
						|
				cand->size = size;
 | 
						|
 | 
						|
				/* let 'y' point to the remaining part */
 | 
						|
				y = next_mblk(cand);
 | 
						|
 | 
						|
				/* initialize some fields */
 | 
						|
				y->free = 1;
 | 
						|
				y->size = rem - MBLKHDRSIZE;
 | 
						|
				y->prev_size = cand->size;
 | 
						|
 | 
						|
				/* add the remaining part to the free list */
 | 
						|
				attach_to_freelist(xma, (hak_xma_fblk_t*)y);
 | 
						|
 | 
						|
				z = next_mblk(y);
 | 
						|
				if ((hak_uint8_t*)z < xma->end) z->prev_size = y->size;
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
				xma->stat.avail -= MBLKHDRSIZE;
 | 
						|
#endif
 | 
						|
			}
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
			else
 | 
						|
			{
 | 
						|
				/* decrement the number of free blocks as the current
 | 
						|
				 * block is allocated as a whole without being split */
 | 
						|
				xma->stat.nfree--;
 | 
						|
			}
 | 
						|
#endif
 | 
						|
 | 
						|
			cand->free = 0;
 | 
						|
			/*
 | 
						|
			cand->free_next = HAK_NULL;
 | 
						|
			cand->free_prev = HAK_NULL;
 | 
						|
			*/
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
			xma->stat.nused++;
 | 
						|
			xma->stat.alloc += cand->size;
 | 
						|
			xma->stat.avail -= cand->size;
 | 
						|
			if (xma->stat.alloc > xma->stat.alloc_hwmark) xma->stat.alloc_hwmark = xma->stat.alloc;
 | 
						|
#endif
 | 
						|
			return cand;
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return HAK_NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
void* hak_xma_alloc (hak_xma_t* xma, hak_oow_t size)
 | 
						|
{
 | 
						|
	hak_xma_fblk_t* cand;
 | 
						|
	hak_oow_t xfi, native_xfi;
 | 
						|
 | 
						|
	DBG_VERIFY(xma, "alloc start");
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	xma->stat.nallocops++;
 | 
						|
#endif
 | 
						|
 | 
						|
	/* round up 'size' to the multiples of ALIGN */
 | 
						|
	if (size < MINALLOCSIZE) size = MINALLOCSIZE;
 | 
						|
	size = HAK_ALIGN_POW2(size, ALIGN);
 | 
						|
 | 
						|
	assert (size >= ALIGN);
 | 
						|
	xfi = getxfi(xma, size);
 | 
						|
	native_xfi = xfi;
 | 
						|
 | 
						|
	/*if (xfi < XFIMAX(xma) && xma->xfree[xfi])*/
 | 
						|
	if (xfi < FIXED && xma->xfree[xfi])
 | 
						|
	{
 | 
						|
		/* try the best fit */
 | 
						|
		cand = xma->xfree[xfi];
 | 
						|
 | 
						|
		assert (cand->free != 0);
 | 
						|
		assert (cand->size == size);
 | 
						|
 | 
						|
		detach_from_freelist(xma, cand);
 | 
						|
		cand->free = 0;
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
		xma->stat.nfree--;
 | 
						|
		xma->stat.nused++;
 | 
						|
		xma->stat.alloc += cand->size;
 | 
						|
		xma->stat.avail -= cand->size;
 | 
						|
		if (xma->stat.alloc > xma->stat.alloc_hwmark) xma->stat.alloc_hwmark = xma->stat.alloc;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	else if (xfi == XFIMAX(xma))
 | 
						|
	{
 | 
						|
		/* huge block */
 | 
						|
		cand = alloc_from_freelist(xma, XFIMAX(xma), size);
 | 
						|
		if (!cand)
 | 
						|
		{
 | 
						|
		#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
			xma->stat.nallocbadops++;
 | 
						|
		#endif
 | 
						|
			return HAK_NULL;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		if (xfi >= FIXED)
 | 
						|
		{
 | 
						|
			/* get the block from its own large chain */
 | 
						|
			cand = alloc_from_freelist(xma, xfi, size);
 | 
						|
			if (!cand)
 | 
						|
			{
 | 
						|
				/* borrow a large block from the huge block chain */
 | 
						|
				cand = alloc_from_freelist(xma, XFIMAX(xma), size);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			/* borrow a small block from the huge block chain */
 | 
						|
			cand = alloc_from_freelist(xma, XFIMAX(xma), size);
 | 
						|
			if (!cand) xfi = FIXED - 1;
 | 
						|
		}
 | 
						|
 | 
						|
		if (!cand)
 | 
						|
		{
 | 
						|
			/* try each large block chain left */
 | 
						|
			for (++xfi; xfi < XFIMAX(xma) - 1; xfi++)
 | 
						|
			{
 | 
						|
				cand = alloc_from_freelist(xma, xfi, size);
 | 
						|
				if (cand) break;
 | 
						|
			}
 | 
						|
			if (!cand)
 | 
						|
			{
 | 
						|
				/* try fixed-sized free chains */
 | 
						|
				for (xfi = native_xfi + 1; xfi < FIXED; xfi++)
 | 
						|
				{
 | 
						|
					cand = alloc_from_freelist(xma, xfi, size);
 | 
						|
					if (cand) break;
 | 
						|
				}
 | 
						|
				if (!cand)
 | 
						|
				{
 | 
						|
				#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
					xma->stat.nallocbadops++;
 | 
						|
				#endif
 | 
						|
					return HAK_NULL;
 | 
						|
				}
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	xma->stat.nallocgoodops++;
 | 
						|
#endif
 | 
						|
	DBG_VERIFY(xma, "alloc end");
 | 
						|
	return SYS_TO_USR(cand);
 | 
						|
}
 | 
						|
 | 
						|
static void* _realloc_merge (hak_xma_t* xma, void* b, hak_oow_t size)
 | 
						|
{
 | 
						|
	hak_uint8_t* blk = (hak_uint8_t*)USR_TO_SYS(b);
 | 
						|
 | 
						|
	DBG_VERIFY(xma, "realloc merge start");
 | 
						|
	/* rounds up 'size' to be multiples of ALIGN */
 | 
						|
	if (size < MINALLOCSIZE) size = MINALLOCSIZE;
 | 
						|
	size = HAK_ALIGN_POW2(size, ALIGN);
 | 
						|
 | 
						|
	if (size > mblk_size(blk))
 | 
						|
	{
 | 
						|
		/* grow the current block */
 | 
						|
		hak_oow_t req;
 | 
						|
		hak_uint8_t* n;
 | 
						|
		hak_oow_t rem;
 | 
						|
 | 
						|
		req = size - mblk_size(blk); /* required size additionally */
 | 
						|
 | 
						|
		n = (hak_uint8_t*)next_mblk(blk);
 | 
						|
		/* check if the next adjacent block is available */
 | 
						|
		if (n >= xma->end || !mblk_free(n) || req > mblk_size(n)) return HAK_NULL; /* no! */
 | 
						|
/* TODO: check more blocks if the next block is free but small in size.
 | 
						|
 *       check the previous adjacent blocks also */
 | 
						|
 | 
						|
		assert(mblk_size(blk) == mblk_prev_size(n));
 | 
						|
 | 
						|
		/* let's merge the current block with the next block */
 | 
						|
		detach_from_freelist(xma, (hak_xma_fblk_t*)n);
 | 
						|
 | 
						|
		rem = (MBLKHDRSIZE + mblk_size(n)) - req;
 | 
						|
		if (rem >= FBLKMINSIZE)
 | 
						|
		{
 | 
						|
			/*
 | 
						|
			 * the remaining part of the next block is large enough
 | 
						|
			 * to hold a block. break the next block.
 | 
						|
			 */
 | 
						|
			hak_uint8_t* y, * z;
 | 
						|
 | 
						|
			mblk_size(blk) += req;
 | 
						|
			y = (hak_uint8_t*)next_mblk(blk);
 | 
						|
			mblk_free(y) = 1;
 | 
						|
			mblk_size(y) = rem - MBLKHDRSIZE;
 | 
						|
			mblk_prev_size(y) = mblk_size(blk);
 | 
						|
			attach_to_freelist(xma, (hak_xma_fblk_t*)y);
 | 
						|
 | 
						|
			z = (hak_uint8_t*)next_mblk(y);
 | 
						|
			if (z < xma->end) mblk_prev_size(z) = mblk_size(y);
 | 
						|
 | 
						|
		#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
			xma->stat.alloc += req;
 | 
						|
			xma->stat.avail -= req; /* req + MBLKHDRSIZE(tmp) - MBLKHDRSIZE(n) */
 | 
						|
			if (xma->stat.alloc > xma->stat.alloc_hwmark) xma->stat.alloc_hwmark = xma->stat.alloc;
 | 
						|
		#endif
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
			hak_uint8_t* z;
 | 
						|
 | 
						|
			/* the remaining part of the next block is too small to form an indepent block.
 | 
						|
			 * utilize the whole block by merging to the resizing block */
 | 
						|
			mblk_size(blk) += MBLKHDRSIZE + mblk_size(n);
 | 
						|
 | 
						|
			z = (hak_uint8_t*)next_mblk(blk);
 | 
						|
			if (z < xma->end) mblk_prev_size(z) = mblk_size(blk);
 | 
						|
 | 
						|
		#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
			xma->stat.nfree--;
 | 
						|
			xma->stat.alloc += MBLKHDRSIZE + mblk_size(n);
 | 
						|
			xma->stat.avail -= mblk_size(n);
 | 
						|
			if (xma->stat.alloc > xma->stat.alloc_hwmark) xma->stat.alloc_hwmark = xma->stat.alloc;
 | 
						|
		#endif
 | 
						|
		}
 | 
						|
	}
 | 
						|
	else if (size < mblk_size(blk))
 | 
						|
	{
 | 
						|
		/* shrink the block */
 | 
						|
		hak_oow_t rem = mblk_size(blk) - size;
 | 
						|
		if (rem >= FBLKMINSIZE)
 | 
						|
		{
 | 
						|
			hak_uint8_t* n;
 | 
						|
 | 
						|
			n = (hak_uint8_t*)next_mblk(blk);
 | 
						|
 | 
						|
			/* the leftover is large enough to hold a block of minimum size.split the current block */
 | 
						|
			if (n < xma->end && mblk_free(n))
 | 
						|
			{
 | 
						|
				hak_uint8_t* y, * z;
 | 
						|
 | 
						|
				/* make the leftover block merge with the next block */
 | 
						|
 | 
						|
				detach_from_freelist(xma, (hak_xma_fblk_t*)n);
 | 
						|
 | 
						|
				mblk_size(blk) = size;
 | 
						|
 | 
						|
				y = (hak_uint8_t*)next_mblk(blk); /* update y to the leftover block with the new block size set above */
 | 
						|
				mblk_free(y) = 1;
 | 
						|
				mblk_size(y) = rem + mblk_size(n); /* add up the adjust block - (rem + MBLKHDRSIZE(n) + n->size) - MBLKHDRSIZE(y) */
 | 
						|
				mblk_prev_size(y) = mblk_size(blk);
 | 
						|
 | 
						|
				/* add 'y' to the free list */
 | 
						|
				attach_to_freelist(xma, (hak_xma_fblk_t*)y);
 | 
						|
 | 
						|
				z = (hak_uint8_t*)next_mblk(y); /* get adjacent block to the merged block */
 | 
						|
				if (z < xma->end) mblk_prev_size(z) = mblk_size(y);
 | 
						|
 | 
						|
			#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
				xma->stat.alloc -= rem;
 | 
						|
				xma->stat.avail += rem; /* rem - MBLKHDRSIZE(y) + MBLKHDRSIZE(n) */
 | 
						|
			#endif
 | 
						|
			}
 | 
						|
			else
 | 
						|
			{
 | 
						|
				hak_uint8_t* y;
 | 
						|
 | 
						|
				/* link the leftover block to the free list */
 | 
						|
				mblk_size(blk) = size;
 | 
						|
 | 
						|
				y = (hak_uint8_t*)next_mblk(blk); /* update y to the leftover block with the new block size set above */
 | 
						|
				mblk_free(y) = 1;
 | 
						|
				mblk_size(y) = rem - MBLKHDRSIZE;
 | 
						|
				mblk_prev_size(y) = mblk_size(blk);
 | 
						|
 | 
						|
				attach_to_freelist(xma, (hak_xma_fblk_t*)y);
 | 
						|
				/*n = (hak_uint8_t*)next_mblk(y);
 | 
						|
				if (n < xma->end)*/ mblk_prev_size(n) = mblk_size(y);
 | 
						|
 | 
						|
			#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
				xma->stat.nfree++;
 | 
						|
				xma->stat.alloc -= rem;
 | 
						|
				xma->stat.avail += mblk_size(y);
 | 
						|
			#endif
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	DBG_VERIFY(xma, "realloc merge end");
 | 
						|
	return b;
 | 
						|
}
 | 
						|
 | 
						|
void* hak_xma_calloc (hak_xma_t* xma, hak_oow_t size)
 | 
						|
{
 | 
						|
	void* ptr = hak_xma_alloc(xma, size);
 | 
						|
	if (ptr) HAK_MEMSET(ptr, 0, size);
 | 
						|
	return ptr;
 | 
						|
}
 | 
						|
 | 
						|
void* hak_xma_realloc (hak_xma_t* xma, void* b, hak_oow_t size)
 | 
						|
{
 | 
						|
	void* n;
 | 
						|
 | 
						|
	if (!b)
 | 
						|
	{
 | 
						|
		/* 'realloc' with NULL is the same as 'alloc' */
 | 
						|
		n = hak_xma_alloc(xma, size);
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		/* try reallocation by merging the adjacent continuous blocks */
 | 
						|
	#if defined(HAWK_XMA_ENABLE_STAT)
 | 
						|
		xma->stat.nreallocops++;
 | 
						|
	#endif
 | 
						|
		n = _realloc_merge(xma, b, size);
 | 
						|
		if (!n)
 | 
						|
		{
 | 
						|
		#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
			xma->stat.nreallocbadops++;
 | 
						|
		#endif
 | 
						|
			/* reallocation by merging failed. fall back to the slow
 | 
						|
			 * allocation-copy-free scheme */
 | 
						|
			n = hak_xma_alloc(xma, size);
 | 
						|
			if (n)
 | 
						|
			{
 | 
						|
				HAK_MEMCPY(n, b, size);
 | 
						|
				hak_xma_free(xma, b);
 | 
						|
			}
 | 
						|
		}
 | 
						|
		else
 | 
						|
		{
 | 
						|
		#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
			xma->stat.nreallocgoodops++;
 | 
						|
		#endif
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return n;
 | 
						|
}
 | 
						|
 | 
						|
void hak_xma_free (hak_xma_t* xma, void* b)
 | 
						|
{
 | 
						|
	hak_uint8_t* blk = (hak_uint8_t*)USR_TO_SYS(b);
 | 
						|
	hak_uint8_t* x, * y;
 | 
						|
	hak_oow_t org_blk_size;
 | 
						|
 | 
						|
	DBG_VERIFY(xma, "free start");
 | 
						|
 | 
						|
	org_blk_size = mblk_size(blk);
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	/* update statistical variables */
 | 
						|
	xma->stat.nused--;
 | 
						|
	xma->stat.alloc -= org_blk_size;
 | 
						|
	xma->stat.nfreeops++;
 | 
						|
#endif
 | 
						|
 | 
						|
	x = (hak_uint8_t*)prev_mblk(blk);
 | 
						|
	y = (hak_uint8_t*)next_mblk(blk);
 | 
						|
	if ((x >= xma->start && mblk_free(x)) && (y < xma->end && mblk_free(y)))
 | 
						|
	{
 | 
						|
		/*
 | 
						|
		 * Merge the block with surrounding blocks
 | 
						|
		 *
 | 
						|
		 *                    blk
 | 
						|
		 *                     |
 | 
						|
		 *                     v
 | 
						|
		 * +------------+------------+------------+------------+
 | 
						|
		 * |     X      |            |     Y      |     Z      |
 | 
						|
		 * +------------+------------+------------+------------+
 | 
						|
		 *
 | 
						|
		 *
 | 
						|
		 * +--------------------------------------+------------+
 | 
						|
		 * |     X                                |     Z      |
 | 
						|
		 * +--------------------------------------+------------+
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
 | 
						|
		hak_uint8_t* z;
 | 
						|
		hak_oow_t ns = MBLKHDRSIZE + org_blk_size + MBLKHDRSIZE;
 | 
						|
		                /* blk's header  size + blk->size + y's header size */
 | 
						|
		hak_oow_t bs = ns + mblk_size(y);
 | 
						|
 | 
						|
		detach_from_freelist(xma, (hak_xma_fblk_t*)x);
 | 
						|
		detach_from_freelist(xma, (hak_xma_fblk_t*)y);
 | 
						|
 | 
						|
		mblk_size(x) += bs;
 | 
						|
		attach_to_freelist(xma, (hak_xma_fblk_t*)x);
 | 
						|
 | 
						|
		z = (hak_uint8_t*)next_mblk(x);
 | 
						|
		if ((hak_uint8_t*)z < xma->end) mblk_prev_size(z) = mblk_size(x);
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
		xma->stat.nfree--;
 | 
						|
		xma->stat.avail += ns;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	else if (y < xma->end && mblk_free(y))
 | 
						|
	{
 | 
						|
		/*
 | 
						|
		 * Merge the block with the next block
 | 
						|
		 *
 | 
						|
		 *   blk
 | 
						|
		 *    |
 | 
						|
		 *    v
 | 
						|
		 * +------------+------------+------------+
 | 
						|
		 * |            |     Y      |     Z      |
 | 
						|
		 * +------------+------------+------------+
 | 
						|
		 *
 | 
						|
		 *
 | 
						|
		 *
 | 
						|
		 *   blk
 | 
						|
		 *    |
 | 
						|
		 *    v
 | 
						|
		 * +-------------------------+------------+
 | 
						|
		 * |                         |     Z      |
 | 
						|
		 * +-------------------------+------------+
 | 
						|
		 *
 | 
						|
		 *
 | 
						|
		 */
 | 
						|
		hak_uint8_t* z = (hak_uint8_t*)next_mblk(y);
 | 
						|
 | 
						|
		/* detach y from the free list */
 | 
						|
		detach_from_freelist(xma, (hak_xma_fblk_t*)y);
 | 
						|
 | 
						|
		/* update the block availability */
 | 
						|
		mblk_free(blk) = 1;
 | 
						|
		/* update the block size. MBLKHDRSIZE for the header space in x */
 | 
						|
		mblk_size(blk) += MBLKHDRSIZE + mblk_size(y);
 | 
						|
 | 
						|
		/* update the backward link of Y */
 | 
						|
		if ((hak_uint8_t*)z < xma->end) mblk_prev_size(z) = mblk_size(blk);
 | 
						|
 | 
						|
		/* attach blk to the free list */
 | 
						|
		attach_to_freelist(xma, (hak_xma_fblk_t*)blk);
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
		xma->stat.avail += org_blk_size + MBLKHDRSIZE;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	else if (x >= xma->start && mblk_free(x))
 | 
						|
	{
 | 
						|
		/*
 | 
						|
		 * Merge the block with the previous block
 | 
						|
		 *
 | 
						|
		 *                 blk
 | 
						|
		 *                 |
 | 
						|
		 *                 v
 | 
						|
		 * +------------+------------+------------+
 | 
						|
		 * |     X      |            |     Y      |
 | 
						|
		 * +------------+------------+------------+
 | 
						|
		 *
 | 
						|
		 * +-------------------------+------------+
 | 
						|
		 * |     X                   |     Y      |
 | 
						|
		 * +-------------------------+------------+
 | 
						|
		 */
 | 
						|
		detach_from_freelist(xma, (hak_xma_fblk_t*)x);
 | 
						|
 | 
						|
		mblk_size(x) += MBLKHDRSIZE + org_blk_size;
 | 
						|
 | 
						|
		assert(y == (hak_uint8_t*)next_mblk(x));
 | 
						|
		if (y < xma->end) mblk_prev_size(y) = mblk_size(x);
 | 
						|
 | 
						|
		attach_to_freelist(xma, (hak_xma_fblk_t*)x);
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
		xma->stat.avail += MBLKHDRSIZE + org_blk_size;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
	else
 | 
						|
	{
 | 
						|
		mblk_free(blk) = 1;
 | 
						|
		attach_to_freelist(xma, (hak_xma_fblk_t*)blk);
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
		xma->stat.nfree++;
 | 
						|
		xma->stat.avail += org_blk_size;
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	DBG_VERIFY(xma, "free end");
 | 
						|
}
 | 
						|
 | 
						|
void hak_xma_dump (hak_xma_t* xma, hak_xma_dumper_t dumper, void* ctx)
 | 
						|
{
 | 
						|
	hak_xma_mblk_t* tmp;
 | 
						|
	hak_oow_t fsum, asum, xfi;
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	hak_oow_t isum;
 | 
						|
#endif
 | 
						|
 | 
						|
	dumper(ctx, "[XMA DUMP]\n");
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	dumper(ctx, "== statistics ==\n");
 | 
						|
	dumper(ctx, "Total                = %zu\n", xma->stat.total);
 | 
						|
	dumper(ctx, "Alloc                = %zu\n", xma->stat.alloc);
 | 
						|
	dumper(ctx, "Avail                = %zu\n", xma->stat.avail);
 | 
						|
	dumper(ctx, "Alloc High Watermark = %zu\n", xma->stat.alloc_hwmark);
 | 
						|
#endif
 | 
						|
 | 
						|
	dumper(ctx, "== blocks ==\n");
 | 
						|
	dumper(ctx, " size               avail address\n");
 | 
						|
	for (tmp = (hak_xma_mblk_t*)xma->start, fsum = 0, asum = 0; (hak_uint8_t*)tmp < xma->end; tmp = next_mblk(tmp))
 | 
						|
	{
 | 
						|
		dumper(ctx, " %-18zu %-5u %p\n", tmp->size, (unsigned int)tmp->free, tmp);
 | 
						|
		if (tmp->free) fsum += tmp->size;
 | 
						|
		else asum += tmp->size;
 | 
						|
	}
 | 
						|
 | 
						|
	dumper(ctx, "== free list ==\n");
 | 
						|
	for (xfi = 0; xfi <= XFIMAX(xma); xfi++)
 | 
						|
	{
 | 
						|
		if (xma->xfree[xfi])
 | 
						|
		{
 | 
						|
			hak_xma_fblk_t* f;
 | 
						|
			for (f = xma->xfree[xfi]; f; f = f->free_next)
 | 
						|
			{
 | 
						|
				dumper(ctx, " xfi %d fblk %p size %lu\n", xfi, f, (unsigned long)f->size);
 | 
						|
			}
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	isum = (xma->stat.nfree + xma->stat.nused) * MBLKHDRSIZE;
 | 
						|
#endif
 | 
						|
 | 
						|
	dumper(ctx, "---------------------------------------\n");
 | 
						|
	dumper(ctx, "Allocated blocks       : %18zu bytes\n", asum);
 | 
						|
	dumper(ctx, "Available blocks       : %18zu bytes\n", fsum);
 | 
						|
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	dumper(ctx, "Internal use           : %18zu bytes\n", isum);
 | 
						|
	dumper(ctx, "Total                  : %18zu bytes\n", (asum + fsum + isum));
 | 
						|
	dumper(ctx, "Alloc operations       : %18zu\n", xma->stat.nallocops);
 | 
						|
	dumper(ctx, "Good alloc operations  : %18zu\n", xma->stat.nallocgoodops);
 | 
						|
	dumper(ctx, "Bad alloc operations   : %18zu\n", xma->stat.nallocbadops);
 | 
						|
	dumper(ctx, "Realloc operations     : %18zu\n", xma->stat.nreallocops);
 | 
						|
	dumper(ctx, "Good realloc operations: %18zu\n", xma->stat.nreallocgoodops);
 | 
						|
	dumper(ctx, "Bad realloc operations : %18zu\n", xma->stat.nreallocbadops);
 | 
						|
	dumper(ctx, "Free operations        : %18zu\n", xma->stat.nfreeops);
 | 
						|
#endif
 | 
						|
 | 
						|
#if defined(HAK_XMA_ENABLE_STAT)
 | 
						|
	assert(asum == xma->stat.alloc);
 | 
						|
	assert(fsum == xma->stat.avail);
 | 
						|
	assert(isum == xma->stat.total - (xma->stat.alloc + xma->stat.avail));
 | 
						|
	assert(asum + fsum + isum == xma->stat.total);
 | 
						|
#endif
 | 
						|
}
 | 
						|
 |