#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "string_buffer.h"
#include <ucs/debug/assert.h>
#include <ucs/debug/log.h>
#include <ucs/debug/memtrack_int.h>
#include <ucs/sys/string.h>
#include <ucs/sys/math.h>
#include <string.h>
#include <ctype.h>
#include <ucs/datastruct/array.inl>
#define UCS_STRING_BUFFER_RESERVE 32
UCS_ARRAY_IMPL(string_buffer, size_t, char, static UCS_F_ALWAYS_INLINE)
void ucs_string_buffer_init(ucs_string_buffer_t *strb)
{
ucs_array_init_dynamic(&strb->str);
}
void ucs_string_buffer_init_fixed(ucs_string_buffer_t *strb, char *buffer,
size_t capacity)
{
ucs_array_init_fixed(&strb->str, buffer, capacity);
}
void ucs_string_buffer_cleanup(ucs_string_buffer_t *strb)
{
ucs_array_cleanup_dynamic(&strb->str);
}
void ucs_string_buffer_reset(ucs_string_buffer_t *strb)
{
ucs_array_length(&strb->str) = 0;
}
size_t ucs_string_buffer_length(ucs_string_buffer_t *strb)
{
return ucs_array_length(&strb->str);
}
void ucs_string_buffer_appendf(ucs_string_buffer_t *strb, const char *fmt, ...)
{
ucs_status_t status;
size_t max_print;
va_list ap;
int ret;
ucs_array_reserve(string_buffer, &strb->str,
ucs_array_length(&strb->str) + UCS_STRING_BUFFER_RESERVE);
va_start(ap, fmt);
max_print = ucs_array_available_length(&strb->str);
ret = vsnprintf(ucs_array_end(&strb->str), max_print, fmt, ap);
va_end(ap);
if (ret >= max_print) {
status = ucs_array_reserve(string_buffer, &strb->str,
ucs_array_length(&strb->str) + ret + 1);
if (status != UCS_OK) {
ucs_array_length(&strb->str) = ucs_array_capacity(&strb->str) - 1;
*ucs_array_end(&strb->str) = '\0';
goto out;
}
va_start(ap, fmt);
max_print = ucs_array_available_length(&strb->str);
ret = vsnprintf(ucs_array_end(&strb->str), max_print, fmt, ap);
va_end(ap);
ucs_assertv(ret < max_print, "ret=%d max_print=%zu", ret, max_print);
}
ucs_array_set_length(&strb->str, ucs_array_length(&strb->str) + ret);
out:
ucs_assert(ucs_array_available_length(&strb->str) >= 1);
ucs_assert(*ucs_array_end(&strb->str) == '\0');
}
void ucs_string_buffer_append_hex(ucs_string_buffer_t *strb, const void *data,
size_t size, size_t per_line)
{
size_t prev_length = ucs_array_length(&strb->str);
size_t hexdump_length = (size * 2) + (size / 4) + (size / per_line);
size_t new_length;
ucs_array_reserve(string_buffer, &strb->str, prev_length + hexdump_length);
ucs_str_dump_hex(data, size, ucs_array_end(&strb->str),
ucs_array_available_length(&strb->str), per_line);
new_length = prev_length + strlen(ucs_array_end(&strb->str));
ucs_array_set_length(&strb->str, new_length);
ucs_assert(*ucs_array_end(&strb->str) == '\0');
}
void ucs_string_buffer_append_flags(ucs_string_buffer_t *strb, uint64_t mask,
const char **flag_names)
{
unsigned flag;
ucs_for_each_bit(flag, mask) {
if (flag_names == NULL) {
ucs_string_buffer_appendf(strb, "%u,", flag);
} else {
ucs_string_buffer_appendf(strb, "%s|", flag_names[flag]);
}
}
ucs_string_buffer_rtrim(strb, ",|");
}
void ucs_string_buffer_append_iovec(ucs_string_buffer_t *strb,
const struct iovec *iov, size_t iovcnt)
{
size_t iov_index;
for (iov_index = 0; iov_index < iovcnt; ++iov_index) {
ucs_string_buffer_appendf(strb, "%p,%zu|", iov[iov_index].iov_base,
iov[iov_index].iov_len);
}
ucs_string_buffer_rtrim(strb, "|");
}
void ucs_string_buffer_rtrim(ucs_string_buffer_t *strb, const char *charset)
{
char *ptr = ucs_array_end(&strb->str);
while (ucs_array_length(&strb->str) > 0) {
--ptr;
if (((charset == NULL) && !isspace(*ptr)) ||
((charset != NULL) && (strchr(charset, *ptr) == NULL))) {
break;
}
ucs_array_set_length(&strb->str, ucs_array_length(&strb->str) - 1);
}
*(ptr + 1) = '\0';
}
const char *ucs_string_buffer_cstr(const ucs_string_buffer_t *strb)
{
char *c_str;
if (ucs_array_is_empty(&strb->str)) {
return "";
}
c_str = ucs_array_begin(&strb->str);
ucs_assert(c_str != NULL);
return c_str;
}
void ucs_string_buffer_dump(const ucs_string_buffer_t *strb,
const char *line_prefix, FILE *stream)
{
const char *next_tok, *tok;
size_t size, remaining;
if (ucs_array_is_empty(&strb->str)) {
return;
}
tok = ucs_array_begin(&strb->str);
next_tok = strchr(tok, '\n');
while (next_tok != NULL) {
fputs(line_prefix, stream);
remaining = UCS_PTR_BYTE_DIFF(tok, next_tok + 1);
while (remaining > 0) {
size = fwrite(tok, sizeof(*tok), remaining, stream);
tok = UCS_PTR_BYTE_OFFSET(tok, size);
remaining -= size;
}
next_tok = strchr(tok, '\n');
}
if (*tok != '\0') {
fputs(line_prefix, stream);
fputs(tok, stream);
}
}
char *ucs_string_buffer_extract_mem(ucs_string_buffer_t *strb)
{
char *c_str;
if (ucs_array_is_fixed(&strb->str)) {
c_str = ucs_strdup(ucs_array_begin(&strb->str), "ucs_string_buffer");
} else {
c_str = ucs_array_begin(&strb->str);
ucs_array_init_dynamic(&strb->str);
}
return c_str;
}