Removed Freya's code: "added null pointer check from freya" it caused NPC bugs: OnTime, OnInit etc sorry 8) git-svn-id: https://svn.code.sf.net/p/rathena/svn/branches/stable@596 54d463be-8e91-2dee-dedb-b68131a5f0ec
507 lines
9.8 KiB
C
507 lines
9.8 KiB
C
// $Id: db.c,v 1.2 2004/09/23 14:43:06 MouseJstr Exp $
|
||
// #define MALLOC_DBN
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include "db.h"
|
||
#include "mmo.h"
|
||
#include "utils.h"
|
||
|
||
#ifdef MEMWATCH
|
||
#include "memwatch.h"
|
||
#endif
|
||
|
||
#define ROOT_SIZE 4096
|
||
#ifdef MALLOC_DBN
|
||
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;
|
||
}
|
||
#endif
|
||
|
||
static int strdb_cmp(struct dbt* table,void* a,void* b)
|
||
{
|
||
if(table->maxlen)
|
||
return strncmp(a,b,table->maxlen);
|
||
return strcmp(a,b);
|
||
}
|
||
|
||
static unsigned int strdb_hash(struct dbt* table,void* a)
|
||
{
|
||
int i;
|
||
unsigned int h;
|
||
unsigned char *p=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)
|
||
{
|
||
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;
|
||
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(void)
|
||
{
|
||
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;
|
||
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, 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は必ず黒で親は赤いので親の親は必ず存在する
|
||
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) { // 左右が両方埋まっていた時 yをzの位置に持ってきて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の位置に持ってきて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;
|
||
}
|
||
// ここまで色の移動の除いて通常の2分木と同じ
|
||
if (y->color != RED) { // 赤が消える分には影響無し
|
||
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;
|
||
}
|
||
}
|
||
|
||
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);
|
||
p->data=data;
|
||
p->key=key;
|
||
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;
|
||
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]);
|
||
}
|
||
}
|
||
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;
|
||
db_rebalance_erase(p,&table->ht[hash]);
|
||
#ifdef MALLOC_DBN
|
||
free_dbn(p);
|
||
#else
|
||
free(p);
|
||
#endif
|
||
return data;
|
||
}
|
||
|
||
void db_foreach(struct dbt *table,int (*func)(void*,void*,va_list),...)
|
||
{
|
||
int i,sp;
|
||
// red-black treeなので64個stackがあれば2^32個ノードまで大丈夫
|
||
struct dbn *p,*pn,*stack[64];
|
||
va_list ap;
|
||
|
||
va_start(ap,func);
|
||
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 {
|
||
func(p->key, p->data, ap);
|
||
//}
|
||
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];
|
||
}
|
||
}
|
||
}
|
||
}
|
||
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);
|
||
for(i=0;i<HASH_SIZE;i++){
|
||
if((p=table->ht[i])==NULL)
|
||
continue;
|
||
sp=0;
|
||
while(1){
|
||
if(func)
|
||
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];
|
||
}
|
||
}
|
||
#ifdef MALLOC_DBN
|
||
free_dbn(p);
|
||
#else
|
||
free(p);
|
||
#endif
|
||
p=pn;
|
||
}
|
||
}
|
||
free(table);
|
||
va_end(ap);
|
||
}
|