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