Coverage Report

Created: 2025-07-11 06:59

/src/Python-3.8.3/Python/pyarena.c
Line
Count
Source (jump to first uncovered line)
1
#include "Python.h"
2
3
/* A simple arena block structure.
4
5
   Measurements with standard library modules suggest the average
6
   allocation is about 20 bytes and that most compiles use a single
7
   block.
8
9
   TODO(jhylton): Think about a realloc API, maybe just for the last
10
   allocation?
11
*/
12
13
16
#define DEFAULT_BLOCK_SIZE 8192
14
#define ALIGNMENT               8
15
16
typedef struct _block {
17
    /* Total number of bytes owned by this block available to pass out.
18
     * Read-only after initialization.  The first such byte starts at
19
     * ab_mem.
20
     */
21
    size_t ab_size;
22
23
    /* Total number of bytes already passed out.  The next byte available
24
     * to pass out starts at ab_mem + ab_offset.
25
     */
26
    size_t ab_offset;
27
28
    /* An arena maintains a singly-linked, NULL-terminated list of
29
     * all blocks owned by the arena.  These are linked via the
30
     * ab_next member.
31
     */
32
    struct _block *ab_next;
33
34
    /* Pointer to the first allocatable byte owned by this block.  Read-
35
     * only after initialization.
36
     */
37
    void *ab_mem;
38
} block;
39
40
/* The arena manages two kinds of memory, blocks of raw memory
41
   and a list of PyObject* pointers.  PyObjects are decrefed
42
   when the arena is freed.
43
*/
44
45
struct _arena {
46
    /* Pointer to the first block allocated for the arena, never NULL.
47
       It is used only to find the first block when the arena is
48
       being freed.
49
     */
50
    block *a_head;
51
52
    /* Pointer to the block currently used for allocation.  Its
53
       ab_next field should be NULL.  If it is not-null after a
54
       call to block_alloc(), it means a new block has been allocated
55
       and a_cur should be reset to point it.
56
     */
57
    block *a_cur;
58
59
    /* A Python list object containing references to all the PyObject
60
       pointers associated with this arena.  They will be DECREFed
61
       when the arena is freed.
62
    */
63
    PyObject *a_objects;
64
65
#if defined(Py_DEBUG)
66
    /* Debug output */
67
    size_t total_allocs;
68
    size_t total_size;
69
    size_t total_blocks;
70
    size_t total_block_size;
71
    size_t total_big_blocks;
72
#endif
73
};
74
75
static block *
76
block_new(size_t size)
77
16
{
78
    /* Allocate header and block as one unit.
79
       ab_mem points just past header. */
80
16
    block *b = (block *)PyMem_Malloc(sizeof(block) + size);
81
16
    if (!b)
82
0
        return NULL;
83
16
    b->ab_size = size;
84
16
    b->ab_mem = (void *)(b + 1);
85
16
    b->ab_next = NULL;
86
16
    b->ab_offset = (char *)_Py_ALIGN_UP(b->ab_mem, ALIGNMENT) -
87
16
            (char *)(b->ab_mem);
88
16
    return b;
89
16
}
90
91
static void
92
16
block_free(block *b) {
93
32
    while (b) {
94
16
        block *next = b->ab_next;
95
16
        PyMem_Free(b);
96
16
        b = next;
97
16
    }
98
16
}
99
100
static void *
101
block_alloc(block *b, size_t size)
102
1.16k
{
103
1.16k
    void *p;
104
1.16k
    assert(b);
105
1.16k
    size = _Py_SIZE_ROUND_UP(size, ALIGNMENT);
106
1.16k
    if (b->ab_offset + size > b->ab_size) {
107
        /* If we need to allocate more memory than will fit in
108
           the default block, allocate a one-off block that is
109
           exactly the right size. */
110
        /* TODO(jhylton): Think about space waste at end of block */
111
0
        block *newbl = block_new(
112
0
                        size < DEFAULT_BLOCK_SIZE ?
113
0
                        DEFAULT_BLOCK_SIZE : size);
114
0
        if (!newbl)
115
0
            return NULL;
116
0
        assert(!b->ab_next);
117
0
        b->ab_next = newbl;
118
0
        b = newbl;
119
0
    }
120
121
1.16k
    assert(b->ab_offset + size <= b->ab_size);
122
1.16k
    p = (void *)(((char *)b->ab_mem) + b->ab_offset);
123
1.16k
    b->ab_offset += size;
124
1.16k
    return p;
125
1.16k
}
126
127
PyArena *
128
PyArena_New()
129
16
{
130
16
    PyArena* arena = (PyArena *)PyMem_Malloc(sizeof(PyArena));
131
16
    if (!arena)
132
0
        return (PyArena*)PyErr_NoMemory();
133
134
16
    arena->a_head = block_new(DEFAULT_BLOCK_SIZE);
135
16
    arena->a_cur = arena->a_head;
136
16
    if (!arena->a_head) {
137
0
        PyMem_Free((void *)arena);
138
0
        return (PyArena*)PyErr_NoMemory();
139
0
    }
140
16
    arena->a_objects = PyList_New(0);
141
16
    if (!arena->a_objects) {
142
0
        block_free(arena->a_head);
143
0
        PyMem_Free((void *)arena);
144
0
        return (PyArena*)PyErr_NoMemory();
145
0
    }
146
#if defined(Py_DEBUG)
147
    arena->total_allocs = 0;
148
    arena->total_size = 0;
149
    arena->total_blocks = 1;
150
    arena->total_block_size = DEFAULT_BLOCK_SIZE;
151
    arena->total_big_blocks = 0;
152
#endif
153
16
    return arena;
154
16
}
155
156
void
157
PyArena_Free(PyArena *arena)
158
16
{
159
16
    assert(arena);
160
#if defined(Py_DEBUG)
161
    /*
162
    fprintf(stderr,
163
        "alloc=%zu size=%zu blocks=%zu block_size=%zu big=%zu objects=%zu\n",
164
        arena->total_allocs, arena->total_size, arena->total_blocks,
165
        arena->total_block_size, arena->total_big_blocks,
166
        PyList_Size(arena->a_objects));
167
    */
168
#endif
169
16
    block_free(arena->a_head);
170
    /* This property normally holds, except when the code being compiled
171
       is sys.getobjects(0), in which case there will be two references.
172
    assert(arena->a_objects->ob_refcnt == 1);
173
    */
174
175
16
    Py_DECREF(arena->a_objects);
176
16
    PyMem_Free(arena);
177
16
}
178
179
void *
180
PyArena_Malloc(PyArena *arena, size_t size)
181
1.16k
{
182
1.16k
    void *p = block_alloc(arena->a_cur, size);
183
1.16k
    if (!p)
184
0
        return PyErr_NoMemory();
185
#if defined(Py_DEBUG)
186
    arena->total_allocs++;
187
    arena->total_size += size;
188
#endif
189
    /* Reset cur if we allocated a new block. */
190
1.16k
    if (arena->a_cur->ab_next) {
191
0
        arena->a_cur = arena->a_cur->ab_next;
192
#if defined(Py_DEBUG)
193
        arena->total_blocks++;
194
        arena->total_block_size += arena->a_cur->ab_size;
195
        if (arena->a_cur->ab_size > DEFAULT_BLOCK_SIZE)
196
            ++arena->total_big_blocks;
197
#endif
198
0
    }
199
1.16k
    return p;
200
1.16k
}
201
202
int
203
PyArena_AddPyObject(PyArena *arena, PyObject *obj)
204
446
{
205
446
    int r = PyList_Append(arena->a_objects, obj);
206
446
    if (r >= 0) {
207
446
        Py_DECREF(obj);
208
446
    }
209
446
    return r;
210
446
}