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