/* vi: set expandtab sw=4 sts=4: */ /* hash.c - hash tables for opkg Steven M. Ayer, Jamey Hicks Copyright (C) 2002 Compaq Computer Corporation SPDX-License-Identifier: GPL-2.0-or-later This program 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 2, or (at your option) any later version. This program 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. */ #include "config.h" #include #include #include #include "hash_table.h" #include "opkg_message.h" #include "xfuncs.h" static unsigned long djb2_hash(const unsigned char *str) { unsigned long hash = 5381; int c; while ((c = *str++)) hash = ((hash << 5) + hash) + c; /* hash * 33 + c */ return hash; } static int hash_index(hash_table_t * hash, const char *key) { return djb2_hash((const unsigned char *)key) % hash->n_buckets; } /* * this is an open table keyed by strings */ void hash_table_init(const char *name, hash_table_t * hash, int len) { if (hash->entries != NULL) { opkg_msg(ERROR, "Internal error: non empty hash table.\n"); return; } memset(hash, 0, sizeof(hash_table_t)); hash->name = name; hash->n_buckets = len; hash->entries = xcalloc(hash->n_buckets, sizeof(hash_entry_t)); } void hash_print_stats(hash_table_t * hash) { printf("hash_table: %s, %d bytes\n" "\tn_buckets=%d, n_elements=%d, n_collisions=%d\n" "\tmax_bucket_len=%d, n_used_buckets=%d, ave_bucket_len=%.2f\n" "\tn_hits=%d, n_misses=%d\n", hash->name, hash->n_buckets * (int)sizeof(hash_entry_t), hash->n_buckets, hash->n_elements, hash->n_collisions, hash->max_bucket_len, hash->n_used_buckets, (hash->n_used_buckets ? ((float)hash->n_elements) / hash->n_used_buckets : 0.0f), hash->n_hits, hash->n_misses); } void hash_table_deinit(hash_table_t * hash) { unsigned int i; if (!hash) return; /* free the reminaing entries */ for (i = 0; i < hash->n_buckets; i++) { hash_entry_t *hash_entry = (hash->entries + i); free(hash_entry->key); /* skip the first entry as this is part of the array */ hash_entry = hash_entry->next; while (hash_entry) { hash_entry_t *old = hash_entry; hash_entry = hash_entry->next; free(old->key); free(old); } } free(hash->entries); hash->entries = NULL; hash->n_buckets = 0; } void *hash_table_get(hash_table_t * hash, const char *key) { int ndx = hash_index(hash, key); hash_entry_t *hash_entry = hash->entries + ndx; while (hash_entry) { if (hash_entry->key) { if (strcmp(key, hash_entry->key) == 0) { hash->n_hits++; return hash_entry->data; } } hash_entry = hash_entry->next; } hash->n_misses++; return NULL; } int hash_table_insert(hash_table_t * hash, const char *key, void *value) { unsigned int bucket_len = 0; int ndx = hash_index(hash, key); hash_entry_t *hash_entry = hash->entries + ndx; if (hash_entry->key) { if (strcmp(hash_entry->key, key) == 0) { /* alread in table, update the value */ hash_entry->data = value; return 0; } else { /* * if this is a collision, we have to go to the end of the ll, * then add a new entry * before we can hook up the value */ while (hash_entry->next) { hash_entry = hash_entry->next; if (strcmp(hash_entry->key, key) == 0) { hash_entry->data = value; return 0; } bucket_len++; } hash_entry->next = xcalloc(1, sizeof(hash_entry_t)); hash_entry = hash_entry->next; hash_entry->next = NULL; hash->n_collisions++; if (++bucket_len > hash->max_bucket_len) hash->max_bucket_len = bucket_len; } } else hash->n_used_buckets++; hash->n_elements++; hash_entry->key = xstrdup(key); hash_entry->data = value; return 0; } int hash_table_remove(hash_table_t * hash, const char *key) { int ndx = hash_index(hash, key); hash_entry_t *hash_entry = hash->entries + ndx; hash_entry_t *next_entry = NULL, *last_entry = NULL; while (hash_entry) { if (hash_entry->key) { if (strcmp(key, hash_entry->key) == 0) { free(hash_entry->key); if (last_entry) { last_entry->next = hash_entry->next; free(hash_entry); } else { next_entry = hash_entry->next; if (next_entry) { memmove(hash_entry, next_entry, sizeof(hash_entry_t)); free(next_entry); } else { memset(hash_entry, 0, sizeof(hash_entry_t)); } } return 1; } } last_entry = hash_entry; hash_entry = hash_entry->next; } return 0; } void hash_table_foreach(hash_table_t * hash, void (*f) (const char *key, void *entry, void *data), void *data) { unsigned int i; if (!hash || !f) return; for (i = 0; i < hash->n_buckets; i++) { hash_entry_t *hash_entry = (hash->entries + i); do { if (hash_entry->key) { f(hash_entry->key, hash_entry->data, data); } } while ((hash_entry = hash_entry->next)); } }