/* Very fast and accurate memory allocator reserves memory via standard malloc Copyright (C) 1998 Valery Shchedrin */ #include #include /* not needed for malloc() #include */ #include #include "config.h" /*#define DEB(x) x */ #define DEB(x) #define BIG_ALIGN 4 /* big block align */ #define HUNK_ALIGN 4 /* must be pow of 2 */ #define HUNK_THRESHOLD 4 /* must be pow of 2 */ #define HUNK_MAXHUNKS 512 #define HUNK_MAXSIZE 2000 #define ALIGN(size,align) (((size)+(align)-1)&(~((align)-1))) #define RESERVE_AMOUNT 11000*4 /* reserve 64*4 kb */ #define BT_NONE 0 /* initial value */ #define BT_BUSY 1 /* busy block */ #define BT_HUNKED 2 /* hunked blocks */ #define BT_PSTART 4 /* start of new block */ #define REGISTER register #define VOLATILE volatile #define INLINE inline typedef struct Node_s Node_t; typedef struct Root_s Root_t; typedef struct Block_s Block_t; typedef struct Hunk_s Hunk_t; struct Node_s { int bal; /* balance */ Node_t *l, *r; /* left & right subtrees */ }; struct Root_s { int offs; /* offset of Node_t in structure */ Node_t *root; /* pointer to root node */ int comp_type; /* 1 if comparing , 0 if comparing */ }; struct Block_s { char *ptr; /* pointer to data */ int size; /* total size of block */ int btype; /* type of block (BT_xx) */ Block_t *next; /* next in free_mem (linked list) */ Node_t free_mem_node, ptrs_node; /* nodes in AVL trees */ }; struct Hunk_s { /* header of hunkblock */ Block_t b; int size, total_hunks, used_hunks; Hunk_t *size_next, *free_next; }; static Root_t free_mem, ptrs; static Hunk_t *size_h[HUNK_MAXSIZE], *free_h[HUNK_MAXSIZE]; static Block_t tmp_block; /* balances partially balanced AVL tree */ /* returns 1 if tree height changed 0 otherwise */ static INLINE int balance(Node_t **root) { REGISTER Node_t *p = *root, *pp; REGISTER int ht_changed; if (p->bal < -1) { if (p->l->bal == 1) { pp = p->l; *root = p->l->r; p->l = (*root)->r; (*root)->r = p; pp->r = (*root)->l; p = *root; p->l = pp; if (p->bal > 0) p->l->bal = -p->bal; else p->l->bal = 0; if (p->bal < 0) p->r->bal = -p->bal; else p->r->bal = 0; p->bal = 0; return 1; } else { ht_changed = (p->l->bal)?1:0; *root = p->l; p->l = (*root)->r; (*root)->r = p; p->bal = - (++((*root)->bal)); return ht_changed; } } else if (p->bal > 1) { if (p->r->bal == -1) { pp = p->r; *root = p->r->l; p->r = (*root)->l; (*root)->l = p; pp->l = (*root)->r; p = *root; p->r = pp; if (p->bal > 0) p->l->bal = -p->bal; else p->l->bal = 0; if (p->bal < 0) p->r->bal = -p->bal; else p->r->bal = 0; p->bal = 0; return 1; } else { ht_changed = (p->r->bal)?1:0; *root = p->r; p->r = (*root)->l; (*root)->l = p; p->bal = - (--((*root)->bal)); return ht_changed; } } return 0; } /* INTERNAL insertion/deletion routines */ static Root_t *cur_tree; static Node_t *fix_node, *new_node; #define NODE_DATA(t) (((char*)(t))-cur_tree->offs) /* rather optimal compare function */ #define COMPARE(var, ctp, a, b) \ if (ctp) { \ var = ((char*)(a)) - ((char*)(b));\ } else { \ var = ((Block_t*)(a))->size - ((Block_t*)(b))->size; \ } static int r_delfix(Node_t **root); static int r_insert(Node_t **root) /* recursive insert */ { REGISTER int i; /* height increase */ /* insert prepared `new_node` */ if (!*root) { *root = new_node; new_node = NULL; return 1; } COMPARE(i, cur_tree->comp_type, NODE_DATA(new_node), NODE_DATA(*root)); if (i < 0) { /* insert into the left subtree */ i = -r_insert(&((*root)->l)); if (new_node != NULL) return 0; /* already there */ } else if (i > 0) { /* insert into the right subtree */ i = r_insert(&((*root)->r)); if (new_node != NULL) return 0; /* already there */ } else { /* found */ new_node = *root; return 0; } if (!i) return 0; (*root)->bal += i; /* update balance factor */ return ((*root)->bal)?(1-balance(root)):0; } static int r_delete(Node_t **root) /* recursive delete */ { REGISTER int i; /* height decrease */ if (!*root) { new_node = NULL; return 0; } /* data not found */ COMPARE(i, cur_tree->comp_type, NODE_DATA(new_node), NODE_DATA(*root)); if (i < 0) { i = -r_delete(&((*root)->l)); } else if (i > 0) { i = r_delete(&((*root)->r)); } else { new_node = *root; if (!new_node->l) { *root = new_node->r; return 1; } else { if (!new_node->r) { *root = new_node->l; return 1; } i = r_delfix(&((*root)->r)); fix_node->l = new_node->l; fix_node->r = new_node->r; fix_node->bal = new_node->bal; *root = fix_node; } } if (!i) return 0; (*root)->bal -= i; if ((*root)->bal) return balance(root); return 1; } static int r_delfix(Node_t **root) { REGISTER int i; /* height increase */ if (!(*root)->l) { fix_node = *root; *root = fix_node->r; return 1; } i = -r_delfix(&((*root)->l)); if (!i) return 0; (*root)->bal -= i; if ((*root)->bal) return balance(root); return 1; } static INLINE void *avl_ins(Root_t *t, void *data) /* insert node */ { cur_tree = t; new_node = (Node_t*)(((char*)data)+t->offs); memset(new_node, 0, sizeof(Node_t)); r_insert(&(cur_tree->root)); if (!new_node) return NULL; return (void*)(((char*)new_node) - t->offs); } static INLINE int avl_del(Root_t *t, void *data) /* delete node */ { cur_tree = t; new_node = (Node_t*)(((char*)data)+t->offs); r_delete(&(cur_tree->root)); if (!new_node) return 0; return 1; } static INLINE void avl_replace(Root_t *t, void *data) /* replace node */ { REGISTER Node_t **p = &t->root; REGISTER int cmp; while (*p) { COMPARE(cmp, t->comp_type, data, ((char*)*p) - t->offs); if (cmp < 0) p = &((*p)->l); else if (cmp > 0) p = &((*p)->r); else { memcpy(((char*)data)+t->offs, *p, sizeof(Node_t)); *p = (Node_t*)(((char*)data)+t->offs); return; } } } static Block_t *bl_alloc(size_t size) { REGISTER Node_t *nd; REGISTER Block_t *p, *pp; size = ALIGN(size, BIG_ALIGN); for (nd=free_mem.root, pp=NULL;nd;) { p = (Block_t*)(((char*)nd) - free_mem.offs); if (p->size > size) { pp = p; nd = nd->l; } else if (p->size < size) nd = nd->r; else { pp = p; break; } } if (!pp) { /* try to allocate block large enough via malloc */ pp = (Block_t *)malloc(RESERVE_AMOUNT + size); DEB(printf(" reserved new block 0x%x (%d)\n", (unsigned)pp, RESERVE_AMOUNT + size)); if (!pp) return NULL; /* unable to allocate */ memset(pp, 0, sizeof(Block_t)); pp->ptr = (char*)ALIGN((unsigned long)(pp+1), BIG_ALIGN); pp->size = ALIGN(RESERVE_AMOUNT + size - (pp->ptr - ((char*)pp)) - BIG_ALIGN + 1, BIG_ALIGN); pp->btype |= BT_PSTART; avl_ins(&ptrs, pp); } else { if (pp->next) avl_replace(&free_mem, pp->next); else avl_del(&free_mem, pp); } if (pp->size-size >= BIG_ALIGN+ALIGN(sizeof(Block_t), BIG_ALIGN)) { REGISTER Block_t *bt; /* split large block */ bt = (Block_t *)(pp->ptr + size); memset(bt, 0, sizeof(Block_t)); bt->ptr = ((char*)bt) + ALIGN(sizeof(Block_t),BIG_ALIGN); bt->size = (pp->ptr + pp->size) - bt->ptr; DEB(printf(" splitted block 0x%x inserted (%d)\n", (unsigned)bt, bt->size)); avl_ins(&ptrs, bt); p = (Block_t *)avl_ins(&free_mem, bt); if (p) { bt->next = p->next; p->next = bt; } else bt->next = NULL; pp->size = size; } pp->btype |= BT_BUSY; DEB(printf(" allocated block 0x%x (%d) - %d\n", (unsigned)pp, pp->size, pp->btype)); DEB(ptrs_prn(ptrs.root)); DEB(fr_prn(free_mem.root)); return pp; } static void ptrs_prn(Node_t *p) { Block_t *b; b = (Block_t*)(((char*)p) - ptrs.offs); if (b->ptrs_node.l) ptrs_prn(b->ptrs_node.l); printf(" PTR 0x%x size %d btype %d\n", (unsigned)b, b->size, b->btype); if (b->ptrs_node.r) ptrs_prn(b->ptrs_node.r); } static void fr1_prn(Block_t *p) { printf(" TFR 0x%x size %d btype %d\n", (unsigned)p, p->size, p->btype); if (p->next) fr1_prn(p->next); } static void fr_prn(Node_t *p) { Block_t *b; b = (Block_t*)(((char*)p) - free_mem.offs); if (b->free_mem_node.l) fr_prn(b->free_mem_node.l); fr1_prn(b); if (b->free_mem_node.r) fr_prn(b->free_mem_node.r); } static void bl_free(Block_t *b) { REGISTER Node_t *nd; REGISTER Block_t *p, *pp; REGISTER Block_t *bl_bef; /* Look for block before & after `b` */ for (nd=ptrs.root,pp=NULL,bl_bef=NULL;nd;) { p = (Block_t*)(((char*)nd) - ptrs.offs); if (((char*)p) > ((char*)b)) { pp = p; nd = nd->l; } else if (((char*)p) < ((char*)b)) { bl_bef = p; nd = nd->r; } else break; } /* Do this if `b` has subtrees */ nd = &b->ptrs_node; if (nd->l) { for (nd = nd->l; nd->r; nd=nd->r); bl_bef = (Block_t*)(((char*)nd) - ptrs.offs); nd = &b->ptrs_node; } if (nd->r) { for (nd = nd->r; nd->l; nd=nd->l); pp = (Block_t*)(((char*)nd) - ptrs.offs); } DEB(printf(" freeing block 0x%x (%d) - %d, prev 0x%x (%d), next 0x%x (%d)\n", (unsigned)b, b->size, b->btype, (unsigned)bl_bef, bl_bef?bl_bef->size:0, (unsigned)pp, pp?pp->size:0)); DEB(ptrs_prn(ptrs.root)); DEB(fr_prn(free_mem.root)); /* Combine with block just after */ if (pp && !(pp->btype & (BT_PSTART|BT_BUSY))) { avl_del(&ptrs, pp); for (nd=free_mem.root,p=NULL;nd;) { p = (Block_t*)(((char*)nd) - free_mem.offs); if (p->size > pp->size) nd = nd->l; else if (p->size < pp->size) nd = nd->r; else break; } if (!nd) { printf(" xmalloc failure - bad afterblock\n"); exit(-1); } if (p == pp) { if (pp->next) avl_replace(&free_mem, pp->next); else avl_del(&free_mem, pp); } else { for (;p->next != pp; p = p->next); p->next = pp->next; } b->size = (pp->ptr + pp->size) - b->ptr; } /* Combine with free block just before */ if (bl_bef && !(bl_bef->btype & BT_BUSY) && !(b->btype & BT_PSTART)) { pp = bl_bef; avl_del(&ptrs, b); for (nd=free_mem.root,p=NULL;nd;) { p = (Block_t*)(((char*)nd) - free_mem.offs); if (p->size > pp->size) nd = nd->l; else if (p->size < pp->size) nd = nd->r; else break; } if (!nd) { printf(" xmalloc failure - bad before block\n"); exit(-1); } if (p == pp) { if (pp->next) avl_replace(&free_mem, pp->next); else avl_del(&free_mem, pp); } else { for(;p->next != pp; p = p->next); p->next = pp->next; } pp->size = (b->ptr + b->size) - pp->ptr; b = pp; } b->btype &= ~BT_BUSY; DEB(printf(" after combination:\n")); DEB(ptrs_prn(ptrs.root)); DEB(fflush(stdout)); if (b->btype & BT_PSTART) { /* free unused blocks */ nd = &b->ptrs_node; if (nd->r) { for (nd = nd->r; nd->l; nd=nd->l); pp = (Block_t *)(((char*)nd) - ptrs.offs); } else { for (nd = ptrs.root,pp=NULL;nd;) { p = (Block_t*)(((char*)nd)-ptrs.offs); if (((char *)p) > ((char *)b)) { pp = p; nd = nd->l; } else if (((char *)p) < ((char *)b)) { nd = nd->r; } else break; } } if (!pp || (pp->btype & BT_PSTART)) { avl_del(&ptrs, b); DEB(printf(" disposing block 0x%x\n", (unsigned)b)); free(b); return; } } /* Put freed element into the tree */ pp = (Block_t *)avl_ins(&free_mem, b); if (pp) { b->next = pp->next; pp->next = b; } else b->next = NULL; } #define usagemap(h) (((unsigned char *)(h))+sizeof(Hunk_t)) static void *hunk_block_alloc(Hunk_t *h) { REGISTER unsigned long *cpl; REGISTER int i,c; /* Locate free point in usagemap */ for (cpl=(unsigned long*)usagemap(h);*cpl==0xFFFFFFFF;cpl++); i = ((unsigned char *)cpl) - usagemap(h); if (*(unsigned short*)cpl != 0xFFFF) { if (*(unsigned char*)cpl == 0xFF) { c = *(int*)(((unsigned char *)cpl)+1); i++; } else c = *(int*)(unsigned char *)cpl; } else { i+=2; c = *(((unsigned char *)cpl)+2); if (c == 0xFF) { c = *(int*)(((unsigned char *)cpl)+3); i++; } } i *= 8; if ((c & 0xF) == 0xF) { c >>= 4; i+=4; } if ((c & 0x3) == 0x3) { c >>= 2; i+=2; } if (c & 1) i++; usagemap(h)[i/8] |= (1 << (i&7)); /* Set bit */ /* Increment counter and update hashes */ if (++h->used_hunks == h->total_hunks) free_h[h->size] = h->free_next; return h->b.ptr+(i*h->size); } static void *hunk_alloc(int size) { REGISTER Hunk_t *p; if (size >= HUNK_THRESHOLD) size = ALIGN(size, HUNK_ALIGN); /* Look for already allocated partially free Hunk_t */ p = free_h[size]; if (!p) { /* try to allocate new one */ REGISTER int bound, hunks; /* Set hunks to allocate */ if (size_h[size]) { hunks = size_h[size]->total_hunks*2; if (hunks > HUNK_MAXHUNKS) hunks = HUNK_MAXHUNKS; } else { bound = sizeof(Hunk_t)+4; hunks = (ALIGN(bound,BIG_ALIGN) - bound)/size; if (!hunks) hunks = 1; } /* Allocate new Hunk_t */ bound = ALIGN((hunks+7)/8, 4); p = (Hunk_t*)bl_alloc(sizeof(Hunk_t)+bound+size*hunks); if (!p) return NULL; /* failed */ /* Initialize new Hunk_t */ p->total_hunks = hunks; p->used_hunks = 0; p->size = size; memset(usagemap(p), 0, bound); p->b.btype |= BT_HUNKED; p->b.ptr = usagemap(p)+bound; p->free_next = free_h[size]; free_h[size] = p; p->size_next = size_h[size]; size_h[size] = p; } return hunk_block_alloc(p); } static void hunk_free(Hunk_t *h, char *ptr) { REGISTER int i; REGISTER unsigned char *up; /* Look in usagemap */ i = (ptr - h->b.ptr)/h->size; if (i < 0 || i >= h->total_hunks) return; up = &(usagemap(h)[i/8]); i = 1 << (i&7); if (!(*up & i)) return; *up ^= i; /* Correct hunk counters */ if (h->used_hunks == h->total_hunks) { if (--h->used_hunks) { /* insert into free_h */ h->free_next = free_h[h->size]; free_h[h->size] = h; } } else { if (!--h->used_hunks) { /* delete from free_h */ REGISTER Hunk_t *p, *pp; for (p=free_h[h->size],pp=NULL;p!=h;pp=p,p=p->free_next); if (!pp) free_h[h->size] = p->free_next; else pp->free_next = p->free_next; } } /* Kill empty Hunk_t */ if (!h->used_hunks) { { /* delete from size_h */ REGISTER Hunk_t *p, *pp; for (p=size_h[h->size],pp=NULL;p!=h;pp=p,p=p->size_next); if (!pp) size_h[h->size] = p->size_next; else pp->size_next = p->size_next; } h->b.btype &= ~BT_HUNKED; h->b.ptr = (char*)ALIGN((unsigned long)((&h->b)+1), BIG_ALIGN); bl_free(&h->b); } } /* PUBLIC xmalloc/xfree functions */ static VOLATILE int initialized = 0; static int empty_element; void *xmalloc(size_t size) { if (!initialized) { /* initialize trees & hashes */ free_mem.offs = (((char*)&(tmp_block.free_mem_node))-((char*)&tmp_block)); ptrs.offs = (((char*)&(tmp_block.ptrs_node))-((char*)&tmp_block)); free_mem.root = NULL; ptrs.root = NULL; free_mem.comp_type = 0; ptrs.comp_type = 1; memset(size_h, 0, sizeof(Hunk_t*)*HUNK_MAXSIZE); memset(free_h, 0, sizeof(Hunk_t*)*HUNK_MAXSIZE); initialized = 1; } if (size <= 0) return (void*)&empty_element; if (size < HUNK_MAXSIZE) return hunk_alloc(size); { REGISTER Block_t *p; p = bl_alloc(size); ass(p!=NULL); return p->ptr; } } void xfree(void *ptr) { REGISTER Block_t *pp; REGISTER Node_t *nd, *bnd; if (!initialized) return; for (nd=ptrs.root, bnd=NULL;nd;) { if (((char*)nd) > (char*)ptr) nd = nd->l; else { bnd = nd; nd = nd->r; } } if (!bnd) return; pp = (Block_t*)(((char*)bnd)-ptrs.offs); if (pp->btype & BT_HUNKED) hunk_free((Hunk_t*)pp, ptr); else if (pp->btype & BT_BUSY) { if (((char*)ptr) != pp->ptr) return; bl_free(pp); } }