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 /* overflow check */ 105 if (newlen < s->str_len || newlen < amt) 106 return (B_FALSE); 107 108 if ((amt > 0) && (s->str_len + amt <= s->str_size)) 109 return (B_TRUE); 110 111 size_t newsize = roundup(newlen, STR_CHUNK_SZ); 112 void *temp; 113 114 if (IS_REF(s)) { 115 temp = zalloc(s->str_ops, newsize); 116 if (temp == NULL) 117 return (B_FALSE); 118 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); 124 } 125 126 s->str_s = temp; 127 s->str_size = newsize; 128 129 return (B_TRUE); 130 } 131 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) 135 { 136 if (cstr != NULL && cstrlen == 0) 137 cstrlen = strlen(cstr); 138 139 const str_t src = { 140 .str_s = (char *)cstr, 141 .str_len = cstrlen, 142 .str_ops = s->str_ops 143 }; 144 145 return (str_append_str(s, &src)); 146 } 147 148 boolean_t 149 str_append_str(str_t *dest, const str_t *src) 150 { 151 /* empty string is a noop */ 152 if (src->str_s == NULL || src->str_len == 0) 153 return (B_TRUE); 154 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); 159 } 160 161 if (!str_reserve(dest, src->str_len)) 162 return (B_FALSE); 163 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); 167 } 168 169 boolean_t 170 str_append_c(str_t *s, char c) 171 { 172 if (!str_reserve(s, 1)) 173 return (B_FALSE); 174 175 s->str_s[s->str_len++] = c; 176 return (B_TRUE); 177 } 178 179 boolean_t 180 str_insert(str_t *s, size_t idx, const char *cstr, size_t cstrlen) 181 { 182 if (cstr == NULL) 183 return (B_TRUE); 184 185 if (cstrlen == 0) 186 cstrlen = strlen(cstr); 187 188 str_t src = { 189 .str_s = (char *)cstr, 190 .str_len = cstrlen, 191 .str_ops = s->str_ops, 192 .str_size = 0 193 }; 194 195 return (str_insert_str(s, idx, &src)); 196 } 197 198 boolean_t 199 str_insert_str(str_t *dest, size_t idx, const str_t *src) 200 { 201 ASSERT3U(idx, <=, dest->str_len); 202 203 if (idx == dest->str_len) 204 return (str_append_str(dest, src)); 205 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); 211 } 212 213 if (!str_reserve(dest, src->str_len)) 214 return (B_FALSE); 215 216 /* 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. 221 */ 222 (void) memmove(dest->str_s + idx + src->str_len, dest->str_s + idx, 223 dest->str_len - idx); 224 225 /* 226 * However the content to insert does not overlap with the destination 227 * so memcpy() is fine here. 228 */ 229 (void) memcpy(dest->str_s + idx, src->str_s, src->str_len); 230 dest->str_len += src->str_len; 231 232 return (B_TRUE); 233 } 234 235 boolean_t 236 str_erase(str_t *s, size_t pos, size_t len) 237 { 238 ASSERT3U(pos, <, s->str_len); 239 ASSERT3U(pos + len, <=, s->str_len); 240 241 if (IS_REF(s)) { 242 if (!str_reserve(s, 0)) 243 return (B_FALSE); 244 } 245 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); 249 } 250 251 str_pair_t * 252 str_pair_init(str_pair_t *sp, sysdem_ops_t *ops) 253 { 254 (void) memset(sp, 0, sizeof (*sp)); 255 str_init(&sp->strp_l, ops); 256 str_init(&sp->strp_r, ops); 257 return (sp); 258 } 259 260 void 261 str_pair_fini(str_pair_t *sp) 262 { 263 str_fini(&sp->strp_l); 264 str_fini(&sp->strp_r); 265 } 266 267 /* combine left and right parts and put result into left part */ 268 boolean_t 269 str_pair_merge(str_pair_t *sp) 270 { 271 /* if right side is empty, don't need to do anything */ 272 if (str_length(&sp->strp_r) == 0) 273 return (B_TRUE); 274 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); 282 } 283 284 if (!str_append_str(&sp->strp_l, &sp->strp_r)) 285 return (B_FALSE); 286 287 str_fini(&sp->strp_r); 288 str_init(&sp->strp_r, sp->strp_l.str_ops); 289 return (B_TRUE); 290 } 291 292 boolean_t 293 str_pair_copy(const str_pair_t *src, str_pair_t *dest) 294 { 295 boolean_t ok = B_TRUE; 296 297 ok &= str_copy(&src->strp_l, &dest->strp_l); 298 ok &= str_copy(&src->strp_r, &dest->strp_r); 299 300 if (!ok) { 301 str_fini(&dest->strp_l); 302 str_fini(&dest->strp_r); 303 return (B_FALSE); 304 } 305 306 return (B_TRUE); 307 } 308 309 size_t 310 str_pair_len(const str_pair_t *sp) 311 { 312 return (str_length(&sp->strp_l) + str_length(&sp->strp_r)); 313 }