Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / lib / libdemangle-sys / str.c
blob014ce8a737121a729d3c59ddf6fdbafee5004880
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.
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.
13 * Copyright 2017 Jason King
15 #include <sys/debug.h>
16 #include <sys/sysmacros.h>
17 #include <string.h>
18 #include "str.h"
19 #include "demangle_int.h"
21 #define STR_CHUNK_SZ (64U)
23 /* are we storing a reference vs. a dynamically allocated copy? */
24 #define IS_REF(s) ((s)->str_s != NULL && (s)->str_size == 0)
27 * Dynamically resizeable strings, with lazy allocation when initialized
28 * with a constant string value
30 * NOTE: these are not necessairly 0-terminated
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.
37 void
38 str_init(str_t *restrict s, sysdem_ops_t *restrict ops)
40 (void) memset(s, 0, sizeof (*s));
41 s->str_ops = (ops != NULL) ? ops : sysdem_ops_default;
44 void
45 str_fini(str_t *s)
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));
54 size_t
55 str_length(const str_t *s)
57 return (s->str_len);
61 * store as a reference instead of a copy
62 * if len == 0, means store entire copy of 0 terminated string
64 void
65 str_set(str_t *s, const char *cstr, size_t len)
67 sysdem_ops_t *ops = s->str_ops;
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;
75 boolean_t
76 str_copy(const str_t *src, str_t *dest)
78 str_fini(dest);
79 str_init(dest, src->str_ops);
81 if (src->str_len == 0)
82 return (B_TRUE);
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);
89 (void) memcpy(dest->str_s, src->str_s, src->str_len);
90 dest->str_len = src->str_len;
91 dest->str_size = len;
93 return (B_TRUE);
97 * ensure s has at least amt bytes free, resizing if necessary
99 static boolean_t
100 str_reserve(str_t *s, size_t amt)
102 size_t newlen = s->str_len + amt;
104 /* overflow check */
105 if (newlen < s->str_len || newlen < amt)
106 return (B_FALSE);
108 if ((amt > 0) && (s->str_len + amt <= s->str_size))
109 return (B_TRUE);
111 size_t newsize = roundup(newlen, STR_CHUNK_SZ);
112 void *temp;
114 if (IS_REF(s)) {
115 temp = zalloc(s->str_ops, newsize);
116 if (temp == NULL)
117 return (B_FALSE);
119 (void) memcpy(temp, s->str_s, s->str_len);
120 } else {
121 temp = xrealloc(s->str_ops, s->str_s, s->str_size, newsize);
122 if (temp == NULL)
123 return (B_FALSE);
126 s->str_s = temp;
127 s->str_size = newsize;
129 return (B_TRUE);
132 /* append to s, cstrlen == 0 means entire length of string */
133 boolean_t
134 str_append(str_t *s, const char *cstr, size_t cstrlen)
136 if (cstr != NULL && cstrlen == 0)
137 cstrlen = strlen(cstr);
139 const str_t src = {
140 .str_s = (char *)cstr,
141 .str_len = cstrlen,
142 .str_ops = s->str_ops
145 return (str_append_str(s, &src));
148 boolean_t
149 str_append_str(str_t *dest, const str_t *src)
151 /* empty string is a noop */
152 if (src->str_s == NULL || src->str_len == 0)
153 return (B_TRUE);
155 /* if src is a reference, we can just copy that */
156 if (dest->str_s == NULL && IS_REF(src)) {
157 *dest = *src;
158 return (B_TRUE);
161 if (!str_reserve(dest, src->str_len))
162 return (B_FALSE);
164 (void) memcpy(dest->str_s + dest->str_len, src->str_s, src->str_len);
165 dest->str_len += src->str_len;
166 return (B_TRUE);
169 boolean_t
170 str_append_c(str_t *s, char c)
172 if (!str_reserve(s, 1))
173 return (B_FALSE);
175 s->str_s[s->str_len++] = c;
176 return (B_TRUE);
179 boolean_t
180 str_insert(str_t *s, size_t idx, const char *cstr, size_t cstrlen)
182 if (cstr == NULL)
183 return (B_TRUE);
185 if (cstrlen == 0)
186 cstrlen = strlen(cstr);
188 str_t src = {
189 .str_s = (char *)cstr,
190 .str_len = cstrlen,
191 .str_ops = s->str_ops,
192 .str_size = 0
195 return (str_insert_str(s, idx, &src));
198 boolean_t
199 str_insert_str(str_t *dest, size_t idx, const str_t *src)
201 ASSERT3U(idx, <=, dest->str_len);
203 if (idx == dest->str_len)
204 return (str_append_str(dest, src));
206 if (idx == 0 && dest->str_s == NULL && IS_REF(src)) {
207 sysdem_ops_t *ops = dest->str_ops;
208 *dest = *src;
209 dest->str_ops = ops;
210 return (B_TRUE);
213 if (!str_reserve(dest, src->str_len))
214 return (B_FALSE);
217 * Shift the contents of dest over at the insertion point. Since
218 * src and dest ranges will overlap, and unlike some programmers,
219 * *I* can read man pages - memmove() is the appropriate function
220 * to this.
222 (void) memmove(dest->str_s + idx + src->str_len, dest->str_s + idx,
223 dest->str_len - idx);
226 * However the content to insert does not overlap with the destination
227 * so memcpy() is fine here.
229 (void) memcpy(dest->str_s + idx, src->str_s, src->str_len);
230 dest->str_len += src->str_len;
232 return (B_TRUE);
235 boolean_t
236 str_erase(str_t *s, size_t pos, size_t len)
238 ASSERT3U(pos, <, s->str_len);
239 ASSERT3U(pos + len, <=, s->str_len);
241 if (IS_REF(s)) {
242 if (!str_reserve(s, 0))
243 return (B_FALSE);
246 (void) memmove(s->str_s + pos, s->str_s + pos + len, s->str_len - len);
247 s->str_len -= len;
248 return (B_TRUE);
251 str_pair_t *
252 str_pair_init(str_pair_t *sp, sysdem_ops_t *ops)
254 (void) memset(sp, 0, sizeof (*sp));
255 str_init(&sp->strp_l, ops);
256 str_init(&sp->strp_r, ops);
257 return (sp);
260 void
261 str_pair_fini(str_pair_t *sp)
263 str_fini(&sp->strp_l);
264 str_fini(&sp->strp_r);
267 /* combine left and right parts and put result into left part */
268 boolean_t
269 str_pair_merge(str_pair_t *sp)
271 /* if right side is empty, don't need to do anything */
272 if (str_length(&sp->strp_r) == 0)
273 return (B_TRUE);
275 /* if left side is empty, just move right to left */
276 if (str_length(&sp->strp_l) == 0) {
277 str_fini(&sp->strp_l);
278 sp->strp_l = sp->strp_r;
279 sp->strp_r.str_s = NULL;
280 sp->strp_r.str_len = sp->strp_r.str_size = 0;
281 return (B_TRUE);
284 if (!str_append_str(&sp->strp_l, &sp->strp_r))
285 return (B_FALSE);
287 str_fini(&sp->strp_r);
288 str_init(&sp->strp_r, sp->strp_l.str_ops);
289 return (B_TRUE);
292 boolean_t
293 str_pair_copy(const str_pair_t *src, str_pair_t *dest)
295 boolean_t ok = B_TRUE;
297 ok &= str_copy(&src->strp_l, &dest->strp_l);
298 ok &= str_copy(&src->strp_r, &dest->strp_r);
300 if (!ok) {
301 str_fini(&dest->strp_l);
302 str_fini(&dest->strp_r);
303 return (B_FALSE);
306 return (B_TRUE);
309 size_t
310 str_pair_len(const str_pair_t *sp)
312 return (str_length(&sp->strp_l) + str_length(&sp->strp_r));