/*
Very fast and accurate memory allocator
reserves memory via standard malloc
Copyright (C) 1998 Valery Shchedrin
*/
#include <stdio.h>
#include <stdlib.h>
/* not needed for malloc() #include <malloc.h> */
#include <string.h>
#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 <ptrs>, 0 if comparing <free_mem> */
};
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);
}
}
syntax highlighted by Code2HTML, v. 0.9.1