summaryrefslogblamecommitdiff
path: root/src/common/db.c
blob: 377128e8f4435e58732930ac423c9b5503cd3dbc (plain) (tree)
1
2
3
4
5
6
7
8
                                                      





                   
                   




                     







                                                                                  
                 
                      

























                                                                              



























                                             

      
                                            


                                                       

                                                                            

 
                                               



                                                         
                                             








                                          
                                                             










                                     


                                 




















                                                         
                                                  










                                     


                                 






















                                                                
                           





                                          
                                                                          














































































































































































































                                                                                                   









                                                                                           
                                                                  



                                                        
                                                     






                                            








                                                             



















                                                                                                          
                                                              


                                         

                                     
                                       























                                                      
                       









                                














                                                         
                            
        





















                                                  




















                                                                                                                               








                                                






                                    





                                                                       
                                      




                                                                      
                            




                                          



                                                                                                           


                                                          
                           















                                                             

                              
                       

                                                                                        

                  









                                                                     
                            




                                          
                                               













                                                             







                                                        


                                    
                                 



                             
                              
                                
                     

                   
// $Id: db.c,v 1.2 2004/09/23 14:43:06 MouseJstr Exp $
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "db.h"
#include "mmo.h"
#include "utils.h"
#include "malloc.h"

#ifdef MEMWATCH
#include "memwatch.h"
#endif

//#define MALLOC_DBN

// Backup cleaning routine in case the core doesn't do so properly,
// only enabled if malloc_dbn is not defined.
// As a temporary solution the root of the problem should still be found and fixed
struct dbn *head;
struct dbn *tail;

#ifdef MALLOC_DBN
#define ROOT_SIZE 4096
static struct dbn *dbn_root[512], *dbn_free;
static int dbn_root_rest=0,dbn_root_num=0;

static void * malloc_dbn(void)
{
	struct dbn* ret;

	if(dbn_free==NULL){
		if(dbn_root_rest<=0){
			CREATE(dbn_root[dbn_root_num], struct dbn, ROOT_SIZE);

			dbn_root_rest=ROOT_SIZE;
			dbn_root_num++;
		}
		return &(dbn_root[dbn_root_num-1][--dbn_root_rest]);
	}
	ret=dbn_free;
	dbn_free = dbn_free->parent;
	return ret;
}

static void free_dbn(struct dbn* add_dbn)
{
	add_dbn->parent = dbn_free;
	dbn_free = add_dbn;
}

void exit_dbn(void)
{
	int i;

	for (i=0;i<dbn_root_num;i++)
		if (dbn_root[i])
			aFree(dbn_root[i]);

	dbn_root_rest=0;
	dbn_root_num=0;

	return;
}
#else
void exit_dbn(void)
{
	int i = 0;
	struct dbn *p = head, *p2;
	while (p) {		
		p2 = p->next;
		aFree(p);
		p = p2;
		i++;
	}
	//printf ("freed %d stray dbn\n", i);
	return;
}
#endif

// maybe change the void* to const char* ???
static int strdb_cmp(struct dbt* table,void* a,void* b)
{
	if(table->maxlen)
		return strncmp((const char*)a,(const char*)b,table->maxlen);
	return strcmp((const char*)a,(const char*)b);
}

// maybe change the void* to unsigned char* ???
static unsigned int strdb_hash(struct dbt* table,void* a)
{
	int i;
	unsigned int h;
	unsigned char *p = (unsigned char*)a;

	i=table->maxlen;
	if(i==0) i=0x7fffffff;
	for(h=0;*p && --i>=0;){
		h=(h*33 + *p++) ^ (h>>24);
	}
	return h;
}

struct dbt* strdb_init_(int maxlen,const char *file,int line)
{
	int i;
	struct dbt* table;

	CREATE(table, struct dbt, 1);

	table->cmp=strdb_cmp;
	table->hash=strdb_hash;
	table->maxlen=maxlen;
	for(i=0;i<HASH_SIZE;i++)
		table->ht[i]=NULL;
	table->alloc_file = file;
	table->alloc_line = line;
	table->item_count = 0;
	return table;
}

static int numdb_cmp(struct dbt* table,void* a,void* b)
{
	int ia,ib;

	ia=(int)a;
	ib=(int)b;

	if((ia^ib) & 0x80000000)
		return ia<0 ? -1 : 1;

	return ia-ib;
}

static unsigned int numdb_hash(struct dbt* table,void* a)
{
	return (unsigned int)a;
}

struct dbt* numdb_init_(const char *file,int line)
{
	int i;
	struct dbt* table;

	CREATE(table, struct dbt, 1);

	table->cmp=numdb_cmp;
	table->hash=numdb_hash;
	table->maxlen=sizeof(int);
	for(i=0;i<HASH_SIZE;i++)
		table->ht[i]=NULL;
	table->alloc_file = file;
	table->alloc_line = line;
	table->item_count = 0;
	return table;
}

void* db_search(struct dbt *table,void* key)
{
	struct dbn *p;

	for(p=table->ht[table->hash(table,key) % HASH_SIZE];p;){
		int c=table->cmp(table,key,p->key);
		if(c==0)
			return p->data;
		if(c<0)
			p=p->left;
		else
			p=p->right;
	}
	return NULL;
}

void * db_search2(struct dbt *table, const char *key)
{
	int i,sp;
	struct dbn *p,*pn,*stack[64];
    int slen = strlen(key);

	for(i=0;i<HASH_SIZE;i++){
		if((p=table->ht[i])==NULL)
			continue;
		sp=0;
		while(1){
                        if (strnicmp(key, (const char*)p->key, slen) == 0)
                           return p->data;
			if((pn=p->left)!=NULL){
				if(p->right){
					stack[sp++]=p->right;
				}
				p=pn;
			} else {
				if(p->right){
					p=p->right;
				} else {
					if(sp==0)
						break;
					p=stack[--sp];
				}
			}
		}
	}
        return 0;
}

static void db_rotate_left(struct dbn *p,struct dbn **root)
{
	struct dbn * y = p->right;
	p->right = y->left;
	if (y->left !=0)
		y->left->parent = p;
	y->parent = p->parent;

	if (p == *root)
		*root = y;
	else if (p == p->parent->left)
		p->parent->left = y;
	else
		p->parent->right = y;
	y->left = p;
	p->parent = y;
}

static void db_rotate_right(struct dbn *p,struct dbn **root)
{
	struct dbn * y = p->left;
	p->left = y->right;
	if (y->right != 0)
		y->right->parent = p;
	y->parent = p->parent;

	if (p == *root)
		*root = y;
	else if (p == p->parent->right)
		p->parent->right = y;
	else
		p->parent->left = y;
	y->right = p;
	p->parent = y;
}

static void db_rebalance(struct dbn *p,struct dbn **root)
{
	p->color = RED;
	while(p!=*root && p->parent->color==RED){ // root�͕K�����Őe�͐Ԃ��̂Őe�̐e�͕K�����݂���
		if (p->parent == p->parent->parent->left) {
			struct dbn *y = p->parent->parent->right;
			if (y && y->color == RED) {
				p->parent->color = BLACK;
				y->color = BLACK;
				p->parent->parent->color = RED;
				p = p->parent->parent;
			} else {
				if (p == p->parent->right) {
					p = p->parent;
					db_rotate_left(p, root);
				}
				p->parent->color = BLACK;
				p->parent->parent->color = RED;
				db_rotate_right(p->parent->parent, root);
			}
		} else {
			struct dbn* y = p->parent->parent->left;
			if (y && y->color == RED) {
				p->parent->color = BLACK;
				y->color = BLACK;
				p->parent->parent->color = RED;
				p = p->parent->parent;
			} else {
				if (p == p->parent->left) {
					p = p->parent;
					db_rotate_right(p, root);
				}
				p->parent->color = BLACK;
				p->parent->parent->color = RED;
				db_rotate_left(p->parent->parent, root);
			}
		}
	}
	(*root)->color=BLACK;
}

static void db_rebalance_erase(struct dbn *z,struct dbn **root)
{
	struct dbn *y = z, *x = NULL, *x_parent = NULL;

	if (y->left == NULL)
		x = y->right;
	else if (y->right == NULL)
		x = y->left;
	else {
		y = y->right;
		while (y->left != NULL)
			y = y->left;
		x = y->right;
	}
	if (y != z) { // ���E���������܂��Ă����� y��z�̈ʒu�Ɏ����Ă���z�𕂂�����
		z->left->parent = y;
		y->left = z->left;
		if (y != z->right) {
			x_parent = y->parent;
			if (x) x->parent = y->parent;
			y->parent->left = x;
			y->right = z->right;
			z->right->parent = y;
		} else
			x_parent = y;
		if (*root == z)
			*root = y;
		else if (z->parent->left == z)
			z->parent->left = y;
		else
			z->parent->right = y;
		y->parent = z->parent;
		{ int tmp=y->color; y->color=z->color; z->color=tmp; }
		y = z;
	} else { // �ǂ��炩�󂢂Ă����ꍇ x��z�̈ʒu�Ɏ����Ă���z�𕂂�����
		x_parent = y->parent;
		if (x) x->parent = y->parent;
		if (*root == z)
			*root = x;
		else if (z->parent->left == z)
			z->parent->left = x;
		else
			z->parent->right = x;
	}
	// �����܂ŐF�̈ړ��̏����Ēʏ��2���؂Ɠ���
	if (y->color != RED) { // �Ԃ������镪�ɂ͉e������
		while (x != *root && (x == NULL || x->color == BLACK))
			if (x == x_parent->left) {
				struct dbn* w = x_parent->right;
				if (w->color == RED) {
					w->color = BLACK;
					x_parent->color = RED;
					db_rotate_left(x_parent, root);
					w = x_parent->right;
				}
				if ((w->left == NULL ||
					 w->left->color == BLACK) &&
					(w->right == NULL ||
					 w->right->color == BLACK)) {
					w->color = RED;
					x = x_parent;
					x_parent = x_parent->parent;
				} else {
					if (w->right == NULL ||
						w->right->color == BLACK) {
						if (w->left) w->left->color = BLACK;
						w->color = RED;
						db_rotate_right(w, root);
						w = x_parent->right;
					}
					w->color = x_parent->color;
					x_parent->color = BLACK;
					if (w->right) w->right->color = BLACK;
					db_rotate_left(x_parent, root);
					break;
				}
			} else {                  // same as above, with right <-> left.
				struct dbn* w = x_parent->left;
				if (w->color == RED) {
					w->color = BLACK;
					x_parent->color = RED;
					db_rotate_right(x_parent, root);
					w = x_parent->left;
				}
				if ((w->right == NULL ||
					 w->right->color == BLACK) &&
					(w->left == NULL ||
					 w->left->color == BLACK)) {
					w->color = RED;
					x = x_parent;
					x_parent = x_parent->parent;
				} else {
					if (w->left == NULL ||
						w->left->color == BLACK) {
						if (w->right) w->right->color = BLACK;
						w->color = RED;
						db_rotate_left(w, root);
						w = x_parent->left;
					}
					w->color = x_parent->color;
					x_parent->color = BLACK;
					if (w->left) w->left->color = BLACK;
					db_rotate_right(x_parent, root);
					break;
				}
			}
		if (x) x->color = BLACK;
	}
}

void db_free_lock(struct dbt *table) {
	table->free_lock++;
}

void db_free_unlock(struct dbt *table) {
	if(--table->free_lock == 0) {
		int i;
		for(i = 0; i < table->free_count ; i++) {
			db_rebalance_erase(table->free_list[i].z,table->free_list[i].root);
			if(table->cmp == strdb_cmp) {
				aFree(table->free_list[i].z->key);
			}
#ifdef MALLOC_DBN
			free_dbn(table->free_list[i].z);
#else
			aFree(table->free_list[i].z);
#endif
			table->item_count--;
		}
		table->free_count = 0;
	}
}

struct dbn* db_insert(struct dbt *table,void* key,void* data)
{
	struct dbn *p,*priv;
	int c,hash;

	hash = table->hash(table,key) % HASH_SIZE;
	for(c=0,priv=NULL ,p = table->ht[hash];p;){
		c=table->cmp(table,key,p->key);
		if(c==0){ // replace
			if (table->release)
				table->release(p, 3);
			if(p->deleted) {
				// �폜���ꂽ�f�[�^�Ȃ̂ŁAfree_list ��̍폜�\�������
				int i;
				for(i = 0; i < table->free_count ; i++) {
					if(table->free_list[i].z == p) {
						memmove(
							&table->free_list[i],
							&table->free_list[i+1],
							sizeof(struct db_free)*(table->free_count - i - 1)
						);
						break;
					}
				}
				if(i == table->free_count || table->free_count <= 0) {
					printf("db_insert: cannnot find deleted db node.\n");
				} else {
					table->free_count--;
					if(table->cmp == strdb_cmp) {
						aFree(p->key);
					}
				}
			}
			p->data=data;
			p->key=key;
			p->deleted = 0;
			return p;
		}
		priv=p;
		if(c<0){
			p=p->left;
		} else {
			p=p->right;
		}
	}
#ifdef MALLOC_DBN
	p=malloc_dbn();
#else
	CREATE(p, struct dbn, 1);
#endif
	if(p==NULL){
		printf("out of memory : db_insert\n");
		return NULL;
	}
	p->parent= NULL;
	p->left  = NULL;
	p->right = NULL;
	p->key   = key;
	p->data  = data;
	p->color = RED;
	p->deleted = 0;
	p->prev = NULL;
	p->next = NULL;
	if (head == NULL)
		head = tail = p;
	else {
		p->prev = tail;
		tail->next = p;
		tail = p;
	}

	if(c==0){ // hash entry is empty
		table->ht[hash] = p;
		p->color = BLACK;
	} else {
		if(c<0){ // left node
			priv->left = p;
			p->parent=priv;
		} else { // right node
			priv->right = p;
			p->parent=priv;
		}
		if(priv->color==RED){ // must rebalance
			db_rebalance(p,&table->ht[hash]);
		}
	}
	table->item_count++;
	
	return p;
}

void* db_erase(struct dbt *table,void* key)
{
	void *data;
	struct dbn *p;
	int c,hash;

	hash = table->hash(table,key) % HASH_SIZE;
	for(c=0,p = table->ht[hash];p;){
		c=table->cmp(table,key,p->key);
		if(c==0)
			break;
		if(c<0)
			p=p->left;
		else
			p=p->right;
	}
	if(!p)
		return NULL;
	data=p->data;
	if(table->free_lock) {
		if(table->free_count == table->free_max) {
			table->free_max += 32;
			table->free_list = (struct db_free*)realloc(table->free_list,sizeof(struct db_free) * table->free_max);
		}
		table->free_list[table->free_count].z    = p;
		table->free_list[table->free_count].root = &table->ht[hash];
		table->free_count++;
		p->deleted = 1;
		p->data    = NULL;
		if(table->cmp == strdb_cmp) {
			if(table->maxlen) {
				char *key = (char*)malloc(table->maxlen);
				memcpy(key,p->key,table->maxlen);
				p->key = key;
			} else {
				p->key = strdup((const char*)p->key);
			}
		}
	} else {
		db_rebalance_erase(p,&table->ht[hash]);
		if (p->prev)
			p->prev->next = p->next;
		else
			head = p->next;
		if (p->next)
			p->next->prev = p->prev;
		else
			tail = p->prev;

	#ifdef MALLOC_DBN
		free_dbn(p);
	#else
		aFree(p);
	#endif
		table->item_count--;
	}
	return data;
}

void db_foreach(struct dbt *table,int (*func)(void*,void*,va_list),...)
{
	int i,sp;
	int count = table->item_count;
	// red-black tree�Ȃ̂�64��stack�������2^32�ƒm�[�h�܂ő��v
	struct dbn *p,*pn,*stack[64];
	va_list ap;

	va_start(ap,func);
	db_free_lock(table);
	for(i=0;i<HASH_SIZE;i++){
		if((p=table->ht[i])==NULL)
			continue;
		sp=0;
		while(1){
			//reverted it back. sorry that brought thios bug from Freya [Lupus]
			//if (!p->data) {
			//	printf("Warning: no data for key %d in db_foreach (db.c) !\n",(int)p->key);
			//} else {
			if(!p->deleted)
				func(p->key, p->data, ap);
			count--;
			//}
			if((pn=p->left)!=NULL){
				if(p->right){
					stack[sp++]=p->right;
				}
				p=pn;
			} else {
				if(p->right){
					p=p->right;
				} else {
					if(sp==0)
						break;
					p=stack[--sp];
				}
			}
		}
	}
	db_free_unlock(table);
	if(count) {
		printf(
			"db_foreach : data lost %d item(s) allocated from %s line %d\n",
			count,table->alloc_file,table->alloc_line
		);
	}
	va_end(ap);
}

void db_final(struct dbt *table,int (*func)(void*,void*,va_list),...)
{
	int i,sp;
	struct dbn *p,*pn,*stack[64];
	va_list ap;

	va_start(ap,func);
	db_free_lock(table);
	for(i=0;i<HASH_SIZE;i++){
		if((p=table->ht[i])==NULL)
			continue;
		sp=0;
		while(1){
			if(func && !p->deleted)
				func(p->key,p->data,ap);
			if((pn=p->left)!=NULL){
				if(p->right){
					stack[sp++]=p->right;
				}
			} else {
				if(p->right){
					pn=p->right;
				} else {
					if(sp==0)
						break;
					pn=stack[--sp];
				}
			}
			if (p->prev)
				p->prev->next = p->next;
			else
				head = p->next;
			if (p->next)
				p->next->prev = p->prev;
			else
				tail = p->prev;
#ifdef MALLOC_DBN
			free_dbn(p);
#else
			aFree(p);
#endif
			p=pn;
		}
	}
	db_free_unlock(table);
	aFree(table->free_list);
	aFree(table);
	va_end(ap);
}