1 /*
2  * This file and its contents are supplied under the terms of the
3  * Common Development and Distribution License ("CDDL"), version 1.0.
4  * You may only use this file in accordance with the terms of version
5  * 1.0 of the CDDL.
6  *
7  * A full copy of the text of the CDDL should have accompanied this
8  * source.  A copy of the CDDL is also available via the Internet at
9  * http://www.illumos.org/license/CDDL.
10  */
11 
12 /*
13  * Copyright 2017 Jason King
14  */
15 #include <string.h>
16 #include "str.h"
17 #include "demangle_int.h"
18 
19 #define	STR_CHUNK_SZ	(64U)
20 
21 /* are we storing a reference vs. a dynamically allocated copy? */
22 #define	IS_REF(s) ((s)->str_s != NULL && (s)->str_size == 0)
23 
24 /*
25  * Dynamically resizeable strings, with lazy allocation when initialized
26  * with a constant string value
27  *
28  * NOTE: these are not necessairly 0-terminated
29  *
30  * Additionally, these can store references instead of copies of strings
31  * (as indicated by the IS_REF() macro.  However mutation may cause a
32  * string to convert from a refence to a dynamically allocated copy.
33  */
34 
35 void
str_init(str_t * restrict s,sysdem_ops_t * restrict ops)36 str_init(str_t *restrict s, sysdem_ops_t *restrict ops)
37 {
38 	(void) memset(s, 0, sizeof (*s));
39 	s->str_ops = (ops != NULL) ? ops : sysdem_ops_default;
40 }
41 
42 void
str_fini(str_t * s)43 str_fini(str_t *s)
44 {
45 	if (s == NULL)
46 		return;
47 	if (!IS_REF(s))
48 		xfree(s->str_ops, s->str_s, s->str_size);
49 	(void) memset(s, 0, sizeof (*s));
50 }
51 
52 size_t
str_length(const str_t * s)53 str_length(const str_t *s)
54 {
55 	return (s->str_len);
56 }
57 
58 /*
59  * store as a reference instead of a copy
60  * if len == 0, means store entire copy of 0 terminated string
61  */
62 void
str_set(str_t * s,const char * cstr,size_t len)63 str_set(str_t *s, const char *cstr, size_t len)
64 {
65 	sysdem_ops_t *ops = s->str_ops;
66 
67 	str_fini(s);
68 	s->str_ops = ops;
69 	s->str_s = (char *)cstr;
70 	s->str_len = (len == 0 && cstr != NULL) ? strlen(cstr) : len;
71 }
72 
73 boolean_t
str_copy(const str_t * src,str_t * dest)74 str_copy(const str_t *src, str_t *dest)
75 {
76 	str_fini(dest);
77 	str_init(dest, src->str_ops);
78 
79 	if (src->str_len == 0)
80 		return (B_TRUE);
81 
82 	size_t len = roundup(src->str_len, STR_CHUNK_SZ);
83 	dest->str_s = zalloc(src->str_ops, len);
84 	if (dest->str_s == NULL)
85 		return (B_FALSE);
86 
87 	(void) memcpy(dest->str_s, src->str_s, src->str_len);
88 	dest->str_len = src->str_len;
89 	dest->str_size = len;
90 
91 	return (B_TRUE);
92 }
93 
94 /*
95  * ensure s has at least amt bytes free, resizing if necessary
96  */
97 static boolean_t
str_reserve(str_t * s,size_t amt)98 str_reserve(str_t *s, size_t amt)
99 {
100 	size_t newlen = s->str_len + amt;
101 
102 	/* overflow check */
103 	if (newlen < s->str_len || newlen < amt)
104 		return (B_FALSE);
105 
106 	if ((amt > 0) && (s->str_len + amt <= s->str_size))
107 		return (B_TRUE);
108 
109 	size_t newsize = roundup(newlen, STR_CHUNK_SZ);
110 	void *temp;
111 
112 	if (IS_REF(s)) {
113 		temp = zalloc(s->str_ops, newsize);
114 		if (temp == NULL)
115 			return (B_FALSE);
116 
117 		(void) memcpy(temp, s->str_s, s->str_len);
118 	} else {
119 		temp = xrealloc(s->str_ops, s->str_s, s->str_size, newsize);
120 		if (temp == NULL)
121 			return (B_FALSE);
122 	}
123 
124 	s->str_s = temp;
125 	s->str_size = newsize;
126 
127 	return (B_TRUE);
128 }
129 
130 /* append to s, cstrlen == 0 means entire length of string */
131 boolean_t
str_append(str_t * s,const char * cstr,size_t cstrlen)132 str_append(str_t *s, const char *cstr, size_t cstrlen)
133 {
134 	if (cstr != NULL && cstrlen == 0)
135 		cstrlen = strlen(cstr);
136 
137 	const str_t src = {
138 		.str_s = (char *)cstr,
139 		.str_len = cstrlen,
140 		.str_ops = s->str_ops
141 	};
142 
143 	return (str_append_str(s, &src));
144 }
145 
146 boolean_t
str_append_str(str_t * dest,const str_t * src)147 str_append_str(str_t *dest, const str_t *src)
148 {
149 	/* empty string is a noop */
150 	if (src->str_s == NULL || src->str_len == 0)
151 		return (B_TRUE);
152 
153 	/* if src is a reference, we can just copy that */
154 	if (dest->str_s == NULL && IS_REF(src)) {
155 		*dest = *src;
156 		return (B_TRUE);
157 	}
158 
159 	if (!str_reserve(dest, src->str_len))
160 		return (B_FALSE);
161 
162 	(void) memcpy(dest->str_s + dest->str_len, src->str_s, src->str_len);
163 	dest->str_len += src->str_len;
164 	return (B_TRUE);
165 }
166 
167 boolean_t
str_append_c(str_t * s,char c)168 str_append_c(str_t *s, char c)
169 {
170 	if (!str_reserve(s, 1))
171 		return (B_FALSE);
172 
173 	s->str_s[s->str_len++] = c;
174 	return (B_TRUE);
175 }
176 
177 boolean_t
str_insert(str_t * s,size_t idx,const char * cstr,size_t cstrlen)178 str_insert(str_t *s, size_t idx, const char *cstr, size_t cstrlen)
179 {
180 	if (cstr == NULL)
181 		return (B_TRUE);
182 
183 	if (cstrlen == 0)
184 		cstrlen = strlen(cstr);
185 
186 	str_t src = {
187 		.str_s = (char *)cstr,
188 		.str_len = cstrlen,
189 		.str_ops = s->str_ops,
190 		.str_size = 0
191 	};
192 
193 	return (str_insert_str(s, idx, &src));
194 }
195 
196 boolean_t
str_insert_str(str_t * dest,size_t idx,const str_t * src)197 str_insert_str(str_t *dest, size_t idx, const str_t *src)
198 {
199 	ASSERT3U(idx, <=, dest->str_len);
200 
201 	if (idx == dest->str_len)
202 		return (str_append_str(dest, src));
203 
204 	if (idx == 0 && dest->str_s == NULL && IS_REF(src)) {
205 		sysdem_ops_t *ops = dest->str_ops;
206 		*dest = *src;
207 		dest->str_ops = ops;
208 		return (B_TRUE);
209 	}
210 
211 	if (!str_reserve(dest, src->str_len))
212 		return (B_FALSE);
213 
214 	/*
215 	 * Shift the contents of dest over at the insertion point.  Since
216 	 * src and dest ranges will overlap, and unlike some programmers,
217 	 * *I* can read man pages - memmove() is the appropriate function
218 	 * to this.
219 	 */
220 	(void) memmove(dest->str_s + idx + src->str_len, dest->str_s + idx,
221 	    dest->str_len - idx);
222 
223 	/*
224 	 * However the content to insert does not overlap with the destination
225 	 * so memcpy() is fine here.
226 	 */
227 	(void) memcpy(dest->str_s + idx, src->str_s, src->str_len);
228 	dest->str_len += src->str_len;
229 
230 	return (B_TRUE);
231 }
232 
233 boolean_t
str_erase(str_t * s,size_t pos,size_t len)234 str_erase(str_t *s, size_t pos, size_t len)
235 {
236 	ASSERT3U(pos, <, s->str_len);
237 	ASSERT3U(pos + len, <=, s->str_len);
238 
239 	if (IS_REF(s)) {
240 		if (!str_reserve(s, 0))
241 			return (B_FALSE);
242 	}
243 
244 	(void) memmove(s->str_s + pos, s->str_s + pos + len, s->str_len - len);
245 	s->str_len -= len;
246 	return (B_TRUE);
247 }
248 
249 str_pair_t *
str_pair_init(str_pair_t * sp,sysdem_ops_t * ops)250 str_pair_init(str_pair_t *sp, sysdem_ops_t *ops)
251 {
252 	(void) memset(sp, 0, sizeof (*sp));
253 	str_init(&sp->strp_l, ops);
254 	str_init(&sp->strp_r, ops);
255 	return (sp);
256 }
257 
258 void
str_pair_fini(str_pair_t * sp)259 str_pair_fini(str_pair_t *sp)
260 {
261 	str_fini(&sp->strp_l);
262 	str_fini(&sp->strp_r);
263 }
264 
265 /* combine left and right parts and put result into left part */
266 boolean_t
str_pair_merge(str_pair_t * sp)267 str_pair_merge(str_pair_t *sp)
268 {
269 	/* if right side is empty, don't need to do anything */
270 	if (str_length(&sp->strp_r) == 0)
271 		return (B_TRUE);
272 
273 	/* if left side is empty, just move right to left */
274 	if (str_length(&sp->strp_l) == 0) {
275 		str_fini(&sp->strp_l);
276 		sp->strp_l = sp->strp_r;
277 		sp->strp_r.str_s = NULL;
278 		sp->strp_r.str_len = sp->strp_r.str_size = 0;
279 		return (B_TRUE);
280 	}
281 
282 	if (!str_append_str(&sp->strp_l, &sp->strp_r))
283 		return (B_FALSE);
284 
285 	str_fini(&sp->strp_r);
286 	str_init(&sp->strp_r, sp->strp_l.str_ops);
287 	return (B_TRUE);
288 }
289 
290 boolean_t
str_pair_copy(const str_pair_t * src,str_pair_t * dest)291 str_pair_copy(const str_pair_t *src, str_pair_t *dest)
292 {
293 	boolean_t ok = B_TRUE;
294 
295 	ok &= str_copy(&src->strp_l, &dest->strp_l);
296 	ok &= str_copy(&src->strp_r, &dest->strp_r);
297 
298 	if (!ok) {
299 		str_fini(&dest->strp_l);
300 		str_fini(&dest->strp_r);
301 		return (B_FALSE);
302 	}
303 
304 	return (B_TRUE);
305 }
306 
307 size_t
str_pair_len(const str_pair_t * sp)308 str_pair_len(const str_pair_t *sp)
309 {
310 	return (str_length(&sp->strp_l) + str_length(&sp->strp_r));
311 }
312