diff options
Diffstat (limited to 'lisp/hash.c')
-rw-r--r-- | lisp/hash.c | 657 |
1 files changed, 657 insertions, 0 deletions
diff --git a/lisp/hash.c b/lisp/hash.c new file mode 100644 index 0000000..3d32f07 --- /dev/null +++ b/lisp/hash.c @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2002 by The XFree86 Project, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE XFREE86 PROJECT BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Except as contained in this notice, the name of the XFree86 Project shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization from the + * XFree86 Project. + * + * Author: Paulo César Pereira de Andrade + */ + +/* $XFree86: xc/programs/xedit/lisp/hash.c,v 1.4 2002/11/23 08:26:48 paulo Exp $ */ + +#include "hash.h" + +/* A simple hash-table implementation + * TODO: implement SXHASH and WITH-HASH-TABLE-ITERATOR + * May need a rewrite for better performance, and will + * need a rewrite if images/bytecode saved on disk. + */ + +#define GET_HASH 1 +#define PUT_HASH 2 +#define REM_HASH 3 + +/* + * Prototypes + */ +static unsigned long LispHashKey(LispObj*, int); +static LispObj *LispHash(LispBuiltin*, int); +static void LispRehash(LispHashTable*); +static void LispFreeHashEntries(LispHashEntry*, long); + +/* + * Initialization + */ +extern LispObj *Oeq, *Oeql, *Oequal, *Oequalp; + +/* Hash tables will have one of these sizes, unless the user + * specified a very large size */ +static long some_primes[] = { + 5, 11, 17, 23, + 31, 47, 71, 97, + 139, 199, 307, 401, + 607, 809, 1213, 1619, + 2437, 3251, 4889, 6521 +}; + +/* + * Implementation + */ +static unsigned long +LispHashKey(LispObj *object, int function) +{ + mpi *bigi; + char *string; + long i, length; + unsigned long key = ((unsigned long)object) >> 4; + + /* Must be the same object for EQ */ + if (function == FEQ) + goto hash_key_done; + + if (function == FEQUALP) { + switch (OBJECT_TYPE(object)) { + case LispSChar_t: + key = (unsigned long)toupper(SCHAR_VALUE(object)); + goto hash_key_done; + case LispString_t: + string = THESTR(object); + length = STRLEN(object); + if (length > 32) + length = 32; + for (i = 0, key = 0; i < length; i++) + key = (key << 1) ^ toupper(string[i]); + goto hash_key_done; + default: + break; + } + } + + /* Function is EQL, EQUAL or EQUALP */ + switch (OBJECT_TYPE(object)) { + case LispFixnum_t: + case LispSChar_t: + key = (unsigned long)FIXNUM_VALUE(object); + goto hash_key_done; + case LispInteger_t: + key = (unsigned long)INT_VALUE(object); + goto hash_key_done; + case LispRatio_t: + key = (object->data.ratio.numerator << 16) ^ + object->data.ratio.denominator; + goto hash_key_done; + case LispDFloat_t: + key = (unsigned long)DFLOAT_VALUE(object); + break; + case LispComplex_t: + key = (LispHashKey(object->data.complex.imag, function) << 16) ^ + LispHashKey(object->data.complex.real, function); + goto hash_key_done; + case LispBignum_t: + bigi = object->data.mp.integer; + length = bigi->size; + if (length > 8) + length = 8; + key = bigi->sign; + for (i = 0; i < length; i++) + key = (key << 8) ^ bigi->digs[i]; + goto hash_key_done; + case LispBigratio_t: + bigi = mpr_num(object->data.mp.ratio); + length = bigi->size; + if (length > 4) + length = 4; + key = bigi->sign; + for (i = 0; i < length; i++) + key = (key << 4) ^ bigi->digs[i]; + bigi = mpr_den(object->data.mp.ratio); + length = bigi->size; + if (length > 4) + length = 4; + for (i = 0; i < length; i++) + key = (key << 4) ^ bigi->digs[i]; + goto hash_key_done; + default: + break; + } + + /* Anything else must be the same object for EQL */ + if (function == FEQL) + goto hash_key_done; + + switch (OBJECT_TYPE(object)) { + case LispString_t: + string = THESTR(object); + length = STRLEN(object); + if (length > 32) + length = 32; + for (i = 0, key = 0; i < length; i++) + key = (key << 1) ^ string[i]; + break; + case LispCons_t: + key = (LispHashKey(CAR(object), function) << 16) ^ + LispHashKey(CDR(object), function); + break; + case LispQuote_t: + case LispBackquote_t: + case LispPathname_t: + key = LispHashKey(object->data.pathname, function); + break; + case LispRegex_t: + key = LispHashKey(object->data.regex.pattern, function); + break; + default: + break; + } + +hash_key_done: + return (key); +} + +static LispObj * +LispHash(LispBuiltin *builtin, int code) +{ + LispHashEntry *entry; + LispHashTable *hash; + unsigned long key; + LispObj *result; + int found; + long i; + + LispObj *okey, *hash_table, *value; + + if (code == REM_HASH) + value = NIL; + else { + value = ARGUMENT(2); + if (value == UNSPEC) + value = NIL; + } + hash_table = ARGUMENT(1); + okey = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + /* get hash entry */ + hash = hash_table->data.hash.table; + key = LispHashKey(okey, hash->function) % hash->num_entries; + entry = hash->entries + key; + + /* search entry in the hash table */ + if (entry->count == 0) + i = 0; + else { + if (hash->function == FEQ) { + for (i = entry->cache; i >= 0; i--) { + if (entry->keys[i] == okey) + goto found_key; + } + for (i = entry->cache + 1; i < entry->count; i++) { + if (entry->keys[i] == okey) + break; + } + } + else { + for (i = entry->cache; i >= 0; i--) { + if (LispObjectCompare(entry->keys[i], okey, + hash->function) == T) + goto found_key; + } + for (i = entry->cache + 1; i < entry->count; i++) { + if (LispObjectCompare(entry->keys[i], okey, + hash->function) == T) + break; + } + } + } + +found_key: + result = value; + if ((found = i < entry->count) == 0) + i = entry->count; + + switch (code) { + case GET_HASH: + RETURN_COUNT = 1; + if (found) { + RETURN(0) = T; + entry->cache = i; + result = entry->values[i]; + } + else + RETURN(0) = NIL; + break; + case PUT_HASH: + entry->cache = i; + if (found) + /* Just replace current entry */ + entry->values[i] = value; + else { + if ((i % 4) == 0) { + LispObj **keys, **values; + + keys = realloc(entry->keys, sizeof(LispObj*) * (i + 4)); + if (keys == NULL) + LispDestroy("out of memory"); + values = realloc(entry->values, sizeof(LispObj*) * (i + 4)); + if (values == NULL) { + free(keys); + LispDestroy("out of memory"); + } + entry->keys = keys; + entry->values = values; + } + entry->keys[i] = okey; + entry->values[i] = value; + ++entry->count; + ++hash->count; + if (hash->count > hash->rehash_threshold * hash->num_entries) + LispRehash(hash); + } + break; + case REM_HASH: + if (found) { + result = T; + --entry->count; + --hash->count; + if (i < entry->count) { + memmove(entry->keys + i, entry->keys + i + 1, + (entry->count - i) * sizeof(LispObj*)); + memmove(entry->values + i, entry->values + i + 1, + (entry->count - i) * sizeof(LispObj*)); + } + if (entry->cache && entry->cache == entry->count) + --entry->cache; + } + break; + } + + return (result); +} + +static void +LispRehash(LispHashTable *hash) +{ + unsigned long key; + LispHashEntry *entries, *nentry, *entry, *last; + long i, size = hash->num_entries * hash->rehash_size; + + for (i = 0; i < sizeof(some_primes) / sizeof(some_primes[0]); i++) + if (some_primes[i] >= size) { + size = some_primes[i]; + break; + } + + entries = calloc(1, sizeof(LispHashEntry) * size); + if (entries == NULL) + goto out_of_memory; + + for (entry = hash->entries, last = entry + hash->num_entries; + entry < last; entry++) { + for (i = 0; i < entry->count; i++) { + key = LispHashKey(entry->keys[i], hash->function) % size; + nentry = entries + key; + if ((nentry->count % 4) == 0) { + LispObj **keys, **values; + + keys = realloc(nentry->keys, sizeof(LispObj*) * (i + 4)); + if (keys == NULL) + goto out_of_memory; + values = realloc(nentry->values, sizeof(LispObj*) * (i + 4)); + if (values == NULL) { + free(keys); + goto out_of_memory; + } + nentry->keys = keys; + nentry->values = values; + } + nentry->keys[nentry->count] = entry->keys[i]; + nentry->values[nentry->count] = entry->values[i]; + ++nentry->count; + + } + } + LispFreeHashEntries(hash->entries, hash->num_entries); + hash->entries = entries; + hash->num_entries = size; + return; + +out_of_memory: + if (entries) + LispFreeHashEntries(entries, size); + LispDestroy("out of memory"); +} + +static void +LispFreeHashEntries(LispHashEntry *entries, long num_entries) +{ + LispHashEntry *entry, *last; + + for (entry = entries, last = entry + num_entries; entry < last; entry++) { + free(entry->keys); + free(entry->values); + } + free(entries); +} + +void +LispFreeHashTable(LispHashTable *hash) +{ + LispFreeHashEntries(hash->entries, hash->num_entries); + free(hash); +} + +LispObj * +Lisp_Clrhash(LispBuiltin *builtin) +/* + clrhash hash-table + */ +{ + LispHashTable *hash; + LispHashEntry *entry, *last; + + LispObj *hash_table = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + hash = hash_table->data.hash.table; + for (entry = hash->entries, last = entry + hash->num_entries; + entry < last; entry++) { + free(entry->keys); + free(entry->values); + entry->keys = entry->values = NULL; + entry->count = entry->cache = 0; + } + hash->count = 0; + + return (hash_table); +} + +LispObj * +Lisp_Gethash(LispBuiltin *builtin) +/* + gethash key hash-table &optional default + */ +{ + return (LispHash(builtin, GET_HASH)); +} + +LispObj * +Lisp_HashTableP(LispBuiltin *builtin) +/* + hash-table-p object + */ +{ + LispObj *object = ARGUMENT(0); + + return (HASHTABLEP(object) ? T : NIL); +} + +LispObj * +Lisp_HashTableCount(LispBuiltin *builtin) +/* + hash-table-count hash-table + */ +{ + LispObj *hash_table = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + return (FIXNUM(hash_table->data.hash.table->count)); +} + +LispObj * +Lisp_HashTableRehashSize(LispBuiltin *builtin) +/* + hash-table-rehash-size hash-table + */ +{ + LispObj *hash_table = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + return (DFLOAT(hash_table->data.hash.table->rehash_size)); +} + +LispObj * +Lisp_HashTableRehashThreshold(LispBuiltin *builtin) +/* + hash-table-rehash-threshold hash-table + */ +{ + LispObj *hash_table = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + return (DFLOAT(hash_table->data.hash.table->rehash_threshold)); +} + +LispObj * +Lisp_HashTableSize(LispBuiltin *builtin) +/* + hash-table-size hash-table + */ +{ + LispObj *hash_table = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + return (FIXNUM(hash_table->data.hash.table->num_entries)); +} + +LispObj * +Lisp_HashTableTest(LispBuiltin *builtin) +/* + hash-table-test hash-table + */ +{ + LispObj *hash_table = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + return (hash_table->data.hash.test); +} + +LispObj * +Lisp_Maphash(LispBuiltin *builtin) +/* + maphash function hash-table + */ +{ + long i; + LispHashEntry *entry, *last; + + LispObj *function, *hash_table; + + hash_table = ARGUMENT(1); + function = ARGUMENT(0); + + CHECK_HASHTABLE(hash_table); + + for (entry = hash_table->data.hash.table->entries, + last = entry + hash_table->data.hash.table->num_entries; + entry < last; entry++) { + for (i = 0; i < entry->count; i++) + APPLY2(function, entry->keys[i], entry->values[i]); + } + + return (NIL); +} + +LispObj * +Lisp_MakeHashTable(LispBuiltin *builtin) +/* + make-hash-table &key test size rehash-size rehash-threshold initial-contents + */ +{ + int function = FEQL; + unsigned long i, isize, xsize; + double drsize, drthreshold; + LispHashTable *hash_table; + LispObj *cons, *result; + + LispObj *test, *size, *rehash_size, *rehash_threshold, *initial_contents; + + initial_contents = ARGUMENT(4); + rehash_threshold = ARGUMENT(3); + rehash_size = ARGUMENT(2); + size = ARGUMENT(1); + test = ARGUMENT(0); + + if (test != UNSPEC) { + if (test == Oeq) + function = FEQ; + else if (test == Oeql) + function = FEQL; + else if (test == Oequal) + function = FEQUAL; + else if (test == Oequalp) + function = FEQUALP; + else + LispDestroy("%s: :TEST must be EQ, EQL, EQUAL, " + "or EQUALP, not %s", STRFUN(builtin), STROBJ(test)); + } + else + test = Oeql; + + if (size != UNSPEC) { + CHECK_INDEX(size); + isize = FIXNUM_VALUE(size); + } + else + isize = 1; + + if (rehash_size != UNSPEC) { + CHECK_DFLOAT(rehash_size); + if (DFLOAT_VALUE(rehash_size) <= 1.0) + LispDestroy("%s: :REHASH-SIZE must a float > 1, not %s", + STRFUN(builtin), STROBJ(rehash_size)); + drsize = DFLOAT_VALUE(rehash_size); + } + else + drsize = 1.5; + + if (rehash_threshold != UNSPEC) { + CHECK_DFLOAT(rehash_threshold); + if (DFLOAT_VALUE(rehash_threshold) < 0.0 || + DFLOAT_VALUE(rehash_threshold) > 1.0) + LispDestroy("%s: :REHASH-THRESHOLD must a float " + "in the range 0.0 - 1.0, not %s", + STRFUN(builtin), STROBJ(rehash_threshold)); + drthreshold = DFLOAT_VALUE(rehash_threshold); + } + else + drthreshold = 0.75; + + if (initial_contents == UNSPEC) + initial_contents = NIL; + CHECK_LIST(initial_contents); + for (xsize = 0, cons = initial_contents; + CONSP(cons); + xsize++, cons = CDR(cons)) + CHECK_CONS(CAR(cons)); + + if (xsize > isize) + isize = xsize; + + for (i = 0; i < sizeof(some_primes) / sizeof(some_primes[0]); i++) + if (some_primes[i] >= isize) { + isize = some_primes[i]; + break; + } + + hash_table = LispMalloc(sizeof(LispHashTable)); + hash_table->entries = LispCalloc(1, sizeof(LispHashEntry) * isize); + hash_table->num_entries = isize; + hash_table->count = 0; + hash_table->function = function; + hash_table->rehash_size = drsize; + hash_table->rehash_threshold = drthreshold; + + result = LispNew(NIL, NIL); + result->type = LispHashTable_t; + result->data.hash.table = hash_table; + result->data.hash.test = test; + + LispMused(hash_table); + LispMused(hash_table->entries); + + if (initial_contents != UNSPEC) { + unsigned long key; + LispHashEntry *entry; + + for (cons = initial_contents; CONSP(cons); cons = CDR(cons)) { + key = LispHashKey(CAAR(cons), function) % isize; + entry = hash_table->entries + key; + + if ((entry->count % 4) == 0) { + LispObj **keys, **values; + + keys = realloc(entry->keys, sizeof(LispObj*) * (i + 4)); + if (keys == NULL) + LispDestroy("out of memory"); + values = realloc(entry->values, sizeof(LispObj*) * (i + 4)); + if (values == NULL) { + free(keys); + LispDestroy("out of memory"); + } + entry->keys = keys; + entry->values = values; + } + entry->keys[entry->count] = CAAR(cons); + entry->values[entry->count] = CDAR(cons); + ++entry->count; + } + hash_table->count = xsize; + } + + return (result); +} + +LispObj * +Lisp_Remhash(LispBuiltin *builtin) +/* + remhash key hash-table + */ +{ + return (LispHash(builtin, REM_HASH)); +} + +LispObj * +Lisp_XeditPuthash(LispBuiltin *builtin) +/* + lisp::puthash key hash-table value + */ +{ + return (LispHash(builtin, PUT_HASH)); +} |