/* * Copyright (c) 2007,2008 Paulo Cesar Pereira de Andrade * * 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 (including the next * paragraph) 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 AUTHORS OR COPYRIGHT HOLDERS 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. * * Author: Paulo Cesar Pereira de Andrade */ #include "util.h" #include #include /* * This is a very simplified and adapted version of the hash tables I am * using in a personal project. It was added to try to have a single hash * table implementation in xedit. The lisp (for user code) version was not * converted, but the following hastables have been converted: * ispell.c - list of replace and ignore words * hook.c - list of auto replace words * internal lisp data structures: * atoms * strings * packages * opaque types * also, all code traversing hash tables is now using * hash_iter_first() and hash_iter_next() * conversion isn't as good as I originally wanted, code is using hash_check * instead of hash_get, but this is due to the code not having a basic * { void *data; int length; } object to store string like objects * * Also, this hash table implementation was added mainly for the tags * support. */ /* * Prototypes */ static int hash_equal(hash_table *hash, hash_key *left, hash_key *right); static unsigned int hash_data(char *value, unsigned int length); static unsigned int hash_value(hash_key *key); /* * Implementation */ static int hash_equal(hash_table *hash, hash_key *left, hash_key *right) { if (left->length == right->length) { if (left == right) return (1); if (hash->compare) return (hash->compare(left, right)); return (memcmp(left->value, right->value, left->length) == 0); } return (0); } static unsigned int hash_data(char *value, unsigned int length) { char *ptr; unsigned int i, key; for (i = key = 0, ptr = value; i < length; i++) key = (key << (key & 1)) ^ ptr[i]; return (key); } static unsigned int hash_value(hash_key *key) { return (hash_data(key->value, key->length)); } hash_table * hash_new(unsigned int length, hash_compare compare) { hash_table *hash; hash = calloc(1, sizeof(hash_table)); if (hash) { hash->entries = calloc(length, sizeof(hash_entry *)); if (hash->entries) { hash->length = length; hash->compare = compare; hash->iter.offset = -1; } else { free(hash); hash = (hash_table *)0; } } return (hash); } hash_entry * hash_put(hash_table *hash, hash_entry *entry) { unsigned int key; hash_entry *prev, *ptr; /* Offset in hash table vector for this entry */ key = hash_value(entry->key) % hash->length; /* We hope this is nil for most calls */ ptr = hash->entries[key]; /* Check if clashed with another entry */ for (prev = ptr; ptr; prev = ptr, ptr = ptr->next) { /* Replace current entry */ if (hash_equal(hash, entry->key, ptr->key)) { /* If not trying to readd same value */ if (entry != ptr) { if (ptr == prev) hash->entries[key] = entry; else prev->next = entry; entry->next = ptr->next; /* Finished */ } else ptr = (hash_entry *)0; goto hash_put_done; } } /* Add new entry */ if (prev == (hash_entry *)0) /* If no entry in offset */ hash->entries[key] = entry; else /* Add to end of clashing list */ prev->next = entry; entry->next = (hash_entry *)0; /* Increase sum of entries counter*/ ++hash->count; hash_put_done: /* ptr will be nil if no entry was replaced, of tried to add * again an entry already in the hash table */ return (ptr); } hash_entry * hash_get(hash_table *hash, hash_key *name) { unsigned int key; hash_entry *entry; key = hash_value(name) % hash->length; for (entry = hash->entries[key]; entry; entry = entry->next) { if (hash_equal(hash, name, entry->key)) { return (entry); } } return ((hash_entry *)0); } hash_entry * hash_check(hash_table *hash, char *name, unsigned int length) { unsigned int key; hash_entry *entry; key = hash_data(name, length) % hash->length; for (entry = hash->entries[key]; entry; entry = entry->next) { if (length == entry->key->length && memcmp(name, entry->key->value, length) == 0) { return (entry); } } return ((hash_entry *)0); } hash_entry * hash_rem_no_free(hash_table *hash, hash_entry *entry) { unsigned int key; hash_entry *ptr, *prev; key = hash_value(entry->key) % hash->length; for (ptr = prev = hash->entries[key]; ptr; prev = ptr, ptr = ptr->next) { if (ptr == entry) { --hash->count; if (ptr == prev) hash->entries[key] = ptr->next; else prev->next = ptr->next; break; } } if (ptr && ptr == hash->iter.entry) hash->iter.entry = ptr->next; /* If entry wasn't in hash table ptr will be nil */ return (ptr); } void hash_rem(hash_table *hash, hash_entry *entry) { entry = hash_rem_no_free(hash, entry); if (entry) { free(entry->key->value); free(entry->key); free(entry); } } void hash_rehash(hash_table *hash, unsigned int length) { unsigned int i, key; hash_entry *entry, *next, **entries; entries = (hash_entry **)calloc(length, sizeof(hash_entry *)); if (entries) { /* Populate the new table, note that clashes are now in reverse order */ for (i = 0; i < hash->length; i++) { for (entry = hash->entries[i]; entry; entry = next) { next = entry->next; key = hash_value(entry->key) % length; entry->next = entries[key]; entries[key] = entry; } } /* Finish updating hash table */ free(hash->entries); hash->entries = entries; hash->length = length; } hash->iter.offset = -1; } hash_entry * hash_iter_first(hash_table *hash) { hash->iter.offset = 0; hash->iter.entry = (hash_entry *)0; return (hash_iter_next(hash)); } hash_entry * hash_iter_next(hash_table *hash) { if (hash->iter.offset >= 0) { if (hash->iter.entry) { if ((hash->iter.entry = hash->iter.entry->next)) return (hash->iter.entry); ++hash->iter.offset; } for (; hash->iter.offset < hash->length; hash->iter.offset++) { if ((hash->iter.entry = hash->entries[hash->iter.offset])) return (hash->iter.entry); } hash->iter.entry = (hash_entry *)0; hash->iter.offset = -1; } return ((hash_entry *)0); } void hash_clr(hash_table *hash) { unsigned int i; hash_entry *entry, *next; /* Extra data should be free'd with the iterator */ for (i = 0; i < hash->length; i++) { entry = hash->entries[i]; if (entry) { for (next = entry; entry; entry = next) { next = entry->next; free(entry->key->value); free(entry->key); free(entry); } hash->entries[i] = (hash_entry *)0; } } hash->count = 0; hash->iter.offset = -1; } void hash_del(hash_table *hash) { hash_clr(hash); free(hash->entries); free(hash); }