mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-27 08:10:37 +01:00
glib-aux: add NMPtrArray helper
Add a simple structure that can own a list of pointers. The main purpose is to return one or more pointers from a function, where ownership gets transferred. This is a more limited form of a GPtrArray. Except, it actually NULL terminates the list, and it requires only one heap allocation for the structure and the list. With using NMPtrArrayStack, it even can track the first 8 pointers (in x64_86) on the stack and avoiding heap allocation altogether. That is of course only useful, when the ptr array is not to be returned from the function (to the caller), but instead passed down to called functions which can fill it.
This commit is contained in:
parent
7bdb606a91
commit
3e8ec66df6
5 changed files with 326 additions and 0 deletions
|
|
@ -462,6 +462,8 @@ src_libnm_glib_aux_libnm_glib_aux_la_SOURCES = \
|
|||
src/libnm-glib-aux/nm-obj.h \
|
||||
src/libnm-glib-aux/nm-prioq.c \
|
||||
src/libnm-glib-aux/nm-prioq.h \
|
||||
src/libnm-glib-aux/nm-ptr-array.c \
|
||||
src/libnm-glib-aux/nm-ptr-array.h \
|
||||
src/libnm-glib-aux/nm-random-utils.c \
|
||||
src/libnm-glib-aux/nm-random-utils.h \
|
||||
src/libnm-glib-aux/nm-ref-string.c \
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ libnm_glib_aux = static_library(
|
|||
'nm-ref-string.c',
|
||||
'nm-secret-utils.c',
|
||||
'nm-shared-utils.c',
|
||||
'nm-ptr-array.c',
|
||||
'nm-time-utils.c',
|
||||
'nm-uuid.c',
|
||||
),
|
||||
|
|
|
|||
153
src/libnm-glib-aux/nm-ptr-array.c
Normal file
153
src/libnm-glib-aux/nm-ptr-array.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "libnm-glib-aux/nm-default-glib-i18n-lib.h"
|
||||
|
||||
#include "nm-ptr-array.h"
|
||||
|
||||
#include "libnm-std-aux/nm-std-utils.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
#define _MALLOCSIZE_FROM_RESERVED(reserved) \
|
||||
(G_STRUCT_OFFSET(NMPtrArray, ptrs) + (sizeof(gpointer) * ((reserved) + 1u)))
|
||||
|
||||
G_STATIC_ASSERT(sizeof(NMPtrArrayStack) <= NM_UTILS_GET_NEXT_REALLOC_SIZE_104);
|
||||
G_STATIC_ASSERT(G_N_ELEMENTS(((NMPtrArrayStack *) NULL)->_ptrs) > 1);
|
||||
G_STATIC_ASSERT(G_N_ELEMENTS(((NMPtrArrayStack *) NULL)->_ptrs)
|
||||
== _NM_PTR_ARRAY_MALLOCSIZE_TO_RESERVED(NM_UTILS_GET_NEXT_REALLOC_SIZE_104) + 1u);
|
||||
|
||||
G_STATIC_ASSERT(G_STRUCT_OFFSET(NMPtrArray, ptrs) == G_STRUCT_OFFSET(NMPtrArrayStack, _ptrs));
|
||||
|
||||
static gsize
|
||||
_mallocsize_from_reserved(gsize reserved)
|
||||
{
|
||||
nm_assert(reserved < ((G_MAXSIZE - G_STRUCT_OFFSET(NMPtrArray, ptrs)) / sizeof(gpointer)) - 1u);
|
||||
|
||||
return _MALLOCSIZE_FROM_RESERVED(reserved);
|
||||
}
|
||||
|
||||
static gsize
|
||||
_mallocsize_to_reserved(gsize size)
|
||||
{
|
||||
gsize reserved;
|
||||
|
||||
reserved = _NM_PTR_ARRAY_MALLOCSIZE_TO_RESERVED(size);
|
||||
nm_assert(size >= _mallocsize_from_reserved(reserved));
|
||||
return reserved;
|
||||
}
|
||||
|
||||
NMPtrArray *
|
||||
nm_ptr_array_new(GDestroyNotify destroy_fcn, gsize reserved)
|
||||
{
|
||||
NMPtrArray *arr;
|
||||
|
||||
if (reserved < 3u)
|
||||
reserved = 3u;
|
||||
|
||||
arr = g_malloc(_mallocsize_from_reserved(reserved));
|
||||
|
||||
*((gsize *) &arr->len) = 0;
|
||||
arr->_reserved = reserved;
|
||||
arr->_destroy_fcn = destroy_fcn;
|
||||
*((bool *) &arr->is_stack) = FALSE;
|
||||
arr->ptrs[0] = NULL;
|
||||
|
||||
return arr;
|
||||
}
|
||||
|
||||
void
|
||||
nm_ptr_array_add_n(NMPtrArray **p_arr, gsize n, gpointer *ptrs)
|
||||
{
|
||||
NMPtrArray *arr;
|
||||
gsize new_reserved;
|
||||
gsize new_len;
|
||||
|
||||
nm_assert(p_arr);
|
||||
nm_assert(*p_arr);
|
||||
|
||||
if (n == 0)
|
||||
return;
|
||||
|
||||
arr = *p_arr;
|
||||
|
||||
nm_assert(n < G_MAXSIZE - arr->len);
|
||||
new_len = arr->len + n;
|
||||
|
||||
nm_assert(new_len > arr->len);
|
||||
|
||||
/* Note that arr->_reserved does not count the element for the
|
||||
* last trailing NULL. That is, arr->ptrs[arr->_reserved] is valid.
|
||||
* In other words, new_len may be as large as arr->reserved before
|
||||
* we need to reallocate, and `arr->ptrs[new_len] = NULL` is correct. */
|
||||
|
||||
if (new_len > arr->_reserved) {
|
||||
gsize n_bytes;
|
||||
|
||||
/* We grow the total buffer size using nm_utils_get_next_realloc_size().
|
||||
* This quite aggressively increases the buffer size. The idea is that
|
||||
* NMPtrArray is mostly used for short lived purposes, and it's OK to
|
||||
* waste some space to reduce re-allocation. */
|
||||
n_bytes = nm_utils_get_next_realloc_size(TRUE, _mallocsize_from_reserved(new_len));
|
||||
|
||||
new_reserved = _mallocsize_to_reserved(n_bytes);
|
||||
|
||||
nm_assert(new_len <= new_reserved);
|
||||
nm_assert(n_bytes >= _mallocsize_from_reserved(new_reserved));
|
||||
|
||||
if (arr->is_stack) {
|
||||
NMPtrArray *arr2 = arr;
|
||||
|
||||
arr = g_malloc(_mallocsize_from_reserved(new_reserved));
|
||||
memcpy(arr, arr2, _mallocsize_from_reserved(arr2->_reserved));
|
||||
*((bool *) &arr->is_stack) = FALSE;
|
||||
} else {
|
||||
arr = g_realloc(arr, _mallocsize_from_reserved(new_reserved));
|
||||
}
|
||||
arr->_reserved = new_reserved;
|
||||
|
||||
*p_arr = arr;
|
||||
}
|
||||
|
||||
memcpy(&arr->ptrs[arr->len], ptrs, sizeof(gpointer) * n);
|
||||
arr->ptrs[new_len] = NULL;
|
||||
*((gsize *) &arr->len) = new_len;
|
||||
}
|
||||
|
||||
void
|
||||
nm_ptr_array_clear(NMPtrArray *arr)
|
||||
{
|
||||
if (!arr)
|
||||
return;
|
||||
|
||||
if (arr->len == 0)
|
||||
return;
|
||||
|
||||
if (!arr->_destroy_fcn) {
|
||||
(*((gsize *) &arr->len)) = 0;
|
||||
arr->ptrs[0] = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
gsize idx;
|
||||
gpointer p;
|
||||
|
||||
idx = (--(*((gsize *) &arr->len)));
|
||||
|
||||
p = g_steal_pointer(&arr->ptrs[idx]);
|
||||
|
||||
if (p)
|
||||
arr->_destroy_fcn(p);
|
||||
} while (arr->len > 0);
|
||||
}
|
||||
|
||||
void
|
||||
nm_ptr_array_destroy(NMPtrArray *arr)
|
||||
{
|
||||
if (!arr)
|
||||
return;
|
||||
|
||||
nm_ptr_array_clear(arr);
|
||||
if (!arr->is_stack)
|
||||
g_free(arr);
|
||||
}
|
||||
105
src/libnm-glib-aux/nm-ptr-array.h
Normal file
105
src/libnm-glib-aux/nm-ptr-array.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#ifndef __NM_PTR_ARRAY_H__
|
||||
#define __NM_PTR_ARRAY_H__
|
||||
|
||||
typedef struct _NMPtrArray {
|
||||
const gsize len;
|
||||
|
||||
/* How many elements are allocated/reserved for the ptrs array.
|
||||
* Note that there is always an extra space reserved for the
|
||||
* NULL termination afterwards. It means, "len" can grow up
|
||||
* until (including) _reserved, before reallocation is necessary.
|
||||
*
|
||||
* In other words, arr->ptrs[arr->_reserved] is allocated and reserved
|
||||
* for the trailing NULL (but may be uninitialized if the array is shorter). */
|
||||
gsize _reserved;
|
||||
|
||||
GDestroyNotify _destroy_fcn;
|
||||
|
||||
const bool is_stack;
|
||||
|
||||
/* This will be the NULL terminated list of pointers. If you
|
||||
* know what you are doing, you can also steal elements from
|
||||
* the list. */
|
||||
gpointer ptrs[];
|
||||
} NMPtrArray;
|
||||
|
||||
#define _NM_PTR_ARRAY_MALLOCSIZE_TO_RESERVED(size) \
|
||||
((((size) -G_STRUCT_OFFSET(NMPtrArray, ptrs)) / sizeof(gpointer)) - 1u)
|
||||
|
||||
#define NM_PTR_ARRAY_STACK_RESERVED \
|
||||
_NM_PTR_ARRAY_MALLOCSIZE_TO_RESERVED(NM_UTILS_GET_NEXT_REALLOC_SIZE_104)
|
||||
|
||||
/* For cases where we don't need to pass a NMPtrArray to the caller, we can
|
||||
* start with a stack-allocated array. That one starts with 8 pointers
|
||||
* reserved (or 104 bytes) on the stack (on x64_86). 104 is also the magic
|
||||
* number that is suitable for reallocation. See NM_UTILS_GET_NEXT_REALLOC_SIZE_104.
|
||||
*
|
||||
* Usage:
|
||||
* NMPtrArrayStack arr_stack = NM_PTR_ARRAY_STACK_INIT(g_free);
|
||||
* nm_auto_ptrarray *arr = &arr_stack.arr;
|
||||
* ...
|
||||
* collect_pointers(args, &arr);
|
||||
* ...
|
||||
* do_something_with_pointers(arr);
|
||||
**/
|
||||
typedef struct {
|
||||
NMPtrArray arr;
|
||||
gpointer _ptrs[NM_PTR_ARRAY_STACK_RESERVED + 1];
|
||||
} NMPtrArrayStack;
|
||||
|
||||
#define NM_PTR_ARRAY_STACK_INIT(destroy_fcn) \
|
||||
((NMPtrArrayStack){ \
|
||||
.arr = \
|
||||
{ \
|
||||
.len = 0, \
|
||||
._reserved = NM_PTR_ARRAY_STACK_RESERVED, \
|
||||
._destroy_fcn = ((GDestroyNotify) (destroy_fcn)), \
|
||||
.is_stack = TRUE, \
|
||||
}, \
|
||||
._ptrs = {NULL}, \
|
||||
})
|
||||
|
||||
NMPtrArray *nm_ptr_array_new(GDestroyNotify destroy_fcn, gsize reserved);
|
||||
|
||||
void nm_ptr_array_add_n(NMPtrArray **arr, gsize n, gpointer *ptrs);
|
||||
|
||||
static inline void
|
||||
nm_ptr_array_add(NMPtrArray **p_arr, gpointer ptr)
|
||||
{
|
||||
NMPtrArray *arr;
|
||||
|
||||
nm_assert(p_arr);
|
||||
nm_assert(*p_arr);
|
||||
|
||||
arr = *p_arr;
|
||||
|
||||
if (G_LIKELY(arr->len < arr->_reserved)) {
|
||||
/* Fast-path. We don't need to reallocate. */
|
||||
arr->ptrs[arr->len] = ptr;
|
||||
*((gsize *) &arr->len) += 1;
|
||||
arr->ptrs[arr->len] = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
nm_ptr_array_add_n(p_arr, 1, &ptr);
|
||||
}
|
||||
|
||||
static inline NMPtrArray *
|
||||
nm_ptr_array_set_free_func(NMPtrArray *arr, GDestroyNotify destroy_fcn)
|
||||
{
|
||||
nm_assert(arr);
|
||||
|
||||
arr->_destroy_fcn = destroy_fcn;
|
||||
return arr;
|
||||
}
|
||||
|
||||
void nm_ptr_array_clear(NMPtrArray *arr);
|
||||
|
||||
void nm_ptr_array_destroy(NMPtrArray *arr);
|
||||
|
||||
NM_AUTO_DEFINE_FCN0(NMPtrArray *, _nm_auto_ptrarray, nm_ptr_array_destroy);
|
||||
#define nm_auto_ptrarray nm_auto(_nm_auto_ptrarray)
|
||||
|
||||
#endif /* __NM_PTR_ARRAY_H__ */
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
#include "libnm-glib-aux/nm-ref-string.h"
|
||||
#include "libnm-glib-aux/nm-io-utils.h"
|
||||
#include "libnm-glib-aux/nm-prioq.h"
|
||||
#include "libnm-glib-aux/nm-ptr-array.h"
|
||||
|
||||
#include "libnm-glib-aux/nm-test-utils.h"
|
||||
|
||||
|
|
@ -2630,6 +2631,69 @@ test_uid_to_name(void)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
test_nm_ptr_array(void)
|
||||
{
|
||||
const int N_RUN = 100;
|
||||
int i_run;
|
||||
|
||||
for (i_run = 0; i_run < N_RUN; i_run++) {
|
||||
NMPtrArrayStack arr_stack = NM_PTR_ARRAY_STACK_INIT(g_free);
|
||||
char sbuf_num[64];
|
||||
nm_auto_ptrarray NMPtrArray *arr = NULL;
|
||||
int n_add;
|
||||
int i_add;
|
||||
guint counter;
|
||||
guint n_ptr;
|
||||
guint i_ptr;
|
||||
|
||||
counter = 0;
|
||||
if (nmtst_get_rand_bool())
|
||||
arr = nm_ptr_array_new(g_free, nmtst_get_rand_uint32() % 10);
|
||||
else {
|
||||
/* NMPtrArray can also start with using a stack allocated array,
|
||||
* via NMPtrArrayStack/NM_PTR_ARRAY_STACK_INIT(). */
|
||||
arr = &arr_stack.arr;
|
||||
}
|
||||
|
||||
n_add = nmtst_get_rand_uint32() % 10u;
|
||||
for (i_add = 0; i_add < n_add; i_add++) {
|
||||
gpointer ptrs[NM_PTR_ARRAY_STACK_RESERVED + 10];
|
||||
|
||||
n_ptr = nmtst_get_rand_uint32() % G_N_ELEMENTS(ptrs);
|
||||
for (i_ptr = 0; i_ptr < n_ptr; i_ptr++)
|
||||
ptrs[i_ptr] = g_strdup(nm_sprintf_buf(sbuf_num, "%u", counter++));
|
||||
|
||||
if (n_ptr == 1 && nmtst_get_rand_bool())
|
||||
nm_ptr_array_add(&arr, ptrs[0]);
|
||||
else
|
||||
nm_ptr_array_add_n(&arr, n_ptr, ptrs);
|
||||
|
||||
if (i_add == n_add - 1 || nmtst_get_rand_one_case_in(5)) {
|
||||
g_assert_cmpint(arr->len, ==, counter);
|
||||
g_assert_cmpint(arr->_reserved, >=, counter);
|
||||
g_assert(arr->_destroy_fcn == g_free);
|
||||
for (i_ptr = 0; i_ptr < counter; i_ptr++) {
|
||||
nm_sprintf_buf(sbuf_num, "%u", i_ptr);
|
||||
g_assert_cmpstr(arr->ptrs[i_ptr], ==, sbuf_num);
|
||||
}
|
||||
g_assert(!arr->ptrs[counter]);
|
||||
}
|
||||
}
|
||||
|
||||
if (nmtst_get_rand_bool()) {
|
||||
nm_ptr_array_set_free_func(arr, NULL);
|
||||
for (i_ptr = 0; i_ptr < arr->len; i_ptr++)
|
||||
nm_clear_g_free(&arr->ptrs[i_ptr]);
|
||||
}
|
||||
|
||||
if (nmtst_get_rand_bool())
|
||||
nm_clear_pointer(&arr, nm_ptr_array_destroy);
|
||||
}
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NMTST_DEFINE();
|
||||
|
||||
int
|
||||
|
|
@ -2682,6 +2746,7 @@ main(int argc, char **argv)
|
|||
g_test_add_func("/general/test_nm_prioq", test_nm_prioq);
|
||||
g_test_add_func("/general/test_nm_random", test_nm_random);
|
||||
g_test_add_func("/general/test_uid_to_name", test_uid_to_name);
|
||||
g_test_add_func("/general/test_nm_ptr_array", test_nm_ptr_array);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue