// $Id: db.c,v 1.2 2004/09/23 14:43:06 MouseJstr Exp $ // #define MALLOC_DBN #include #include #include #include "db.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 (strncasecmp (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) { 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); }