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 }