1 1.13 kre /* $NetBSD: hcreate.c,v 1.13 2022/03/13 01:44:37 kre Exp $ */ 2 1.1 cgd 3 1.1 cgd /* 4 1.1 cgd * Copyright (c) 2001 Christopher G. Demetriou 5 1.1 cgd * All rights reserved. 6 1.1 cgd * 7 1.1 cgd * Redistribution and use in source and binary forms, with or without 8 1.1 cgd * modification, are permitted provided that the following conditions 9 1.1 cgd * are met: 10 1.1 cgd * 1. Redistributions of source code must retain the above copyright 11 1.1 cgd * notice, this list of conditions and the following disclaimer. 12 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 cgd * notice, this list of conditions and the following disclaimer in the 14 1.1 cgd * documentation and/or other materials provided with the distribution. 15 1.8 christos * 3. The name of the author may not be used to endorse or promote products 16 1.1 cgd * derived from this software without specific prior written permission. 17 1.1 cgd * 18 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 1.1 cgd * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 1.1 cgd * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 1.1 cgd * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 1.1 cgd * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 1.1 cgd * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 1.1 cgd * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 1.1 cgd * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 1.1 cgd * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 1.1 cgd * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 1.1 cgd * 29 1.1 cgd * <<Id: LICENSE,v 1.2 2000/06/14 15:57:33 cgd Exp>> 30 1.1 cgd */ 31 1.1 cgd 32 1.1 cgd /* 33 1.1 cgd * hcreate() / hsearch() / hdestroy() 34 1.1 cgd * 35 1.1 cgd * SysV/XPG4 hash table functions. 36 1.1 cgd * 37 1.1 cgd * Implementation done based on NetBSD manual page and Solaris manual page, 38 1.1 cgd * plus my own personal experience about how they're supposed to work. 39 1.1 cgd * 40 1.1 cgd * I tried to look at Knuth (as cited by the Solaris manual page), but 41 1.1 cgd * nobody had a copy in the office, so... 42 1.1 cgd */ 43 1.1 cgd 44 1.1 cgd #include <sys/cdefs.h> 45 1.1 cgd #if defined(LIBC_SCCS) && !defined(lint) 46 1.13 kre __RCSID("$NetBSD: hcreate.c,v 1.13 2022/03/13 01:44:37 kre Exp $"); 47 1.1 cgd #endif /* LIBC_SCCS and not lint */ 48 1.1 cgd 49 1.1 cgd #if !defined(lint) 50 1.6 lukem __COPYRIGHT("@(#) Copyright (c) 2001\ 51 1.6 lukem Christopher G. Demetriou. All rights reserved."); 52 1.1 cgd #endif /* not lint */ 53 1.1 cgd 54 1.1 cgd #include "namespace.h" 55 1.1 cgd #include <assert.h> 56 1.1 cgd #include <errno.h> 57 1.1 cgd #include <inttypes.h> 58 1.1 cgd #include <search.h> 59 1.1 cgd #include <stdlib.h> 60 1.1 cgd #include <string.h> 61 1.1 cgd #include <sys/queue.h> 62 1.1 cgd 63 1.1 cgd /* 64 1.1 cgd * DO NOT MAKE THIS STRUCTURE LARGER THAN 32 BYTES (4 ptrs on 64-bit 65 1.1 cgd * ptr machine) without adjusting MAX_BUCKETS_LG2 below. 66 1.1 cgd */ 67 1.1 cgd struct internal_entry { 68 1.1 cgd SLIST_ENTRY(internal_entry) link; 69 1.1 cgd ENTRY ent; 70 1.1 cgd }; 71 1.1 cgd SLIST_HEAD(internal_head, internal_entry); 72 1.1 cgd 73 1.1 cgd #define MIN_BUCKETS_LG2 4 74 1.1 cgd #define MIN_BUCKETS (1 << MIN_BUCKETS_LG2) 75 1.1 cgd 76 1.1 cgd /* 77 1.1 cgd * max * sizeof internal_entry must fit into size_t. 78 1.1 cgd * assumes internal_entry is <= 32 (2^5) bytes. 79 1.1 cgd */ 80 1.1 cgd #define MAX_BUCKETS_LG2 (sizeof (size_t) * 8 - 1 - 5) 81 1.2 ross #define MAX_BUCKETS ((size_t)1 << MAX_BUCKETS_LG2) 82 1.1 cgd 83 1.1 cgd /* Default hash function, from db/hash/hash_func.c */ 84 1.1 cgd extern u_int32_t (*__default_hash)(const void *, size_t); 85 1.1 cgd 86 1.7 christos static struct hsearch_data htable; 87 1.1 cgd 88 1.1 cgd int 89 1.1 cgd hcreate(size_t nel) 90 1.1 cgd { 91 1.7 christos _DIAGASSERT(htable.table == NULL); 92 1.1 cgd 93 1.5 simonb /* Make sure this isn't called when a table already exists. */ 94 1.7 christos if (htable.table != NULL) { 95 1.1 cgd errno = EINVAL; 96 1.1 cgd return 0; 97 1.1 cgd } 98 1.7 christos return hcreate_r(nel, &htable); 99 1.7 christos } 100 1.7 christos 101 1.7 christos int 102 1.7 christos hcreate_r(size_t nel, struct hsearch_data *head) 103 1.7 christos { 104 1.7 christos struct internal_head *table; 105 1.7 christos size_t idx; 106 1.7 christos unsigned int p2; 107 1.1 cgd 108 1.1 cgd /* If nel is too small, make it min sized. */ 109 1.1 cgd if (nel < MIN_BUCKETS) 110 1.1 cgd nel = MIN_BUCKETS; 111 1.1 cgd 112 1.1 cgd /* If it's too large, cap it. */ 113 1.1 cgd if (nel > MAX_BUCKETS) 114 1.1 cgd nel = MAX_BUCKETS; 115 1.1 cgd 116 1.1 cgd /* If it's is not a power of two in size, round up. */ 117 1.1 cgd if ((nel & (nel - 1)) != 0) { 118 1.1 cgd for (p2 = 0; nel != 0; p2++) 119 1.1 cgd nel >>= 1; 120 1.1 cgd _DIAGASSERT(p2 <= MAX_BUCKETS_LG2); 121 1.1 cgd nel = 1 << p2; 122 1.1 cgd } 123 1.1 cgd 124 1.1 cgd /* Allocate the table. */ 125 1.7 christos head->size = nel; 126 1.7 christos head->filled = 0; 127 1.13 kre table = NULL; 128 1.12 christos errno = reallocarr(&table, nel, sizeof(*table)); 129 1.12 christos if (errno) 130 1.1 cgd return 0; 131 1.12 christos head->table = (void *)table; 132 1.1 cgd 133 1.1 cgd /* Initialize it. */ 134 1.7 christos for (idx = 0; idx < nel; idx++) 135 1.7 christos SLIST_INIT(&table[idx]); 136 1.1 cgd 137 1.1 cgd return 1; 138 1.1 cgd } 139 1.1 cgd 140 1.1 cgd void 141 1.10 christos hdestroy1(void (*freekey)(void *), void (*freedata)(void *)) 142 1.9 christos { 143 1.9 christos _DIAGASSERT(htable.table != NULL); 144 1.10 christos hdestroy1_r(&htable, freekey, freedata); 145 1.9 christos } 146 1.9 christos 147 1.9 christos void 148 1.1 cgd hdestroy(void) 149 1.1 cgd { 150 1.10 christos hdestroy1(NULL, NULL); 151 1.7 christos } 152 1.7 christos 153 1.7 christos void 154 1.10 christos hdestroy1_r(struct hsearch_data *head, void (*freekey)(void *), 155 1.10 christos void (*freedata)(void *)) 156 1.7 christos { 157 1.1 cgd struct internal_entry *ie; 158 1.1 cgd size_t idx; 159 1.7 christos void *p; 160 1.7 christos struct internal_head *table; 161 1.1 cgd 162 1.7 christos if (head == NULL) 163 1.1 cgd return; 164 1.1 cgd 165 1.7 christos p = head->table; 166 1.7 christos head->table = NULL; 167 1.7 christos table = p; 168 1.7 christos 169 1.7 christos for (idx = 0; idx < head->size; idx++) { 170 1.7 christos while (!SLIST_EMPTY(&table[idx])) { 171 1.7 christos ie = SLIST_FIRST(&table[idx]); 172 1.7 christos SLIST_REMOVE_HEAD(&table[idx], link); 173 1.10 christos if (freekey) 174 1.10 christos (*freekey)(ie->ent.key); 175 1.10 christos if (freedata) 176 1.10 christos (*freedata)(ie->ent.data); 177 1.1 cgd free(ie); 178 1.1 cgd } 179 1.1 cgd } 180 1.7 christos free(table); 181 1.1 cgd } 182 1.1 cgd 183 1.9 christos void 184 1.9 christos hdestroy_r(struct hsearch_data *head) 185 1.9 christos { 186 1.10 christos hdestroy1_r(head, NULL, NULL); 187 1.9 christos } 188 1.9 christos 189 1.1 cgd ENTRY * 190 1.1 cgd hsearch(ENTRY item, ACTION action) 191 1.1 cgd { 192 1.7 christos ENTRY *ep; 193 1.7 christos _DIAGASSERT(htable.table != NULL); 194 1.7 christos (void)hsearch_r(item, action, &ep, &htable); 195 1.7 christos return ep; 196 1.7 christos } 197 1.7 christos 198 1.7 christos int 199 1.7 christos hsearch_r(ENTRY item, ACTION action, ENTRY **itemp, struct hsearch_data *head) 200 1.7 christos { 201 1.7 christos struct internal_head *table, *chain; 202 1.1 cgd struct internal_entry *ie; 203 1.1 cgd uint32_t hashval; 204 1.1 cgd size_t len; 205 1.7 christos void *p; 206 1.1 cgd 207 1.3 lukem _DIAGASSERT(item.key != NULL); 208 1.3 lukem _DIAGASSERT(action == ENTER || action == FIND); 209 1.1 cgd 210 1.7 christos p = head->table; 211 1.7 christos table = p; 212 1.7 christos 213 1.1 cgd len = strlen(item.key); 214 1.1 cgd hashval = (*__default_hash)(item.key, len); 215 1.1 cgd 216 1.7 christos chain = &table[hashval & (head->size - 1)]; 217 1.7 christos ie = SLIST_FIRST(chain); 218 1.1 cgd while (ie != NULL) { 219 1.1 cgd if (strcmp(ie->ent.key, item.key) == 0) 220 1.1 cgd break; 221 1.1 cgd ie = SLIST_NEXT(ie, link); 222 1.1 cgd } 223 1.1 cgd 224 1.7 christos if (ie != NULL) { 225 1.7 christos *itemp = &ie->ent; 226 1.7 christos return 1; 227 1.7 christos } else if (action == FIND) { 228 1.7 christos *itemp = NULL; 229 1.7 christos errno = ESRCH; 230 1.7 christos return 1; 231 1.7 christos } 232 1.1 cgd 233 1.1 cgd ie = malloc(sizeof *ie); 234 1.1 cgd if (ie == NULL) 235 1.7 christos return 0; 236 1.1 cgd ie->ent.key = item.key; 237 1.1 cgd ie->ent.data = item.data; 238 1.1 cgd 239 1.7 christos SLIST_INSERT_HEAD(chain, ie, link); 240 1.7 christos *itemp = &ie->ent; 241 1.7 christos head->filled++; 242 1.7 christos return 1; 243 1.1 cgd } 244