1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
  24  * Use is subject to license terms.
  25  */
  26 
  27 #pragma ident   "%Z%%M% %I%     %E% SMI"
  28 
  29 /*
  30  * Routines for manipulating hash tables
  31  */
  32 
  33 #include <stdio.h>
  34 #include <stdlib.h>
  35 #include <strings.h>
  36 #include <sys/types.h>
  37 #include <sys/sysmacros.h>
  38 
  39 #include "hash.h"
  40 #include "memory.h"
  41 #include "list.h"
  42 
  43 struct hash {
  44         int h_nbuckets;
  45         list_t **h_buckets;
  46 
  47         int (*h_hashfn)(int, void *);
  48         int (*h_cmp)(void *, void *);
  49 };
  50 
  51 struct hash_data {
  52         hash_t *hd_hash;
  53         int (*hd_fun)();
  54         void *hd_key;
  55         void *hd_private;
  56 
  57         void *hd_ret;
  58 };
  59 
  60 static int
  61 hash_def_hash(int nbuckets, uintptr_t data)
  62 {
  63         return (data % nbuckets);
  64 }
  65 
  66 static int
  67 hash_def_cmp(uintptr_t d1, uintptr_t d2)
  68 {
  69         return (d1 != d2);
  70 }
  71 
  72 
  73 int
  74 hash_name(int nbuckets, const char *name)
  75 {
  76         const char *c;
  77         ulong_t g;
  78         int h = 0;
  79 
  80         for (c = name; *c; c++) {
  81                 h = (h << 4) + *c;
  82                 if ((g = (h & 0xf0000000)) != 0) {
  83                         h ^= (g >> 24);
  84                         h ^= g;
  85                 }
  86         }
  87 
  88         return (h % nbuckets);
  89 }
  90 
  91 hash_t *
  92 hash_new(int nbuckets, int (*hashfn)(int, void *), int (*cmp)(void *, void *))
  93 {
  94         hash_t *hash;
  95 
  96         hash = xmalloc(sizeof (hash_t));
  97         hash->h_buckets = xcalloc(sizeof (list_t *) * nbuckets);
  98         hash->h_nbuckets = nbuckets;
  99         hash->h_hashfn = hashfn ? hashfn : (int (*)())hash_def_hash;
 100         hash->h_cmp = cmp ? cmp : (int (*)())hash_def_cmp;
 101 
 102         return (hash);
 103 }
 104 
 105 void
 106 hash_add(hash_t *hash, void *key)
 107 {
 108         int bucket = hash->h_hashfn(hash->h_nbuckets, key);
 109 
 110         list_add(&hash->h_buckets[bucket], key);
 111 }
 112 
 113 static int
 114 hash_add_cb(void *node, void *private)
 115 {
 116         hash_add((hash_t *)private, node);
 117         return (0);
 118 }
 119 
 120 void
 121 hash_merge(hash_t *to, hash_t *from)
 122 {
 123         (void) hash_iter(from, hash_add_cb, to);
 124 }
 125 
 126 static int
 127 hash_remove_cb(void *key1, void *key2, hash_t *hash)
 128 {
 129         return (hash->h_cmp(key1, key2));
 130 }
 131 
 132 void
 133 hash_remove(hash_t *hash, void *key)
 134 {
 135         int bucket = hash->h_hashfn(hash->h_nbuckets, key);
 136 
 137         (void) list_remove(&hash->h_buckets[bucket], key,
 138             (int (*)())hash_remove_cb, hash);
 139 }
 140 
 141 int
 142 hash_match(hash_t *hash, void *key, int (*fun)(void *, void *),
 143     void *private)
 144 {
 145         int bucket = hash->h_hashfn(hash->h_nbuckets, key);
 146 
 147         return (list_iter(hash->h_buckets[bucket], fun, private) < 0);
 148 }
 149 
 150 static int
 151 hash_find_list_cb(void *node, struct hash_data *hd)
 152 {
 153         int cbrc;
 154         int rc = 0;
 155 
 156         if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) {
 157                 if ((cbrc = hd->hd_fun(node, hd->hd_private)) < 0)
 158                         return (cbrc);
 159                 rc += cbrc;
 160         }
 161 
 162         return (rc);
 163 }
 164 
 165 int
 166 hash_find_iter(hash_t *hash, void *key, int (*fun)(void *, void *),
 167     void *private)
 168 {
 169         int bucket = hash->h_hashfn(hash->h_nbuckets, key);
 170         struct hash_data hd;
 171 
 172         hd.hd_hash = hash;
 173         hd.hd_fun = fun;
 174         hd.hd_key = key;
 175         hd.hd_private = private;
 176 
 177         return (list_iter(hash->h_buckets[bucket], (int (*)())hash_find_list_cb,
 178             &hd));
 179 }
 180 
 181 /* stop on first match */
 182 static int
 183 hash_find_first_cb(void *node, struct hash_data *hd)
 184 {
 185         if (hd->hd_hash->h_cmp(hd->hd_key, node) == 0) {
 186                 hd->hd_ret = node;
 187                 return (-1);
 188         }
 189 
 190         return (0);
 191 }
 192 
 193 int
 194 hash_find(hash_t *hash, void *key, void **value)
 195 {
 196         int ret;
 197         struct hash_data hd;
 198 
 199         hd.hd_hash = hash;
 200         hd.hd_fun = hash_find_first_cb;
 201         hd.hd_key = key;
 202 
 203         ret = hash_match(hash, key, (int (*)())hash_find_first_cb, &hd);
 204         if (ret && value)
 205                 *value = hd.hd_ret;
 206 
 207         return (ret);
 208 }
 209 
 210 int
 211 hash_iter(hash_t *hash, int (*fun)(void *, void *), void *private)
 212 {
 213         int cumrc = 0;
 214         int cbrc;
 215         int i;
 216 
 217         for (i = 0; i < hash->h_nbuckets; i++) {
 218                 if (hash->h_buckets[i] != NULL) {
 219                         if ((cbrc = list_iter(hash->h_buckets[i], fun,
 220                             private)) < 0)
 221                                 return (cbrc);
 222                         cumrc += cbrc;
 223                 }
 224         }
 225 
 226         return (cumrc);
 227 }
 228 
 229 int
 230 hash_count(hash_t *hash)
 231 {
 232         int num, i;
 233 
 234         for (num = 0, i = 0; i < hash->h_nbuckets; i++)
 235                 num += list_count(hash->h_buckets[i]);
 236 
 237         return (num);
 238 }
 239 
 240 void
 241 hash_free(hash_t *hash, void (*datafree)(void *, void *), void *private)
 242 {
 243         int i;
 244 
 245         if (hash == NULL)
 246                 return;
 247 
 248         for (i = 0; i < hash->h_nbuckets; i++)
 249                 list_free(hash->h_buckets[i], datafree, private);
 250         free(hash->h_buckets);
 251         free(hash);
 252 }
 253 
 254 void
 255 hash_stats(hash_t *hash, int verbose)
 256 {
 257         int min = list_count(hash->h_buckets[0]);
 258         int minidx = 0;
 259         int max = min;
 260         int maxidx = 0;
 261         int tot = min;
 262         int count;
 263         int i;
 264 
 265         if (min && verbose)
 266                 printf("%3d: %d\n", 0, min);
 267         for (i = 1; i < hash->h_nbuckets; i++) {
 268                 count = list_count(hash->h_buckets[i]);
 269                 if (min > count) {
 270                         min = count;
 271                         minidx = i;
 272                 }
 273                 if (max < count) {
 274                         max = count;
 275                         maxidx = i;
 276                 }
 277                 if (count && verbose)
 278                         printf("%3d: %d\n", i, count);
 279                 tot += count;
 280         }
 281 
 282         printf("Hash statistics:\n");
 283         printf(" Buckets: %d\n", hash->h_nbuckets);
 284         printf(" Items  : %d\n", tot);
 285         printf(" Min/Max: %d in #%d, %d in #%d\n", min, minidx, max, maxidx);
 286         printf(" Average: %5.2f\n", (float)tot / (float)hash->h_nbuckets);
 287 }