rizin/librz/util/strbuf.c

359 lines
7.5 KiB
C

// SPDX-FileCopyrightText: 2013-2020 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only
#include "rz_types.h"
#include "rz_util.h"
#include <stdio.h>
RZ_API RzStrBuf *rz_strbuf_new(const char *str) {
RzStrBuf *s = RZ_NEW0(RzStrBuf);
if (str) {
rz_strbuf_set(s, str);
}
return s;
}
RZ_API bool rz_strbuf_equals(RzStrBuf *sa, RzStrBuf *sb) {
rz_return_val_if_fail(sa && sb, false);
if (sa->len != sb->len) {
return false;
}
return strcmp(rz_strbuf_get(sa), rz_strbuf_get(sb)) == 0;
}
RZ_API bool rz_strbuf_is_empty(RzStrBuf *sb) {
return sb->len == 0;
}
RZ_API size_t rz_strbuf_length(RzStrBuf *sb) {
rz_return_val_if_fail(sb, 0);
return sb->len;
}
RZ_API void rz_strbuf_init(RzStrBuf *sb) {
rz_return_if_fail(sb);
memset(sb, 0, sizeof(RzStrBuf));
}
RZ_API const char *rz_strbuf_initf(RzStrBuf *sb, const char *fmt, ...) {
rz_return_val_if_fail(sb && fmt, NULL);
rz_strbuf_init(sb);
va_list ap;
va_start(ap, fmt);
const char *r = rz_strbuf_vsetf(sb, fmt, ap);
va_end(ap);
return r;
}
RZ_API bool rz_strbuf_copy(RzStrBuf *dst, RzStrBuf *src) {
rz_return_val_if_fail(dst && src, false);
if (src->ptr) {
char *p = malloc(src->ptrlen);
if (!p) {
return false;
}
memcpy(p, src->ptr, src->ptrlen);
free(dst->ptr);
dst->ptr = p;
dst->ptrlen = src->ptrlen;
} else {
RZ_FREE(dst->ptr);
memcpy(dst->buf, src->buf, sizeof(dst->buf));
}
dst->len = src->len;
return true;
}
RZ_API bool rz_strbuf_reserve(RzStrBuf *sb, size_t len) {
rz_return_val_if_fail(sb, false);
if ((sb->ptr && len < sb->ptrlen) || (!sb->ptr && len < sizeof(sb->buf))) {
return true;
}
char *newptr = realloc(sb->ptr, len + 1);
if (!newptr) {
return false;
}
if (!sb->ptr) {
memcpy(newptr, sb->buf, sizeof(sb->buf));
}
sb->ptr = newptr;
sb->ptrlen = len + 1;
return true;
}
RZ_API bool rz_strbuf_setbin(RzStrBuf *sb, const ut8 *s, size_t l) {
rz_return_val_if_fail(sb && s, false);
if (l >= sizeof(sb->buf)) {
char *ptr = sb->ptr;
if (!ptr || l + 1 > sb->ptrlen) {
ptr = malloc(l + 1);
if (!ptr) {
return false;
}
RZ_FREE(sb->ptr);
sb->ptrlen = l + 1;
sb->ptr = ptr;
}
memcpy(ptr, s, l);
ptr[l] = 0;
} else {
RZ_FREE(sb->ptr);
memcpy(sb->buf, s, l);
sb->buf[l] = 0;
}
sb->len = l;
return true;
}
/**
* \brief Cuts the current string into a substring
*
* Only to be used on strings, not binary buffers.
* If `len > sb->len - from`, then the resulting size will be truncated appropriately.
*
* \param sb RzStrBuf to use
* \param from Begin index from where to cut
* \param len Length of the substring to cut
*
* \return false when fails to to cut the current buffer into a substring
*/
RZ_API bool rz_strbuf_slice(RZ_NONNULL RzStrBuf *sb, size_t from, size_t len) {
rz_return_val_if_fail(sb, false);
if (from >= sb->len) {
// trying to cut outside the buf
return !sb->len && !from; // but it's fine if both are 0
}
char *s = rz_strbuf_get(sb);
len = RZ_MIN(sb->len - from, len);
if (from) {
memmove(s, s + from, len);
}
sb->len = len;
sb->ptrlen = len + 1;
s[len] = 0;
return true;
}
RZ_API const char *rz_strbuf_set(RzStrBuf *sb, const char *s) {
rz_return_val_if_fail(sb, NULL);
if (!s) {
rz_strbuf_init(sb);
return rz_strbuf_get(sb);
}
size_t len = strlen(s);
if (!rz_strbuf_setbin(sb, (const ut8 *)s, len)) {
return NULL;
}
sb->len = len;
return rz_strbuf_get(sb);
}
RZ_API const char *rz_strbuf_setf(RzStrBuf *sb, const char *fmt, ...) {
rz_return_val_if_fail(sb && fmt, NULL);
va_list ap;
va_start(ap, fmt);
const char *ret = rz_strbuf_vsetf(sb, fmt, ap);
va_end(ap);
return ret;
}
RZ_API const char *rz_strbuf_vsetf(RzStrBuf *sb, const char *fmt, va_list ap) {
rz_return_val_if_fail(sb && fmt, NULL);
const char *ret = NULL;
va_list ap2;
va_copy(ap2, ap);
char string[1024];
int rc = vsnprintf(string, sizeof(string), fmt, ap);
if (rc >= sizeof(string)) {
char *p = malloc(rc + 1);
if (!p) {
goto done;
}
vsnprintf(p, rc + 1, fmt, ap2);
ret = rz_strbuf_set(sb, p);
free(p);
} else if (rc >= 0) {
ret = rz_strbuf_set(sb, string);
}
done:
va_end(ap2);
return ret;
}
RZ_API bool rz_strbuf_prepend(RzStrBuf *sb, const char *s) {
rz_return_val_if_fail(sb && s, false);
int l = strlen(s);
// fast path if no chars to append
if (l == 0) {
return true;
}
int newlen = l + sb->len;
char *ns = malloc(newlen + 1);
bool ret = false;
if (ns) {
memcpy(ns, s, l);
char *s = sb->ptr ? sb->ptr : sb->buf;
memcpy(ns + l, s, sb->len);
ns[newlen] = 0;
ret = rz_strbuf_set(sb, ns);
free(ns);
}
return ret;
}
RZ_API bool rz_strbuf_append(RzStrBuf *sb, const char *s) {
rz_return_val_if_fail(sb && s, false);
int l = strlen(s);
return rz_strbuf_append_n(sb, s, l);
}
RZ_API bool rz_strbuf_append_n(RzStrBuf *sb, const char *s, size_t l) {
rz_return_val_if_fail(sb && s, false);
// fast path if no chars to append
if (l == 0) {
return true;
}
if ((sb->len + l + 1) <= sizeof(sb->buf)) {
memcpy(sb->buf + sb->len, s, l);
sb->buf[sb->len + l] = 0;
RZ_FREE(sb->ptr);
sb->ptrlen = 0;
} else {
size_t newlen = sb->len + l + 128;
char *p = sb->ptr;
bool allocated = true;
if (!sb->ptr) {
p = calloc(newlen, sizeof(char));
if (p && sb->len > 0) {
memcpy(p, sb->buf, sb->len);
}
} else if (sb->len + l + 1 > sb->ptrlen) {
if (SZT_MUL_OVFCHK(newlen, 2)) {
return false;
}
newlen *= 2;
p = realloc(sb->ptr, newlen);
// preserve the existing buffer on OOM
if (!p) {
return false;
}
memset((char *)p + sb->ptrlen, 0, newlen - sb->ptrlen);
} else {
allocated = false;
}
if (allocated) {
if (!p) {
return false;
}
sb->ptr = p;
sb->ptrlen = newlen;
}
if (p) {
memcpy(p + sb->len, s, l);
*(p + sb->len + l) = 0;
}
}
sb->len += l;
return true;
}
RZ_API bool rz_strbuf_appendf(RzStrBuf *sb, const char *fmt, ...) {
va_list ap;
rz_return_val_if_fail(sb && fmt, false);
va_start(ap, fmt);
bool ret = rz_strbuf_vappendf(sb, fmt, ap);
va_end(ap);
return ret;
}
RZ_API bool rz_strbuf_vappendf(RzStrBuf *sb, const char *fmt, va_list ap) {
int ret;
va_list ap2;
char string[1024];
rz_return_val_if_fail(sb && fmt, false);
va_copy(ap2, ap);
ret = vsnprintf(string, sizeof(string), fmt, ap);
if (ret >= sizeof(string)) {
char *p = malloc(ret + 1);
if (!p) {
va_end(ap2);
return false;
}
*p = 0;
vsnprintf(p, ret + 1, fmt, ap2);
ret = rz_strbuf_append(sb, p);
free(p);
} else if (ret >= 0) {
ret = rz_strbuf_append(sb, string);
} else {
ret = false;
}
va_end(ap2);
return ret;
}
RZ_API char *rz_strbuf_get(RzStrBuf *sb) {
rz_return_val_if_fail(sb, NULL);
return sb->ptr ? sb->ptr : sb->buf;
}
RZ_API ut8 *rz_strbuf_getbin(RzStrBuf *sb, size_t *len) {
rz_return_val_if_fail(sb, NULL);
if (len) {
*len = sb->len;
}
return (ut8 *)(sb->ptr ? sb->ptr : sb->buf);
}
static inline char *drain(RzStrBuf *sb) {
return sb->ptr ? sb->ptr : rz_str_dup(sb->buf);
}
/**
* \brief Drains the buffer, frees it and returns the drained string.
*
* \param sb The string buffer to drain.
*
* \return The string of \p sb.
*/
RZ_API RZ_OWN char *rz_strbuf_drain(RZ_OWN RZ_NONNULL RzStrBuf *sb) {
rz_return_val_if_fail(sb, NULL);
char *ret = drain(sb);
free(sb);
return ret;
}
RZ_API RZ_OWN char *rz_strbuf_drain_nofree(RzStrBuf *sb) {
rz_return_val_if_fail(sb, NULL);
char *ret = drain(sb);
sb->ptr = NULL;
sb->len = 0;
sb->buf[0] = '\0';
return ret;
}
RZ_API void rz_strbuf_free(RzStrBuf *sb) {
if (sb) {
rz_strbuf_fini(sb);
free(sb);
}
}
RZ_API void rz_strbuf_fini(RzStrBuf *sb) {
if (sb) {
RZ_FREE(sb->ptr);
sb->ptr = NULL;
sb->len = 0;
sb->buf[0] = '\0';
}
}