diff --git a/qse/cmd/awk/awk.c b/qse/cmd/awk/awk.c index 030aa685..65dbb615 100644 --- a/qse/cmd/awk/awk.c +++ b/qse/cmd/awk/awk.c @@ -1005,7 +1005,7 @@ static int awk_main (int argc, qse_char_t* argv[]) #endif if (arg.memlimit > 0) { - xma_mmgr.ctx = qse_xma_open (QSE_MMGR_GETDFL(), 0, arg.memlimit); + xma_mmgr.ctx = qse_xma_open (QSE_MMGR_GETDFL(), 0, QSE_NULL, arg.memlimit); if (xma_mmgr.ctx == QSE_NULL) { qse_printf (QSE_T("ERROR: cannot open memory heap\n")); @@ -1108,7 +1108,11 @@ oops: if (rtx) qse_awk_rtx_close (rtx); if (awk) qse_awk_close (awk); - if (xma_mmgr.ctx) qse_xma_close (xma_mmgr.ctx); + if (xma_mmgr.ctx) + { + if (app_debug) qse_xma_dump (xma_mmgr.ctx, qse_fprintf, QSE_STDERR); + qse_xma_close (xma_mmgr.ctx); + } freearg (&arg); #if defined(QSE_BUILD_DEBUG) diff --git a/qse/cmd/sed/sed.c b/qse/cmd/sed/sed.c index cfe42713..e5ee02ad 100644 --- a/qse/cmd/sed/sed.c +++ b/qse/cmd/sed/sed.c @@ -663,7 +663,7 @@ static int sed_main (int argc, qse_char_t* argv[]) #endif if (g_memlimit > 0) { - xma_mmgr.ctx = qse_xma_open (QSE_MMGR_GETDFL(), 0, g_memlimit); + xma_mmgr.ctx = qse_xma_open (QSE_MMGR_GETDFL(), 0, QSE_NULL, g_memlimit); if (xma_mmgr.ctx == QSE_NULL) { qse_printf (QSE_T("ERROR: cannot open memory heap\n")); diff --git a/qse/cmd/xli/xli.c b/qse/cmd/xli/xli.c index ff36911f..7585db54 100644 --- a/qse/cmd/xli/xli.c +++ b/qse/cmd/xli/xli.c @@ -431,7 +431,7 @@ static int xli_main (int argc, qse_char_t* argv[]) #endif if (g_memlimit > 0) { - xma_mmgr.ctx = qse_xma_open (QSE_MMGR_GETDFL(), 0, g_memlimit); + xma_mmgr.ctx = qse_xma_open (QSE_MMGR_GETDFL(), 0, QSE_NULL, g_memlimit); if (xma_mmgr.ctx == QSE_NULL) { qse_fprintf (QSE_STDERR, QSE_T("ERROR: cannot open memory heap\n")); diff --git a/qse/include/qse/cmn/xma.h b/qse/include/qse/cmn/xma.h index 52d09a12..6da96e8d 100644 --- a/qse/include/qse/cmn/xma.h +++ b/qse/include/qse/cmn/xma.h @@ -46,11 +46,11 @@ * * // create a new memory allocator obtaining a 100K byte zone * // with the default memory allocator - * xma = qse_xma_open (QSE_NULL, 0, 100000L); + * xma = qse_xma_open(QSE_NULL, 0, 100000L); * - * ptr1 = qse_xma_alloc (xma, 5000); // allocate a 5K block from the zone - * ptr2 = qse_xma_alloc (xma, 1000); // allocate a 1K block from the zone - * ptr1 = qse_xma_realloc (xma, ptr1, 6000); // resize the 5K block to 6K. + * ptr1 = qse_xma_alloc(xma, 5000); // allocate a 5K block from the zone + * ptr2 = qse_xma_alloc(xma, 1000); // allocate a 1K block from the zone + * ptr1 = qse_xma_realloc(xma, ptr1, 6000); // resize the 5K block to 6K. * * qse_xma_dump (xma, qse_fprintf, QSE_STDOUT); // dump memory blocks * @@ -67,7 +67,7 @@ #include #include -#ifdef QSE_BUILD_DEBUG +#if defined(QSE_BUILD_DEBUG) # define QSE_XMA_ENABLE_STAT #endif @@ -78,27 +78,29 @@ typedef struct qse_xma_t qse_xma_t; /** - * The qse_xma_blk_t type defines a memory block allocated. + * The qse_xma_fblk_t type defines a memory block allocated. */ -typedef struct qse_xma_blk_t qse_xma_blk_t; +typedef struct qse_xma_fblk_t qse_xma_fblk_t; +typedef struct qse_xma_mblk_t qse_xma_mblk_t; #define QSE_XMA_FIXED 32 #define QSE_XMA_SIZE_BITS ((QSE_SIZEOF_SIZE_T*8)-1) struct qse_xma_t { - qse_mmgr_t* mmgr; + qse_mmgr_t* _mmgr; - /** pointer to the first memory block */ - qse_xma_blk_t* head; + qse_uint8_t* start; /* zone beginning */ + qse_uint8_t* end; /* zone end */ + int internal; /** pointer array to free memory blocks */ - qse_xma_blk_t* xfree[QSE_XMA_FIXED + QSE_XMA_SIZE_BITS + 1]; + qse_xma_fblk_t* xfree[QSE_XMA_FIXED + QSE_XMA_SIZE_BITS + 1]; /** pre-computed value for fast xfree index calculation */ qse_size_t bdec; -#ifdef QSE_XMA_ENABLE_STAT +#if defined(QSE_XMA_ENABLE_STAT) struct { qse_size_t total; @@ -115,7 +117,7 @@ struct qse_xma_t * for qse_xma_dump(). */ typedef int (*qse_xma_dumper_t) ( - void* ctx, + void* ctx, const qse_char_t* fmt, ... ); @@ -134,8 +136,9 @@ extern "C" { */ QSE_EXPORT qse_xma_t* qse_xma_open ( qse_mmgr_t* mmgr, /**< memory manager */ - qse_size_t xtnsize, /**< extension size in bytes */ - qse_size_t zonesize /**< zone size in bytes */ + qse_size_t xtnsize, /**< extension size in bytes */ + void* zoneptr, + qse_size_t zonesize /**< zone size in bytes */ ); /** @@ -148,13 +151,17 @@ QSE_EXPORT void qse_xma_close ( qse_xma_t* xma /**< memory allocator */ ); -QSE_EXPORT qse_mmgr_t* qse_xma_getmmgr ( - qse_xma_t* xma -); +#if defined(QSE_HAVE_INLINE) +static QSE_INLINE qse_mmgr_t* qse_xma_getmmgr (qse_xma_t* xma) { return xma->_mmgr; } +#else +# define qse_xma_getmmgr(xma) (((qse_xma_t*)(xma))->_mmgr) +#endif -QSE_EXPORT void* qse_xma_getxtn ( - qse_xma_t* xma -); +#if defined(QSE_HAVE_INLINE) +static QSE_INLINE void* qse_xma_getxtn (qse_xma_t* xma) { return (void*)(xma + 1); } +#else +#define qse_xma_getxtn(xma) ((void*)((qse_xma_t*)(xma) + 1)) +#endif /** * The qse_xma_init() initializes a memory allocator. If you have the qse_xma_t @@ -167,7 +174,8 @@ QSE_EXPORT void* qse_xma_getxtn ( QSE_EXPORT int qse_xma_init ( qse_xma_t* xma, /**< memory allocator */ qse_mmgr_t* mmgr, /**< memory manager */ - qse_size_t zonesize /**< zone size in bytes */ + void* zoneptr, /**< pointer to memory zone. if #QSE_NULL, a zone is auto-created */ + qse_size_t zonesize /**< zone size in bytes */ ); /** @@ -184,12 +192,12 @@ QSE_EXPORT void qse_xma_fini ( */ QSE_EXPORT void* qse_xma_alloc ( qse_xma_t* xma, /**< memory allocator */ - qse_size_t size /**< size in bytes */ + qse_size_t size /**< size in bytes */ ); QSE_EXPORT void* qse_xma_calloc ( qse_xma_t* xma, - qse_size_t size + qse_size_t size ); /** @@ -199,7 +207,7 @@ QSE_EXPORT void* qse_xma_calloc ( QSE_EXPORT void* qse_xma_realloc ( qse_xma_t* xma, /**< memory allocator */ void* b, /**< memory block */ - qse_size_t size /**< new size in bytes */ + qse_size_t size /**< new size in bytes */ ); /** diff --git a/qse/lib/cmn/HeapMmgr.cpp b/qse/lib/cmn/HeapMmgr.cpp index 183e01de..9dba151c 100644 --- a/qse/lib/cmn/HeapMmgr.cpp +++ b/qse/lib/cmn/HeapMmgr.cpp @@ -50,7 +50,7 @@ void* HeapMmgr::allocMem (qse_size_t n) QSE_CPP_NOEXCEPT { if (!this->xma) { - this->xma = qse_xma_open(this->getMmgr(), QSE_SIZEOF(xma_xtn_t), heap_size); + this->xma = qse_xma_open(this->getMmgr(), QSE_SIZEOF(xma_xtn_t), QSE_NULL, heap_size); if (!this->xma) return QSE_NULL; xma_xtn_t* xtn = (xma_xtn_t*)qse_xma_getxtn(this->xma); @@ -64,7 +64,7 @@ void* HeapMmgr::reallocMem (void* ptr, qse_size_t n) QSE_CPP_NOEXCEPT { if (!this->xma) { - this->xma = qse_xma_open(this->getMmgr(), QSE_SIZEOF(xma_xtn_t), heap_size); + this->xma = qse_xma_open(this->getMmgr(), QSE_SIZEOF(xma_xtn_t), QSE_NULL, heap_size); if (!this->xma) return QSE_NULL; xma_xtn_t* xtn = (xma_xtn_t*)qse_xma_getxtn(this->xma); diff --git a/qse/lib/cmn/xma.c b/qse/lib/cmn/xma.c index ed941aca..16889818 100644 --- a/qse/lib/cmn/xma.c +++ b/qse/lib/cmn/xma.c @@ -27,12 +27,34 @@ #include #include "mem-prv.h" -#define ALIGN QSE_SIZEOF(qse_size_t) /* this must be a power of 2 */ -#define HDRSIZE QSE_SIZEOF(qse_xma_blk_t) -#define MINBLKLEN (HDRSIZE + ALIGN) +/* 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 + * + * 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/qseawk +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 = (qse_int_t *) 0x7fffea722f78 +(gdb) disp /i 0x42156a +1: x/i 0x42156a +=> 0x42156a : movaps %xmm2,0x9f80(%rbp) +*/ +#define ALIGN (QSE_SIZEOF_VOID_P * 2) /* this must be a power of 2 */ -#define SYS_TO_USR(_) (((qse_xma_blk_t*)_) + 1) -#define USR_TO_SYS(_) (((qse_xma_blk_t*)_) - 1) +#define MBLKHDRSIZE (QSE_SIZEOF(qse_xma_mblk_t)) +#define MINALLOCSIZE (QSE_SIZEOF(qse_xma_fblk_t) - QSE_SIZEOF(qse_xma_mblk_t)) +#define FBLKMINSIZE (MBLKHDRSIZE + MINALLOCSIZE) /* or QSE_SIZEOF(qse_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) ((qse_uint8_t*)(b) + MBLKHDRSIZE) +#define USR_TO_SYS(b) ((qse_uint8_t*)(b) - MBLKHDRSIZE) /* * the xfree array is divided into three region @@ -42,25 +64,78 @@ #define FIXED QSE_XMA_FIXED #define XFIMAX(xma) (QSE_COUNTOF(xma->xfree)-1) -struct qse_xma_blk_t +#define mblk_size(b) (((qse_xma_mblk_t*)(b))->size) +#define mblk_prev_size(b) (((qse_xma_mblk_t*)(b))->prev_size) +#define next_mblk(b) ((qse_xma_mblk_t*)((qse_uint8_t*)b + MBLKHDRSIZE + mblk_size(b))) +#define prev_mblk(b) ((qse_xma_mblk_t*)((qse_uint8_t*)b - (MBLKHDRSIZE + mblk_prev_size(b)))) + +struct qse_xma_mblk_t { - qse_size_t avail: 1; + qse_size_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. */ + qse_size_t free: 1; qse_size_t size: QSE_XMA_SIZE_BITS;/**< block size */ - - 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 */ - } b; }; -static QSE_INLINE_ALWAYS qse_size_t szlog2 (qse_size_t n) +struct qse_xma_fblk_t +{ + qse_size_t prev_size; + qse_size_t free: 1; + qse_size_t size: QSE_XMA_SIZE_BITS;/**< block size */ + + /* these two fields are used only if the block is free */ + qse_xma_fblk_t* free_prev; /**< link to the previous free block */ + qse_xma_fblk_t* free_next; /**< link to the next free block */ +}; + +/*#define VERIFY*/ +#if defined(VERIFY) +static void DBG_VERIFY (qse_xma_t* xma, const char* desc) +{ + qse_xma_mblk_t* tmp, * next; + qse_size_t cnt; + qse_size_t fsum, asum; +#if defined(QSE_XMA_ENABLE_STAT) + qse_size_t isum; +#endif + + for (tmp = (qse_xma_mblk_t*)xma->start, cnt = 0, fsum = 0, asum = 0; (qse_uint8_t*)tmp < xma->end; tmp = next, cnt++) + { + next = next_mblk(tmp); + + if ((qse_uint8_t*)tmp == xma->start) + { + QSE_ASSERT (tmp->prev_size == 0); + } + if ((qse_uint8_t*)next < xma->end) + { + QSE_ASSERT (next->prev_size == tmp->size); + } + + if (tmp->free) fsum += tmp->size; + else asum += tmp->size; + } + +#if defined(QSE_XMA_ENABLE_STAT) + isum = (xma->stat.nfree + xma->stat.nused) * MBLKHDRSIZE; + 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); +#endif +} +#else +#define DBG_VERIFY(xma, desc) +#endif + +static QSE_INLINE qse_size_t szlog2 (qse_size_t n) { /* * 2**x = n; @@ -106,7 +181,7 @@ static QSE_INLINE_ALWAYS qse_size_t szlog2 (qse_size_t n) #undef BITS } -static QSE_INLINE_ALWAYS qse_size_t getxfi (qse_xma_t* xma, qse_size_t size) +static QSE_INLINE 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; @@ -114,71 +189,79 @@ static QSE_INLINE_ALWAYS qse_size_t getxfi (qse_xma_t* xma, qse_size_t size) return xfi; } -qse_xma_t* qse_xma_open ( - qse_mmgr_t* mmgr, qse_size_t xtnsize, qse_size_t zonesize) + +qse_xma_t* qse_xma_open (qse_mmgr_t* mmgr, qse_size_t xtnsize, void* zoneptr, qse_size_t zonesize) { qse_xma_t* xma; - xma = (qse_xma_t*) QSE_MMGR_ALLOC (mmgr, QSE_SIZEOF(*xma) + xtnsize); - if (xma == QSE_NULL) return QSE_NULL; + xma = (qse_xma_t*)QSE_MMGR_ALLOC(mmgr, QSE_SIZEOF(*xma) + xtnsize); + if (QSE_UNLIKELY(!xma)) return QSE_NULL; - if (qse_xma_init (xma, mmgr, zonesize) <= -1) + if (qse_xma_init(xma, mmgr, zoneptr, zonesize) <= -1) { QSE_MMGR_FREE (mmgr, xma); return QSE_NULL; } - QSE_MEMSET (QSE_XTN(xma), 0, xtnsize); + QSE_MEMSET (xma + 1, 0, xtnsize); return xma; } void qse_xma_close (qse_xma_t* xma) { qse_xma_fini (xma); - QSE_MMGR_FREE (xma->mmgr, xma); + QSE_MMGR_FREE (xma->_mmgr, xma); } -int qse_xma_init (qse_xma_t* xma, qse_mmgr_t* mmgr, qse_size_t zonesize) +int qse_xma_init (qse_xma_t* xma, qse_mmgr_t* mmgr, void* zoneptr, qse_size_t zonesize) { - qse_xma_blk_t* free; + qse_xma_fblk_t* first; qse_size_t xfi; + int internal = 0; - /* round 'zonesize' to be the multiples of ALIGN */ - zonesize = QSE_ALIGNTO_POW2(zonesize, ALIGN); + if (!zoneptr) + { + /* round 'zonesize' to be the multiples of ALIGN */ + zonesize = QSE_ALIGNTO_POW2(zonesize, ALIGN); - /* adjust 'zonesize' to be large enough to hold a single smallest block */ - if (zonesize < MINBLKLEN) zonesize = MINBLKLEN; + /* adjust 'zonesize' to be large enough to hold a single smallest block */ + if (zonesize < FBLKMINSIZE) zonesize = FBLKMINSIZE; - /* allocate a memory chunk to use for actual memory allocation */ - free = QSE_MMGR_ALLOC (mmgr, zonesize); - if (free == QSE_NULL) return -1; - - /* initialize the header part of the free chunk */ - free->avail = 1; - free->size = zonesize - HDRSIZE; /* size excluding the block header */ - free->f.prev = QSE_NULL; - free->f.next = QSE_NULL; - free->b.next = QSE_NULL; - free->b.prev = QSE_NULL; + zoneptr = QSE_MMGR_ALLOC(mmgr, zonesize); + if (QSE_UNLIKELY(!zoneptr)) return -1; + + internal = 1; + } + + first = (qse_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 = QSE_NULL; + first->free_next = QSE_NULL; QSE_MEMSET (xma, 0, QSE_SIZEOF(*xma)); - xma->mmgr = mmgr; + 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,free->size); + xfi = getxfi(xma, first->size); /* locate it into an apporopriate slot */ - xma->xfree[xfi] = free; + xma->xfree[xfi] = first; /* let it be the head, which is natural with only a block */ - xma->head = free; + xma->start = (qse_uint8_t*)first; + xma->end = xma->start + zonesize; + xma->internal = 1; /* initialize some statistical variables */ #if defined(QSE_XMA_ENABLE_STAT) xma->stat.total = zonesize; xma->stat.alloc = 0; - xma->stat.avail = zonesize - HDRSIZE; + xma->stat.avail = zonesize - MBLKHDRSIZE; xma->stat.nfree = 1; xma->stat.nused = 0; #endif @@ -190,58 +273,48 @@ void qse_xma_fini (qse_xma_t* xma) { /* the head must point to the free chunk allocated in init(). * let's deallocate it */ - QSE_MMGR_FREE (xma->mmgr, xma->head); + if (xma->internal) QSE_MMGR_FREE (xma->_mmgr, xma->start); + xma->start = QSE_NULL; + xma->end = QSE_NULL; } -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); -} - -static QSE_INLINE void attach_to_freelist (qse_xma_t* xma, qse_xma_blk_t* b) +static QSE_INLINE void attach_to_freelist (qse_xma_t* xma, qse_xma_fblk_t* b) { /* * attach a block to a free list */ /* get the free list index for the block size */ - qse_size_t xfi = getxfi(xma,b->size); + qse_size_t xfi = getxfi(xma, b->size); /* let it be the head of the free list doubly-linked */ - b->f.prev = QSE_NULL; - b->f.next = xma->xfree[xfi]; - if (xma->xfree[xfi]) xma->xfree[xfi]->f.prev = b; - xma->xfree[xfi] = b; + b->free_prev = QSE_NULL; + b->free_next = xma->xfree[xfi]; + if (xma->xfree[xfi]) xma->xfree[xfi]->free_prev = b; + xma->xfree[xfi] = b; } -static QSE_INLINE void detach_from_freelist (qse_xma_t* xma, qse_xma_blk_t* b) +static QSE_INLINE void detach_from_freelist (qse_xma_t* xma, qse_xma_fblk_t* b) { - /* - * detach a block from a free list - */ - qse_xma_blk_t* p, * n; + /* detach a block from a free list */ + qse_xma_fblk_t* p, * n; /* alias the previous and the next with short variable names */ - p = b->f.prev; - n = b->f.next; + 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->f.next = n; + p->free_next = n; } else { /* the previous item does not exist. the block is the first * item in the free list. */ - qse_size_t xfi = getxfi(xma,b->size); + qse_size_t xfi = getxfi(xma, b->size); QSE_ASSERT (b == xma->xfree[xfi]); /* let's update the free list head */ xma->xfree[xfi] = n; @@ -249,52 +322,49 @@ static QSE_INLINE void detach_from_freelist (qse_xma_t* xma, qse_xma_blk_t* b) /* let the 'prev' pointer of the block's next item point to the * block's previous item */ - if (n) n->f.prev = p; + if (n) n->free_prev = p; } -static qse_xma_blk_t* alloc_from_freelist ( - qse_xma_t* xma, qse_size_t xfi, qse_size_t size) +static qse_xma_fblk_t* alloc_from_freelist (qse_xma_t* xma, qse_size_t xfi, qse_size_t size) { - qse_xma_blk_t* free; + qse_xma_fblk_t* cand; - for (free = xma->xfree[xfi]; free; free = free->f.next) + for (cand = xma->xfree[xfi]; cand; cand = cand->free_next) { - if (free->size >= size) + if (cand->size >= size) { qse_size_t rem; - detach_from_freelist (xma, free); + detach_from_freelist (xma, cand); - rem = free->size - size; - if (rem >= MINBLKLEN) + rem = cand->size - size; + if (rem >= FBLKMINSIZE) { - qse_xma_blk_t* tmp; + qse_xma_mblk_t* y, * z; /* the remaining part is large enough to hold * another block. let's split it */ - /* shrink the size of the 'free' block */ - free->size = size; + /* shrink the size of the 'cand' block */ + cand->size = size; /* let 'tmp' point to the remaining part */ - tmp = (qse_xma_blk_t*)(((qse_byte_t*)(free + 1)) + size); + y = next_mblk(cand); /* get the next adjacent block */ /* initialize some fields */ - tmp->avail = 1; - tmp->size = rem - HDRSIZE; - - /* link 'tmp' to the block list */ - tmp->b.next = free->b.next; - tmp->b.prev = free; - if (free->b.next) free->b.next->b.prev = tmp; - free->b.next = tmp; + y->free = 1; + y->size = rem - MBLKHDRSIZE; + y->prev_size = cand->size; /* add the remaining part to the free list */ - attach_to_freelist (xma, tmp); + attach_to_freelist (xma, (qse_xma_fblk_t*)y); + + z = next_mblk(y); + if ((qse_uint8_t*)z < xma->end) z->prev_size = y->size; #if defined(QSE_XMA_ENABLE_STAT) - xma->stat.avail -= HDRSIZE; + xma->stat.avail -= MBLKHDRSIZE; #endif } #if defined(QSE_XMA_ENABLE_STAT) @@ -306,236 +376,240 @@ static qse_xma_blk_t* alloc_from_freelist ( } #endif - free->avail = 0; + cand->free = 0; /* - free->f.next = QSE_NULL; - free->f.prev = QSE_NULL; + cand->free_next = QSE_NULL; + cand->free_prev = QSE_NULL; */ #if defined(QSE_XMA_ENABLE_STAT) xma->stat.nused++; - xma->stat.alloc += free->size; - xma->stat.avail -= free->size; + xma->stat.alloc += cand->size; + xma->stat.avail -= cand->size; #endif - return free; + return cand; } } return QSE_NULL; } + void* qse_xma_alloc (qse_xma_t* xma, qse_size_t size) { - qse_xma_blk_t* free; + qse_xma_fblk_t* cand; qse_size_t xfi; - if (size <= 0) size = 1; + DBG_VERIFY (xma, "alloc start"); /* round up 'size' to the multiples of ALIGN */ + if (size < MINALLOCSIZE) size = MINALLOCSIZE; size = QSE_ALIGNTO_POW2(size, ALIGN); QSE_ASSERT (size >= ALIGN); - xfi = getxfi(xma,size); + xfi = getxfi(xma, size); /*if (xfi < XFIMAX(xma) && xma->xfree[xfi])*/ if (xfi < FIXED && xma->xfree[xfi]) { /* try the best fit */ - free = xma->xfree[xfi]; + cand = xma->xfree[xfi]; - QSE_ASSERT (free->avail != 0); - QSE_ASSERT (free->size == size); + QSE_ASSERT (cand->free != 0); + QSE_ASSERT (cand->size == size); - detach_from_freelist (xma, free); - free->avail = 0; + detach_from_freelist (xma, cand); + cand->free = 0; #if defined(QSE_XMA_ENABLE_STAT) xma->stat.nfree--; xma->stat.nused++; - xma->stat.alloc += free->size; - xma->stat.avail -= free->size; + xma->stat.alloc += cand->size; + xma->stat.avail -= cand->size; #endif } else if (xfi == XFIMAX(xma)) { /* huge block */ - free = alloc_from_freelist (xma, XFIMAX(xma), size); - if (free == QSE_NULL) return QSE_NULL; + cand = alloc_from_freelist(xma, XFIMAX(xma), size); + if (!cand) 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) + cand = alloc_from_freelist(xma, xfi, size); + if (!cand) { /* borrow a large block from the huge block chain */ - free = alloc_from_freelist (xma, XFIMAX(xma), size); + cand = 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; + cand = alloc_from_freelist(xma, XFIMAX(xma), size); + if (!cand) xfi = FIXED - 1; } - if (free == QSE_NULL) + if (!cand) { /* try each large block chain left */ for (++xfi; xfi < XFIMAX(xma) - 1; xfi++) { - free = alloc_from_freelist (xma, xfi, size); - if (free) break; + cand = alloc_from_freelist(xma, xfi, size); + if (cand) break; } - if (free == QSE_NULL) return QSE_NULL; + if (!cand) return QSE_NULL; } } - return SYS_TO_USR(free); + DBG_VERIFY (xma, "alloc end"); + return SYS_TO_USR(cand); } static void* _realloc_merge (qse_xma_t* xma, void* b, qse_size_t size) { - qse_xma_blk_t* blk = USR_TO_SYS(b); + qse_xma_mblk_t* blk = (qse_xma_mblk_t*)USR_TO_SYS(b); + DBG_VERIFY (xma, "realloc merge start"); /* rounds up 'size' to be multiples of ALIGN */ - size = QSE_ALIGNTO_POW2 (size, ALIGN); + if (size < MINALLOCSIZE) size = MINALLOCSIZE; + size = QSE_ALIGNTO_POW2(size, ALIGN); if (size > blk->size) { - /* - * grow the current block - */ + /* grow the current block */ qse_size_t req; - qse_xma_blk_t* n; + qse_xma_mblk_t* n; qse_size_t rem; - - req = size - blk->size; - n = blk->b.next; + req = size - blk->size; /* required size additionally */ + n = next_mblk(blk); /* check if the next adjacent block is available */ - if (!n || !n->avail || req > n->size) return QSE_NULL; /* no! */ + if ((qse_uint8_t*)n >= xma->end || !n->free || req > n->size) return QSE_NULL; /* no! */ +/* TODO: check more blocks if the next block is free but small in size. + * check the previous adjacent blocks also */ + + QSE_ASSERT (blk->size == n->prev_size); /* let's merge the current block with the next block */ - detach_from_freelist (xma, n); + detach_from_freelist (xma, (qse_xma_fblk_t*)n); - rem = (HDRSIZE + n->size) - req; - if (rem >= MINBLKLEN) + rem = (MBLKHDRSIZE + n->size) - req; + if (rem >= FBLKMINSIZE) { - /* + /* * the remaining part of the next block is large enough * to hold a block. break the next block. */ - qse_xma_blk_t* tmp; - - /* 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); + qse_xma_mblk_t* y, * z; blk->size += req; + y = next_mblk(blk); + y->free = 1; + y->size = rem - MBLKHDRSIZE; + y->prev_size = blk->size; + attach_to_freelist (xma, (qse_xma_fblk_t*)y); - tmp->b.next = nn; - if (nn) nn->b.prev = tmp; - - blk->b.next = tmp; - tmp->b.prev = blk; + z = next_mblk(y); + if ((qse_uint8_t*)z < xma->end) z->prev_size = y->size; #if defined(QSE_XMA_ENABLE_STAT) xma->stat.alloc += req; - xma->stat.avail -= req; /* req + HDRSIZE(tmp) - HDRSIZE(n) */ + xma->stat.avail -= req; /* req + MBLKHDRSIZE(tmp) - MBLKHDRSIZE(n) */ #endif } else { - /* the remaining part of the next block is negligible. + qse_xma_mblk_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 */ - blk->size += HDRSIZE + n->size; - blk->b.next = n->b.next; - if (n->b.next) n->b.next->b.prev = blk; + blk->size += MBLKHDRSIZE + n->size; + + z = next_mblk(blk); + if ((qse_uint8_t*)z < xma->end) z->prev_size = blk->size; #if defined(QSE_XMA_ENABLE_STAT) xma->stat.nfree--; - xma->stat.alloc += HDRSIZE + n->size; + xma->stat.alloc += MBLKHDRSIZE + n->size; xma->stat.avail -= n->size; #endif } } else if (size < blk->size) { - /* - * shrink the block - */ - + /* shrink the block */ qse_size_t rem = blk->size - size; - if (rem >= MINBLKLEN) + if (rem >= FBLKMINSIZE) { - qse_xma_blk_t* tmp; - qse_xma_blk_t* n = blk->b.next; + qse_xma_mblk_t* n; - /* 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; + n = next_mblk(blk); - if (n && n->avail) + /* the leftover is large enough to hold a block of minimum size.split the current block */ + if ((qse_uint8_t*)n < xma->end && n->free) { - /* merge with the next block */ - detach_from_freelist (xma, n); + qse_xma_mblk_t* y, * z; + + /* make the leftover block merge with the next block */ + + detach_from_freelist (xma, (qse_xma_fblk_t*)n); - 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; - tmp->size = rem - HDRSIZE + HDRSIZE + n->size; + y = next_mblk(blk); /* update y to the leftover block with the new block size set above */ + y->free = 1; + y->size = rem + n->size; /* add up the adjust block - (rem + MBLKHDRSIZE(n) + n->size) - MBLKHDRSIZE(y) */ + y->prev_size = blk->size; + + /* add 'y' to the free list */ + attach_to_freelist (xma, (qse_xma_fblk_t*)y); + + z = next_mblk(y); /* get adjacent block to the merged block */ + if ((qse_uint8_t*)z < xma->end) z->prev_size = y->size; #if defined(QSE_XMA_ENABLE_STAT) xma->stat.alloc -= rem; - /* rem - HDRSIZE(tmp) + HDRSIZE(n) */ - xma->stat.avail += rem; + xma->stat.avail += rem; /* rem - MBLKHDRSIZE(y) + MBLKHDRSIZE(n) */ #endif } 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; + qse_xma_mblk_t* y; + + /* link the leftover block to the free list */ blk->size = size; - tmp->size = rem - HDRSIZE; + y = next_mblk(blk); /* update y to the leftover block with the new block size set above */ + y->free = 1; + y->size = rem - MBLKHDRSIZE; + y->prev_size = blk->size; + + attach_to_freelist (xma, (qse_xma_fblk_t*)y); + /*n = next_mblk(y); + if ((qse_uint8_t*)n < xma->end)*/ n->prev_size = y->size; #if defined(QSE_XMA_ENABLE_STAT) xma->stat.nfree++; xma->stat.alloc -= rem; - xma->stat.avail += tmp->size; + xma->stat.avail += y->size; #endif } - - /* add 'tmp' to the free list */ - attach_to_freelist (xma, tmp); } } + DBG_VERIFY (xma, "realloc merge end"); return b; } void* qse_xma_calloc (qse_xma_t* xma, qse_size_t size) { - void* ptr = qse_xma_alloc (xma, size); + void* ptr = qse_xma_alloc(xma, size); if (ptr) QSE_MEMSET (ptr, 0, size); return ptr; } @@ -547,17 +621,17 @@ void* qse_xma_realloc (qse_xma_t* xma, void* b, qse_size_t size) if (b == QSE_NULL) { /* 'realloc' with NULL is the same as 'alloc' */ - n = qse_xma_alloc (xma, size); + 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) + if (!n) { /* reallocation by merging failed. fall back to the slow * allocation-copy-free scheme */ - n = qse_xma_alloc (xma, size); + n = qse_xma_alloc(xma, size); if (n) { QSE_MEMCPY (n, b, size); @@ -571,9 +645,10 @@ void* qse_xma_realloc (qse_xma_t* xma, void* b, qse_size_t size) void qse_xma_free (qse_xma_t* xma, void* b) { - qse_xma_blk_t* blk = USR_TO_SYS(b); + qse_xma_mblk_t* blk = (qse_xma_mblk_t*)USR_TO_SYS(b); + qse_xma_mblk_t* x, * y; - /*QSE_ASSERT (blk->f.next == QSE_NULL);*/ + DBG_VERIFY (xma, "free start"); #if defined(QSE_XMA_ENABLE_STAT) /* update statistical variables */ @@ -581,231 +656,170 @@ void qse_xma_free (qse_xma_t* xma, void* b) xma->stat.alloc -= blk->size; #endif - if ((blk->b.prev && blk->b.prev->avail) && - (blk->b.next && blk->b.next->avail)) + x = prev_mblk(blk); + y = next_mblk(blk); + if (((qse_uint8_t*)x >= xma->start && x->free) && ((qse_uint8_t*)y < xma->end && y->free)) { /* * Merge the block with surrounding blocks * * blk - * +-----+ | +-----+ +------+ - * | V v | v | V + * | + * v * +------------+------------+------------+------------+ * | X | | Y | Z | * +------------+------------+------------+------------+ - * ^ | ^ | ^ | - * +-----+ +------+ +------+ - * - * - * +-----------------------------------+ - * | V + * + * * +--------------------------------------+------------+ * | X | Z | * +--------------------------------------+------------+ - * ^ | - * +-----------------------------------+ + * */ - qse_xma_blk_t* x = blk->b.prev; - qse_xma_blk_t* y = blk->b.next; - qse_xma_blk_t* z = y->b.next; - qse_size_t ns = HDRSIZE + blk->size + HDRSIZE; + + qse_xma_mblk_t* z = next_mblk(y); + qse_size_t ns = MBLKHDRSIZE + blk->size + MBLKHDRSIZE; qse_size_t bs = ns + y->size; - detach_from_freelist (xma, x); - detach_from_freelist (xma, y); + detach_from_freelist (xma, (qse_xma_fblk_t*)x); + detach_from_freelist (xma, (qse_xma_fblk_t*)y); x->size += bs; - x->b.next = z; - if (z) z->b.prev = x; + attach_to_freelist (xma, (qse_xma_fblk_t*)x); - attach_to_freelist (xma, x); + z = next_mblk(x); + if ((qse_uint8_t*)z < xma->end) z->prev_size = x->size; #if defined(QSE_XMA_ENABLE_STAT) xma->stat.nfree--; xma->stat.avail += ns; #endif } - else if (blk->b.next && blk->b.next->avail) + else if ((qse_uint8_t*)y < xma->end && y->free) { /* * Merge the block with the next block * * blk - * | +-----+ +------+ - * v | v | V + * | + * v * +------------+------------+------------+ - * | | X | Y | + * | | Y | Z | * +------------+------------+------------+ - * ^ | ^ | - * +------+ +------+ + * + * * * blk - * | +------------------+ - * v | V + * | + * v * +-------------------------+------------+ - * | | Y | + * | | Z | * +-------------------------+------------+ - * ^ | - * +-------------------+ + * + * */ - qse_xma_blk_t* x = blk->b.next; - qse_xma_blk_t* y = x->b.next; + qse_xma_mblk_t* z = next_mblk(y); #if defined(QSE_XMA_ENABLE_STAT) - xma->stat.avail += blk->size + HDRSIZE; + xma->stat.avail += blk->size + MBLKHDRSIZE; #endif - /* detach x from the free list */ - detach_from_freelist (xma, x); + /* detach y from the free list */ + detach_from_freelist (xma, (qse_xma_fblk_t*)y); /* update the block availability */ - blk->avail = 1; - /* update the block size. HDRSIZE for the header space in x */ - blk->size += HDRSIZE + x->size; + blk->free = 1; + /* update the block size. MBLKHDRSIZE for the header space in x */ + blk->size += MBLKHDRSIZE + y->size; /* update the backward link of Y */ - if (y) y->b.prev = blk; - /* update the forward link of the block being freed */ - blk->b.next = y; + if ((qse_uint8_t*)z < xma->end) z->prev_size = blk->size; /* attach blk to the free list */ - attach_to_freelist (xma, blk); - + attach_to_freelist (xma, (qse_xma_fblk_t*)blk); } - else if (blk->b.prev && blk->b.prev->avail) + else if ((qse_uint8_t*)x >= xma->start && x->free) { /* * Merge the block with the previous block * - * blk - * +-----+ | +-----+ - * | V v | v + * blk + * | + * v * +------------+------------+------------+ * | X | | Y | * +------------+------------+------------+ - * ^ | ^ | - * +------+ +------+ * - * - * +---------------------+ - * | v * +-------------------------+------------+ * | X | Y | * +-------------------------+------------+ - * ^ | - * +--------------------+ - * */ - qse_xma_blk_t* x = blk->b.prev; - qse_xma_blk_t* y = blk->b.next; - #if defined(QSE_XMA_ENABLE_STAT) - xma->stat.avail += HDRSIZE + blk->size; + xma->stat.avail += MBLKHDRSIZE + blk->size; #endif - detach_from_freelist (xma, x); + detach_from_freelist (xma, (qse_xma_fblk_t*)x); - x->size += HDRSIZE + blk->size; - x->b.next = y; - if (y) y->b.prev = x; + x->size += MBLKHDRSIZE + blk->size; - attach_to_freelist (xma, x); + QSE_ASSERT (y == next_mblk(x)); + if ((qse_uint8_t*)y < xma->end) y->prev_size = x->size; + + attach_to_freelist (xma, (qse_xma_fblk_t*)x); } else { - blk->avail = 1; - attach_to_freelist (xma, blk); + + blk->free = 1; + attach_to_freelist (xma, (qse_xma_fblk_t*)blk); #if defined(QSE_XMA_ENABLE_STAT) xma->stat.nfree++; xma->stat.avail += blk->size; #endif } + + DBG_VERIFY (xma, "free end"); } void qse_xma_dump (qse_xma_t* xma, qse_xma_dumper_t dumper, void* ctx) { - qse_xma_blk_t* tmp; - qse_ulong_t fsum, asum; + qse_xma_mblk_t* tmp; + qse_size_t fsum, asum; #if defined(QSE_XMA_ENABLE_STAT) - qse_ulong_t isum; + qse_size_t isum; #endif - dumper (ctx, QSE_T("\n")); + 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 + dumper (ctx, QSE_T("total = %zu\n"), xma->stat.total); + dumper (ctx, QSE_T("alloc = %zu\n"), xma->stat.alloc); + dumper (ctx, QSE_T("avail = %zu\n"), xma->stat.avail); #endif dumper (ctx, QSE_T("== blocks ==\n")); dumper (ctx, QSE_T(" size avail address\n")); - for (tmp = xma->head, fsum = 0, asum = 0; tmp; tmp = tmp->b.next) + for (tmp = (qse_xma_mblk_t*)xma->start, fsum = 0, asum = 0; (qse_uint8_t*)tmp < xma->end; tmp = next_mblk(tmp)) { -#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 - if (tmp->avail) fsum += tmp->size; + dumper (ctx, QSE_T(" %-18zu %-5u %p\n"), tmp->size, (unsigned int)tmp->free, tmp); + if (tmp->free) fsum += tmp->size; else asum += tmp->size; } #if defined(QSE_XMA_ENABLE_STAT) - isum = (xma->stat.nfree + xma->stat.nused) * HDRSIZE; + isum = (xma->stat.nfree + xma->stat.nused) * MBLKHDRSIZE; #endif 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 + dumper (ctx, QSE_T("Allocated blocks: %18zu bytes\n"), asum); + dumper (ctx, QSE_T("Available blocks: %18zu bytes\n"), fsum); + #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 + dumper (ctx, QSE_T("Internal use : %18zu bytes\n"), isum); + dumper (ctx, QSE_T("Total : %18zu bytes\n"), (asum + fsum + isum)); #endif #if defined(QSE_XMA_ENABLE_STAT)