/* Copyright 2010, 2011, 2012 Tony Garnock-Jones . * * This file is part of Hop. * * Hop is free software: you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Hop is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public * License for more details. * * You should have received a copy of the GNU General Public License * along with Hop. If not, see . */ #include #include #include #include #include #include "cmsg_private.h" #include "hashtable.h" uint32_t hash_bytes(cmsg_bytes_t bytes) { /* http://en.wikipedia.org/wiki/Jenkins_hash_function */ uint32_t hash = 0; size_t i; for (i = 0; i < bytes.len; i++) { hash += bytes.bytes[i]; hash += (hash << 10); hash ^= (hash >> 6); } hash += (hash << 3); hash ^= (hash >> 11); hash += (hash << 15); return hash; } void init_hashtable(hashtable_t *table, size_t initial_bucket_count, void *(*dup_value)(void *), void (*free_value)(void *)) { table->bucket_count = initial_bucket_count; table->entry_count = 0; table->buckets = NULL; table->dup_value = dup_value; table->free_value = free_value; if (initial_bucket_count > 0) { table->buckets = calloc(initial_bucket_count, sizeof(hashtable_entry_t *)); } } static void destroy_entry(hashtable_t *table, hashtable_entry_t *entry) { cmsg_bytes_free(entry->key); if (table->free_value != NULL) { table->free_value(entry->value); } free(entry); } void destroy_hashtable(hashtable_t *table) { if (table->buckets != NULL) { int i; for (i = 0; i < table->bucket_count; i++) { hashtable_entry_t *chain = table->buckets[i]; table->buckets[i] = NULL; while (chain != NULL) { hashtable_entry_t *next = chain->next; destroy_entry(table, chain); chain = next; } } free(table->buckets); } } static hashtable_entry_t **hashtable_find(hashtable_t *table, cmsg_bytes_t key) { uint32_t h = hash_bytes(key) % table->bucket_count; hashtable_entry_t **entryptr = &(table->buckets[h]); hashtable_entry_t *entry = *entryptr; while (entry != NULL) { if ((entry->key.len == key.len) && !memcmp(entry->key.bytes, key.bytes, key.len)) { break; } entryptr = &entry->next; entry = *entryptr; } return entryptr; } int hashtable_contains(hashtable_t *table, cmsg_bytes_t key) { hashtable_entry_t **entryptr = hashtable_find(table, key); return (*entryptr != NULL); } int hashtable_get(hashtable_t *table, cmsg_bytes_t key, void **valueptr) { hashtable_entry_t **entryptr = hashtable_find(table, key); if (*entryptr == NULL) { return 0; } else { *valueptr = (*entryptr)->value; return 1; } } int hashtable_put(hashtable_t *table, cmsg_bytes_t key, void *value) { /* TODO: grow and rehash */ hashtable_entry_t **entryptr = hashtable_find(table, key); if (*entryptr == NULL) { hashtable_entry_t *entry = malloc(sizeof(hashtable_entry_t)); entry->next = NULL; entry->key = cmsg_bytes_malloc_dup(key); entry->value = (table->dup_value == NULL) ? value : table->dup_value(value); *entryptr = entry; table->entry_count++; return 1; } else { if (table->free_value != NULL) { table->free_value((*entryptr)->value); } (*entryptr)->value = (table->dup_value == NULL) ? value : table->dup_value(value); return 0; } } int hashtable_erase(hashtable_t *table, cmsg_bytes_t key) { hashtable_entry_t **entryptr = hashtable_find(table, key); if (*entryptr == NULL) { return 0; } else { hashtable_entry_t *entry = *entryptr; *entryptr = entry->next; destroy_entry(table, entry); table->entry_count--; return 1; } } void hashtable_foreach(hashtable_t *table, hashtable_iterator_t iterator, void *context) { int i; for (i = 0; i < table->bucket_count; i++) { hashtable_entry_t *chain = table->buckets[i]; while (chain != NULL) { hashtable_entry_t *next = chain->next; iterator(context, chain->key, chain->value); chain = next; } } }