Annotation of mandoc/compat_ohash.c, Revision 1.1
1.1 ! kristaps 1: #ifdef HAVE_CONFIG_H
! 2: #include "config.h"
! 3: #endif
! 4:
! 5: #ifdef HAVE_OHASH
! 6:
! 7: int dummy;
! 8:
! 9: #else
! 10:
! 11: /* $OpenBSD: ohash_int.h,v 1.3 2006/01/16 15:52:25 espie Exp $ */
! 12:
! 13: #include <stddef.h>
! 14: #include <stdint.h>
! 15: #include <stdlib.h>
! 16: #include <string.h>
! 17: #include "compat_ohash.h"
! 18:
! 19: struct _ohash_record {
! 20: u_int32_t hv;
! 21: const char *p;
! 22: };
! 23:
! 24: #define DELETED ((const char *)h)
! 25: #define NONE (h->size)
! 26:
! 27: /* Don't bother changing the hash table if the change is small enough. */
! 28: #define MINSIZE (1UL << 4)
! 29: #define MINDELETED 4
! 30: /* $OpenBSD: ohash_create_entry.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */
! 31: /* ex:ts=8 sw=4:
! 32: */
! 33:
! 34: /* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org>
! 35: *
! 36: * Permission to use, copy, modify, and distribute this software for any
! 37: * purpose with or without fee is hereby granted, provided that the above
! 38: * copyright notice and this permission notice appear in all copies.
! 39: *
! 40: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 41: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 42: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 43: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 44: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 45: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 46: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 47: */
! 48:
! 49: /* This handles the common case of variable length keys, where the
! 50: * key is stored at the end of the record.
! 51: */
! 52: void *
! 53: ohash_create_entry(struct ohash_info *i, const char *start, const char **end)
! 54: {
! 55: char *p;
! 56:
! 57: if (!*end)
! 58: *end = start + strlen(start);
! 59: p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data);
! 60: if (p) {
! 61: memcpy(p+i->key_offset, start, *end-start);
! 62: p[i->key_offset + (*end - start)] = '\0';
! 63: }
! 64: return (void *)p;
! 65: }
! 66:
! 67: /* hash_delete only frees the hash structure. Use hash_first/hash_next
! 68: * to free entries as well. */
! 69: void
! 70: ohash_delete(struct ohash *h)
! 71: {
! 72: (h->info.hfree)(h->t, sizeof(struct _ohash_record) * h->size,
! 73: h->info.data);
! 74: #ifndef NDEBUG
! 75: h->t = NULL;
! 76: #endif
! 77: }
! 78:
! 79: static void ohash_resize(struct ohash *);
! 80:
! 81: static void
! 82: ohash_resize(struct ohash *h)
! 83: {
! 84: struct _ohash_record *n;
! 85: unsigned int ns, j;
! 86: unsigned int i, incr;
! 87:
! 88: if (4 * h->deleted < h->total)
! 89: ns = h->size << 1;
! 90: else if (3 * h->deleted > 2 * h->total)
! 91: ns = h->size >> 1;
! 92: else
! 93: ns = h->size;
! 94: if (ns < MINSIZE)
! 95: ns = MINSIZE;
! 96: #ifdef STATS_HASH
! 97: STAT_HASH_EXPAND++;
! 98: STAT_HASH_SIZE += ns - h->size;
! 99: #endif
! 100: n = (h->info.halloc)(sizeof(struct _ohash_record) * ns, h->info.data);
! 101: if (!n)
! 102: return;
! 103:
! 104: for (j = 0; j < h->size; j++) {
! 105: if (h->t[j].p != NULL && h->t[j].p != DELETED) {
! 106: i = h->t[j].hv % ns;
! 107: incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1;
! 108: while (n[i].p != NULL) {
! 109: i += incr;
! 110: if (i >= ns)
! 111: i -= ns;
! 112: }
! 113: n[i].hv = h->t[j].hv;
! 114: n[i].p = h->t[j].p;
! 115: }
! 116: }
! 117: (h->info.hfree)(h->t, sizeof(struct _ohash_record) * h->size,
! 118: h->info.data);
! 119: h->t = n;
! 120: h->size = ns;
! 121: h->total -= h->deleted;
! 122: h->deleted = 0;
! 123: }
! 124:
! 125: void *
! 126: ohash_remove(struct ohash *h, unsigned int i)
! 127: {
! 128: void *result = (void *)h->t[i].p;
! 129:
! 130: if (result == NULL || result == DELETED)
! 131: return NULL;
! 132:
! 133: #ifdef STATS_HASH
! 134: STAT_HASH_ENTRIES--;
! 135: #endif
! 136: h->t[i].p = DELETED;
! 137: h->deleted++;
! 138: if (h->deleted >= MINDELETED && 4 * h->deleted > h->total)
! 139: ohash_resize(h);
! 140: return result;
! 141: }
! 142:
! 143: void *
! 144: ohash_find(struct ohash *h, unsigned int i)
! 145: {
! 146: if (h->t[i].p == DELETED)
! 147: return NULL;
! 148: else
! 149: return (void *)h->t[i].p;
! 150: }
! 151:
! 152: void *
! 153: ohash_insert(struct ohash *h, unsigned int i, void *p)
! 154: {
! 155: #ifdef STATS_HASH
! 156: STAT_HASH_ENTRIES++;
! 157: #endif
! 158: if (h->t[i].p == DELETED) {
! 159: h->deleted--;
! 160: h->t[i].p = p;
! 161: } else {
! 162: h->t[i].p = p;
! 163: /* Arbitrary resize boundary. Tweak if not efficient enough. */
! 164: if (++h->total * 4 > h->size * 3)
! 165: ohash_resize(h);
! 166: }
! 167: return p;
! 168: }
! 169:
! 170: unsigned int
! 171: ohash_entries(struct ohash *h)
! 172: {
! 173: return h->total - h->deleted;
! 174: }
! 175:
! 176: void *
! 177: ohash_first(struct ohash *h, unsigned int *pos)
! 178: {
! 179: *pos = 0;
! 180: return ohash_next(h, pos);
! 181: }
! 182:
! 183: void *
! 184: ohash_next(struct ohash *h, unsigned int *pos)
! 185: {
! 186: for (; *pos < h->size; (*pos)++)
! 187: if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL)
! 188: return (void *)h->t[(*pos)++].p;
! 189: return NULL;
! 190: }
! 191:
! 192: void
! 193: ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info)
! 194: {
! 195: h->size = 1UL << size;
! 196: if (h->size < MINSIZE)
! 197: h->size = MINSIZE;
! 198: #ifdef STATS_HASH
! 199: STAT_HASH_CREATION++;
! 200: STAT_HASH_SIZE += h->size;
! 201: #endif
! 202: /* Copy info so that caller may free it. */
! 203: h->info.key_offset = info->key_offset;
! 204: h->info.halloc = info->halloc;
! 205: h->info.hfree = info->hfree;
! 206: h->info.alloc = info->alloc;
! 207: h->info.data = info->data;
! 208: h->t = (h->info.halloc)(sizeof(struct _ohash_record) * h->size,
! 209: h->info.data);
! 210: h->total = h->deleted = 0;
! 211: }
! 212:
! 213: uint32_t
! 214: ohash_interval(const char *s, const char **e)
! 215: {
! 216: uint32_t k;
! 217:
! 218: if (!*e)
! 219: *e = s + strlen(s);
! 220: if (s == *e)
! 221: k = 0;
! 222: else
! 223: k = *s++;
! 224: while (s != *e)
! 225: k = ((k << 2) | (k >> 30)) ^ *s++;
! 226: return k;
! 227: }
! 228:
! 229: unsigned int
! 230: ohash_lookup_interval(struct ohash *h, const char *start, const char *end,
! 231: uint32_t hv)
! 232: {
! 233: unsigned int i, incr;
! 234: unsigned int empty;
! 235:
! 236: #ifdef STATS_HASH
! 237: STAT_HASH_LOOKUP++;
! 238: #endif
! 239: empty = NONE;
! 240: i = hv % h->size;
! 241: incr = ((hv % (h->size-2)) & ~1) + 1;
! 242: while (h->t[i].p != NULL) {
! 243: #ifdef STATS_HASH
! 244: STAT_HASH_LENGTH++;
! 245: #endif
! 246: if (h->t[i].p == DELETED) {
! 247: if (empty == NONE)
! 248: empty = i;
! 249: } else if (h->t[i].hv == hv &&
! 250: strncmp(h->t[i].p+h->info.key_offset, start,
! 251: end - start) == 0 &&
! 252: (h->t[i].p+h->info.key_offset)[end-start] == '\0') {
! 253: if (empty != NONE) {
! 254: h->t[empty].hv = hv;
! 255: h->t[empty].p = h->t[i].p;
! 256: h->t[i].p = DELETED;
! 257: return empty;
! 258: } else {
! 259: #ifdef STATS_HASH
! 260: STAT_HASH_POSITIVE++;
! 261: #endif
! 262: return i;
! 263: }
! 264: }
! 265: i += incr;
! 266: if (i >= h->size)
! 267: i -= h->size;
! 268: }
! 269:
! 270: /* Found an empty position. */
! 271: if (empty != NONE)
! 272: i = empty;
! 273: h->t[i].hv = hv;
! 274: return i;
! 275: }
! 276:
! 277: unsigned int
! 278: ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv)
! 279: {
! 280: unsigned int i, incr;
! 281: unsigned int empty;
! 282:
! 283: #ifdef STATS_HASH
! 284: STAT_HASH_LOOKUP++;
! 285: #endif
! 286: empty = NONE;
! 287: i = hv % h->size;
! 288: incr = ((hv % (h->size-2)) & ~1) + 1;
! 289: while (h->t[i].p != NULL) {
! 290: #ifdef STATS_HASH
! 291: STAT_HASH_LENGTH++;
! 292: #endif
! 293: if (h->t[i].p == DELETED) {
! 294: if (empty == NONE)
! 295: empty = i;
! 296: } else if (h->t[i].hv == hv &&
! 297: memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) {
! 298: if (empty != NONE) {
! 299: h->t[empty].hv = hv;
! 300: h->t[empty].p = h->t[i].p;
! 301: h->t[i].p = DELETED;
! 302: return empty;
! 303: } else {
! 304: #ifdef STATS_HASH
! 305: STAT_HASH_POSITIVE++;
! 306: #endif
! 307: } return i;
! 308: }
! 309: i += incr;
! 310: if (i >= h->size)
! 311: i -= h->size;
! 312: }
! 313:
! 314: /* Found an empty position. */
! 315: if (empty != NONE)
! 316: i = empty;
! 317: h->t[i].hv = hv;
! 318: return i;
! 319: }
! 320:
! 321: unsigned int
! 322: ohash_qlookup(struct ohash *h, const char *s)
! 323: {
! 324: const char *e = NULL;
! 325: return ohash_qlookupi(h, s, &e);
! 326: }
! 327:
! 328: unsigned int
! 329: ohash_qlookupi(struct ohash *h, const char *s, const char **e)
! 330: {
! 331: u_int32_t hv;
! 332:
! 333: hv = ohash_interval(s, e);
! 334: return ohash_lookup_interval(h, s, *e, hv);
! 335: }
! 336:
! 337: #endif /*!HAVE_OHASH*/
CVSweb