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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 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 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 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 308 str_pair_len(const str_pair_t *sp) 309 { 310 return (str_length(&sp->strp_l) + str_length(&sp->strp_r)); 311 } 312