/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * stable.c -- string table module * * simple string table module. all read-only strings are entered in * this table, allowing us to compare pointers rather than characters * to see if two strings are equal. * */ #include #include #include "alloc.h" #include "out.h" #include "stats.h" #include "stable.h" #define MINPTR_ALIGN sizeof (char *) /* alignment boundary for pointers */ #define DEF_HASH_SIZE 11113 /* default hash table size */ #define CHUNK_SIZE 8192 /* grab more memory with this chunk size */ static char **Stable; /* the hash table */ static unsigned Stablesz; static char *Stableblock; static char *Stablenext; static struct stats *Stablecount; static struct stats *Blockcount; static struct stats *Add0; static struct stats *Add1; static struct stats *Add2; static struct stats *Add3; static struct stats *Addn; struct chunklst { struct chunklst *next; char *chunkp; }; struct chunklst *Stablechunks; /* * stable_init -- initialize the stable module * * hash table is sized according to sz. sz of zero means pick * reasonable default size. */ void stable_init(unsigned sz) { /* allocate hash table */ if (sz == 0) Stablesz = DEF_HASH_SIZE; else Stablesz = sz; Stable = MALLOC(Stablesz * sizeof (*Stable)); bzero((void *)Stable, Stablesz * sizeof (*Stable)); Stablecount = stats_new_counter("stable.size", "hash table size", 1); Blockcount = stats_new_counter("stable.blocks", "blocks allocated", 1); Add0 = stats_new_counter("stable.add0", "adds to empty buckets", 1); Add1 = stats_new_counter("stable.add1", "adds to 1-entry buckets", 1); Add2 = stats_new_counter("stable.add2", "adds to 2-entry buckets", 1); Add3 = stats_new_counter("stable.add3", "adds to 3-entry buckets", 1); Addn = stats_new_counter("stable.addn", "adds to n-entry buckets", 1); stats_counter_add(Stablecount, Stablesz); } void stable_fini(void) { struct chunklst *cp, *nc; stats_delete(Stablecount); stats_delete(Blockcount); stats_delete(Add0); stats_delete(Add1); stats_delete(Add2); stats_delete(Add3); stats_delete(Addn); FREE(Stable); cp = Stablechunks; nc = NULL; while (cp != NULL) { nc = cp->next; FREE(cp->chunkp); FREE(cp); cp = nc; } Stablechunks = NULL; } static char * stable_newchunk(void) { struct chunklst *save = Stablechunks; char *n; n = MALLOC(CHUNK_SIZE); bzero((void *)n, CHUNK_SIZE); stats_counter_bump(Blockcount); Stablechunks = MALLOC(sizeof (struct chunklst)); Stablechunks->next = save; Stablechunks->chunkp = n; return (n); } /* * stable -- create/lookup a string table entry */ const char * stable(const char *s) { unsigned slen = 0; unsigned hash = DEF_HASH_SIZE ^ ((unsigned)*s << 2); char **ptrp; char *ptr; char *eptr; const char *sptr; int collisions = 0; if (Stablesz == 0) out(O_DIE, "internal error: Stablesz not set"); for (sptr = &s[1]; *sptr; sptr++) { slen++; hash ^= (((unsigned)*sptr) << (slen % 3)) + ((unsigned)*(sptr - 1) << ((slen % 3 + 7))); } hash ^= slen; if (slen > CHUNK_SIZE - sizeof (char *) - 1 - 4) out(O_DIE, "too big for string table %.20s...", s); hash %= Stablesz; ptrp = &Stable[hash]; ptr = *ptrp; while (ptr) { /* hash brought us to something, see if it is the string */ sptr = s; eptr = ptr; while (*sptr && *eptr && *sptr++ == *eptr++) ; if (*sptr == '\0' && *eptr == '\0') return (ptr); /* found it */ /* strings didn't match, advance eptr to end of string */ while (*eptr) eptr++; eptr++; /* move past '\0' */ while ((uintptr_t)eptr % MINPTR_ALIGN) eptr++; /* pull in next pointer in bucket */ ptrp = (char **)(void *)eptr; ptr = *ptrp; collisions++; } /* string wasn't in table, add it and point ptr to it */ if (Stablenext == NULL || (&Stableblock[CHUNK_SIZE] - Stablenext) < (slen + sizeof (char *) + MINPTR_ALIGN + 4)) { /* need more room */ Stablenext = Stableblock = stable_newchunk(); } /* current chunk has room in it */ ptr = *ptrp = Stablenext; sptr = s; while (*Stablenext++ = *sptr++) ; while ((uintptr_t)Stablenext % MINPTR_ALIGN) Stablenext++; ptrp = (char **)(void *)Stablenext; Stablenext += sizeof (char *); *ptrp = NULL; /* just did an add, update stats */ if (collisions == 0) stats_counter_bump(Add0); else if (collisions == 1) stats_counter_bump(Add1); else if (collisions == 2) stats_counter_bump(Add2); else if (collisions == 3) stats_counter_bump(Add3); else stats_counter_bump(Addn); return (ptr); }