gopher/dictionary.c
2012-04-27 21:18:31 -04:00

345 lines
5.8 KiB
C

#pragma optimize 79
#pragma noroot
#include "dictionary.h"
#include <memory.h>
#include <string.h>
#include "fast.memory.h"
//#pragma debug -1
typedef struct _Dictionary {
Word count;
Word offset;
Word table[16];
} _Dictionary;
typedef struct _DictionaryEntry {
Word hash;
Word next;
Word keySize;
Word dataSize;
} _DictionaryEntry;
#define SIZEOF_D sizeof(_Dictionary)
#define SIZEOF_DE sizeof(_DictionaryEntry)
#define H_TO_D(h) *((_Dictionary **)h)
#define D_TO_DE(d,offset) (_DictionaryEntry *)((char *)d + offset)
#define DE_TO_KEY(de) ((char *)de + SIZEOF_DE)
#define DE_TO_DATA(de,keySize) ((char *)de + SIZEOF_DE + keySize)
static Boolean keycmp(const char *key1, const char *key2, Word size)
{
// key1 is guaranteed to be lowercase.
Word i;
for (i = 0; i < size; ++i)
{
char a, b;
a = key1[i];
b = key2[i];
if (b >= 'A' && b <= 'Z') b |= 0x20;
if (a != b) return 0;
}
return 1;
}
static Word findEntry(_Dictionary *d, const char *key, Word keySize, Word hash)
{
Word offset;
offset = d->table[hash & 0x0f];
while (offset)
{
_DictionaryEntry *de;
de = D_TO_DE(d, offset);
if (de->hash == hash && de->keySize == keySize)
{
char *tmp = DE_TO_KEY(de);
if (keycmp(tmp, key, keySize)) return offset;
}
offset = de->next;
}
return offset;
}
void deleteEntry(_Dictionary *d, Word offset)
{
// remove an entry, adjusting the hashtable and next pointers.
_DictionaryEntry *de;
Word size;
Word hash;
Word xoffset;
Word i;
de = D_TO_DE(d, offset);
size = SIZEOF_DE + de->keySize + de->dataSize;
hash = de->hash;
if (d->table[hash & 0x0f] == offset)
d->table[hash & 0x0f] = de->next;
for (i = 0; i < 16; ++i)
{
if (d->table[i] > offset) d->table[i] -= size;
}
// remove the entry.
memmove(de, de + size, d->offset - offset - size);
// de now points to the *next* entry.
d->count--;
d->offset -= size;
xoffset = offset;
while (xoffset < d->offset)
{
Word tmp = SIZEOF_DE + de->keySize + de->dataSize;
if (de->next >= offset) de->next -= size;
xoffset += tmp;
de = (_DictionaryEntry *)((char *)de + tmp);
}
}
// djb hash, modified to be case-insensitive
// and 16-bit.
static Word hash(const char *key, Word keySize)
{
Word h = 5381;
unsigned char c;
Word i;
for (i = 0; i < keySize; ++i)
{
c = key[i];
if (c >= 'A' && c <= 'Z') c |= 0x20; // lowercase it.
h = ((h << 5) + h) ^ c;
}
return h;
}
Handle DictionaryCreate(Word MemID, Word initialSize)
{
Word i;
Handle h;
_Dictionary *d;
if (initialSize < 2048) initialSize = 2048;
h = NewHandle(initialSize, MemID, attrNoSpec | attrLocked, NULL);
if (_toolErr) return NULL;
d = H_TO_D(h);
//bzero(d, SIZEOF_D);
d->count = 0;
for (i = 0; i < 16; ++i)
d->table[i] = 0;
d->offset = SIZEOF_D; // next entry.
return h;
}
Word DictionaryCount(Handle h)
{
_Dictionary *d;
if (!h) return 0;
_HLock(h);
d = H_TO_D(h);
return d->count;
}
Boolean DictionaryContains(Handle h, const char *key, Word keySize)
{
Word hh;
Word offset;
_Dictionary *d;
if (!h) return 0;
_HLock(h);
d = H_TO_D(h);
hh = hash(key, keySize);
offset = findEntry(d, key, keySize, hh);
return offset != 0;
}
void *DictionaryGet(Handle h, const char *key, Word keySize, Word *dataSize)
{
Word hh;
Word offset;
_Dictionary *d;
_DictionaryEntry *de;
if (!h) return NULL;
_HLock(h);
d = H_TO_D(h);
hh = hash(key, keySize);
offset = findEntry(d, key, keySize, hh);
if (offset == 0)
{
if (dataSize) *dataSize = 0;
return NULL;
}
de = D_TO_DE(d, offset);
if (dataSize) *dataSize = de->dataSize;
return DE_TO_DATA(de,keySize);
}
Word DictionaryAdd(Handle h,
const char *key, Word keySize,
const char *data, Word dataSize,
Boolean replace)
{
_Dictionary *d;
_DictionaryEntry *de;
Word hh;
Word offset;
Word size;
Word i;
LongWord hSize;
LongWord newSize;
if (!h) return 0;
_HLock(h);
d = H_TO_D(h);
hh = hash(key, keySize);
offset = findEntry(d, key, keySize, hh);
if (offset)
{
if (!replace) return 0;
// delete the previous entry iff datasize differs.
de = D_TO_DE(d, offset);
if (de->dataSize == dataSize)
{
memcpy(DE_TO_DATA(de, keySize), data, dataSize);
return 1;
}
//
deleteEntry(d, offset);
}
// create and insert it.
offset = d->offset;
hSize = _GetHandleSize(h);
newSize = offset + SIZEOF_DE + keySize + dataSize;
if (newSize > 0xffff) return 0;
if (newSize > hSize)
{
_HUnlock(h);
newSize = (newSize + 4095) & ~4095;
SetHandleSize(newSize, h);
if (_toolErr) return 0;
_HLock(h);
d = H_TO_D(h);
}
de = D_TO_DE(d, offset);
de->hash = hh;
de->next = d->table[hh & 0x0f];
de->keySize = keySize;
de->dataSize = dataSize;
// copy the key (lowercase).
for (i = 0; i < keySize; ++i)
{
char c;
c = key[i];
if (c >= 'A' && c <= 'Z') c |= 0x020;
DE_TO_KEY(de)[i] = c;
}
memcpy(DE_TO_DATA(de, keySize), data, dataSize);
d->table[hh & 0x0f] = offset;
d->count++;
d->offset = newSize;
return 1;
}
void DictionaryRemove(Handle h, const char *key, Word keySize)
{
_Dictionary *d;
Word offset;
Word hh;
if (!h) return;
_HLock(h);
d = H_TO_D(h);
hh = hash(key, keySize);
offset = findEntry(d, key, keySize, hh);
if (offset)
{
deleteEntry(d, offset);
}
}
Word DictionaryEnumerate(Handle h, DictionaryEnumerator *e, Word cookie)
{
// returns the next offset;
_Dictionary *d;
_DictionaryEntry *de;
if (!h) return 0;
if (cookie == 0) cookie = SIZEOF_D;
_HLock(h);
d = H_TO_D(h);
if (cookie >= d->offset) return 0;
de = D_TO_DE(d, cookie);
if (e)
{
e->keySize = de->keySize;
e->valueSize = de->dataSize;
e->key = DE_TO_KEY(de);
e->value = DE_TO_DATA(de,de->keySize);
}
return cookie + SIZEOF_DE + de->keySize + de->dataSize;
}