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