Nuitka
The Python compiler
Loading...
Searching...
No Matches
HelpersTuples.c
1// Copyright 2026, Kay Hayen, mailto:kay.hayen@gmail.com find license text at end of file
2
3/* These helpers are used to work with tuples.
4
5*/
6
7// This file is included from another C file, help IDEs to still parse it on
8// its own.
9#ifdef __IDE_ONLY__
10#include "nuitka/prelude.h"
11#endif
12
13#if NUITKA_TUPLE_HAS_FREELIST
14
15PyObject *MAKE_TUPLE_EMPTY(PyThreadState *tstate, Py_ssize_t size) {
16 PyTupleObject *result_tuple;
17
18 // Lets not get called other than this
19 assert(size > 0);
20
21#if PYTHON_VERSION >= 0x3e0
22 Py_ssize_t index = size - 1;
23 bool needs_recycle = false;
24
25 if (index < PyTuple_MAXSAVESIZE) {
26 result_tuple = (PyTupleObject *)Nuitka_PyFreeList_Pop(&Nuitka_Py_freelists_GET(tstate)->tuples[index]);
27 } else {
28 result_tuple = NULL;
29 }
30
31 if (result_tuple == NULL) {
32 result_tuple = (PyTupleObject *)Nuitka_GC_NewVar(&PyTuple_Type, size);
33 } else {
34 Nuitka_Py_NewReference((PyObject *)result_tuple);
35 needs_recycle = true;
36 }
37#else
38 // This is the CPython name, spell-checker: ignore numfree
39#if PYTHON_VERSION < 0x3d0
40 PyTupleObject **items = tstate->interp->tuple.free_list;
41 int *numfree = tstate->interp->tuple.numfree;
42#else
43 struct _Py_object_freelists *freelists = _Nuitka_object_freelists_GET(tstate);
44 struct _Py_tuple_freelist *state = &freelists->tuples;
45 PyTupleObject **items = state->items;
46 int *numfree = state->numfree;
47#endif
48
49#if PYTHON_VERSION < 0x3b0
50 Py_ssize_t index = size;
51#else
52 Py_ssize_t index = size - 1;
53#endif
54
55 if ((size < PyTuple_MAXSAVESIZE) && (result_tuple = items[index]) != NULL) {
56 items[index] = (PyTupleObject *)result_tuple->ob_item[0];
57 numfree[index] -= 1;
58
59 assert(Py_SIZE(result_tuple) == size);
60 assert(Py_TYPE(result_tuple) == &PyTuple_Type);
61
62 Nuitka_Py_NewReference((PyObject *)result_tuple);
63 } else {
64 // Check for overflow
65 if ((size_t)size >
66 ((size_t)PY_SSIZE_T_MAX - (sizeof(PyTupleObject) - sizeof(PyObject *))) / sizeof(PyObject *)) {
67 return PyErr_NoMemory();
68 }
69
70 result_tuple = (PyTupleObject *)Nuitka_GC_NewVar(&PyTuple_Type, size);
71 }
72#endif
73
74 // TODO: Why not use memset here, and can we rely on memory being cleared
75 // by allocator?
76
77 // TODO: When first initializing the tuple, we might skip this and use
78 // assignments that ignore this.
79 for (Py_ssize_t i = 0; i < size; i++) {
80 result_tuple->ob_item[i] = NULL;
81 }
82
83#if PYTHON_VERSION >= 0x3e0
84 // Python 3.14 tuples carry a cached hash value. Re-track recycled tuples
85 // through "Nuitka_GC_Track", because CPython changed where GC accounting
86 // happens between 3.14.0 and 3.14.1+.
87 _PyTuple_RESET_HASH_CACHE((PyObject *)result_tuple);
88 if (!needs_recycle || !_PyObject_GC_IS_TRACKED((PyObject *)result_tuple)) {
89 Nuitka_GC_Track(result_tuple);
90 }
91#else
92 Nuitka_GC_Track(result_tuple);
93#endif
94
95 assert(PyTuple_CheckExact(result_tuple));
96 assert(PyTuple_GET_SIZE(result_tuple) == size);
97
98 return (PyObject *)result_tuple;
99}
100
101PyObject *MAKE_TUPLE_EMPTY_VAR(PyThreadState *tstate, Py_ssize_t size) {
102 if (size == 0) {
103 PyObject *result = const_tuple_empty;
104 Py_INCREF(result);
105 return result;
106 } else {
107 return MAKE_TUPLE_EMPTY(tstate, size);
108 }
109}
110
111#endif
112
113PyObject *TUPLE_CONCAT(PyThreadState *tstate, PyObject *tuple1, PyObject *tuple2) {
114 Py_ssize_t size = Py_SIZE(tuple1) + Py_SIZE(tuple2);
115
116 // Do not ignore MemoryError, may actually happen.
117 PyTupleObject *result = (PyTupleObject *)MAKE_TUPLE_EMPTY_VAR(tstate, size);
118 if (unlikely(result == NULL)) {
119 return NULL;
120 }
121
122 PyObject **src = ((PyTupleObject *)tuple1)->ob_item;
123 PyObject **dest = result->ob_item;
124
125 for (Py_ssize_t i = 0; i < Py_SIZE(tuple1); i++) {
126 PyObject *v = src[i];
127 Py_INCREF(v);
128 dest[i] = v;
129 }
130
131 src = ((PyTupleObject *)tuple2)->ob_item;
132 dest = result->ob_item + Py_SIZE(tuple1);
133
134 for (Py_ssize_t i = 0; i < Py_SIZE(tuple2); i++) {
135 PyObject *v = src[i];
136 Py_INCREF(v);
137 dest[i] = v;
138 }
139
140 return (PyObject *)result;
141}
142
143PyObject *TUPLE_COPY(PyThreadState *tstate, PyObject *tuple) {
144 CHECK_OBJECT(tuple);
145 assert(PyTuple_CheckExact(tuple));
146
147 Py_ssize_t size = PyTuple_GET_SIZE(tuple);
148 PyObject *result = MAKE_TUPLE_EMPTY_VAR(tstate, size);
149
150 if (unlikely(result == NULL)) {
151 return NULL;
152 }
153
154 for (Py_ssize_t i = 0; i < size; i++) {
155 PyObject *item = PyTuple_GET_ITEM(tuple, i);
156 Py_INCREF(item);
157 PyList_SET_ITEM(result, i, item);
158 }
159
160 return result;
161}
162
163// Part of "Nuitka", an optimizing Python compiler that is compatible and
164// integrates with CPython, but also works on its own.
165//
166// Licensed under the GNU Affero General Public License, Version 3 (the "License");
167// you may not use this file except in compliance with the License.
168// You may obtain a copy of the License at
169//
170// http://www.gnu.org/licenses/agpl.txt
171//
172// Unless required by applicable law or agreed to in writing, software
173// distributed under the License is distributed on an "AS IS" BASIS,
174// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
175// See the License for the specific language governing permissions and
176// limitations under the License.