From 369233927c6fc7f398d6ff4ea14ac8d648e19eae Mon Sep 17 00:00:00 2001 From: Wayne Davison Date: Mon, 3 Sep 2007 04:46:57 +0000 Subject: [PATCH] The hashtable routines from hlink.c modified to have more generic names, to support 2 sizes of key (32 and 64 bits), and to have a non-allocating option for the find routine (returning NULL for no match). --- hashtable.c | 145 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 145 insertions(+) create mode 100644 hashtable.c diff --git a/hashtable.c b/hashtable.c new file mode 100644 index 00000000..d1be1b8a --- /dev/null +++ b/hashtable.c @@ -0,0 +1,145 @@ +/* + * Routines to provide a memory-efficient hashtable. + * + * Copyright (C) 2007 Wayne Davison + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, visit the http://fsf.org website. + */ + +#include "rsync.h" + +#define HASH_LOAD_LIMIT(size) ((size)*3/4) + +struct hashtable *hashtable_create(int size, int key64) +{ + struct hashtable *tbl; + int node_size = key64 ? sizeof (struct ht_int64_node ) + : sizeof (struct ht_int32_node); + + /* Pick a power of 2 that can hold the requested size. */ + if (size & (size-1) || size < 16) { + int req = size; + size = 16; + while (size < req) + size *= 2; + } + + if (!(tbl = new(struct hashtable)) + || !(tbl->nodes = new_array0(char, size * node_size))) + out_of_memory("hashtable_create"); + tbl->size = size; + tbl->entries = 0; + tbl->node_size = node_size; + + return tbl; +} + +void hashtable_destroy(struct hashtable *tbl) +{ + free(tbl->nodes); + free(tbl); +} + +/* This returns the node for the indicated key, either newly created or + * already existing. Returns NULL if not allocating and not found. */ +void *hashtable_find(struct hashtable *tbl, int64 key, int allocate_if_missing) +{ + int key64 = (tbl->node_size > sizeof (struct ht_int32_node)); + struct ht_int32_node *node; + uint32 ndx; + + if (allocate_if_missing && tbl->entries > HASH_LOAD_LIMIT(tbl->size)) { + void *old_nodes = tbl->nodes; + int size = tbl->size * 2; + int i; + + if (!(tbl->nodes = new_array0(char, size * tbl->node_size))) + out_of_memory("hashtable_node"); + tbl->size = size; + tbl->entries = 0; + + for (i = size / 2; i-- > 0; ) { + struct ht_int32_node *move_node = HT_NODE(tbl, old_nodes, i); + int64 move_key = HT_KEY(move_node, key64); + if (move_key == 0) + continue; + node = hashtable_find(tbl, move_key, 1); + node->data = move_node->data; + } + + free(old_nodes); + } + + if (!key64) { + /* Based on Jenkins One-at-a-time hash. */ + uchar buf[4], *keyp = buf; + int i; + + SIVAL(buf, 0, key); + for (ndx = 0, i = 0; i < 4; i++) { + ndx += keyp[i]; + ndx += (ndx << 10); + ndx ^= (ndx >> 6); + } + ndx += (ndx << 3); + ndx ^= (ndx >> 11); + ndx += (ndx << 15); + } else { + /* Based on Jenkins hashword() from lookup3.c. */ + uint32 a, b, c; + + /* Set up the internal state */ + a = b = c = 0xdeadbeef + (8 << 2); + +#define rot(x,k) (((x)<<(k)) ^ ((x)>>(32-(k)))) + b += (uint32)(key >> 32); + a += (uint32)key; + c ^= b; c -= rot(b, 14); + a ^= c; a -= rot(c, 11); + b ^= a; b -= rot(a, 25); + c ^= b; c -= rot(b, 16); + a ^= c; a -= rot(c, 4); + b ^= a; b -= rot(a, 14); + c ^= b; c -= rot(b, 24); +#undef rot + ndx = c; + } + + /* If it already exists, return the node. If we're not + * allocating, return NULL if the key is not found. */ + while (1) { + int64 nkey; + + ndx &= tbl->size - 1; + node = HT_NODE(tbl, tbl->nodes, ndx); + nkey = HT_KEY(node, key64); + + if (nkey == key) + return node; + if (nkey == 0) { + if (!allocate_if_missing) + return NULL; + break; + } + ndx++; + } + + /* Take over this empty spot and then return the node. */ + if (key64) + ((struct ht_int64_node*)node)->key = key; + else + node->key = key; + tbl->entries++; + return node; +} -- 2.34.1