mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-01-04 09:50:17 +01:00
shared: move nm_utils_get_next_realloc_size() to nm-std-aux
This commit is contained in:
parent
19613016e2
commit
220825836a
4 changed files with 100 additions and 95 deletions
|
|
@ -97,75 +97,6 @@ G_STATIC_ASSERT (ETH_ALEN == 6);
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
gsize
|
||||
nm_utils_get_next_realloc_size (gboolean true_realloc, gsize requested)
|
||||
{
|
||||
gsize n, x;
|
||||
|
||||
/* https://doc.qt.io/qt-5/containers.html#growth-strategies */
|
||||
|
||||
if (requested <= 40) {
|
||||
/* small allocations. Increase in small steps of 8 bytes.
|
||||
*
|
||||
* We get thus sizes of 8, 16, 32, 40. */
|
||||
if (requested <= 8)
|
||||
return 8;
|
||||
if (requested <= 16)
|
||||
return 16;
|
||||
if (requested <= 32)
|
||||
return 32;
|
||||
|
||||
/* The return values for < 104 are essentially hard-coded, and the choice here is
|
||||
* made without very strong reasons.
|
||||
*
|
||||
* We want to stay 24 bytes below the power-of-two border 64. Hence, return 40 here.
|
||||
* However, the next step then is already 104 (128 - 24). It's a larger gap than in
|
||||
* the steps before.
|
||||
*
|
||||
* It's not clear whether some of the steps should be adjusted (or how exactly). */
|
||||
return 40;
|
||||
}
|
||||
|
||||
if ( requested <= 0x2000u - 24u
|
||||
|| G_UNLIKELY (!true_realloc)) {
|
||||
/* mid sized allocations. Return next power of two, minus 24 bytes extra space
|
||||
* at the beginning.
|
||||
* That means, we double the size as we grow.
|
||||
*
|
||||
* With !true_realloc, it means that the caller does not intend to call
|
||||
* realloc() but instead clone the buffer. This is for example the case, when we
|
||||
* want to nm_explicit_bzero() the old buffer. In that case we really want to grow
|
||||
* the buffer exponentially every time and not increment in page sizes of 4K (below).
|
||||
*
|
||||
* We get thus sizes of 104, 232, 488, 1000, 2024, 4072, 8168... */
|
||||
|
||||
if (G_UNLIKELY (requested > G_MAXSIZE / 2u - 24u))
|
||||
return G_MAXSIZE;
|
||||
|
||||
x = requested + 24u;
|
||||
n = 128u;
|
||||
while (n < x) {
|
||||
n <<= 1;
|
||||
nm_assert (n > 128u);
|
||||
}
|
||||
|
||||
nm_assert (n > 24u && n - 24u >= requested);
|
||||
return n - 24u;
|
||||
}
|
||||
|
||||
if (G_UNLIKELY (requested > G_MAXSIZE - 0x1000u - 24u))
|
||||
return G_MAXSIZE;
|
||||
|
||||
/* For large allocations (with !true_realloc) we allocate memory in chunks of
|
||||
* 4K (- 24 bytes extra), assuming that the memory gets mmapped and thus
|
||||
* realloc() is efficient by just reordering pages. */
|
||||
n = ((requested + (0x0FFFu + 24u)) & ~((gsize) 0x0FFFu)) - 24u;
|
||||
nm_assert (n >= requested);
|
||||
return n;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
pid_t
|
||||
nm_utils_gettid (void)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1993,32 +1993,6 @@ void nm_indirect_g_free (gpointer arg);
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* nm_utils_get_next_realloc_size() is used to grow buffers exponentially, when
|
||||
* the final size is unknown. As such, it has borders for which it allocates
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* via nm_utils_get_next_realloc_size() gives you 232, and so on. By using
|
||||
* these sizes, it results in one less allocation, if you anyway don't know the
|
||||
* exact size in advance. */
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_32 ((gsize) 32)
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_40 ((gsize) 40)
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_104 ((gsize) 104)
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 ((gsize) 1000)
|
||||
|
||||
gsize nm_utils_get_next_realloc_size (gboolean true_realloc, gsize requested);
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
NMU_IFACE_ANY,
|
||||
NMU_IFACE_KERNEL,
|
||||
|
|
|
|||
|
|
@ -4,3 +4,73 @@
|
|||
|
||||
#include "nm-std-utils.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
size_t
|
||||
nm_utils_get_next_realloc_size (bool true_realloc, size_t requested)
|
||||
{
|
||||
size_t n, x;
|
||||
|
||||
/* https://doc.qt.io/qt-5/containers.html#growth-strategies */
|
||||
|
||||
if (requested <= 40) {
|
||||
/* small allocations. Increase in small steps of 8 bytes.
|
||||
*
|
||||
* We get thus sizes of 8, 16, 32, 40. */
|
||||
if (requested <= 8)
|
||||
return 8;
|
||||
if (requested <= 16)
|
||||
return 16;
|
||||
if (requested <= 32)
|
||||
return 32;
|
||||
|
||||
/* The return values for < 104 are essentially hard-coded, and the choice here is
|
||||
* made without very strong reasons.
|
||||
*
|
||||
* We want to stay 24 bytes below the power-of-two border 64. Hence, return 40 here.
|
||||
* However, the next step then is already 104 (128 - 24). It's a larger gap than in
|
||||
* the steps before.
|
||||
*
|
||||
* It's not clear whether some of the steps should be adjusted (or how exactly). */
|
||||
return 40;
|
||||
}
|
||||
|
||||
if ( requested <= 0x2000u - 24u
|
||||
|| NM_UNLIKELY (!true_realloc)) {
|
||||
/* mid sized allocations. Return next power of two, minus 24 bytes extra space
|
||||
* at the beginning.
|
||||
* That means, we double the size as we grow.
|
||||
*
|
||||
* With !true_realloc, it means that the caller does not intend to call
|
||||
* realloc() but instead clone the buffer. This is for example the case, when we
|
||||
* want to nm_explicit_bzero() the old buffer. In that case we really want to grow
|
||||
* the buffer exponentially every time and not increment in page sizes of 4K (below).
|
||||
*
|
||||
* We get thus sizes of 104, 232, 488, 1000, 2024, 4072, 8168... */
|
||||
|
||||
if (NM_UNLIKELY (requested > SIZE_MAX / 2u - 24u))
|
||||
return SIZE_MAX;
|
||||
|
||||
x = requested + 24u;
|
||||
n = 128u;
|
||||
while (n < x) {
|
||||
n <<= 1;
|
||||
nm_assert (n > 128u);
|
||||
}
|
||||
|
||||
nm_assert (n > 24u && n - 24u >= requested);
|
||||
return n - 24u;
|
||||
}
|
||||
|
||||
if (NM_UNLIKELY (requested > SIZE_MAX - 0x1000u - 24u))
|
||||
return SIZE_MAX;
|
||||
|
||||
/* For large allocations (with !true_realloc) we allocate memory in chunks of
|
||||
* 4K (- 24 bytes extra), assuming that the memory gets mmapped and thus
|
||||
* realloc() is efficient by just reordering pages. */
|
||||
n = ((requested + (0x0FFFu + 24u)) & ~((size_t) 0x0FFFu)) - 24u;
|
||||
nm_assert (n >= requested);
|
||||
return n;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,4 +3,34 @@
|
|||
#ifndef __NM_STD_UTILS_H__
|
||||
#define __NM_STD_UTILS_H__
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "nm-std-aux.h"
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/* nm_utils_get_next_realloc_size() is used to grow buffers exponentially, when
|
||||
* the final size is unknown. As such, it has borders for which it allocates
|
||||
* 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
|
||||
* 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
|
||||
* 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
|
||||
* via nm_utils_get_next_realloc_size() gives you 232, and so on. By using
|
||||
* these sizes, it results in one less allocation, if you anyway don't know the
|
||||
* exact size in advance. */
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_32 ((size_t) 32)
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_40 ((size_t) 40)
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_104 ((size_t) 104)
|
||||
#define NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 ((size_t) 1000)
|
||||
|
||||
size_t nm_utils_get_next_realloc_size (bool true_realloc, size_t requested);
|
||||
|
||||
#endif /* __NM_STD_UTILS_H__ */
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue