/** * \file hashtable.c * \brief Hashtable implementation */ /* * Copyright (c) 2008,2009,2011 ETH Zurich. * All rights reserved. * * This file is distributed under the terms in the attached LICENSE file. * If you do not find this file, copies can be found by writing to: * ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. */ #include #include #include #include #include #include #include #include #include /** * \brief get a hash value for a string * \param str the string * \return the hash value */ static inline int hash(const char *str, size_t key_len) { register int _hash = 5381; register int _c; for(size_t i = 0; i < key_len; i++) { _c = str[i]; _hash = ((_hash << 5) + _hash) + _c; } return _hash; } /** * \brief get the index for an given hash in the bucket table * \param table_length the length of the table * \param hash_value the hash * \return the index */ static inline int index_for(int table_length, int hash_value) { return ((unsigned)hash_value % table_length); } /** * \brief check two keys for equality * \param k1 the first string * \param k2 the second string * \return true if the strings are equal */ #define equals(_k1, _k2, len) (!memcmp((_k1), (_k2), (len))) /** * \brief get the number of entries in a hashtable * \param h the hashtable * \return the number of entries */ static int ht_size(struct dictionary *dict) { assert(dict != NULL); struct hashtable *ht = (struct hashtable*) dict; return ht->entry_count; } /** * \brief put a new key/value pair into the hashtable * \param ht the hashtable * \param key the key. Has to be a string. * \param value the value. Can be any pointer. This function does * not copy the value or stores it. The caller is responsible for * maintaining the value, the hashtable only keeps pointers. * \return 0 if the operation succeeded, otherwise an error code. */ static int ht_put(struct dictionary *dict, struct _ht_entry *entry) { assert(dict != NULL); struct hashtable *ht = (struct hashtable*) dict; int _hash_value = hash(entry->key, entry->key_len); // TODO: XXX check for size and increase capacity, if necessary ++(ht->entry_count); entry->hash_value = _hash_value; int _index = index_for(ht->table_length, _hash_value); entry->next = ht->entries[_index]; ht->entries[_index] = entry; return 0; } static int ht_put_word(struct dictionary *dict, const char *key, size_t key_len, uintptr_t value) { struct _ht_entry *e = malloc(sizeof(struct _ht_entry)); if (NULL == e) { return 1; } e->key = key; e->key_len = key_len; e->value = (void*) value; e->type = TYPE_WORD; return ht_put(dict, e); } static int ht_put_capability(struct dictionary *dict, char *key, struct capref cap) { struct _ht_entry *e = malloc(sizeof(struct _ht_entry)); if (NULL == e) { return 1; } e->key = key; e->key_len = strlen(key); e->capvalue = cap; e->type = TYPE_CAPABILITY; return ht_put(dict, e); } /** * \brief get a value from the hashtable for a given key * \param ht the hashtable * \param key the key. Has to be a zero-terminated string. * \return the value or NULL if there is no such key/value pair */ static ENTRY_TYPE ht_get(struct dictionary *dict, const char *key, size_t key_len, void **value) { assert(dict != NULL); assert(key != NULL); assert(value != NULL); struct hashtable *ht = (struct hashtable*) dict; int _hash_value = hash(key, key_len); int _index = index_for(ht->table_length, _hash_value); struct _ht_entry *_e = ht->entries[_index]; while (NULL != _e) { if ((_hash_value == _e->hash_value) && (equals(key, _e->key, key_len))) { assert(_e->type != TYPE_CAPABILITY); *value = _e->value; return _e->type; } _e = _e->next; } *value = NULL; return 0; } static ENTRY_TYPE ht_get_capability(struct dictionary *dict, char *key, struct capref *value) { assert(dict != NULL); assert(key != NULL); assert(value != NULL); struct hashtable *ht = (struct hashtable*) dict; size_t key_len = strlen(key); int _hash_value = hash(key, key_len); int _index = index_for(ht->table_length, _hash_value); struct _ht_entry *_e = ht->entries[_index]; while (NULL != _e) { if ((_hash_value == _e->hash_value) && (equals(key, _e->key, key_len))) { assert(_e->type == TYPE_CAPABILITY); *value = _e->capvalue; return _e->type; } _e = _e->next; } *value = NULL_CAP; return 0; } static int ht_remove(struct dictionary *dict, const char *key, size_t key_len) { assert(dict != NULL); struct hashtable *ht = (struct hashtable*) dict; int _hash_value = hash(key, key_len); int _index = index_for(ht->table_length, _hash_value); struct _ht_entry *_e = ht->entries[_index]; struct _ht_entry *_prev = NULL; while (NULL != _e) { if ((_hash_value == _e->hash_value) && (equals(key, _e->key, key_len))) { if (_prev == NULL) { ht->entries[_index] = _e->next; } else { _prev->next = _e->next; } free(_e); return 0; } _prev = _e; _e = _e->next; } return 1; } // XXX implement destructors /** * \brief create an empty hashtable with a given capacity and load factor * \param capacity the capacity * \param the load factor * \return an empty hashtable. */ static void ht_init(struct hashtable *_ht, int capacity, int load_factor) { _ht->capacity = capacity; _ht->load_factor = load_factor; _ht->table_length = capacity; _ht->entries = malloc(_ht->table_length * sizeof(struct _ht_entry)); assert(_ht->entries != NULL); memset(_ht->entries, 0, _ht->table_length * sizeof(struct _ht_entry)); _ht->threshold = (capacity * load_factor) / 100; _ht->d.size = ht_size; _ht->d.put_word = ht_put_word; _ht->d.put_capability = ht_put_capability; _ht->d.get = ht_get; _ht->d.get_capability = ht_get_capability; _ht->d.remove = ht_remove; } // XXX TODO: loadFactor should be a float, 0.75 instead of 75 struct hashtable* create_hashtable2(int capacity, int load_factor) { struct hashtable *_ht = malloc(sizeof(struct hashtable)); assert(_ht != NULL); ht_init(_ht, capacity, load_factor); return _ht; } /** * \brief create an empty hashtable with default capacity and load factor * \return an empty hashtable */ struct hashtable* create_hashtable(void) { return create_hashtable2(11, 75); }