dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / sgs / tools / common / string_table.c
blob0a5a395ddfaa4b4a7e43ea4650ff2dabdf8af775
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include <_string_table.h>
28 #include <strings.h>
29 #include <sgs.h>
30 #include <stdio.h>
33 * This file provides the interfaces to build a Str_tbl suitable for use by
34 * either the sgsmsg message system, or a standard ELF string table (SHT_STRTAB)
35 * as created by ld(1).
37 * There are two modes which can be used when constructing a string table:
39 * st_new(0)
40 * standard string table - no compression. This is the
41 * traditional, fast method.
43 * st_new(FLG_STTAB_COMPRESS)
44 * builds a compressed string table which both eliminates
45 * duplicate strings, and permits strings with common suffixes
46 * (atexit vs. exit) to overlap in the table. This provides space
47 * savings for many string tables. Although more work than the
48 * traditional method, the algorithms used are designed to scale
49 * and keep any overhead at a minimum.
51 * These string tables are built with a common interface in a two-pass manner.
52 * The first pass finds all of the strings required for the string-table and
53 * calculates the size required for the final string table.
55 * The second pass allocates the string table, populates the strings into the
56 * table and returns the offsets the strings have been assigned.
58 * The calling sequence to build and populate a string table is:
60 * st_new(); // initialize strtab
62 * st_insert(st1); // first pass of strings ...
63 * // calculates size required for
64 * // string table
66 * st_delstring(st?); // remove string previously
67 * // inserted
68 * st_insert(stN);
70 * st_getstrtab_sz(); // freezes strtab and computes
71 * // size of table.
73 * st_setstrbuf(); // associates a final destination
74 * // for the string table
76 * st_setstring(st1); // populate the string table
77 * ... // offsets are based off of second
78 * // pass through the string table
79 * st_setstring(stN);
81 * st_destroy(); // tear down string table
82 * // structures.
84 * String Suffix Compression Algorithm:
86 * Here's a quick high level overview of the Suffix String
87 * compression algorithm used. First - the heart of the algorithm
88 * is a Hash table list which represents a dictionary of all unique
89 * strings inserted into the string table. The hash function for
90 * this table is a standard string hash except that the hash starts
91 * at the last character in the string (&str[n - 1]) and works towards
92 * the first character in the function (&str[0]). As we compute the
93 * HASH value for a given string, we also compute the hash values
94 * for all of the possible suffix strings for that string.
96 * As we compute the hash - at each character see if the current
97 * suffix string for that hash is already present in the table. If
98 * it is, and the string is a master string. Then change that
99 * string to a suffix string of the new string being inserted.
101 * When the final hash value is found (hash for str[0...n]), check
102 * to see if it is in the hash table - if so increment the reference
103 * count for the string. If it is not yet in the table, insert a
104 * new hash table entry for a master string.
106 * The above method will find all suffixes of a given string given
107 * that the strings are inserted from shortest to longest. That is
108 * why this is a two phase method, we first collect all of the
109 * strings and store them based off of their length in an AVL tree.
110 * Once all of the strings have been submitted we then start the
111 * hash table build by traversing the AVL tree in order and
112 * inserting the strings from shortest to longest as described
113 * above.
116 /* LINTLIBRARY */
118 static int
119 avl_len_compare(const void *n1, const void *n2)
121 size_t len1, len2;
123 len1 = ((LenNode *)n1)->ln_strlen;
124 len2 = ((LenNode *)n2)->ln_strlen;
126 if (len1 == len2)
127 return (0);
128 if (len2 < len1)
129 return (1);
130 return (-1);
133 static int
134 avl_str_compare(const void *n1, const void *n2)
136 const char *str1, *str2;
137 int rc;
139 str1 = ((StrNode *)n1)->sn_str;
140 str2 = ((StrNode *)n2)->sn_str;
142 rc = strcmp(str1, str2);
143 if (rc > 0)
144 return (1);
145 if (rc < 0)
146 return (-1);
147 return (0);
151 * Return an initialized Str_tbl - returns NULL on failure.
153 * flags:
154 * FLG_STTAB_COMPRESS - build a compressed string table
156 Str_tbl *
157 st_new(uint_t flags)
159 Str_tbl *stp;
161 if ((stp = calloc(sizeof (*stp), 1)) == NULL)
162 return (NULL);
165 * Start with a leading '\0' - it's tradition.
167 stp->st_strsize = stp->st_fullstrsize = stp->st_nextoff = 1;
170 * Do we compress this string table?
172 stp->st_flags = flags;
173 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
174 return (stp);
176 if ((stp->st_lentree = calloc(sizeof (*stp->st_lentree), 1)) == NULL)
177 return (NULL);
179 avl_create(stp->st_lentree, &avl_len_compare, sizeof (LenNode),
180 SGSOFFSETOF(LenNode, ln_avlnode));
182 return (stp);
186 * Insert a new string into the Str_tbl. There are two AVL trees used.
188 * - The first LenNode AVL tree maintains a tree of nodes based on string
189 * sizes.
190 * - Each LenNode maintains a StrNode AVL tree for each string. Large
191 * applications have been known to contribute thousands of strings of
192 * the same size. Should strings need to be removed (-z ignore), then
193 * the string AVL tree makes this removal efficient and scalable.
196 st_insert(Str_tbl *stp, const char *str)
198 size_t len;
199 StrNode *snp, sn = { 0 };
200 LenNode *lnp, ln = { 0 };
201 avl_index_t where;
204 * String table can't have been cooked
206 assert((stp->st_flags & FLG_STTAB_COOKED) == 0);
209 * Null strings always point to the head of the string
210 * table - no reason to keep searching.
212 if ((len = strlen(str)) == 0)
213 return (0);
215 stp->st_fullstrsize += len + 1;
216 stp->st_strcnt++;
218 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
219 return (0);
222 * From the controlling string table, determine which LenNode AVL node
223 * provides for this string length. If the node doesn't exist, insert
224 * a new node to represent this string length.
226 ln.ln_strlen = len;
227 if ((lnp = avl_find(stp->st_lentree, &ln, &where)) == NULL) {
228 if ((lnp = calloc(sizeof (*lnp), 1)) == NULL)
229 return (-1);
230 lnp->ln_strlen = len;
231 avl_insert(stp->st_lentree, lnp, where);
233 if ((lnp->ln_strtree = calloc(sizeof (*lnp->ln_strtree), 1)) ==
234 NULL)
235 return (0);
237 avl_create(lnp->ln_strtree, &avl_str_compare, sizeof (StrNode),
238 SGSOFFSETOF(StrNode, sn_avlnode));
242 * From the string length AVL node determine whether a StrNode AVL node
243 * provides this string. If the node doesn't exist, insert a new node
244 * to represent this string.
246 sn.sn_str = str;
247 if ((snp = avl_find(lnp->ln_strtree, &sn, &where)) == NULL) {
248 if ((snp = calloc(sizeof (*snp), 1)) == NULL)
249 return (-1);
250 snp->sn_str = str;
251 avl_insert(lnp->ln_strtree, snp, where);
253 snp->sn_refcnt++;
255 return (0);
259 * Remove a previously inserted string from the Str_tbl.
262 st_delstring(Str_tbl *stp, const char *str)
264 size_t len;
265 LenNode *lnp, ln = { 0 };
266 StrNode *snp, sn = { 0 };
269 * String table can't have been cooked
271 assert((stp->st_flags & FLG_STTAB_COOKED) == 0);
273 len = strlen(str);
274 stp->st_fullstrsize -= len + 1;
276 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0)
277 return (0);
280 * Determine which LenNode AVL node provides for this string length.
282 ln.ln_strlen = len;
283 if ((lnp = avl_find(stp->st_lentree, &ln, 0)) != NULL) {
284 sn.sn_str = str;
285 if ((snp = avl_find(lnp->ln_strtree, &sn, 0)) != NULL) {
287 * Reduce the reference count, and if zero remove the
288 * node.
290 if (--snp->sn_refcnt == 0)
291 avl_remove(lnp->ln_strtree, snp);
292 return (0);
297 * No strings of this length, or no string itself - someone goofed.
299 return (-1);
303 * Tear down a String_Table structure.
305 void
306 st_destroy(Str_tbl *stp)
308 Str_hash *sthash, *psthash;
309 Str_master *mstr, *pmstr;
310 uint_t i;
313 * cleanup the master strings
315 for (mstr = stp->st_mstrlist, pmstr = 0; mstr;
316 mstr = mstr->sm_next) {
317 free(pmstr);
318 pmstr = mstr;
320 free(pmstr);
322 if (stp->st_hashbcks) {
323 for (i = 0; i < stp->st_hbckcnt; i++) {
324 for (sthash = stp->st_hashbcks[i], psthash = 0;
325 sthash; sthash = sthash->hi_next) {
326 free(psthash);
327 psthash = sthash;
329 free(psthash);
331 free(stp->st_hashbcks);
333 free(stp);
338 * For a given string - copy it into the buffer associated with
339 * the string table - and return the offset it has been assigned.
341 * If a value of '-1' is returned - the string was not found in
342 * the Str_tbl.
345 st_setstring(Str_tbl *stp, const char *str, size_t *stoff)
347 size_t stlen;
348 uint_t hashval;
349 Str_hash *sthash;
350 Str_master *mstr;
351 int i;
354 * String table *must* have been previously cooked
356 assert(stp->st_strbuf);
358 assert(stp->st_flags & FLG_STTAB_COOKED);
359 stlen = strlen(str);
361 * Null string always points to head of string table
363 if (stlen == 0) {
364 *stoff = 0;
365 return (0);
368 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
369 size_t _stoff;
371 stlen++; /* count for trailing '\0' */
372 _stoff = stp->st_nextoff;
374 * Have we overflowed our assigned buffer?
376 if ((_stoff + stlen) > stp->st_fullstrsize)
377 return (-1);
378 memcpy(stp->st_strbuf + _stoff, str, stlen);
379 *stoff = _stoff;
380 stp->st_nextoff += stlen;
381 return (0);
385 * Calculate reverse hash for string.
387 hashval = HASHSEED;
388 for (i = stlen; i >= 0; i--) {
389 hashval = ((hashval << 5) + hashval) +
390 str[i]; /* h = ((h * 33) + c) */
393 for (sthash = stp->st_hashbcks[hashval % stp->st_hbckcnt]; sthash;
394 sthash = sthash->hi_next) {
395 const char *hstr;
397 if (sthash->hi_hashval != hashval)
398 continue;
400 hstr = &sthash->hi_mstr->sm_str[sthash->hi_mstr->sm_strlen -
401 sthash->hi_strlen];
402 if (strcmp(str, hstr) == 0)
403 break;
407 * Did we find the string?
409 if (sthash == 0)
410 return (-1);
413 * Has this string been copied into the string table?
415 mstr = sthash->hi_mstr;
416 if (mstr->sm_stroff == 0) {
417 size_t mstrlen = mstr->sm_strlen + 1;
419 mstr->sm_stroff = stp->st_nextoff;
422 * Have we overflowed our assigned buffer?
424 if ((mstr->sm_stroff + mstrlen) > stp->st_fullstrsize)
425 return (-1);
427 (void) memcpy(stp->st_strbuf + mstr->sm_stroff,
428 mstr->sm_str, mstrlen);
429 stp->st_nextoff += mstrlen;
433 * Calculate offset of (sub)string.
435 *stoff = mstr->sm_stroff + mstr->sm_strlen - sthash->hi_strlen;
437 return (0);
441 static int
442 st_hash_insert(Str_tbl *stp, const char *str, size_t len)
444 int i;
445 uint_t hashval = HASHSEED;
446 uint_t bckcnt = stp->st_hbckcnt;
447 Str_hash **hashbcks = stp->st_hashbcks;
448 Str_hash *sthash;
449 Str_master *mstr = 0;
452 * We use a classic 'Bernstein k=33' hash function. But
453 * instead of hashing from the start of the string to the
454 * end, we do it in reverse.
456 * This way - we are essentially building all of the
457 * suffix hashvalues as we go. We can check to see if
458 * any suffixes already exist in the tree as we generate
459 * the hash.
461 for (i = len; i >= 0; i--) {
462 hashval = ((hashval << 5) + hashval) +
463 str[i]; /* h = ((h * 33) + c) */
465 for (sthash = hashbcks[hashval % bckcnt];
466 sthash; sthash = sthash->hi_next) {
467 const char *hstr;
468 Str_master *_mstr;
470 if (sthash->hi_hashval != hashval)
471 continue;
473 _mstr = sthash->hi_mstr;
474 hstr = &_mstr->sm_str[_mstr->sm_strlen -
475 sthash->hi_strlen];
477 if (strcmp(&str[i], hstr))
478 continue;
480 if (i == 0) {
482 * Entry already in table, increment refcnt and
483 * get out.
485 sthash->hi_refcnt++;
486 return (0);
487 } else {
489 * If this 'suffix' is presently a 'master
490 * string, then take over it's record.
492 if (sthash->hi_strlen == _mstr->sm_strlen) {
494 * we should only do this once.
496 assert(mstr == 0);
497 mstr = _mstr;
504 * Do we need a new master string, or can we take over
505 * one we already found in the table?
507 if (mstr == 0) {
509 * allocate a new master string
511 if ((mstr = calloc(sizeof (*mstr), 1)) == 0)
512 return (-1);
513 mstr->sm_next = stp->st_mstrlist;
514 stp->st_mstrlist = mstr;
515 stp->st_strsize += len + 1;
516 } else {
518 * We are taking over a existing master string, the string size
519 * only increments by the difference between the current string
520 * and the previous master.
522 assert(len > mstr->sm_strlen);
523 stp->st_strsize += len - mstr->sm_strlen;
526 if ((sthash = calloc(sizeof (*sthash), 1)) == 0)
527 return (-1);
529 mstr->sm_hashval = sthash->hi_hashval = hashval;
530 mstr->sm_strlen = sthash->hi_strlen = len;
531 mstr->sm_str = str;
532 sthash->hi_refcnt = 1;
533 sthash->hi_mstr = mstr;
536 * Insert string element into head of hash list
538 hashval = hashval % bckcnt;
539 sthash->hi_next = hashbcks[hashval];
540 hashbcks[hashval] = sthash;
541 return (0);
545 * Return amount of space required for the string table.
547 size_t
548 st_getstrtab_sz(Str_tbl *stp)
550 assert(stp->st_fullstrsize > 0);
552 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
553 stp->st_flags |= FLG_STTAB_COOKED;
554 return (stp->st_fullstrsize);
557 if ((stp->st_flags & FLG_STTAB_COOKED) == 0) {
558 LenNode *lnp;
559 void *cookie;
561 stp->st_flags |= FLG_STTAB_COOKED;
563 * allocate a hash table about the size of # of
564 * strings input.
566 stp->st_hbckcnt = findprime(stp->st_strcnt);
567 if ((stp->st_hashbcks = calloc(sizeof (*stp->st_hashbcks),
568 stp->st_hbckcnt)) == NULL)
569 return (0);
572 * We now walk all of the strings in the list, from shortest to
573 * longest, and insert them into the hashtable.
575 if ((lnp = avl_first(stp->st_lentree)) == NULL) {
577 * Is it possible we have an empty string table, if so,
578 * the table still contains '\0', so return the size.
580 if (avl_numnodes(stp->st_lentree) == 0) {
581 assert(stp->st_strsize == 1);
582 return (stp->st_strsize);
584 return (0);
587 while (lnp) {
588 StrNode *snp;
591 * Walk the string lists and insert them into the hash
592 * list. Once a string is inserted we no longer need
593 * it's entry, so the string can be freed.
595 for (snp = avl_first(lnp->ln_strtree); snp;
596 snp = AVL_NEXT(lnp->ln_strtree, snp)) {
597 if (st_hash_insert(stp, snp->sn_str,
598 lnp->ln_strlen) == -1)
599 return (0);
603 * Now that the strings have been copied, walk the
604 * StrNode tree and free all the AVL nodes. Note,
605 * avl_destroy_nodes() beats avl_remove() as the
606 * latter balances the nodes as they are removed.
607 * We just want to tear the whole thing down fast.
609 cookie = NULL;
610 while ((snp = avl_destroy_nodes(lnp->ln_strtree,
611 &cookie)) != NULL)
612 free(snp);
613 avl_destroy(lnp->ln_strtree);
614 free(lnp->ln_strtree);
615 lnp->ln_strtree = NULL;
618 * Move on to the next LenNode.
620 lnp = AVL_NEXT(stp->st_lentree, lnp);
624 * Now that all of the strings have been freed, walk the
625 * LenNode tree and free all of the AVL nodes. Note,
626 * avl_destroy_nodes() beats avl_remove() as the latter
627 * balances the nodes as they are removed. We just want to
628 * tear the whole thing down fast.
630 cookie = NULL;
631 while ((lnp = avl_destroy_nodes(stp->st_lentree,
632 &cookie)) != NULL)
633 free(lnp);
634 avl_destroy(stp->st_lentree);
635 free(stp->st_lentree);
636 stp->st_lentree = 0;
639 assert(stp->st_strsize > 0);
640 assert(stp->st_fullstrsize >= stp->st_strsize);
642 return (stp->st_strsize);
646 * Associate a buffer with a string table.
648 const char *
649 st_getstrbuf(Str_tbl *stp)
651 return (stp->st_strbuf);
655 st_setstrbuf(Str_tbl *stp, char *stbuf, size_t bufsize)
657 assert(stp->st_flags & FLG_STTAB_COOKED);
659 if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
660 if (bufsize < stp->st_fullstrsize)
661 return (-1);
662 } else {
663 if (bufsize < stp->st_strsize)
664 return (-1);
667 stp->st_strbuf = stbuf;
668 #ifdef DEBUG
670 * for debug builds - start with a stringtable filled in
671 * with '0xff'. This makes it very easy to spot unfilled
672 * holes in the strtab.
674 memset(stbuf, 0xff, bufsize);
675 stbuf[0] = '\0';
676 #else
677 memset(stbuf, 0x0, bufsize);
678 #endif
679 return (0);