summary refs log tree commit diff
path: root/shared/nm-std-aux/nm-std-utils.c
blob: 6f7f4c58af8087e63e0ca35e5b730375cecf8018 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/* SPDX-License-Identifier: LGPL-2.1+ */

#include "nm-default.h"

#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;
}