qse/qse/lib/cmn/xma.c

819 lines
21 KiB
C
Raw Normal View History

2010-07-24 07:24:44 +00:00
/*
* $Id$
*
Copyright (c) 2006-2019 Chung, Hyung-Hwan. All rights reserved.
2010-07-24 07:24:44 +00:00
2014-11-19 14:42:24 +00:00
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.
2010-07-24 07:24:44 +00:00
2014-11-19 14:42:24 +00:00
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.
2010-07-24 07:24:44 +00:00
*/
#include <qse/cmn/xma.h>
2016-04-29 03:55:42 +00:00
#include "mem-prv.h"
2010-07-24 07:24:44 +00:00
#define ALIGN QSE_SIZEOF(qse_size_t) /* this must be a power of 2 */
2010-07-24 07:24:44 +00:00
#define HDRSIZE QSE_SIZEOF(qse_xma_blk_t)
2010-07-31 07:24:19 +00:00
#define MINBLKLEN (HDRSIZE + ALIGN)
2010-07-24 07:24:44 +00:00
#define SYS_TO_USR(_) (((qse_xma_blk_t*)_) + 1)
#define USR_TO_SYS(_) (((qse_xma_blk_t*)_) - 1)
2010-07-29 07:27:03 +00:00
/*
* the xfree array is divided into three region
* 0 ....................... FIXED ......................... XFIMAX-1 ... XFIMAX
* | small fixed-size chains | large chains | huge chain |
*/
#define FIXED QSE_XMA_FIXED
2010-07-25 06:43:26 +00:00
#define XFIMAX(xma) (QSE_COUNTOF(xma->xfree)-1)
2010-07-24 07:24:44 +00:00
struct qse_xma_blk_t
{
qse_size_t avail: 1;
2010-07-29 07:27:03 +00:00
qse_size_t size: QSE_XMA_SIZE_BITS;/**< block size */
2010-07-24 07:24:44 +00:00
2010-07-25 06:43:26 +00:00
struct
{
qse_xma_blk_t* prev; /**< link to the previous free block */
qse_xma_blk_t* next; /**< link to the next free block */
} f;
struct
{
qse_xma_blk_t* prev; /**< link to the previous adjacent block */
qse_xma_blk_t* next; /**< link to the next adjacent block */
2010-07-25 06:43:26 +00:00
} b;
2010-07-24 07:24:44 +00:00
};
2010-07-31 07:24:19 +00:00
static QSE_INLINE_ALWAYS qse_size_t szlog2 (qse_size_t n)
2010-07-29 07:27:03 +00:00
{
/*
* 2**x = n;
* x = log2(n);
* -------------------------------------------
* unsigned int x = 0;
* while((n >> x) > 1) ++x;
* return x;
*/
#define BITS (QSE_SIZEOF_SIZE_T * 8)
int x = BITS - 1;
#if QSE_SIZEOF_SIZE_T >= 128
2010-07-29 07:27:03 +00:00
# error qse_size_t too large. unsupported platform
#endif
#if QSE_SIZEOF_SIZE_T >= 64
if ((n & (~(qse_size_t)0 << (BITS-128))) == 0) { x -= 256; n <<= 256; }
#endif
#if QSE_SIZEOF_SIZE_T >= 32
if ((n & (~(qse_size_t)0 << (BITS-128))) == 0) { x -= 128; n <<= 128; }
#endif
2010-07-29 07:27:03 +00:00
#if QSE_SIZEOF_SIZE_T >= 16
if ((n & (~(qse_size_t)0 << (BITS-64))) == 0) { x -= 64; n <<= 64; }
#endif
#if QSE_SIZEOF_SIZE_T >= 8
if ((n & (~(qse_size_t)0 << (BITS-32))) == 0) { x -= 32; n <<= 32; }
#endif
#if QSE_SIZEOF_SIZE_T >= 4
if ((n & (~(qse_size_t)0 << (BITS-16))) == 0) { x -= 16; n <<= 16; }
#endif
#if QSE_SIZEOF_SIZE_T >= 2
if ((n & (~(qse_size_t)0 << (BITS-8))) == 0) { x -= 8; n <<= 8; }
#endif
#if QSE_SIZEOF_SIZE_T >= 1
if ((n & (~(qse_size_t)0 << (BITS-4))) == 0) { x -= 4; n <<= 4; }
#endif
if ((n & (~(qse_size_t)0 << (BITS-2))) == 0) { x -= 2; n <<= 2; }
if ((n & (~(qse_size_t)0 << (BITS-1))) == 0) { x -= 1; }
return x;
#undef BITS
}
2010-07-31 07:24:19 +00:00
static QSE_INLINE_ALWAYS qse_size_t getxfi (qse_xma_t* xma, qse_size_t size)
{
qse_size_t xfi = ((size) / ALIGN) - 1;
if (xfi >= FIXED) xfi = szlog2(size) - (xma)->bdec + FIXED;
if (xfi > XFIMAX(xma)) xfi = XFIMAX(xma);
return xfi;
}
2010-08-05 01:25:48 +00:00
qse_xma_t* qse_xma_open (
qse_mmgr_t* mmgr, qse_size_t xtnsize, qse_size_t zonesize)
2010-07-25 06:43:26 +00:00
{
qse_xma_t* xma;
2010-08-05 01:25:48 +00:00
xma = (qse_xma_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(*xma) + xtnsize);
2010-07-25 06:43:26 +00:00
if (xma == QSE_NULL) return QSE_NULL;
2011-09-01 09:43:46 +00:00
if (qse_xma_init (xma, mmgr, zonesize) <= -1)
2010-07-25 06:43:26 +00:00
{
QSE_MMGR_FREE (mmgr, xma);
return QSE_NULL;
}
2014-07-11 14:17:00 +00:00
QSE_MEMSET (QSE_XTN(xma), 0, xtnsize);
2010-07-25 06:43:26 +00:00
return xma;
}
void qse_xma_close (qse_xma_t* xma)
{
qse_xma_fini (xma);
QSE_MMGR_FREE (xma->mmgr, xma);
}
2011-09-01 09:43:46 +00:00
int qse_xma_init (qse_xma_t* xma, qse_mmgr_t* mmgr, qse_size_t zonesize)
2010-07-24 07:24:44 +00:00
{
qse_xma_blk_t* free;
2010-07-29 07:27:03 +00:00
qse_size_t xfi;
2010-07-24 07:24:44 +00:00
2010-08-26 07:15:40 +00:00
/* round 'zonesize' to be the multiples of ALIGN */
zonesize = QSE_ALIGNTO_POW2(zonesize, ALIGN);
2010-08-26 07:15:40 +00:00
2010-08-05 01:25:48 +00:00
/* adjust 'zonesize' to be large enough to hold a single smallest block */
if (zonesize < MINBLKLEN) zonesize = MINBLKLEN;
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
/* allocate a memory chunk to use for actual memory allocation */
2010-08-05 01:25:48 +00:00
free = QSE_MMGR_ALLOC (mmgr, zonesize);
2011-09-01 09:43:46 +00:00
if (free == QSE_NULL) return -1;
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
/* initialize the header part of the free chunk */
free->avail = 1;
2010-08-05 01:25:48 +00:00
free->size = zonesize - HDRSIZE; /* size excluding the block header */
2010-07-25 06:43:26 +00:00
free->f.prev = QSE_NULL;
free->f.next = QSE_NULL;
free->b.next = QSE_NULL;
free->b.prev = QSE_NULL;
2010-07-24 07:24:44 +00:00
2010-07-25 06:43:26 +00:00
QSE_MEMSET (xma, 0, QSE_SIZEOF(*xma));
xma->mmgr = mmgr;
xma->bdec = szlog2(FIXED * ALIGN); /* precalculate the decrement value */
2010-07-25 06:43:26 +00:00
2010-08-26 07:15:40 +00:00
/* at this point, the 'free' chunk is a only block available */
/* get the free block index */
2010-07-31 07:24:19 +00:00
xfi = getxfi(xma,free->size);
2010-08-26 07:15:40 +00:00
/* locate it into an apporopriate slot */
xma->xfree[xfi] = free;
/* let it be the head, which is natural with only a block */
xma->head = free;
2010-07-24 07:24:44 +00:00
/* initialize some statistical variables */
#if defined(QSE_XMA_ENABLE_STAT)
2010-08-05 01:25:48 +00:00
xma->stat.total = zonesize;
2010-07-25 06:43:26 +00:00
xma->stat.alloc = 0;
2010-08-05 01:25:48 +00:00
xma->stat.avail = zonesize - HDRSIZE;
2010-07-25 06:43:26 +00:00
xma->stat.nfree = 1;
xma->stat.nused = 0;
2010-07-29 07:27:03 +00:00
#endif
2011-09-01 09:43:46 +00:00
return 0;
2010-07-24 07:24:44 +00:00
}
2010-07-25 06:43:26 +00:00
void qse_xma_fini (qse_xma_t* xma)
2010-07-24 07:24:44 +00:00
{
2010-08-26 07:15:40 +00:00
/* the head must point to the free chunk allocated in init().
* let's deallocate it */
2010-07-25 06:43:26 +00:00
QSE_MMGR_FREE (xma->mmgr, xma->head);
2010-07-24 07:24:44 +00:00
}
qse_mmgr_t* qse_xma_getmmgr (qse_xma_t* xma)
{
return xma->mmgr;
}
void* qse_xma_getxtn (qse_xma_t* xma)
{
return QSE_XTN (xma);
}
2010-07-29 07:27:03 +00:00
static QSE_INLINE void attach_to_freelist (qse_xma_t* xma, qse_xma_blk_t* b)
2010-07-24 07:24:44 +00:00
{
2010-08-26 07:15:40 +00:00
/*
* attach a block to a free list
*/
/* get the free list index for the block size */
qse_size_t xfi = getxfi(xma,b->size);
2010-07-24 07:24:44 +00:00
2010-08-26 07:15:40 +00:00
/* let it be the head of the free list doubly-linked */
b->f.prev = QSE_NULL;
2010-07-25 06:43:26 +00:00
b->f.next = xma->xfree[xfi];
if (xma->xfree[xfi]) xma->xfree[xfi]->f.prev = b;
xma->xfree[xfi] = b;
2010-07-24 07:24:44 +00:00
}
2010-07-29 07:27:03 +00:00
static QSE_INLINE void detach_from_freelist (qse_xma_t* xma, qse_xma_blk_t* b)
2010-07-24 07:24:44 +00:00
{
2010-08-26 07:15:40 +00:00
/*
* detach a block from a free list
*/
2010-07-29 07:27:03 +00:00
qse_xma_blk_t* p, * n;
2010-07-24 07:24:44 +00:00
2010-08-26 07:15:40 +00:00
/* alias the previous and the next with short variable names */
2010-07-29 07:27:03 +00:00
p = b->f.prev;
n = b->f.next;
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
if (p)
{
2010-08-26 07:15:40 +00:00
/* the previous item exists. let its 'next' pointer point to
* the block's next item. */
2010-07-29 07:27:03 +00:00
p->f.next = n;
}
2010-07-24 07:24:44 +00:00
else
{
2010-08-26 07:15:40 +00:00
/* the previous item does not exist. the block is the first
* item in the free list. */
2010-07-31 07:24:19 +00:00
qse_size_t xfi = getxfi(xma,b->size);
2010-07-29 07:27:03 +00:00
QSE_ASSERT (b == xma->xfree[xfi]);
2010-08-26 07:15:40 +00:00
/* let's update the free list head */
2010-07-29 07:27:03 +00:00
xma->xfree[xfi] = n;
2010-07-24 07:24:44 +00:00
}
2010-08-26 07:15:40 +00:00
/* let the 'prev' pointer of the block's next item point to the
* block's previous item */
if (n) n->f.prev = p;
2010-07-24 07:24:44 +00:00
}
static qse_xma_blk_t* alloc_from_freelist (
2010-07-25 06:43:26 +00:00
qse_xma_t* xma, qse_size_t xfi, qse_size_t size)
2010-07-24 07:24:44 +00:00
{
qse_xma_blk_t* free;
2010-07-25 06:43:26 +00:00
for (free = xma->xfree[xfi]; free; free = free->f.next)
2010-07-24 07:24:44 +00:00
{
if (free->size >= size)
{
qse_size_t rem;
2010-07-29 07:27:03 +00:00
detach_from_freelist (xma, free);
2010-07-24 07:24:44 +00:00
rem = free->size - size;
2010-07-31 07:24:19 +00:00
if (rem >= MINBLKLEN)
2010-07-24 07:24:44 +00:00
{
qse_xma_blk_t* tmp;
/* the remaining part is large enough to hold
2010-07-29 07:27:03 +00:00
* another block. let's split it
*/
/* shrink the size of the 'free' block */
free->size = size;
/* let 'tmp' point to the remaining part */
2010-07-24 07:24:44 +00:00
tmp = (qse_xma_blk_t*)(((qse_byte_t*)(free + 1)) + size);
2010-07-29 07:27:03 +00:00
/* initialize some fields */
2010-07-24 07:24:44 +00:00
tmp->avail = 1;
tmp->size = rem - HDRSIZE;
2010-07-29 07:27:03 +00:00
/* link 'tmp' to the block list */
2010-07-25 06:43:26 +00:00
tmp->b.next = free->b.next;
tmp->b.prev = free;
if (free->b.next) free->b.next->b.prev = tmp;
free->b.next = tmp;
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
/* add the remaining part to the free list */
attach_to_freelist (xma, tmp);
2010-07-24 07:24:44 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-25 06:43:26 +00:00
xma->stat.avail -= HDRSIZE;
2010-07-29 07:27:03 +00:00
#endif
2010-07-24 07:24:44 +00:00
}
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-24 07:24:44 +00:00
else
{
/* decrement the number of free blocks as the current
* block is allocated as a whole without being split */
2010-07-25 06:43:26 +00:00
xma->stat.nfree--;
2010-07-24 07:24:44 +00:00
}
2010-07-29 07:27:03 +00:00
#endif
2010-07-24 07:24:44 +00:00
free->avail = 0;
/*
2010-07-25 06:43:26 +00:00
free->f.next = QSE_NULL;
free->f.prev = QSE_NULL;
2010-07-24 07:24:44 +00:00
*/
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-25 06:43:26 +00:00
xma->stat.nused++;
xma->stat.alloc += free->size;
xma->stat.avail -= free->size;
2010-07-29 07:27:03 +00:00
#endif
2010-07-24 07:24:44 +00:00
return free;
}
}
return QSE_NULL;
}
2010-07-25 06:43:26 +00:00
void* qse_xma_alloc (qse_xma_t* xma, qse_size_t size)
2010-07-24 07:24:44 +00:00
{
qse_xma_blk_t* free;
qse_size_t xfi;
if (size <= 0) size = 1;
/* round up 'size' to the multiples of ALIGN */
size = QSE_ALIGNTO_POW2(size, ALIGN);
2010-07-24 07:24:44 +00:00
QSE_ASSERT (size >= ALIGN);
2010-07-31 07:24:19 +00:00
xfi = getxfi(xma,size);
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
/*if (xfi < XFIMAX(xma) && xma->xfree[xfi])*/
if (xfi < FIXED && xma->xfree[xfi])
2010-07-24 07:24:44 +00:00
{
/* try the best fit */
2010-07-25 06:43:26 +00:00
free = xma->xfree[xfi];
2010-07-24 07:24:44 +00:00
QSE_ASSERT (free->avail != 0);
QSE_ASSERT (free->size == size);
2010-07-29 07:27:03 +00:00
detach_from_freelist (xma, free);
2010-07-24 07:24:44 +00:00
free->avail = 0;
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-25 06:43:26 +00:00
xma->stat.nfree--;
xma->stat.nused++;
xma->stat.alloc += free->size;
xma->stat.avail -= free->size;
2010-07-29 07:27:03 +00:00
#endif
2010-07-24 07:24:44 +00:00
}
2010-07-29 07:27:03 +00:00
else if (xfi == XFIMAX(xma))
2010-07-24 07:24:44 +00:00
{
2010-07-29 07:27:03 +00:00
/* huge block */
2010-07-25 06:43:26 +00:00
free = alloc_from_freelist (xma, XFIMAX(xma), size);
2010-07-29 07:27:03 +00:00
if (free == QSE_NULL) return QSE_NULL;
}
else
{
if (xfi >= FIXED)
{
/* get the block from its own large chain */
free = alloc_from_freelist (xma, xfi, size);
if (free == QSE_NULL)
{
/* borrow a large block from the huge block chain */
free = alloc_from_freelist (xma, XFIMAX(xma), size);
}
}
else
{
/* borrow a small block from the huge block chain */
free = alloc_from_freelist (xma, XFIMAX(xma), size);
if (free == QSE_NULL) xfi = FIXED - 1;
}
2010-07-24 07:24:44 +00:00
if (free == QSE_NULL)
{
2010-07-29 07:27:03 +00:00
/* try each large block chain left */
2010-07-25 06:43:26 +00:00
for (++xfi; xfi < XFIMAX(xma) - 1; xfi++)
2010-07-24 07:24:44 +00:00
{
2010-07-25 06:43:26 +00:00
free = alloc_from_freelist (xma, xfi, size);
if (free) break;
2010-07-24 07:24:44 +00:00
}
if (free == QSE_NULL) return QSE_NULL;
}
}
return SYS_TO_USR(free);
}
2010-07-31 07:24:19 +00:00
static void* _realloc_merge (qse_xma_t* xma, void* b, qse_size_t size)
{
qse_xma_blk_t* blk = USR_TO_SYS(b);
/* rounds up 'size' to be multiples of ALIGN */
size = QSE_ALIGNTO_POW2 (size, ALIGN);
2010-07-31 07:24:19 +00:00
if (size > blk->size)
{
2010-08-26 07:15:40 +00:00
/*
* grow the current block
*/
qse_size_t req;
2010-07-31 07:24:19 +00:00
qse_xma_blk_t* n;
qse_size_t rem;
req = size - blk->size;
2010-07-31 07:24:19 +00:00
n = blk->b.next;
2010-08-26 07:15:40 +00:00
/* check if the next adjacent block is available */
if (!n || !n->avail || req > n->size) return QSE_NULL; /* no! */
2010-07-31 07:24:19 +00:00
2010-08-26 07:15:40 +00:00
/* let's merge the current block with the next block */
2010-07-31 07:24:19 +00:00
detach_from_freelist (xma, n);
rem = (HDRSIZE + n->size) - req;
if (rem >= MINBLKLEN)
{
2010-08-26 07:15:40 +00:00
/*
* the remaining part of the next block is large enough
* to hold a block. break the next block.
*/
2010-07-31 07:24:19 +00:00
qse_xma_blk_t* tmp;
2010-08-26 07:15:40 +00:00
2010-07-31 07:24:19 +00:00
/* store n->b.next in case 'tmp' begins somewhere
* in the header part of n */
qse_xma_blk_t* nn = n->b.next;
tmp = (qse_xma_blk_t*)(((qse_byte_t*)n) + req);
tmp->avail = 1;
tmp->size = rem - HDRSIZE;
attach_to_freelist (xma, tmp);
blk->size += req;
tmp->b.next = nn;
if (nn) nn->b.prev = tmp;
blk->b.next = tmp;
tmp->b.prev = blk;
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-31 07:24:19 +00:00
xma->stat.alloc += req;
xma->stat.avail -= req; /* req + HDRSIZE(tmp) - HDRSIZE(n) */
#endif
}
else
{
2010-08-26 07:15:40 +00:00
/* the remaining part of the next block is negligible.
* utilize the whole block by merging to the resizing block */
2010-07-31 07:24:19 +00:00
blk->size += HDRSIZE + n->size;
blk->b.next = n->b.next;
if (n->b.next) n->b.next->b.prev = blk;
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-31 07:24:19 +00:00
xma->stat.nfree--;
xma->stat.alloc += HDRSIZE + n->size;
xma->stat.avail -= n->size;
#endif
}
}
else if (size < blk->size)
{
2010-08-26 07:15:40 +00:00
/*
* shrink the block
*/
2010-07-31 07:24:19 +00:00
qse_size_t rem = blk->size - size;
if (rem >= MINBLKLEN)
{
qse_xma_blk_t* tmp;
2010-08-01 01:45:47 +00:00
qse_xma_blk_t* n = blk->b.next;
2010-07-31 07:24:19 +00:00
/* the leftover is large enough to hold a block
* of minimum size. split the current block.
* let 'tmp' point to the leftover. */
tmp = (qse_xma_blk_t*)(((qse_byte_t*)(blk + 1)) + size);
tmp->avail = 1;
2010-08-01 01:45:47 +00:00
if (n && n->avail)
{
/* merge with the next block */
detach_from_freelist (xma, n);
2010-07-31 07:24:19 +00:00
2010-08-01 01:45:47 +00:00
tmp->b.next = n->b.next;
tmp->b.prev = blk;
if (n->b.next) n->b.next->b.prev = tmp;
blk->b.next = tmp;
blk->size = size;
2010-07-31 07:24:19 +00:00
2010-08-01 01:45:47 +00:00
tmp->size = rem - HDRSIZE + HDRSIZE + n->size;
2010-07-31 07:24:19 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-08-01 01:45:47 +00:00
xma->stat.alloc -= rem;
/* rem - HDRSIZE(tmp) + HDRSIZE(n) */
xma->stat.avail += rem;
2010-07-31 07:24:19 +00:00
#endif
2010-08-01 01:45:47 +00:00
}
else
{
/* link 'tmp' to the block list */
tmp->b.next = n;
tmp->b.prev = blk;
if (n) n->b.prev = tmp;
blk->b.next = tmp;
blk->size = size;
tmp->size = rem - HDRSIZE;
#if defined(QSE_XMA_ENABLE_STAT)
2010-08-01 01:45:47 +00:00
xma->stat.nfree++;
xma->stat.alloc -= rem;
xma->stat.avail += tmp->size;
#endif
}
/* add 'tmp' to the free list */
attach_to_freelist (xma, tmp);
2010-07-31 07:24:19 +00:00
}
}
return b;
}
void* qse_xma_calloc (qse_xma_t* xma, qse_size_t size)
{
void* ptr = qse_xma_alloc (xma, size);
if (ptr) QSE_MEMSET (ptr, 0, size);
return ptr;
}
2010-07-29 07:27:03 +00:00
void* qse_xma_realloc (qse_xma_t* xma, void* b, qse_size_t size)
{
2010-07-31 07:24:19 +00:00
void* n;
if (b == QSE_NULL)
{
/* 'realloc' with NULL is the same as 'alloc' */
n = qse_xma_alloc (xma, size);
}
else
{
/* try reallocation by merging the adjacent continuous blocks */
n = _realloc_merge (xma, b, size);
if (n == QSE_NULL)
{
/* reallocation by merging failed. fall back to the slow
* allocation-copy-free scheme */
n = qse_xma_alloc (xma, size);
if (n)
{
QSE_MEMCPY (n, b, size);
qse_xma_free (xma, b);
}
}
}
return n;
2010-07-29 07:27:03 +00:00
}
2010-07-25 06:43:26 +00:00
void qse_xma_free (qse_xma_t* xma, void* b)
2010-07-24 07:24:44 +00:00
{
qse_xma_blk_t* blk = USR_TO_SYS(b);
2010-07-25 06:43:26 +00:00
/*QSE_ASSERT (blk->f.next == QSE_NULL);*/
2010-07-24 07:24:44 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-24 07:24:44 +00:00
/* update statistical variables */
2010-07-25 06:43:26 +00:00
xma->stat.nused--;
xma->stat.alloc -= blk->size;
2010-07-29 07:27:03 +00:00
#endif
2010-07-24 07:24:44 +00:00
2010-07-25 06:43:26 +00:00
if ((blk->b.prev && blk->b.prev->avail) &&
(blk->b.next && blk->b.next->avail))
2010-07-24 07:24:44 +00:00
{
/*
* Merge the block with surrounding blocks
*
* blk
* +-----+ | +-----+ +------+
* | V v | v | V
* +------------+------------+------------+------------+
* | X | | Y | Z |
* +------------+------------+------------+------------+
* ^ | ^ | ^ |
* +-----+ +------+ +------+
*
*
* +-----------------------------------+
* | V
* +--------------------------------------+------------+
* | X | Z |
* +--------------------------------------+------------+
* ^ |
* +-----------------------------------+
*/
2010-07-25 06:43:26 +00:00
qse_xma_blk_t* x = blk->b.prev;
qse_xma_blk_t* y = blk->b.next;
qse_xma_blk_t* z = y->b.next;
2010-07-31 07:24:19 +00:00
qse_size_t ns = HDRSIZE + blk->size + HDRSIZE;
qse_size_t bs = ns + y->size;
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
detach_from_freelist (xma, x);
detach_from_freelist (xma, y);
2010-07-24 07:24:44 +00:00
x->size += bs;
2010-07-25 06:43:26 +00:00
x->b.next = z;
if (z) z->b.prev = x;
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
attach_to_freelist (xma, x);
2010-07-24 07:24:44 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-25 06:43:26 +00:00
xma->stat.nfree--;
2010-07-31 07:24:19 +00:00
xma->stat.avail += ns;
2010-07-29 07:27:03 +00:00
#endif
2010-07-24 07:24:44 +00:00
}
2010-07-25 06:43:26 +00:00
else if (blk->b.next && blk->b.next->avail)
2010-07-24 07:24:44 +00:00
{
/*
* Merge the block with the next block
*
* blk
* | +-----+ +------+
* v | v | V
* +------------+------------+------------+
* | | X | Y |
* +------------+------------+------------+
* ^ | ^ |
* +------+ +------+
*
* blk
* | +------------------+
* v | V
* +-------------------------+------------+
* | | Y |
* +-------------------------+------------+
* ^ |
* +-------------------+
*/
2010-07-25 06:43:26 +00:00
qse_xma_blk_t* x = blk->b.next;
qse_xma_blk_t* y = x->b.next;
2010-07-31 07:24:19 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-31 07:24:19 +00:00
xma->stat.avail += blk->size + HDRSIZE;
#endif
2010-07-24 07:24:44 +00:00
/* detach x from the free list */
2010-07-29 07:27:03 +00:00
detach_from_freelist (xma, x);
2010-07-24 07:24:44 +00:00
/* update the block availability */
blk->avail = 1;
/* update the block size. HDRSIZE for the header space in x */
2010-07-31 07:24:19 +00:00
blk->size += HDRSIZE + x->size;
2010-07-24 07:24:44 +00:00
/* update the backward link of Y */
2010-07-25 06:43:26 +00:00
if (y) y->b.prev = blk;
2010-07-24 07:24:44 +00:00
/* update the forward link of the block being freed */
2010-07-25 06:43:26 +00:00
blk->b.next = y;
2010-07-24 07:24:44 +00:00
/* attach blk to the free list */
2010-07-29 07:27:03 +00:00
attach_to_freelist (xma, blk);
2010-07-24 07:24:44 +00:00
}
2010-07-25 06:43:26 +00:00
else if (blk->b.prev && blk->b.prev->avail)
2010-07-24 07:24:44 +00:00
{
/*
* Merge the block with the previous block
*
* blk
* +-----+ | +-----+
* | V v | v
* +------------+------------+------------+
* | X | | Y |
* +------------+------------+------------+
* ^ | ^ |
* +------+ +------+
*
*
* +---------------------+
* | v
* +-------------------------+------------+
* | X | Y |
* +-------------------------+------------+
* ^ |
* +--------------------+
*
*/
2010-07-25 06:43:26 +00:00
qse_xma_blk_t* x = blk->b.prev;
qse_xma_blk_t* y = blk->b.next;
2010-07-31 07:24:19 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-31 07:24:19 +00:00
xma->stat.avail += HDRSIZE + blk->size;
#endif
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
detach_from_freelist (xma, x);
2010-07-24 07:24:44 +00:00
2010-07-31 07:24:19 +00:00
x->size += HDRSIZE + blk->size;
2010-07-25 06:43:26 +00:00
x->b.next = y;
if (y) y->b.prev = x;
2010-07-24 07:24:44 +00:00
2010-07-29 07:27:03 +00:00
attach_to_freelist (xma, x);
2010-07-24 07:24:44 +00:00
}
else
{
blk->avail = 1;
2010-07-29 07:27:03 +00:00
attach_to_freelist (xma, blk);
2010-07-24 07:24:44 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-25 06:43:26 +00:00
xma->stat.nfree++;
xma->stat.avail += blk->size;
2010-07-29 07:27:03 +00:00
#endif
2010-07-24 07:24:44 +00:00
}
}
void qse_xma_dump (qse_xma_t* xma, qse_xma_dumper_t dumper, void* ctx)
2010-07-24 07:24:44 +00:00
{
qse_xma_blk_t* tmp;
qse_ulong_t fsum, asum;
#if defined(QSE_XMA_ENABLE_STAT)
qse_ulong_t isum;
2010-07-31 07:24:19 +00:00
#endif
2010-07-24 07:24:44 +00:00
dumper (ctx, QSE_T("<XMA DUMP>\n"));
#if defined(QSE_XMA_ENABLE_STAT)
dumper (ctx, QSE_T("== statistics ==\n"));
#if (QSE_SIZEOF_SIZE_T == QSE_SIZEOF_LONG)
dumper (ctx, QSE_T("total = %lu\n"), (unsigned long)xma->stat.total);
dumper (ctx, QSE_T("alloc = %lu\n"), (unsigned long)xma->stat.alloc);
dumper (ctx, QSE_T("avail = %lu\n"), (unsigned long)xma->stat.avail);
#elif (QSE_SIZEOF_SIZE_T == QSE_SIZEOF_LONG_LONG)
dumper (ctx, QSE_T("total = %llu\n"), (unsigned long long)xma->stat.total);
dumper (ctx, QSE_T("alloc = %llu\n"), (unsigned long long)xma->stat.alloc);
dumper (ctx, QSE_T("avail = %llu\n"), (unsigned long long)xma->stat.avail);
#elif (QSE_SIZEOF_SIZE_T == QSE_SIZEOF_INT)
dumper (ctx, QSE_T("total = %u\n"), (unsigned int)xma->stat.total);
dumper (ctx, QSE_T("alloc = %u\n"), (unsigned int)xma->stat.alloc);
dumper (ctx, QSE_T("avail = %u\n"), (unsigned int)xma->stat.avail);
#else
# error weird size of qse_size_t. unsupported platform
#endif
2010-07-31 07:24:19 +00:00
#endif
2010-07-24 07:24:44 +00:00
dumper (ctx, QSE_T("== blocks ==\n"));
dumper (ctx, QSE_T(" size avail address\n"));
2010-07-25 06:43:26 +00:00
for (tmp = xma->head, fsum = 0, asum = 0; tmp; tmp = tmp->b.next)
2010-07-24 07:24:44 +00:00
{
#if (QSE_SIZEOF_SIZE_T == QSE_SIZEOF_LONG)
dumper (ctx, QSE_T(" %-18lu %-5u %p\n"),
(unsigned long)tmp->size, (unsigned int)tmp->avail, tmp
);
#elif (QSE_SIZEOF_SIZE_T == QSE_SIZEOF_LONG_LONG)
dumper (ctx, QSE_T(" %-18llu %-5u %p\n"),
(unsigned long long)tmp->size, (unsigned int)tmp->avail, tmp
);
#elif (QSE_SIZEOF_SIZE_T == QSE_SIZEOF_INT)
dumper (ctx, QSE_T(" %-18u %-5u %p\n"),
(unsigned int)tmp->size, (unsigned int)tmp->avail, tmp
);
#else
# error weird size of qse_size_t. unsupported platform
#endif
2010-07-24 07:24:44 +00:00
if (tmp->avail) fsum += tmp->size;
else asum += tmp->size;
}
#if defined(QSE_XMA_ENABLE_STAT)
2010-07-25 06:43:26 +00:00
isum = (xma->stat.nfree + xma->stat.nused) * HDRSIZE;
2010-07-31 07:24:19 +00:00
#endif
2010-07-24 07:24:44 +00:00
dumper (ctx, QSE_T("---------------------------------------\n"));
#if (QSE_SIZEOF_ULONG_T == QSE_SIZEOF_LONG)
dumper (ctx, QSE_T("Allocated blocks: %18lu bytes\n"), (unsigned long)asum);
dumper (ctx, QSE_T("Available blocks: %18lu bytes\n"), (unsigned long)fsum);
#elif (QSE_SIZEOF_ULONG_T == QSE_SIZEOF_LONG_LONG)
dumper (ctx, QSE_T("Allocated blocks: %18llu bytes\n"), (unsigned long long)asum);
dumper (ctx, QSE_T("Available blocks: %18llu bytes\n"), (unsigned long long)fsum);
#elif (QSE_SIZEOF_ULONG_T == QSE_SIZEOF_INT)
dumper (ctx, QSE_T("Allocated blocks: %18u bytes\n"), (unsigned int)asum);
dumper (ctx, QSE_T("Available blocks: %18u bytes\n"), (unsigned int)fsum);
#else
# error weird size of qse_ulong_t. unsupported platform
#endif
#if defined(QSE_XMA_ENABLE_STAT)
#if (QSE_SIZEOF_ULONG_T == QSE_SIZEOF_LONG)
dumper (ctx, QSE_T("Internal use : %18lu bytes\n"), (unsigned long)isum);
dumper (ctx, QSE_T("Total : %18lu bytes\n"), (unsigned long)(asum + fsum + isum));
#elif (QSE_SIZEOF_ULONG_T == QSE_SIZEOF_LONG_LONG)
dumper (ctx, QSE_T("Internal use : %18llu bytes\n"), (unsigned long long)isum);
dumper (ctx, QSE_T("Total : %18llu bytes\n"), (unsigned long long)(asum + fsum + isum));
#elif (QSE_SIZEOF_ULONG_T == QSE_SIZEOF_INT)
dumper (ctx, QSE_T("Internal use : %18u bytes\n"), (unsigned int)isum);
dumper (ctx, QSE_T("Total : %18u bytes\n"), (unsigned int)(asum + fsum + isum));
#else
# error weird size of qse_ulong_t. unsupported platform
#endif
2010-07-31 07:24:19 +00:00
#endif
2011-03-17 02:37:06 +00:00
#if defined(QSE_XMA_ENABLE_STAT)
2010-08-01 01:45:47 +00:00
QSE_ASSERT (asum == xma->stat.alloc);
QSE_ASSERT (fsum == xma->stat.avail);
QSE_ASSERT (isum == xma->stat.total - (xma->stat.alloc + xma->stat.avail));
QSE_ASSERT (asum + fsum + isum == xma->stat.total);
2010-07-24 07:24:44 +00:00
#endif
2010-08-01 01:45:47 +00:00
}