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 (c) 2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/types.h>
30 #include <sys/sysmacros.h>
31 #include <strings.h>
32 #include <stdlib.h>
33 #include <stdio.h>
34 
35 #include "strtab.h"
36 #include "memory.h"
37 
38 #define	STRTAB_HASHSZ	211		/* use a prime number of hash buckets */
39 #define	STRTAB_BUFSZ	(64 * 1024)	/* use 64K data buffers by default */
40 
41 static void
42 strtab_grow(strtab_t *sp)
43 {
44 	sp->str_nbufs++;
45 	sp->str_bufs = xrealloc(sp->str_bufs, sp->str_nbufs * sizeof (char *));
46 	sp->str_ptr = xmalloc(sp->str_bufsz);
47 	sp->str_bufs[sp->str_nbufs - 1] = sp->str_ptr;
48 }
49 
50 void
51 strtab_create(strtab_t *sp)
52 {
53 	sp->str_hash = xcalloc(STRTAB_HASHSZ * sizeof (strhash_t *));
54 	sp->str_hashsz = STRTAB_HASHSZ;
55 	sp->str_bufs = NULL;
56 	sp->str_ptr = NULL;
57 	sp->str_nbufs = 0;
58 	sp->str_bufsz = STRTAB_BUFSZ;
59 	sp->str_nstrs = 1;
60 	sp->str_size = 1;
61 
62 	strtab_grow(sp);
63 	*sp->str_ptr++ = '\0';
64 }
65 
66 void
67 strtab_destroy(strtab_t *sp)
68 {
69 	strhash_t *hp, *hq;
70 	ulong_t i;
71 
72 	for (i = 0; i < sp->str_hashsz; i++) {
73 		for (hp = sp->str_hash[i]; hp != NULL; hp = hq) {
74 			hq = hp->str_next;
75 			free(hp);
76 		}
77 	}
78 
79 	for (i = 0; i < sp->str_nbufs; i++)
80 		free(sp->str_bufs[i]);
81 
82 	free(sp->str_hash);
83 	free(sp->str_bufs);
84 }
85 
86 static ulong_t
87 strtab_hash(const char *key, size_t *len)
88 {
89 	ulong_t g, h = 0;
90 	const char *p;
91 	size_t n = 0;
92 
93 	for (p = key; *p != '\0'; p++, n++) {
94 		h = (h << 4) + *p;
95 
96 		if ((g = (h & 0xf0000000)) != 0) {
97 			h ^= (g >> 24);
98 			h ^= g;
99 		}
100 	}
101 
102 	*len = n;
103 	return (h);
104 }
105 
106 static int
107 strtab_compare(strtab_t *sp, strhash_t *hp, const char *str, size_t len)
108 {
109 	ulong_t b = hp->str_buf;
110 	const char *buf = hp->str_data;
111 	size_t resid, n;
112 	int rv;
113 
114 	while (len != 0) {
115 		if (buf == sp->str_bufs[b] + sp->str_bufsz)
116 			buf = sp->str_bufs[++b];
117 
118 		resid = sp->str_bufs[b] + sp->str_bufsz - buf;
119 		n = MIN(resid, len);
120 
121 		if ((rv = strncmp(buf, str, n)) != 0)
122 			return (rv);
123 
124 		buf += n;
125 		str += n;
126 		len -= n;
127 	}
128 
129 	return (0);
130 }
131 
132 static void
133 strtab_copyin(strtab_t *sp, const char *str, size_t len)
134 {
135 	ulong_t b = sp->str_nbufs - 1;
136 	size_t resid, n;
137 
138 	while (len != 0) {
139 		if (sp->str_ptr == sp->str_bufs[b] + sp->str_bufsz) {
140 			strtab_grow(sp);
141 			b++;
142 		}
143 
144 		resid = sp->str_bufs[b] + sp->str_bufsz - sp->str_ptr;
145 		n = MIN(resid, len);
146 		bcopy(str, sp->str_ptr, n);
147 
148 		sp->str_ptr += n;
149 		str += n;
150 		len -= n;
151 	}
152 }
153 
154 size_t
155 strtab_insert(strtab_t *sp, const char *str)
156 {
157 	strhash_t *hp;
158 	size_t len;
159 	ulong_t h;
160 
161 	if (str == NULL || str[0] == '\0')
162 		return (0); /* we keep a \0 at offset 0 to simplify things */
163 
164 	h = strtab_hash(str, &len) % sp->str_hashsz;
165 
166 	/*
167 	 * If the string is already in our hash table, just return the offset
168 	 * of the existing string element and do not add a duplicate string.
169 	 */
170 	for (hp = sp->str_hash[h]; hp != NULL; hp = hp->str_next) {
171 		if (strtab_compare(sp, hp, str, len + 1) == 0)
172 			return (hp->str_off);
173 	}
174 
175 	/*
176 	 * Create a new hash bucket, initialize it, and insert it at the front
177 	 * of the hash chain for the appropriate bucket.
178 	 */
179 	hp = xmalloc(sizeof (strhash_t));
180 
181 	hp->str_data = sp->str_ptr;
182 	hp->str_buf = sp->str_nbufs - 1;
183 	hp->str_off = sp->str_size;
184 	hp->str_len = len;
185 	hp->str_next = sp->str_hash[h];
186 
187 	sp->str_hash[h] = hp;
188 
189 	/*
190 	 * Now copy the string data into our buffer list, and then update
191 	 * the global counts of strings and bytes.  Return str's byte offset.
192 	 */
193 	strtab_copyin(sp, str, len + 1);
194 	sp->str_nstrs++;
195 	sp->str_size += len + 1;
196 
197 	return (hp->str_off);
198 }
199 
200 size_t
201 strtab_size(const strtab_t *sp)
202 {
203 	return (sp->str_size);
204 }
205 
206 ssize_t
207 strtab_write(const strtab_t *sp,
208     ssize_t (*func)(void *, size_t, void *), void *priv)
209 {
210 	ssize_t res, total = 0;
211 	ulong_t i;
212 	size_t n;
213 
214 	for (i = 0; i < sp->str_nbufs; i++, total += res) {
215 		if (i == sp->str_nbufs - 1)
216 			n = sp->str_ptr - sp->str_bufs[i];
217 		else
218 			n = sp->str_bufsz;
219 
220 		if ((res = func(sp->str_bufs[i], n, priv)) <= 0)
221 			break;
222 	}
223 
224 	if (total == 0 && sp->str_size != 0)
225 		return (-1);
226 
227 	return (total);
228 }
229 
230 void
231 strtab_print(const strtab_t *sp)
232 {
233 	const strhash_t *hp;
234 	ulong_t i;
235 
236 	for (i = 0; i < sp->str_hashsz; i++) {
237 		for (hp = sp->str_hash[i]; hp != NULL; hp = hp->str_next) {
238 			const char *buf = hp->str_data;
239 			ulong_t b = hp->str_buf;
240 			size_t resid, len, n;
241 
242 			(void) printf("[%lu] %lu \"", (ulong_t)hp->str_off, b);
243 
244 			for (len = hp->str_len; len != 0; len -= n) {
245 				if (buf == sp->str_bufs[b] + sp->str_bufsz)
246 					buf = sp->str_bufs[++b];
247 
248 				resid = sp->str_bufs[b] + sp->str_bufsz - buf;
249 				n = MIN(resid, len);
250 
251 				(void) printf("%.*s", (int)n, buf);
252 				buf += n;
253 			}
254 
255 			(void) printf("\"\n");
256 		}
257 	}
258 }
259