glib-aux: add support for starting with stack-allocated buffer in NMStrBuf
Allow to initialize NMStrBuf with an externally allocated array. Usually a stack buffer. If the NMStrBuf grows beyond the size of that initial buffer, then it would switch using malloc. The idea is to support the common case where the result is small enough to fit on the stack. I always wanted to do such optimization because the main purpose of NMStrBuf is to put it on the stack and ad-hoc construct a string. I just figured, it would complicate the implementation and add a runtime overhead. But turns out, it doesn't really. The biggest question is how NMStrBuf should behave with a pre-allocated buffer? Turns out, most choices can be made in a rather obvious way. The only non-obvious thing is that nm_str_buf_finalize() would malloc() a buffer, but that too seems consistent and what a user would probably expect. As such, this doesn't seem to add unexpected semantics to the API.
This commit is contained in:
@@ -5841,10 +5841,22 @@ _nm_str_buf_ensure_size(NMStrBuf *strbuf, gsize new_size, gboolean reserve_exact
|
||||
new_size = nm_utils_get_next_realloc_size(!strbuf->_priv_do_bzero_mem, new_size);
|
||||
}
|
||||
|
||||
strbuf->_priv_str = nm_secret_mem_realloc(strbuf->_priv_str,
|
||||
strbuf->_priv_do_bzero_mem,
|
||||
strbuf->_priv_allocated,
|
||||
new_size);
|
||||
if (strbuf->_priv_malloced) {
|
||||
strbuf->_priv_str = nm_secret_mem_realloc(strbuf->_priv_str,
|
||||
strbuf->_priv_do_bzero_mem,
|
||||
strbuf->_priv_allocated,
|
||||
new_size);
|
||||
} else {
|
||||
char *old = strbuf->_priv_str;
|
||||
|
||||
strbuf->_priv_str = g_malloc(new_size);
|
||||
if (strbuf->_priv_len > 0) {
|
||||
memcpy(strbuf->_priv_str, old, strbuf->_priv_len);
|
||||
if (strbuf->_priv_do_bzero_mem)
|
||||
nm_explicit_bzero(old, strbuf->_priv_len);
|
||||
}
|
||||
strbuf->_priv_malloced = TRUE;
|
||||
}
|
||||
strbuf->_priv_allocated = new_size;
|
||||
}
|
||||
|
||||
|
@@ -26,6 +26,7 @@ typedef struct _NMStrBuf {
|
||||
};
|
||||
|
||||
bool _priv_do_bzero_mem;
|
||||
bool _priv_malloced;
|
||||
} NMStrBuf;
|
||||
|
||||
/*****************************************************************************/
|
||||
@@ -36,21 +37,56 @@ _nm_str_buf_assert(const NMStrBuf *strbuf)
|
||||
nm_assert(strbuf);
|
||||
nm_assert((!!strbuf->_priv_str) == (strbuf->_priv_allocated > 0));
|
||||
nm_assert(strbuf->_priv_len <= strbuf->_priv_allocated);
|
||||
nm_assert(!strbuf->_priv_malloced || strbuf->_priv_str);
|
||||
}
|
||||
|
||||
static inline NMStrBuf
|
||||
NM_STR_BUF_INIT_FULL(char *str,
|
||||
gsize len,
|
||||
gsize allocated,
|
||||
gboolean malloced,
|
||||
gboolean do_bzero_mem)
|
||||
{
|
||||
NMStrBuf strbuf = {
|
||||
._priv_str = allocated > 0 ? str : NULL,
|
||||
._priv_allocated = allocated,
|
||||
._priv_len = len,
|
||||
._priv_do_bzero_mem = do_bzero_mem,
|
||||
._priv_malloced = allocated > 0 && malloced,
|
||||
};
|
||||
|
||||
_nm_str_buf_assert(&strbuf);
|
||||
|
||||
return strbuf;
|
||||
}
|
||||
|
||||
static inline NMStrBuf
|
||||
NM_STR_BUF_INIT(gsize allocated, gboolean do_bzero_mem)
|
||||
{
|
||||
NMStrBuf strbuf = {
|
||||
._priv_str = allocated ? g_malloc(allocated) : NULL,
|
||||
._priv_allocated = allocated,
|
||||
._priv_len = 0,
|
||||
._priv_do_bzero_mem = do_bzero_mem,
|
||||
};
|
||||
|
||||
return strbuf;
|
||||
return NM_STR_BUF_INIT_FULL(allocated > 0 ? g_malloc(allocated) : NULL,
|
||||
0,
|
||||
allocated,
|
||||
allocated > 0,
|
||||
do_bzero_mem);
|
||||
}
|
||||
|
||||
#define NM_STR_BUF_INIT_A(size, do_bzero_mem) \
|
||||
NM_STR_BUF_INIT_FULL( \
|
||||
g_alloca(size), \
|
||||
0, \
|
||||
NM_STATIC_ASSERT_EXPR_1((size) > 0 && (size) <= NM_UTILS_GET_NEXT_REALLOC_SIZE_488) \
|
||||
? (size) \
|
||||
: 0, \
|
||||
FALSE, \
|
||||
(do_bzero_mem));
|
||||
|
||||
#define NM_STR_BUF_INIT_ARR(arr, do_bzero_mem) \
|
||||
NM_STR_BUF_INIT_FULL((arr), \
|
||||
0, \
|
||||
NM_STATIC_ASSERT_EXPR_1(sizeof(arr) > sizeof(char *)) ? sizeof(arr) : 0, \
|
||||
FALSE, \
|
||||
(do_bzero_mem));
|
||||
|
||||
static inline void
|
||||
nm_str_buf_init(NMStrBuf *strbuf, gsize len, bool do_bzero_mem)
|
||||
{
|
||||
@@ -465,7 +501,9 @@ nm_str_buf_get_char(const NMStrBuf *strbuf, gsize index)
|
||||
* is afterwards in undefined state, though it can be
|
||||
* reused after nm_str_buf_init().
|
||||
* Note that if no string is allocated yet (after nm_str_buf_init() with
|
||||
* length zero), this will return %NULL. */
|
||||
* length zero), this will return %NULL.
|
||||
*
|
||||
* If the buffer was not malloced before, it will be malloced now. */
|
||||
static inline char *
|
||||
nm_str_buf_finalize(NMStrBuf *strbuf, gsize *out_len)
|
||||
{
|
||||
@@ -476,6 +514,16 @@ nm_str_buf_finalize(NMStrBuf *strbuf, gsize *out_len)
|
||||
if (!strbuf->_priv_str)
|
||||
return NULL;
|
||||
|
||||
if (!strbuf->_priv_malloced) {
|
||||
char *str = g_steal_pointer(&strbuf->_priv_str);
|
||||
char *result;
|
||||
|
||||
result = g_strndup(str, strbuf->_priv_len);
|
||||
if (strbuf->_priv_do_bzero_mem)
|
||||
nm_explicit_bzero(str, strbuf->_priv_len);
|
||||
return result;
|
||||
}
|
||||
|
||||
nm_str_buf_maybe_expand(strbuf, 1, TRUE);
|
||||
strbuf->_priv_str[strbuf->_priv_len] = '\0';
|
||||
|
||||
@@ -517,7 +565,8 @@ nm_str_buf_destroy(NMStrBuf *strbuf)
|
||||
_nm_str_buf_assert(strbuf);
|
||||
if (strbuf->_priv_do_bzero_mem)
|
||||
nm_explicit_bzero(strbuf->_priv_str, strbuf->_priv_len);
|
||||
g_free(strbuf->_priv_str);
|
||||
if (strbuf->_priv_malloced)
|
||||
g_free(strbuf->_priv_str);
|
||||
|
||||
/* the buffer is in invalid state afterwards, however, we clear it
|
||||
* so far, that nm_auto_str_buf is happy when calling
|
||||
|
@@ -917,27 +917,58 @@ test_nm_str_buf(void)
|
||||
{
|
||||
guint i_run;
|
||||
|
||||
for (i_run = 0; TRUE; i_run++) {
|
||||
nm_auto_str_buf NMStrBuf strbuf = {};
|
||||
nm_auto_free_gstring GString *gstr = NULL;
|
||||
for (i_run = 0; i_run < 1000; i_run++) {
|
||||
char stack_buf[1024];
|
||||
nm_auto_str_buf NMStrBuf strbuf;
|
||||
nm_auto_free_gstring GString *gstr = NULL;
|
||||
int i, j, k;
|
||||
int c;
|
||||
|
||||
nm_str_buf_init(&strbuf, nmtst_get_rand_uint32() % 200u + 1u, nmtst_get_rand_bool());
|
||||
switch (nmtst_get_rand_uint32() % 10) {
|
||||
case 0:
|
||||
memset(&strbuf, 0, sizeof(strbuf));
|
||||
break;
|
||||
case 1 ... 4:
|
||||
strbuf = NM_STR_BUF_INIT_FULL(stack_buf,
|
||||
0,
|
||||
nmtst_get_rand_uint32() % sizeof(stack_buf),
|
||||
FALSE,
|
||||
nmtst_get_rand_bool());
|
||||
break;
|
||||
default:
|
||||
nm_str_buf_init(&strbuf, nmtst_get_rand_uint32() % 200u + 1u, nmtst_get_rand_bool());
|
||||
break;
|
||||
}
|
||||
|
||||
if (i_run < 1000) {
|
||||
c = nmtst_get_rand_word_length(NULL);
|
||||
for (i = 0; i < c; i++)
|
||||
nm_str_buf_append_c(&strbuf, '0' + (i % 10));
|
||||
gstr = g_string_new(nm_str_buf_get_str(&strbuf));
|
||||
j = nmtst_get_rand_uint32() % (strbuf.len + 1);
|
||||
k = nmtst_get_rand_uint32() % (strbuf.len - j + 2) - 1;
|
||||
c = nmtst_get_rand_word_length(NULL);
|
||||
for (i = 0; i < c; i++)
|
||||
nm_str_buf_append_c(&strbuf, '0' + (i % 10));
|
||||
gstr = g_string_new(nm_str_buf_get_str(&strbuf));
|
||||
j = nmtst_get_rand_uint32() % (strbuf.len + 1);
|
||||
k = nmtst_get_rand_uint32() % (strbuf.len - j + 2) - 1;
|
||||
|
||||
nm_str_buf_erase(&strbuf, j, k, nmtst_get_rand_bool());
|
||||
g_string_erase(gstr, j, k);
|
||||
nm_str_buf_erase(&strbuf, j, k, nmtst_get_rand_bool());
|
||||
g_string_erase(gstr, j, k);
|
||||
if (gstr->str[0])
|
||||
g_assert_cmpstr(gstr->str, ==, nm_str_buf_get_str(&strbuf));
|
||||
else
|
||||
g_assert(NM_IN_STRSET(nm_str_buf_get_str(&strbuf), NULL, ""));
|
||||
}
|
||||
|
||||
for (i_run = 0; i_run < 50; i_run++) {
|
||||
char stack_buf[20];
|
||||
nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT_ARR(stack_buf, nmtst_get_rand_bool());
|
||||
|
||||
nm_str_buf_append_c_len(&strbuf, 'a', nmtst_get_rand_uint32() % (sizeof(stack_buf) * 2));
|
||||
if (strbuf.len <= sizeof(stack_buf)) {
|
||||
g_assert(stack_buf == nm_str_buf_get_str_unsafe(&strbuf));
|
||||
} else
|
||||
return;
|
||||
g_assert(stack_buf != nm_str_buf_get_str_unsafe(&strbuf));
|
||||
|
||||
if (strbuf.len < sizeof(stack_buf)) {
|
||||
g_assert(stack_buf == nm_str_buf_get_str(&strbuf));
|
||||
} else
|
||||
g_assert(stack_buf != nm_str_buf_get_str(&strbuf));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user