/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2004 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #pragma ident "%Z%%M% %I% %E% SMI" /* * Routines for manipulating hash tables */ #include #include #include #include #include #include "hash.h" #include "memory.h" #include "list.h" struct hash { int h_nbuckets; list_t **h_buckets; int (*h_hashfn)(int, void *); int (*h_cmp)(void *, void *); }; struct hash_data { hash_t *hd_hash; int (*hd_fun)(); void *hd_key; void *hd_private; void *hd_ret; }; static int hash_def_hash(int nbuckets, uintptr_t data) { return (data % nbuckets); } static int hash_def_cmp(uintptr_t d1, uintptr_t d2) { return (d1 != d2); } int hash_name(int nbuckets, const char *name) { const char *c; ulong_t g; int h = 0; for (c = name; *c; c++) { h = (h << 4) + *c; if ((g = (h & 0xf0000000)) != 0) { h ^= (g >> 24); h ^= g; } } return (h % nbuckets); } hash_t * hash_new(int nbuckets, int (*hashfn)(int, void *), int (*cmp)(void *, void *)) { hash_t *hash; hash = xmalloc(sizeof (hash_t)); hash->h_buckets = xcalloc(sizeof (list_t *) * nbuckets); hash->h_nbuckets = nbuckets; hash->h_hashfn = hashfn ? hashfn : (int (*)())hash_def_hash; hash->h_cmp = cmp ? cmp : (int (*)())hash_def_cmp; return (hash); } void hash_add(hash_t *hash, void *key) { int bucket = hash->h_hashfn(hash->h_nbuckets, key); list_add(&hash->h_buckets[bucket], key); } static int hash_add_cb(void *node, void *private) { hash_add((hash_t *)private, node); return (0); } void hash_merge(hash_t *to, hash_t *from) { (void) hash_iter(from, hash_add_cb, to); } static int hash_remove_cb(void *key1, void *key2, hash_t *hash) { return (hash->h_cmp(key1, key2)); } void hash_remove(hash_t *hash, void *key) { int bucket = hash->h_hashfn(hash->h_nbuckets, key); (void) list_remove(&hash->h_buckets[bucket], key, (int (*)())hash_remove_cb, hash); } int hash_match(hash_t *hash, void *key, int (*fun)(void *, void *), void *private) { int bucket = hash->h_hashfn(hash->h_nbuckets, key); return (list_iter(hash->h_buckets[bucket], fun, private) < 0); } static int hash_find_list_cb(void *node, struct hash_data *hd) { int cbrc; int rc = 0; if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) { if ((cbrc = hd->hd_fun(node, hd->hd_private)) < 0) return (cbrc); rc += cbrc; } return (rc); } int hash_find_iter(hash_t *hash, void *key, int (*fun)(void *, void *), void *private) { int bucket = hash->h_hashfn(hash->h_nbuckets, key); struct hash_data hd; hd.hd_hash = hash; hd.hd_fun = fun; hd.hd_key = key; hd.hd_private = private; return (list_iter(hash->h_buckets[bucket], (int (*)())hash_find_list_cb, &hd)); } /* stop on first match */ static int hash_find_first_cb(void *node, struct hash_data *hd) { if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) { hd->hd_ret = node; return (-1); } return (0); } int hash_find(hash_t *hash, void *key, void **value) { int ret; struct hash_data hd; hd.hd_hash = hash; hd.hd_fun = hash_find_first_cb; hd.hd_key = key; ret = hash_match(hash, key, (int (*)())hash_find_first_cb, &hd); if (ret && value) *value = hd.hd_ret; return (ret); } int hash_iter(hash_t *hash, int (*fun)(void *, void *), void *private) { int cumrc = 0; int cbrc; int i; for (i = 0; i < hash->h_nbuckets; i++) { if (hash->h_buckets[i] != NULL) { if ((cbrc = list_iter(hash->h_buckets[i], fun, private)) < 0) return (cbrc); cumrc += cbrc; } } return (cumrc); } int hash_count(hash_t *hash) { int num, i; for (num = 0, i = 0; i < hash->h_nbuckets; i++) num += list_count(hash->h_buckets[i]); return (num); } void hash_free(hash_t *hash, void (*datafree)(void *, void *), void *private) { int i; if (hash == NULL) return; for (i = 0; i < hash->h_nbuckets; i++) list_free(hash->h_buckets[i], datafree, private); free(hash->h_buckets); free(hash); } void hash_stats(hash_t *hash, int verbose) { int min = list_count(hash->h_buckets[0]); int minidx = 0; int max = min; int maxidx = 0; int tot = min; int count; int i; if (min && verbose) printf("%3d: %d\n", 0, min); for (i = 1; i < hash->h_nbuckets; i++) { count = list_count(hash->h_buckets[i]); if (min > count) { min = count; minidx = i; } if (max < count) { max = count; maxidx = i; } if (count && verbose) printf("%3d: %d\n", i, count); tot += count; } printf("Hash statistics:\n"); printf(" Buckets: %d\n", hash->h_nbuckets); printf(" Items : %d\n", tot); printf(" Min/Max: %d in #%d, %d in #%d\n", min, minidx, max, maxidx); printf(" Average: %5.2f\n", (float)tot / (float)hash->h_nbuckets); }