/* * This file and its contents are supplied under the terms of the * Common Development and Distribution License ("CDDL"), version 1.0. * You may only use this file in accordance with the terms of version * 1.0 of the CDDL. * * A full copy of the text of the CDDL should have accompanied this * source. A copy of the CDDL is also available via the Internet at * http://www.illumos.org/license/CDDL. */ /* * Copyright 2017 Jason King */ #include #include "str.h" #include "demangle_int.h" #define STR_CHUNK_SZ (64U) /* are we storing a reference vs. a dynamically allocated copy? */ #define IS_REF(s) ((s)->str_s != NULL && (s)->str_size == 0) /* * Dynamically resizeable strings, with lazy allocation when initialized * with a constant string value * * NOTE: these are not necessairly 0-terminated * * Additionally, these can store references instead of copies of strings * (as indicated by the IS_REF() macro. However mutation may cause a * string to convert from a refence to a dynamically allocated copy. */ void str_init(str_t *restrict s, sysdem_ops_t *restrict ops) { (void) memset(s, 0, sizeof (*s)); s->str_ops = (ops != NULL) ? ops : sysdem_ops_default; } void str_fini(str_t *s) { if (s == NULL) return; if (!IS_REF(s)) xfree(s->str_ops, s->str_s, s->str_size); (void) memset(s, 0, sizeof (*s)); } size_t str_length(const str_t *s) { return (s->str_len); } /* * store as a reference instead of a copy * if len == 0, means store entire copy of 0 terminated string */ void str_set(str_t *s, const char *cstr, size_t len) { sysdem_ops_t *ops = s->str_ops; str_fini(s); s->str_ops = ops; s->str_s = (char *)cstr; s->str_len = (len == 0 && cstr != NULL) ? strlen(cstr) : len; } boolean_t str_copy(const str_t *src, str_t *dest) { str_fini(dest); str_init(dest, src->str_ops); if (src->str_len == 0) return (B_TRUE); size_t len = roundup(src->str_len, STR_CHUNK_SZ); dest->str_s = zalloc(src->str_ops, len); if (dest->str_s == NULL) return (B_FALSE); (void) memcpy(dest->str_s, src->str_s, src->str_len); dest->str_len = src->str_len; dest->str_size = len; return (B_TRUE); } /* * ensure s has at least amt bytes free, resizing if necessary */ static boolean_t str_reserve(str_t *s, size_t amt) { size_t newlen = s->str_len + amt; /* overflow check */ if (newlen < s->str_len || newlen < amt) return (B_FALSE); if ((amt > 0) && (s->str_len + amt <= s->str_size)) return (B_TRUE); size_t newsize = roundup(newlen, STR_CHUNK_SZ); void *temp; if (IS_REF(s)) { temp = zalloc(s->str_ops, newsize); if (temp == NULL) return (B_FALSE); (void) memcpy(temp, s->str_s, s->str_len); } else { temp = xrealloc(s->str_ops, s->str_s, s->str_size, newsize); if (temp == NULL) return (B_FALSE); } s->str_s = temp; s->str_size = newsize; return (B_TRUE); } /* append to s, cstrlen == 0 means entire length of string */ boolean_t str_append(str_t *s, const char *cstr, size_t cstrlen) { if (cstr != NULL && cstrlen == 0) cstrlen = strlen(cstr); const str_t src = { .str_s = (char *)cstr, .str_len = cstrlen, .str_ops = s->str_ops }; return (str_append_str(s, &src)); } boolean_t str_append_str(str_t *dest, const str_t *src) { /* empty string is a noop */ if (src->str_s == NULL || src->str_len == 0) return (B_TRUE); /* if src is a reference, we can just copy that */ if (dest->str_s == NULL && IS_REF(src)) { *dest = *src; return (B_TRUE); } if (!str_reserve(dest, src->str_len)) return (B_FALSE); (void) memcpy(dest->str_s + dest->str_len, src->str_s, src->str_len); dest->str_len += src->str_len; return (B_TRUE); } boolean_t str_append_c(str_t *s, char c) { if (!str_reserve(s, 1)) return (B_FALSE); s->str_s[s->str_len++] = c; return (B_TRUE); } boolean_t str_insert(str_t *s, size_t idx, const char *cstr, size_t cstrlen) { if (cstr == NULL) return (B_TRUE); if (cstrlen == 0) cstrlen = strlen(cstr); str_t src = { .str_s = (char *)cstr, .str_len = cstrlen, .str_ops = s->str_ops, .str_size = 0 }; return (str_insert_str(s, idx, &src)); } boolean_t str_insert_str(str_t *dest, size_t idx, const str_t *src) { ASSERT3U(idx, <=, dest->str_len); if (idx == dest->str_len) return (str_append_str(dest, src)); if (idx == 0 && dest->str_s == NULL && IS_REF(src)) { sysdem_ops_t *ops = dest->str_ops; *dest = *src; dest->str_ops = ops; return (B_TRUE); } if (!str_reserve(dest, src->str_len)) return (B_FALSE); /* * Shift the contents of dest over at the insertion point. Since * src and dest ranges will overlap, and unlike some programmers, * *I* can read man pages - memmove() is the appropriate function * to this. */ (void) memmove(dest->str_s + idx + src->str_len, dest->str_s + idx, dest->str_len - idx); /* * However the content to insert does not overlap with the destination * so memcpy() is fine here. */ (void) memcpy(dest->str_s + idx, src->str_s, src->str_len); dest->str_len += src->str_len; return (B_TRUE); } boolean_t str_erase(str_t *s, size_t pos, size_t len) { ASSERT3U(pos, <, s->str_len); ASSERT3U(pos + len, <=, s->str_len); if (IS_REF(s)) { if (!str_reserve(s, 0)) return (B_FALSE); } (void) memmove(s->str_s + pos, s->str_s + pos + len, s->str_len - len); s->str_len -= len; return (B_TRUE); } str_pair_t * str_pair_init(str_pair_t *sp, sysdem_ops_t *ops) { (void) memset(sp, 0, sizeof (*sp)); str_init(&sp->strp_l, ops); str_init(&sp->strp_r, ops); return (sp); } void str_pair_fini(str_pair_t *sp) { str_fini(&sp->strp_l); str_fini(&sp->strp_r); } /* combine left and right parts and put result into left part */ boolean_t str_pair_merge(str_pair_t *sp) { /* if right side is empty, don't need to do anything */ if (str_length(&sp->strp_r) == 0) return (B_TRUE); /* if left side is empty, just move right to left */ if (str_length(&sp->strp_l) == 0) { str_fini(&sp->strp_l); sp->strp_l = sp->strp_r; sp->strp_r.str_s = NULL; sp->strp_r.str_len = sp->strp_r.str_size = 0; return (B_TRUE); } if (!str_append_str(&sp->strp_l, &sp->strp_r)) return (B_FALSE); str_fini(&sp->strp_r); str_init(&sp->strp_r, sp->strp_l.str_ops); return (B_TRUE); } boolean_t str_pair_copy(const str_pair_t *src, str_pair_t *dest) { boolean_t ok = B_TRUE; ok &= str_copy(&src->strp_l, &dest->strp_l); ok &= str_copy(&src->strp_r, &dest->strp_r); if (!ok) { str_fini(&dest->strp_l); str_fini(&dest->strp_r); return (B_FALSE); } return (B_TRUE); } size_t str_pair_len(const str_pair_t *sp) { return (str_length(&sp->strp_l) + str_length(&sp->strp_r)); }