summaryrefslogtreecommitdiff
path: root/lisp/hash.c
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/hash.c')
-rw-r--r--lisp/hash.c657
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));
+}