diff options
author | nobody <nobody@localhost> | 2000-11-07 05:40:14 +0800 |
---|---|---|
committer | nobody <nobody@localhost> | 2000-11-07 05:40:14 +0800 |
commit | 4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a (patch) | |
tree | 786145ed8b82796d89e41d2ed37058c0097ace13 /libibex/hash.c | |
parent | b4c61eba8055f666fd8a2270879a6f9722faa2b3 (diff) | |
download | gsoc2013-evolution-4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a.tar gsoc2013-evolution-4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a.tar.gz gsoc2013-evolution-4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a.tar.bz2 gsoc2013-evolution-4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a.tar.lz gsoc2013-evolution-4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a.tar.xz gsoc2013-evolution-4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a.tar.zst gsoc2013-evolution-4882b9f898a1e44b7462f0eb1cdc0c8d11897c6a.zip |
This commit was manufactured by cvs2svn to create tag 'BONOBO_0_31'.BONOBO_0_31
svn path=/tags/BONOBO_0_31/; revision=6435
Diffstat (limited to 'libibex/hash.c')
-rw-r--r-- | libibex/hash.c | 859 |
1 files changed, 0 insertions, 859 deletions
diff --git a/libibex/hash.c b/libibex/hash.c deleted file mode 100644 index f79ee8f55e..0000000000 --- a/libibex/hash.c +++ /dev/null @@ -1,859 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- - * - * Copyright (C) 2000 Helix Code, Inc. - * - * Authors: Michael Zucchi <notzed@helixcode.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public License - * as published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with the Gnome Library; see the file COPYING.LIB. If not, - * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -/* hash based index mechanism */ - -#include <stdio.h> -#include <stdlib.h> - -#include <sys/types.h> -#include <sys/stat.h> -#include <fcntl.h> -#include <unistd.h> -#include <string.h> - -#include "block.h" -#include "index.h" - -#define d(x) - -#define HASH_SIZE (1024) -#define KEY_THRESHOLD (sizeof(struct _hashkey) + 4) /* minimum number of free bytes we worry about - maintaining free blocks for */ -#define ARRAY_LEN(a) (sizeof(a)/sizeof(a[0])) - -typedef guint32 hashid_t; - -struct _HASHCursor { - struct _IBEXCursor cursor; - - hashid_t key; - hashid_t block; - unsigned int index; - unsigned int size; -}; - -static struct _IBEXIndex *hash_create(struct _memcache *bc, int size); -static struct _IBEXIndex *hash_open(struct _memcache *bc, blockid_t root); -static int hash_sync(struct _IBEXIndex *index); -static int hash_close(struct _IBEXIndex *index); - -static hashid_t hash_find(struct _IBEXIndex *index, const char *key, int keylen); -static void hash_remove(struct _IBEXIndex *index, const char *key, int keylen); -static hashid_t hash_insert(struct _IBEXIndex *index, const char *key, int keylen); -static char *hash_get_key(struct _IBEXIndex *index, hashid_t hashbucket, int *len); -static void hash_set_data_block(struct _IBEXIndex *index, hashid_t keyid, blockid_t blockid, blockid_t tail); -static blockid_t hash_get_data_block(struct _IBEXIndex *index, hashid_t keyid, blockid_t *tail); -static struct _IBEXCursor *hash_get_cursor(struct _IBEXIndex *index); - -static struct _IBEXCursor *hash_cursor_create(struct _IBEXIndex *); -static void hash_cursor_close(struct _IBEXCursor *); -static guint32 hash_cursor_next(struct _IBEXCursor *); -static char *hash_cursor_next_key(struct _IBEXCursor *, int *keylenptr); - -struct _IBEXIndexClass ibex_hash_class = { - hash_create, hash_open, - hash_sync, hash_close, - hash_find, - hash_remove, - hash_insert, - hash_get_key, - hash_set_data_block, - hash_get_data_block, - hash_get_cursor, -}; - -struct _IBEXCursorClass ibex_hash_cursor_class = { - hash_cursor_close, - hash_cursor_next, - hash_cursor_next_key -}; - -/* the reason we have the tail here is that otherwise we need to - have a 32 bit blockid for the root node; which would make this - structure the same size anyway, with about 24 wasted bits */ -struct _hashkey { - blockid_t next; /* next in hash chain */ - blockid_t tail; - unsigned int root:32-BLOCK_BITS; - unsigned int keyoffset:BLOCK_BITS; -}; - -struct _hashblock { - unsigned int next:32-BLOCK_BITS; /* next block, linked list of all key blocks: block number */ - unsigned int used:BLOCK_BITS; /* number of elements used */ - union { - struct _hashkey keys[(BLOCK_SIZE-4)/sizeof(struct _hashkey)]; - char keydata[BLOCK_SIZE-4]; - } hashblock_u; -}; -#define hb_keys hashblock_u.keys -#define hb_keydata hashblock_u.keydata - -/* size of block overhead + 2 key block overhead */ -#define MAX_KEYLEN (BLOCK_SIZE - 4 - 12 - 12) - -/* root block for a hash index */ -struct _hashroot { - hashid_t free; /* free list */ - guint32 size; /* how big the hash table is */ - hashid_t keys; /* linked list of blocks */ - hashid_t table[(BLOCK_SIZE-8)/sizeof(hashid_t)]; /* pointers to blocks of pointers */ -}; - -struct _hashtableblock { - hashid_t buckets[BLOCK_SIZE/sizeof(hashid_t)]; -}; - -/* map a hash index to a block index */ -#define HASH_INDEX(b) ((b) & (BLOCK_SIZE-1)) -/* map a hash index to a block number */ -#define HASH_BLOCK(b) ((b) & ~(BLOCK_SIZE-1)) -/* map a block + index to a hash key */ -#define HASH_KEY(b, i) (((b) & ~(BLOCK_SIZE-1)) | ((i) & (BLOCK_SIZE-1))) - -/* taken from tdb/gdbm */ -static unsigned int hash_key(const unsigned char *key, int keylen) -{ - char *newkey; - newkey = alloca(keylen+1); - memcpy(newkey, key, keylen); - newkey[keylen]=0; - return g_str_hash(newkey); -#if 0 - unsigned int value; /* Used to compute the hash value. */ - unsigned int i; /* Used to cycle through random values. */ - - /* Set the initial value from the key size. */ - value = 0x238F13AF * keylen; - for (i=0; i < keylen; i++) { - value = (value + (key[i] << (i*5 % 24))); - } - - value = (1103515243 * value + 12345); - - return value; -#endif -} - -/* create a new hash table, return a pointer to its root block */ -static struct _IBEXIndex * -hash_create(struct _memcache *bc, int size) -{ - blockid_t root, block; - struct _hashroot *hashroot; - int i; - struct _hashtableblock *table; - struct _IBEXIndex *index; - - g_assert(size<=10240); - - d(printf("initialising hash table, size = %d\n", size)); - - index = g_malloc0(sizeof(*index)); - index->blocks = bc; - index->klass = &ibex_hash_class; - root = ibex_block_get(bc); - index->root = root; - d(printf(" root = %d\n", root)); - hashroot = (struct _hashroot *)ibex_block_read(bc, root); - hashroot->free = 0; - hashroot->size = size; - ibex_block_dirty((struct _block *)hashroot); - for (i=0;i<size/(BLOCK_SIZE/sizeof(blockid_t));i++) { - d(printf("initialising hash table index block %d\n", i)); - block = hashroot->table[i] = ibex_block_get(bc); - table = (struct _hashtableblock *)ibex_block_read(bc, block); - memset(table, 0, sizeof(table)); - ibex_block_dirty((struct _block *)table); - } - - return index; -} - -static struct _IBEXIndex * -hash_open(struct _memcache *bc, blockid_t root) -{ - struct _IBEXIndex *index; - - /* FIXME: check a 'magic', and the root for validity */ - - index = g_malloc0(sizeof(*index)); - index->blocks = bc; - index->root = root; - index->klass = &ibex_hash_class; - - return index; -} - - -static int hash_sync(struct _IBEXIndex *index) -{ - /* nop, index always synced on disk (at least, to blocks) */ - return 0; -} - -static int hash_close(struct _IBEXIndex *index) -{ -#ifdef INDEX_STAT - printf("Performed %d lookups, average %f depth\n", index->lookups, (double)index->lookup_total/index->lookups); -#endif - g_free(index); - return 0; -} - -/* get an iterator class */ -static struct _IBEXCursor *hash_get_cursor(struct _IBEXIndex *index) -{ - return hash_cursor_create(index); -} - -/* convert a hashbucket id into a name */ -static char * -hash_get_key(struct _IBEXIndex *index, hashid_t hashbucket, int *len) -{ - struct _hashblock *bucket; - int ind; - char *ret, *start, *end; - - if (hashbucket == 0) { - if (len) - *len = 0; - return g_strdup(""); - } - - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(hashbucket)); - ind = HASH_INDEX(hashbucket); - - g_assert(ind < bucket->used); - - start = &bucket->hb_keydata[bucket->hb_keys[ind].keyoffset]; - if (ind == 0) { - end = &bucket->hb_keydata[sizeof(bucket->hb_keydata)/sizeof(bucket->hb_keydata[0])]; - } else { - end = &bucket->hb_keydata[bucket->hb_keys[ind-1].keyoffset]; - } - - ret = g_malloc(end-start+1); - memcpy(ret, start, end-start); - ret[end-start]=0; - if (len) - *len = end-start; - return ret; -} - -/* sigh, this is fnugly code ... */ -static hashid_t -hash_find(struct _IBEXIndex *index, const char *key, int keylen) -{ - struct _hashroot *hashroot; - guint32 hash; - int hashentry; - blockid_t hashtable; - hashid_t hashbucket; - struct _hashtableblock *table; - - g_assert(index != 0); - g_assert(index->root != 0); - - d(printf("finding hash %.*s\n", keylen, key)); - - /* truncate the key to the maximum size */ - if (keylen > MAX_KEYLEN) - keylen = MAX_KEYLEN; - - hashroot = (struct _hashroot *)ibex_block_read(index->blocks, index->root); - - /* find the table containing this entry */ - hash = hash_key(key, keylen) % hashroot->size; - hashtable = hashroot->table[hash / (BLOCK_SIZE/sizeof(blockid_t))]; - g_assert(hashtable != 0); - table = (struct _hashtableblock *)ibex_block_read(index->blocks, hashtable); - hashentry = hash % (BLOCK_SIZE/sizeof(blockid_t)); - /* and its bucket */ - hashbucket = table->buckets[hashentry]; - -#ifdef INDEX_STAT - index->lookups++; -#endif - /* go down the bucket chain, reading each entry till we are done ... */ - while (hashbucket != 0) { - struct _hashblock *bucket; - char *start, *end; - int ind; - -#ifdef INDEX_STAT - index->lookup_total++; -#endif - - d(printf(" checking bucket %d\n", hashbucket)); - - /* get the real bucket id from the hashbucket id */ - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(hashbucket)); - /* and get the key number within the block */ - ind = HASH_INDEX(hashbucket); - - g_assert(ind < bucket->used); - - start = &bucket->hb_keydata[bucket->hb_keys[ind].keyoffset]; - if (ind == 0) { - end = &bucket->hb_keydata[sizeof(bucket->hb_keydata)/sizeof(bucket->hb_keydata[0])]; - } else { - end = &bucket->hb_keydata[bucket->hb_keys[ind-1].keyoffset]; - } - if ( (end-start) == keylen - && memcmp(start, key, keylen) == 0) { - return hashbucket; - } - hashbucket = bucket->hb_keys[ind].next; - } - return 0; -} - -static int -hash_info(struct _hashblock *bucket, int index) -{ - char *start, *end; - - g_assert(index < bucket->used); - - start = &bucket->hb_keydata[bucket->hb_keys[index].keyoffset]; - if (index == 0) { - end = &bucket->hb_keydata[sizeof(bucket->hb_keydata)/sizeof(bucket->hb_keydata[0])]; - } else { - end = &bucket->hb_keydata[bucket->hb_keys[index-1].keyoffset]; - } - - return end-start; -} - - -/* TODO: get rid of hash_compress/remove and just have one a-la the disktail code */ - -/* compresses the bucket 'bucket', removing data - at index 'index' */ -static void -hash_compress(struct _hashblock *bucket, int index) -{ - int i; - char *start, *end, *newstart; - - /* get start/end of area to zap */ - start = &bucket->hb_keydata[bucket->hb_keys[index].keyoffset]; - if (index == 0) { - end = &bucket->hb_keydata[sizeof(bucket->hb_keydata)/sizeof(bucket->hb_keydata[0])]; - } else { - end = &bucket->hb_keydata[bucket->hb_keys[index-1].keyoffset]; - } - - if (start == end) - return; - - /* fixup data */ - newstart = &bucket->hb_keydata[bucket->hb_keys[bucket->used-1].keyoffset]; - memmove(newstart+(end-start), newstart, start-newstart); - - /* fixup key pointers */ - for (i=index;i<bucket->used;i++) { - bucket->hb_keys[i].keyoffset += (end-start); - } - ibex_block_dirty((struct _block *)bucket); -} - -/* make room 'len' for the key 'index' */ -/* assumes key 'index' is already empty (0 length) */ -static void -hash_expand(struct _hashblock *bucket, int index, int len) -{ - int i; - char *end, *newstart; - - /* get start/end of area to zap */ - if (index == 0) { - end = &bucket->hb_keydata[sizeof(bucket->hb_keydata)/sizeof(bucket->hb_keydata[0])]; - } else { - end = &bucket->hb_keydata[bucket->hb_keys[index-1].keyoffset]; - } - - /* fixup data */ - newstart = &bucket->hb_keydata[bucket->hb_keys[bucket->used-1].keyoffset]; - memmove(newstart-len, newstart, end-newstart); - - /* fixup key pointers */ - for (i=index;i<bucket->used;i++) { - bucket->hb_keys[i].keyoffset -= len; - } - ibex_block_dirty((struct _block *)bucket); -} - -static void -hash_remove(struct _IBEXIndex *index, const char *key, int keylen) -{ - struct _hashroot *hashroot; - guint32 hash; - int hashentry; - blockid_t hashtable; - hashid_t hashbucket, hashprev; - struct _hashtableblock *table; - - g_assert(index != 0); - g_assert(index->root != 0); - - d(printf("removing hash %.*s\n", keylen, key)); - - /* truncate the key to the maximum size */ - if (keylen > MAX_KEYLEN) - keylen = MAX_KEYLEN; - - hashroot = (struct _hashroot *)ibex_block_read(index->blocks, index->root); - - /* find the table containing this entry */ - hash = hash_key(key, keylen) % hashroot->size; - hashtable = hashroot->table[hash / (BLOCK_SIZE/sizeof(blockid_t))]; - table = (struct _hashtableblock *)ibex_block_read(index->blocks, hashtable); - hashentry = hash % (BLOCK_SIZE/sizeof(blockid_t)); - /* and its bucket */ - hashbucket = table->buckets[hashentry]; - - /* go down the bucket chain, reading each entry till we are done ... */ - hashprev = 0; - while (hashbucket != 0) { - struct _hashblock *bucket; - char *start, *end; - int ind; - - d(printf(" checking bucket %d\n", hashbucket)); - - /* get the real bucket id from the hashbucket id */ - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(hashbucket)); - /* and get the key number within the block */ - ind = HASH_INDEX(hashbucket); - - g_assert(ind < bucket->used); - - start = &bucket->hb_keydata[bucket->hb_keys[ind].keyoffset]; - if (ind == 0) { - end = &bucket->hb_keydata[sizeof(bucket->hb_keydata)/sizeof(bucket->hb_keydata[0])]; - } else { - end = &bucket->hb_keydata[bucket->hb_keys[ind-1].keyoffset]; - } - if ( (end-start) == keylen - && memcmp(start, key, keylen) == 0) { - struct _hashblock *prevbucket; - - if (hashprev == 0) { - /* unlink from hash chain */ - table->buckets[hashentry] = bucket->hb_keys[HASH_INDEX(hashbucket)].next; - /* link into free list */ - bucket->hb_keys[HASH_INDEX(hashbucket)].next = hashroot->free; - hashroot->free = hashbucket; - /* compress away data */ - hash_compress(bucket, HASH_INDEX(hashbucket)); - ibex_block_dirty((struct _block *)bucket); - ibex_block_dirty((struct _block *)table); - ibex_block_dirty((struct _block *)hashroot); - } else { - prevbucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(hashprev)); - prevbucket->hb_keys[HASH_INDEX(hashprev)].next = - bucket->hb_keys[ind].next; - /* link into free list */ - bucket->hb_keys[ind].next = hashroot->free; - hashroot->free = hashbucket; - /* compress entry */ - hash_compress(bucket, ind); - ibex_block_dirty((struct _block *)bucket); - ibex_block_dirty((struct _block *)prevbucket); - ibex_block_dirty((struct _block *)hashroot); - } - return; - } - hashprev = hashbucket; - hashbucket = bucket->hb_keys[ind].next; - } -} - -/* set where the datablock is located */ -static void -hash_set_data_block(struct _IBEXIndex *index, hashid_t keyid, blockid_t blockid, blockid_t tail) -{ - struct _hashblock *bucket; - - d(printf("setting data block hash %d to %d tail %d\n", keyid, blockid, tail)); - - /* map to a block number */ - g_assert((blockid & (BLOCK_SIZE-1)) == 0); - blockid >>= BLOCK_BITS; - - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(keyid)); - if (bucket->hb_keys[HASH_INDEX(keyid)].root != blockid - || bucket->hb_keys[HASH_INDEX(keyid)].tail != tail) { - bucket->hb_keys[HASH_INDEX(keyid)].tail = tail; - bucket->hb_keys[HASH_INDEX(keyid)].root = blockid; - ibex_block_dirty((struct _block *)bucket); - } -} - -static blockid_t -hash_get_data_block(struct _IBEXIndex *index, hashid_t keyid, blockid_t *tail) -{ - struct _hashblock *bucket; - - d(printf("getting data block hash %d\n", keyid)); - - if (keyid == 0) { - if (tail) - *tail = 0; - return 0; - } - - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(keyid)); - if (tail) - *tail = bucket->hb_keys[HASH_INDEX(keyid)].tail; - return bucket->hb_keys[HASH_INDEX(keyid)].root << BLOCK_BITS; -} - -static hashid_t -hash_insert(struct _IBEXIndex *index, const char *key, int keylen) -{ - struct _hashroot *hashroot; - guint32 hash; - int hashentry; - blockid_t hashtable; - hashid_t hashbucket, keybucket, keyprev, keyfree; - struct _hashtableblock *table; - struct _hashblock *bucket; - int count; - - g_assert(index != 0); - g_assert(index->root != 0); - - /* truncate the key to the maximum size */ - if (keylen > MAX_KEYLEN) - keylen = MAX_KEYLEN; - - d(printf("inserting hash %.*s\n", keylen, key)); - - hashroot = (struct _hashroot *)ibex_block_read(index->blocks, index->root); - - /* find the table containing this entry */ - hash = hash_key(key, keylen) % hashroot->size; - hashtable = hashroot->table[hash / (BLOCK_SIZE/sizeof(blockid_t))]; - table = (struct _hashtableblock *)ibex_block_read(index->blocks, hashtable); - hashentry = hash % (BLOCK_SIZE/sizeof(blockid_t)); - /* and its bucket */ - hashbucket = table->buckets[hashentry]; - - /* now look for a free slot, first try the free list */ - /* but dont try too hard if our key is just too long ... so just scan upto - 4 blocks, but if we dont find a space, tough ... */ - keybucket = hashroot->free; - keyprev = 0; - count = 0; - while (keybucket && count<4) { - int space; - - d(printf(" checking free %d\n", keybucket)); - - /* read the bucket containing this free key */ - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(keybucket)); - - /* check if there is enough space for the key */ - space = &bucket->hb_keydata[bucket->hb_keys[bucket->used-1].keyoffset] - - (char *)&bucket->hb_keys[bucket->used]; - if (space >= keylen) { - hash_expand(bucket, HASH_INDEX(keybucket), keylen); - memcpy(&bucket->hb_keydata[bucket->hb_keys[HASH_INDEX(keybucket)].keyoffset], - key, keylen); - - /* check if there is free space still in this node, and there are no other empty blocks */ - keyfree = bucket->hb_keys[HASH_INDEX(keybucket)].next; - if ((space-keylen) >= KEY_THRESHOLD) { - int i; - int head = ARRAY_LEN(bucket->hb_keydata); - int found = FALSE; - - for (i=0;i<bucket->used;i++) { - if (bucket->hb_keys[i].keyoffset == head) { - /* already have a free slot in this block, leave it */ - found = TRUE; - break; - } - head = bucket->hb_keys[i].keyoffset; - } - if (!found) { - /* we should link in a new free slot for this node */ - bucket->hb_keys[bucket->used].next = bucket->hb_keys[HASH_INDEX(keybucket)].next; - bucket->hb_keys[bucket->used].keyoffset = bucket->hb_keys[bucket->used-1].keyoffset; - keyfree = HASH_KEY(HASH_BLOCK(keybucket), bucket->used); - bucket->used++; - } - } - /* link 'keyfree' back to the parent ... */ - if (keyprev == 0) { - hashroot->free = keyfree; - ibex_block_dirty((struct _block *)hashroot); - } else { - struct _hashblock *prevbucket; - prevbucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(keyprev)); - prevbucket->hb_keys[HASH_INDEX(keyprev)].next = keyfree; - ibex_block_dirty((struct _block *)prevbucket); - } - - /* link into the hash chain */ - bucket->hb_keys[HASH_INDEX(keybucket)].next = hashbucket; - bucket->hb_keys[HASH_INDEX(keybucket)].root = 0; - bucket->hb_keys[HASH_INDEX(keybucket)].tail = 0; - table->buckets[hashentry] = keybucket; - ibex_block_dirty((struct _block *)table); - ibex_block_dirty((struct _block *)bucket); - - d(printf(" new key id %d\n", keybucket)); - d(printf(" new free id %d\n", hashroot->free)); - - return keybucket; - } - - count++; - keyprev = keybucket; - keybucket = bucket->hb_keys[HASH_INDEX(keybucket)].next; - } - - /* else create a new block ... */ - keybucket = ibex_block_get(index->blocks); - bucket = (struct _hashblock *)ibex_block_read(index->blocks, keybucket); - - d(printf("creating new key bucket %d\n", keybucket)); - - memset(bucket, 0, sizeof(*bucket)); - - bucket->used = 2; - /* first block, is the new key */ - bucket->hb_keys[0].keyoffset = ARRAY_LEN(bucket->hb_keydata) - keylen; - memcpy(&bucket->hb_keydata[bucket->hb_keys[0].keyoffset], key, keylen); - bucket->hb_keys[0].next = hashbucket; - bucket->hb_keys[0].root = 0; - bucket->hb_keys[0].tail = 0; - - table->buckets[hashentry] = HASH_KEY(keybucket, 0); - - /* next block is a free block, link into free list */ - bucket->hb_keys[1].keyoffset = bucket->hb_keys[0].keyoffset; - bucket->hb_keys[1].next = hashroot->free; - hashroot->free = HASH_KEY(keybucket, 1); - - /* link new block into keys list */ - bucket->next = block_number(hashroot->keys); - hashroot->keys = keybucket; - - ibex_block_dirty((struct _block *)hashroot); - ibex_block_dirty((struct _block *)table); - ibex_block_dirty((struct _block *)bucket); - - d(printf(" new key id %d\n", HASH_KEY(keybucket, 0))); - d(printf(" new free id %d\n", hashroot->free)); - - return HASH_KEY(keybucket, 0); -} - -/* hash cursor functions */ -static struct _IBEXCursor * -hash_cursor_create(struct _IBEXIndex *idx) -{ - struct _HASHCursor *idc; - struct _hashroot *hashroot; - - idc = g_malloc(sizeof(*idc)); - idc->cursor.klass = &ibex_hash_cursor_class; - idc->cursor.index = idx; - idc->key = 0; - idc->index = 0; - - hashroot = (struct _hashroot *)ibex_block_read(idx->blocks, idx->root); - idc->size = hashroot->size; - idc->block = hashroot->keys; - - return &idc->cursor; -} - -static void -hash_cursor_close(struct _IBEXCursor *idc) -{ - g_free(idc); -} - -static guint32 -hash_cursor_next(struct _IBEXCursor *idc) -{ - struct _HASHCursor *hc = (struct _HASHCursor *)idc; - struct _hashblock *bucket; - - while (hc->block != 0) { - bucket = (struct _hashblock *)ibex_block_read(idc->index->blocks, hc->block); - while (hc->index < bucket->used) { - if (hash_info(bucket, hc->index) > 0) { - hc->key = HASH_KEY(hc->block, hc->index); - hc->index++; - if (hc->index == bucket->used) { - hc->index = 0; - hc->block = block_location(bucket->next); - } - return hc->key; - } - hc->index++; - } - hc->index = 0; - hc->block = block_location(bucket->next); - } - - return hc->block; -} - -static char * -hash_cursor_next_key(struct _IBEXCursor *idc, int *keylenptr) -{ - /* TODO: this could be made slightly mroe efficient going to the structs direct. - but i'm lazy today */ - return idc->index->klass->get_key(idc->index, idc->klass->next(idc), keylenptr); -} - -/* debug */ -void ibex_hash_dump(struct _IBEXIndex *index); -static void ibex_hash_dump_rec(struct _IBEXIndex *index, int *words, int *wordslen); - -void ibex_hash_dump(struct _IBEXIndex *index) -{ - int words = 0, wordslen=0; - - ibex_hash_dump_rec(index, &words, &wordslen); - - printf("Total words = %d, bytes = %d, ave length = %f\n", words, wordslen, (double)wordslen/(double)words); -} - - -static void -ibex_hash_dump_rec(struct _IBEXIndex *index, int *words, int *wordslen) -{ - int i; - struct _hashtableblock *table; - struct _hashblock *bucket; - struct _hashroot *hashroot; - blockid_t hashtable; - hashid_t hashbucket; - extern void ibex_diskarray_dump(struct _memcache *blocks, blockid_t head, blockid_t tail); - - - printf("Walking hash tree:\n"); - hashroot = (struct _hashroot *)ibex_block_read(index->blocks, index->root); - for (i=0;i<hashroot->size;i++) { - printf("Hash table chain: %d\n", i); - hashtable = hashroot->table[i / (BLOCK_SIZE/sizeof(blockid_t))]; - table = (struct _hashtableblock *)ibex_block_read(index->blocks, hashtable); - hashbucket = table->buckets[i % (BLOCK_SIZE/sizeof(blockid_t))]; - while (hashbucket) { - int len; - - *words = *words + 1; - - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(hashbucket)); - printf(" bucket %d: [used %d]", hashbucket, bucket->used); - if (HASH_INDEX(hashbucket) == 0) { - len = ARRAY_LEN(bucket->hb_keydata) - - bucket->hb_keys[HASH_INDEX(hashbucket)].keyoffset; - } else { - len = bucket->hb_keys[HASH_INDEX(hashbucket)-1].keyoffset - - bucket->hb_keys[HASH_INDEX(hashbucket)].keyoffset; - } - printf("'%.*s' = %d next=%d\n", len, &bucket->hb_keydata[bucket->hb_keys[HASH_INDEX(hashbucket)].keyoffset], - bucket->hb_keys[HASH_INDEX(hashbucket)].root, - bucket->hb_keys[HASH_INDEX(hashbucket)].next); - - *wordslen = *wordslen + len; - - ibex_diskarray_dump(index->blocks, - bucket->hb_keys[HASH_INDEX(hashbucket)].root << BLOCK_BITS, - bucket->hb_keys[HASH_INDEX(hashbucket)].tail); - - hashbucket = bucket->hb_keys[HASH_INDEX(hashbucket)].next; - } - /* make sure its still in the cache */ - hashroot = (struct _hashroot *)ibex_block_read(index->blocks, index->root); - } - - hashbucket = hashroot->free; - printf("Dumping free lists ..\n"); - while (hashbucket) { - printf(" %d", hashbucket); - bucket = (struct _hashblock *)ibex_block_read(index->blocks, HASH_BLOCK(hashbucket)); - hashbucket = bucket->hb_keys[HASH_INDEX(hashbucket)].next; - } - printf("\n"); -} - -#if 0 -int main(int argc, char **argv) -{ - struct _memcache *bc; - struct _IBEXIndex *hash; - int i; - - bc = ibex_block_cache_open("index.db", O_CREAT|O_RDWR, 0600); - hash = ibex_hash_class.create(bc, 1024); - for (i=0;i<10000;i++) { - char key[16]; - sprintf(key, "key %d", i); - ibex_hash_class.insert(hash, key, strlen(key)); - } - - for (i=500;i<1000;i++) { - char key[16]; - sprintf(key, "key %d", i); - ibex_hash_class.remove(hash, key, strlen(key)); - } - - for (i=500;i<1000;i++) { - char key[16]; - sprintf(key, "key %d", i); - ibex_hash_class.insert(hash, key, strlen(key)); - } - - - ibex_hash_dump(hash); - - for (i=0;i<2000;i++) { - char key[16], *lookup; - hashid_t keyid; - blockid_t root, tail; - - sprintf(key, "key %d", i); - keyid = ibex_hash_class.find(hash, key, strlen(key)); - lookup = ibex_hash_class.get_key(hash, keyid, 0); - root = ibex_hash_class.get_data(hash, keyid, &tail); - printf("key %s = %d = '%s' root:%d tail:%d \n", key, keyid, lookup, root, tail); - g_free(lookup); - } - - ibex_hash_class.close(hash); - ibex_block_cache_close(bc); - return 0; -} - -#endif |