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 }