glib-aux: merge branch 'th/str-buf-stack-allocated'

https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1203
This commit is contained in:
Thomas Haller 2022-05-09 19:18:48 +02:00
commit c6e41b2df3
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
13 changed files with 143 additions and 60 deletions

View file

@ -1553,7 +1553,7 @@ _external_ids_to_string(const GArray *arr)
if (!arr)
return g_strdup("empty");
nm_str_buf_init(&strbuf, NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE);
strbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_104, FALSE);
nm_str_buf_append(&strbuf, "[");
for (i = 0; i < arr->len; i++) {
const NMUtilsNamedValue *n = &g_array_index(arr, NMUtilsNamedValue, i);

View file

@ -5113,7 +5113,7 @@ nm_utils_spawn_helper(const char *const *args,
fcntl(info->child_stdout, F_SETFL, fd_flags | O_NONBLOCK);
/* Watch process stdin */
nm_str_buf_init(&info->out_buffer, 32, TRUE);
info->out_buffer = NM_STR_BUF_INIT(32, TRUE);
for (arg = args; *arg; arg++) {
nm_str_buf_append(&info->out_buffer, *arg);
nm_str_buf_append_c(&info->out_buffer, '\0');
@ -5127,7 +5127,7 @@ nm_utils_spawn_helper(const char *const *args,
g_source_attach(info->output_source, g_main_context_get_thread_default());
/* Watch process stdout */
nm_str_buf_init(&info->in_buffer, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
info->in_buffer = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
info->input_source = nm_g_unix_fd_source_new(info->child_stdout,
G_IO_IN | G_IO_ERR | G_IO_HUP,
G_PRIORITY_DEFAULT,

View file

@ -210,7 +210,7 @@ nm_keyfile_plugin_kf_set_integer_list_uint(GKeyFile *kf,
g_return_if_fail(group && group[0]);
g_return_if_fail(key && key[0]);
nm_str_buf_init(&strbuf, length * 4u + 2u, FALSE);
strbuf = NM_STR_BUF_INIT(length * 4u + 2u, FALSE);
for (i = 0; i < length; i++)
nm_str_buf_append_printf(&strbuf, "%u;", data[i]);
nm_keyfile_plugin_kf_set_value(kf, group, key, nm_str_buf_get_str(&strbuf));
@ -231,7 +231,7 @@ nm_keyfile_plugin_kf_set_integer_list_uint8(GKeyFile *kf,
g_return_if_fail(group && group[0]);
g_return_if_fail(key && key[0]);
nm_str_buf_init(&strbuf, length * 4u + 2u, FALSE);
strbuf = NM_STR_BUF_INIT(length * 4u + 2u, FALSE);
for (i = 0; i < length; i++)
nm_str_buf_append_printf(&strbuf, "%u;", (guint) data[i]);
nm_keyfile_plugin_kf_set_value(kf, group, key, nm_str_buf_get_str(&strbuf));
@ -542,7 +542,7 @@ _keyfile_key_encode(const char *name, char **out_to_free)
len = i + strlen(&name[i]);
nm_assert(len == strlen(name));
nm_str_buf_init(&str, len + 15u, FALSE);
str = NM_STR_BUF_INIT(len + 15u, FALSE);
if (name[0] == ' ') {
nm_assert(i == 0);

View file

@ -4374,7 +4374,7 @@ nm_keyfile_utils_create_filename(const char *name, gboolean with_extension)
g_return_val_if_fail(name && name[0], NULL);
nm_str_buf_init(&str, 0, FALSE);
str = NM_STR_BUF_INIT(0, FALSE);
len = strlen(name);

View file

@ -435,7 +435,7 @@ nm_bridge_vlan_to_str(const NMBridgeVlan *vlan, GError **error)
* future if more parameters are added to the object that could
* make it invalid. */
nm_str_buf_init(&string, NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE);
string = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE);
if (vlan->vid_start == vlan->vid_end)
nm_str_buf_append_printf(&string, "%u", vlan->vid_start);

View file

@ -3814,7 +3814,7 @@ nm_ip_routing_rule_to_string(const NMIPRoutingRule *self,
}
}
nm_str_buf_init(&str, NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE);
str = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_32, FALSE);
if (self->priority_has) {
nm_str_buf_append_printf(nm_str_buf_append_required_delimiter(&str, ' '),

View file

@ -142,7 +142,7 @@ _nm_utils_enum_to_str_full(GType type,
flags_separator = flags_separator ?: " ";
nm_str_buf_init(&strbuf, 16, FALSE);
strbuf = NM_STR_BUF_INIT(16, FALSE);
for (; value_infos && value_infos->nick; value_infos++) {
nm_assert(_enum_is_valid_flags_nick(value_infos->nick));

View file

@ -2998,7 +2998,7 @@ nm_utils_buf_utf8safe_unescape(const char *str,
return str;
}
nm_str_buf_init(&strbuf, len + 1u, FALSE);
strbuf = NM_STR_BUF_INIT(len + 1u, FALSE);
nm_str_buf_append_len(&strbuf, str, s - str);
str = s;
@ -3165,7 +3165,7 @@ nm_utils_buf_utf8safe_escape(gconstpointer buf,
return str;
}
nm_str_buf_init(&strbuf, buflen + 5, NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET));
strbuf = NM_STR_BUF_INIT(buflen + 5, NM_FLAGS_HAS(flags, NM_UTILS_STR_UTF8_SAFE_FLAG_SECRET));
s = str;
do {
@ -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;
}

View file

@ -26,6 +26,7 @@ typedef struct _NMStrBuf {
};
bool _priv_do_bzero_mem;
bool _priv_malloced;
} NMStrBuf;
/*****************************************************************************/
@ -36,28 +37,55 @@ _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);
}
static inline void
nm_str_buf_init(NMStrBuf *strbuf, gsize len, bool do_bzero_mem)
{
nm_assert(strbuf);
*strbuf = NM_STR_BUF_INIT(len, do_bzero_mem);
_nm_str_buf_assert(strbuf);
}
#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));
void _nm_str_buf_ensure_size(NMStrBuf *strbuf, gsize new_size, gboolean reserve_exact);
@ -359,10 +387,10 @@ static inline gboolean
nm_str_buf_is_initalized(NMStrBuf *strbuf)
{
nm_assert(strbuf);
#if NM_MORE_ASSERTS
if (strbuf->_priv_str)
_nm_str_buf_assert(strbuf);
#endif
if (NM_MORE_ASSERTS > 0) {
if (strbuf->_priv_str)
_nm_str_buf_assert(strbuf);
}
return !!strbuf->_priv_str;
}
@ -463,9 +491,11 @@ nm_str_buf_get_char(const NMStrBuf *strbuf, gsize index)
* Returns: (transfer full): the string of the buffer
* which must be freed by the caller. The @strbuf
* 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. */
* reused after resetting with NM_STR_BUF_INIT().
* Note that if no string is allocated yet (after NM_STR_BUF_INIT() with
* 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 +506,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';
@ -507,7 +547,7 @@ nm_str_buf_finalize_to_gbytes(NMStrBuf *strbuf)
*
* Frees the associated memory of @strbuf. The buffer
* afterwards is in undefined state, but can be re-initialized
* with nm_str_buf_init().
* with NM_STR_BUF_INIT().
*/
static inline void
nm_str_buf_destroy(NMStrBuf *strbuf)
@ -517,7 +557,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

View file

@ -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:
strbuf = NM_STR_BUF_INIT(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));
}
}

View file

@ -468,7 +468,7 @@ _domains_to_string(gboolean include_level_override,
* nm_logging_setup(), because we want to expand "DEFAULT" and "ALL".
*/
nm_str_buf_init(&sbuf, NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE);
sbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE);
for (diter = &domain_desc[0]; diter->name; diter++) {
/* If it's set for any lower level, it will also be set for LOGL_ERR */

View file

@ -14,12 +14,12 @@
* certain buffer sizes.
*
* The use of these defines is to get favorable allocation sequences.
* For example, nm_str_buf_init() asks for an initial allocation size. Note that
* For example, NM_STR_BUF_INIT() asks for an initial allocation size. Note that
* it reserves the exactly requested amount, under the assumption that the
* user may know how many bytes will be required. However, often the caller
* doesn't know in advance, and NMStrBuf grows exponentially by calling
* nm_utils_get_next_realloc_size().
* Imagine you call nm_str_buf_init() with an initial buffer size 100, and you
* Imagine you call NM_STR_BUF_INIT() with an initial buffer size 100, and you
* add one character at a time. Then the first reallocation will increase the
* buffer size only from 100 to 104.
* If you however start with an initial buffer size of 104, then the next reallocation

View file

@ -393,7 +393,7 @@ queue_string_to_helper(AuthRequest *request, const char *response)
g_return_if_fail(response);
if (!nm_str_buf_is_initalized(&request->out_buffer))
nm_str_buf_init(&request->out_buffer, strlen(response) + 2u, TRUE);
request->out_buffer = NM_STR_BUF_INIT(strlen(response) + 2u, TRUE);
nm_str_buf_append(&request->out_buffer, response);
nm_str_buf_ensure_trailing_c(&request->out_buffer, '\n');
@ -587,10 +587,9 @@ create_request(NMPolkitListener *listener,
.cookie = g_strdup(cookie),
.request_any_response = FALSE,
.request_is_completed = FALSE,
.in_buffer = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE),
};
nm_str_buf_init(&request->in_buffer, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, FALSE);
c_list_link_tail(&listener->request_lst_head, &request->request_lst);
return request;
}