mirror of
https://github.com/ksherlock/gopher.git
synced 2024-12-21 21:29:19 +00:00
342 lines
5.8 KiB
C
342 lines
5.8 KiB
C
|
#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;
|
||
|
}
|