mirror of
https://github.com/reactos/reactos.git
synced 2024-11-10 00:34:39 +00:00
435a566751
* sumatrapdf - vendor import * everything compiles (libjpeg, poppler, fitz, sumatrapdf) * does NOT link (remove the comment tags in the parent directory.rbuild file (rosapps dir) to build it) svn path=/trunk/; revision=29295
274 lines
4.7 KiB
C
274 lines
4.7 KiB
C
/* Linear probe hash table.
|
|
* 2004 (C) Tor Andersson.
|
|
* BSD license.
|
|
*
|
|
* Simple hashtable with open adressing linear probe.
|
|
* Unlike text book examples, removing entries works
|
|
* correctly in this implementation so it wont start
|
|
* exhibiting bad behaviour if entries are inserted
|
|
* and removed frequently.
|
|
*/
|
|
|
|
#include "fitz-base.h"
|
|
|
|
enum { MAXKEYLEN = 16 };
|
|
|
|
typedef struct fz_hashentry_s fz_hashentry;
|
|
|
|
struct fz_hashentry_s
|
|
{
|
|
unsigned char key[MAXKEYLEN];
|
|
void *val;
|
|
};
|
|
|
|
struct fz_hashtable_s
|
|
{
|
|
int keylen;
|
|
int size;
|
|
int load;
|
|
fz_hashentry *ents;
|
|
};
|
|
|
|
static unsigned hash(unsigned char *s, int len)
|
|
{
|
|
unsigned hash = 0;
|
|
int i;
|
|
for (i = 0; i < len; i++)
|
|
{
|
|
hash += s[i];
|
|
hash += (hash << 10);
|
|
hash ^= (hash >> 6);
|
|
}
|
|
hash += (hash << 3);
|
|
hash ^= (hash >> 11);
|
|
hash += (hash << 15);
|
|
return hash;
|
|
}
|
|
|
|
fz_error *
|
|
fz_newhash(fz_hashtable **tablep, int initialsize, int keylen)
|
|
{
|
|
fz_hashtable *table;
|
|
|
|
assert(keylen <= MAXKEYLEN);
|
|
|
|
table = *tablep = fz_malloc(sizeof(fz_hashtable));
|
|
if (!table)
|
|
return fz_outofmem;
|
|
|
|
table->keylen = keylen;
|
|
table->size = initialsize;
|
|
table->load = 0;
|
|
|
|
table->ents = fz_malloc(sizeof(fz_hashentry) * table->size);
|
|
if (!table->ents)
|
|
{
|
|
fz_free(table);
|
|
*tablep = nil;
|
|
return fz_outofmem;
|
|
}
|
|
|
|
memset(table->ents, 0, sizeof(fz_hashentry) * table->size);
|
|
|
|
return nil;
|
|
}
|
|
|
|
void
|
|
fz_emptyhash(fz_hashtable *table)
|
|
{
|
|
table->load = 0;
|
|
memset(table->ents, 0, sizeof(fz_hashentry) * table->size);
|
|
}
|
|
|
|
int
|
|
fz_hashlen(fz_hashtable *table)
|
|
{
|
|
return table->size;
|
|
}
|
|
|
|
void *
|
|
fz_hashgetkey(fz_hashtable *table, int idx)
|
|
{
|
|
return table->ents[idx].key;
|
|
}
|
|
|
|
void *
|
|
fz_hashgetval(fz_hashtable *table, int idx)
|
|
{
|
|
return table->ents[idx].val;
|
|
}
|
|
|
|
void
|
|
fz_drophash(fz_hashtable *table)
|
|
{
|
|
fz_free(table->ents);
|
|
fz_free(table);
|
|
}
|
|
|
|
fz_error *
|
|
fz_resizehash(fz_hashtable *table, int newsize)
|
|
{
|
|
fz_error *error;
|
|
fz_hashentry *newents;
|
|
fz_hashentry *oldents;
|
|
int oldload;
|
|
int oldsize;
|
|
int i;
|
|
|
|
oldsize = table->size;
|
|
oldload = table->load;
|
|
oldents = table->ents;
|
|
|
|
if (newsize < oldload * 8 / 10)
|
|
return fz_throw("rangecheck: resize hash too small");
|
|
|
|
newents = fz_malloc(sizeof(fz_hashentry) * newsize);
|
|
if (!newents)
|
|
return fz_outofmem;
|
|
|
|
table->size = newsize;
|
|
table->load = 0;
|
|
table->ents = newents;
|
|
memset(table->ents, 0, sizeof(fz_hashentry) * table->size);
|
|
|
|
for (i = 0; i < oldsize; i++)
|
|
{
|
|
if (oldents[i].val)
|
|
{
|
|
error = fz_hashinsert(table, oldents[i].key, oldents[i].val);
|
|
if (error)
|
|
{
|
|
table->size = oldsize;
|
|
table->load = oldload;
|
|
table->ents = oldents;
|
|
fz_free(newents);
|
|
return error;
|
|
}
|
|
}
|
|
}
|
|
|
|
fz_free(oldents);
|
|
return nil;
|
|
}
|
|
|
|
void *
|
|
fz_hashfind(fz_hashtable *table, void *key)
|
|
{
|
|
fz_hashentry *ents = table->ents;
|
|
unsigned size = table->size;
|
|
unsigned pos = hash(key, table->keylen) % size;
|
|
|
|
while (1)
|
|
{
|
|
if (!ents[pos].val)
|
|
return nil;
|
|
|
|
if (memcmp(key, &ents[pos].key, table->keylen) == 0)
|
|
return ents[pos].val;
|
|
|
|
pos = (pos + 1) % size;
|
|
}
|
|
}
|
|
|
|
fz_error *
|
|
fz_hashinsert(fz_hashtable *table, void *key, void *val)
|
|
{
|
|
fz_error *error;
|
|
fz_hashentry *ents;
|
|
unsigned size;
|
|
unsigned pos;
|
|
|
|
if (table->load > table->size * 8 / 10)
|
|
{
|
|
error = fz_resizehash(table, table->size * 2);
|
|
if (error)
|
|
return error;
|
|
}
|
|
|
|
ents = table->ents;
|
|
size = table->size;
|
|
pos = hash(key, table->keylen) % size;
|
|
|
|
while (1)
|
|
{
|
|
if (!ents[pos].val)
|
|
{
|
|
memcpy(ents[pos].key, key, table->keylen);
|
|
ents[pos].val = val;
|
|
table->load ++;
|
|
return nil;
|
|
}
|
|
|
|
if (memcmp(key, &ents[pos].key, table->keylen) == 0)
|
|
return fz_throw("rangecheck: overwrite hash slot");
|
|
|
|
pos = (pos + 1) % size;
|
|
}
|
|
|
|
return nil;
|
|
}
|
|
|
|
fz_error *
|
|
fz_hashremove(fz_hashtable *table, void *key)
|
|
{
|
|
fz_hashentry *ents = table->ents;
|
|
unsigned size = table->size;
|
|
unsigned pos = hash(key, table->keylen) % size;
|
|
unsigned hole, look, code;
|
|
|
|
while (1)
|
|
{
|
|
if (!ents[pos].val)
|
|
return fz_throw("rangecheck: remove inexistant hash entry");
|
|
|
|
if (memcmp(key, &ents[pos].key, table->keylen) == 0)
|
|
{
|
|
ents[pos].val = nil;
|
|
|
|
hole = pos;
|
|
look = (hole + 1) % size;
|
|
|
|
while (ents[look].val)
|
|
{
|
|
code = hash(ents[look].key, table->keylen) % size;
|
|
if ((code <= hole && hole < look) ||
|
|
(look < code && code <= hole) ||
|
|
(hole < look && look < code))
|
|
{
|
|
ents[hole] = ents[look];
|
|
ents[look].val = nil;
|
|
hole = look;
|
|
}
|
|
|
|
look = (look + 1) % size;
|
|
}
|
|
|
|
table->load --;
|
|
return nil;
|
|
}
|
|
|
|
pos = (pos + 1) % size;
|
|
}
|
|
}
|
|
|
|
void
|
|
fz_debughash(fz_hashtable *table)
|
|
{
|
|
int i, k;
|
|
|
|
printf("cache load %d / %d\n", table->load, table->size);
|
|
|
|
for (i = 0; i < table->size; i++)
|
|
{
|
|
if (!table->ents[i].val)
|
|
printf("table % 4d: empty\n", i);
|
|
else
|
|
{
|
|
printf("table % 4d: key=", i);
|
|
for (k = 0; k < MAXKEYLEN; k++)
|
|
printf("%02x", ((char*)table->ents[i].key)[k]);
|
|
printf(" val=$%p\n", table->ents[i].val);
|
|
}
|
|
}
|
|
}
|
|
|