shared: avoid allocating temporary buffer for nm_utils_named_values_from_strdict()

Iterating hash tables gives an undefined order. Often we want to have
a stable order, for example when printing the content of a hash or
when converting it to a "a{sv}" variant.

How to achieve that best? I think we should only iterate the hash once,
and not require additional lookups. nm_utils_named_values_from_strdict()
achieves that by returning the key and the value together. Also, often
we only need the list for a short time, so we can avoid heap allocating
the list, if it is short enough. This works by allowing the caller to
provide a pre-allocated buffer (usually on the stack) and only as fallback
allocate a new list.
This commit is contained in:
Thomas Haller 2020-06-06 20:58:08 +02:00
parent 4d6b96b48f
commit ee9e1ceefc
No known key found for this signature in database
GPG key ID: 29C2366E4DFC5728
4 changed files with 50 additions and 24 deletions

View file

@ -368,7 +368,7 @@ static void
_ensure_options_idx_cache (NMSettingBondPrivate *priv)
{
if (!G_UNLIKELY (priv->options_idx_cache))
priv->options_idx_cache = nm_utils_named_values_from_str_dict_with_sort (priv->options, NULL, _get_option_sort, NULL);
priv->options_idx_cache = nm_utils_named_values_from_strdict_full (priv->options, NULL, _get_option_sort, NULL, NULL, 0, NULL);
}
/**

View file

@ -2909,28 +2909,44 @@ nm_utils_fd_read_loop_exact (int fd, void *buf, size_t nbytes, bool do_poll)
/*****************************************************************************/
G_STATIC_ASSERT (G_STRUCT_OFFSET (NMUtilsNamedValue, name) == 0);
NMUtilsNamedValue *
nm_utils_named_values_from_str_dict_with_sort (GHashTable *hash,
guint *out_len,
GCompareDataFunc compare_func,
gpointer user_data)
nm_utils_named_values_from_strdict_full (GHashTable *hash,
guint *out_len,
GCompareDataFunc compare_func,
gpointer user_data,
NMUtilsNamedValue *provided_buffer,
guint provided_buffer_len,
NMUtilsNamedValue **out_allocated_buffer)
{
GHashTableIter iter;
NMUtilsNamedValue *values;
guint i, len;
nm_assert (provided_buffer_len == 0 || provided_buffer);
nm_assert (!out_allocated_buffer || !*out_allocated_buffer);
if ( !hash
|| !(len = g_hash_table_size (hash))) {
NM_SET_OUT (out_len, 0);
return NULL;
}
if (provided_buffer_len >= len + 1) {
/* the buffer provided by the caller is large enough. Use it. */
values = provided_buffer;
} else {
/* allocate a new buffer. */
values = g_new (NMUtilsNamedValue, len + 1);
NM_SET_OUT (out_allocated_buffer, values);
}
i = 0;
values = g_new (NMUtilsNamedValue, len + 1);
g_hash_table_iter_init (&iter, hash);
while (g_hash_table_iter_next (&iter,
(gpointer *) &values[i].name,
(gpointer *) &values[i].value_ptr))
&values[i].value_ptr))
i++;
nm_assert (i == len);
values[i].name = NULL;
@ -4925,7 +4941,7 @@ _nm_utils_format_variant_attributes_full (GString *str,
for (i = 0; i < num_values; i++) {
name = values[i].name;
variant = (GVariant *) values[i].value_ptr;
variant = values[i].value_ptr;
value = NULL;
if (g_variant_is_of_type (variant, G_VARIANT_TYPE_UINT32))
@ -4969,17 +4985,24 @@ _nm_utils_format_variant_attributes (GHashTable *attributes,
char attr_separator,
char key_value_separator)
{
gs_free NMUtilsNamedValue *values_free = NULL;
NMUtilsNamedValue values_prepared[20];
const NMUtilsNamedValue *values;
GString *str = NULL;
gs_free NMUtilsNamedValue *values = NULL;
guint len;
g_return_val_if_fail (attr_separator, NULL);
g_return_val_if_fail (key_value_separator, NULL);
if (!attributes || !g_hash_table_size (attributes))
if (!attributes)
return NULL;
values = nm_utils_named_values_from_str_dict (attributes, &len);
values = nm_utils_named_values_from_strdict (attributes,
&len,
values_prepared,
&values_free);
if (len == 0)
return NULL;
str = g_string_new ("");
_nm_utils_format_variant_attributes_full (str,

View file

@ -1452,18 +1452,16 @@ typedef struct {
#define NM_UTILS_NAMED_VALUE_INIT(n, v) { .name = (n), .value_ptr = (v) }
NMUtilsNamedValue *nm_utils_named_values_from_str_dict_with_sort (GHashTable *hash,
guint *out_len,
GCompareDataFunc compare_func,
gpointer user_data);
NMUtilsNamedValue *nm_utils_named_values_from_strdict_full (GHashTable *hash,
guint *out_len,
GCompareDataFunc compare_func,
gpointer user_data,
NMUtilsNamedValue *provided_buffer,
guint provided_buffer_len,
NMUtilsNamedValue **out_allocated_buffer);
static inline NMUtilsNamedValue *
nm_utils_named_values_from_str_dict (GHashTable *hash, guint *out_len)
{
G_STATIC_ASSERT (G_STRUCT_OFFSET (NMUtilsNamedValue, name) == 0);
return nm_utils_named_values_from_str_dict_with_sort (hash, out_len, nm_strcmp_p_with_data, NULL);
}
#define nm_utils_named_values_from_strdict(hash, out_len, array, out_allocated_buffer) \
nm_utils_named_values_from_strdict_full ((hash), (out_len), nm_strcmp_p_with_data, NULL, (array), G_N_ELEMENTS (array), (out_allocated_buffer))
gssize nm_utils_named_value_list_find (const NMUtilsNamedValue *arr,
gsize len,

View file

@ -4111,12 +4111,17 @@ nm_utils_parse_dns_domain (const char *domain, gboolean *is_routing)
GVariant *
nm_utils_strdict_to_variant (GHashTable *options)
{
gs_free NMUtilsNamedValue *values_free = NULL;
NMUtilsNamedValue values_prepared[20];
const NMUtilsNamedValue *values;
GVariantBuilder builder;
gs_free NMUtilsNamedValue *values = NULL;
guint i;
guint n;
values = nm_utils_named_values_from_str_dict (options, &n);
values = nm_utils_named_values_from_strdict (options,
&n,
values_prepared,
&values_free);
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
for (i = 0; i < n; i++) {