Coverage Report

Created: 2025-08-26 06:26

/src/cpython/Objects/tupleobject.c
Line
Count
Source (jump to first uncovered line)
1
/* Tuple object implementation */
2
3
#include "Python.h"
4
#include "pycore_abstract.h"      // _PyIndex_Check()
5
#include "pycore_ceval.h"         // _PyEval_GetBuiltin()
6
#include "pycore_freelist.h"      // _Py_FREELIST_PUSH()
7
#include "pycore_gc.h"            // _PyObject_GC_IS_TRACKED()
8
#include "pycore_list.h"          // _Py_memory_repeat()
9
#include "pycore_modsupport.h"    // _PyArg_NoKwnames()
10
#include "pycore_object.h"        // _PyObject_GC_TRACK()
11
#include "pycore_stackref.h"      // PyStackRef_AsPyObjectSteal()
12
#include "pycore_tuple.h"         // _PyTupleIterObject
13
14
15
/*[clinic input]
16
class tuple "PyTupleObject *" "&PyTuple_Type"
17
[clinic start generated code]*/
18
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=f051ba3cfdf9a189]*/
19
20
#include "clinic/tupleobject.c.h"
21
22
23
static inline int maybe_freelist_push(PyTupleObject *);
24
25
26
/* Allocate an uninitialized tuple object. Before making it public, following
27
   steps must be done:
28
29
   - Initialize its items.
30
   - Call _PyObject_GC_TRACK() on it.
31
32
   Because the empty tuple is always reused and it's already tracked by GC,
33
   this function must not be called with size == 0 (unless from PyTuple_New()
34
   which wraps this function).
35
*/
36
static PyTupleObject *
37
tuple_alloc(Py_ssize_t size)
38
393M
{
39
393M
    if (size < 0) {
40
0
        PyErr_BadInternalCall();
41
0
        return NULL;
42
0
    }
43
393M
    assert(size != 0);    // The empty tuple is statically allocated.
44
393M
    Py_ssize_t index = size - 1;
45
393M
    if (index < PyTuple_MAXSAVESIZE) {
46
393M
        PyTupleObject *op = _Py_FREELIST_POP(PyTupleObject, tuples[index]);
47
393M
        if (op != NULL) {
48
329M
            _PyTuple_RESET_HASH_CACHE(op);
49
329M
            return op;
50
329M
        }
51
393M
    }
52
    /* Check for overflow */
53
63.8M
    if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - (sizeof(PyTupleObject) -
54
63.8M
                sizeof(PyObject *))) / sizeof(PyObject *)) {
55
0
        return (PyTupleObject *)PyErr_NoMemory();
56
0
    }
57
63.8M
    PyTupleObject *result = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
58
63.8M
    if (result != NULL) {
59
63.8M
        _PyTuple_RESET_HASH_CACHE(result);
60
63.8M
    }
61
63.8M
    return result;
62
63.8M
}
63
64
// The empty tuple singleton is not tracked by the GC.
65
// It does not contain any Python object.
66
// Note that tuple subclasses have their own empty instances.
67
68
static inline PyObject *
69
tuple_get_empty(void)
70
64.7M
{
71
64.7M
    return (PyObject *)&_Py_SINGLETON(tuple_empty);
72
64.7M
}
73
74
PyObject *
75
PyTuple_New(Py_ssize_t size)
76
65.2M
{
77
65.2M
    PyTupleObject *op;
78
65.2M
    if (size == 0) {
79
13.4k
        return tuple_get_empty();
80
13.4k
    }
81
65.1M
    op = tuple_alloc(size);
82
65.1M
    if (op == NULL) {
83
0
        return NULL;
84
0
    }
85
239M
    for (Py_ssize_t i = 0; i < size; i++) {
86
173M
        op->ob_item[i] = NULL;
87
173M
    }
88
65.1M
    _PyObject_GC_TRACK(op);
89
65.1M
    return (PyObject *) op;
90
65.1M
}
91
92
Py_ssize_t
93
PyTuple_Size(PyObject *op)
94
6.62M
{
95
6.62M
    if (!PyTuple_Check(op)) {
96
0
        PyErr_BadInternalCall();
97
0
        return -1;
98
0
    }
99
6.62M
    else
100
6.62M
        return Py_SIZE(op);
101
6.62M
}
102
103
PyObject *
104
PyTuple_GetItem(PyObject *op, Py_ssize_t i)
105
31.8M
{
106
31.8M
    if (!PyTuple_Check(op)) {
107
0
        PyErr_BadInternalCall();
108
0
        return NULL;
109
0
    }
110
31.8M
    if (i < 0 || i >= Py_SIZE(op)) {
111
0
        PyErr_SetString(PyExc_IndexError, "tuple index out of range");
112
0
        return NULL;
113
0
    }
114
31.8M
    return ((PyTupleObject *)op) -> ob_item[i];
115
31.8M
}
116
117
int
118
PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem)
119
72.9k
{
120
72.9k
    PyObject **p;
121
72.9k
    if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) {
122
0
        Py_XDECREF(newitem);
123
0
        PyErr_BadInternalCall();
124
0
        return -1;
125
0
    }
126
72.9k
    if (i < 0 || i >= Py_SIZE(op)) {
127
0
        Py_XDECREF(newitem);
128
0
        PyErr_SetString(PyExc_IndexError,
129
0
                        "tuple assignment index out of range");
130
0
        return -1;
131
0
    }
132
72.9k
    p = ((PyTupleObject *)op) -> ob_item + i;
133
72.9k
    Py_XSETREF(*p, newitem);
134
72.9k
    return 0;
135
72.9k
}
136
137
void
138
_PyTuple_MaybeUntrack(PyObject *op)
139
70.6M
{
140
70.6M
    PyTupleObject *t;
141
70.6M
    Py_ssize_t i, n;
142
143
70.6M
    if (!PyTuple_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op))
144
0
        return;
145
70.6M
    t = (PyTupleObject *) op;
146
70.6M
    n = Py_SIZE(t);
147
206M
    for (i = 0; i < n; i++) {
148
138M
        PyObject *elt = PyTuple_GET_ITEM(t, i);
149
        /* Tuple with NULL elements aren't
150
           fully constructed, don't untrack
151
           them yet. */
152
138M
        if (!elt ||
153
138M
            _PyObject_GC_MAY_BE_TRACKED(elt))
154
2.20M
            return;
155
138M
    }
156
68.4M
    _PyObject_GC_UNTRACK(op);
157
68.4M
}
158
159
PyObject *
160
PyTuple_Pack(Py_ssize_t n, ...)
161
6.50M
{
162
6.50M
    Py_ssize_t i;
163
6.50M
    PyObject *o;
164
6.50M
    PyObject **items;
165
6.50M
    va_list vargs;
166
167
6.50M
    if (n == 0) {
168
0
        return tuple_get_empty();
169
0
    }
170
171
6.50M
    va_start(vargs, n);
172
6.50M
    PyTupleObject *result = tuple_alloc(n);
173
6.50M
    if (result == NULL) {
174
0
        va_end(vargs);
175
0
        return NULL;
176
0
    }
177
6.50M
    items = result->ob_item;
178
19.5M
    for (i = 0; i < n; i++) {
179
13.0M
        o = va_arg(vargs, PyObject *);
180
13.0M
        items[i] = Py_NewRef(o);
181
13.0M
    }
182
6.50M
    va_end(vargs);
183
6.50M
    _PyObject_GC_TRACK(result);
184
6.50M
    return (PyObject *)result;
185
6.50M
}
186
187
188
/* Methods */
189
190
static void
191
tuple_dealloc(PyObject *self)
192
394M
{
193
394M
    PyTupleObject *op = _PyTuple_CAST(self);
194
394M
    if (Py_SIZE(op) == 0) {
195
        /* The empty tuple is statically allocated. */
196
0
        if (op == &_Py_SINGLETON(tuple_empty)) {
197
#ifdef Py_DEBUG
198
            _Py_FatalRefcountError("deallocating the empty tuple singleton");
199
#else
200
0
            return;
201
0
#endif
202
0
        }
203
#ifdef Py_DEBUG
204
        /* tuple subclasses have their own empty instances. */
205
        assert(!PyTuple_CheckExact(op));
206
#endif
207
0
    }
208
209
394M
    PyObject_GC_UnTrack(op);
210
211
394M
    Py_ssize_t i = Py_SIZE(op);
212
1.26G
    while (--i >= 0) {
213
874M
        Py_XDECREF(op->ob_item[i]);
214
874M
    }
215
    // This will abort on the empty singleton (if there is one).
216
394M
    if (!maybe_freelist_push(op)) {
217
65.5M
        Py_TYPE(op)->tp_free((PyObject *)op);
218
65.5M
    }
219
394M
}
220
221
static PyObject *
222
tuple_repr(PyObject *self)
223
0
{
224
0
    PyTupleObject *v = _PyTuple_CAST(self);
225
0
    Py_ssize_t n = PyTuple_GET_SIZE(v);
226
0
    if (n == 0) {
227
0
        return PyUnicode_FromString("()");
228
0
    }
229
230
    /* While not mutable, it is still possible to end up with a cycle in a
231
       tuple through an object that stores itself within a tuple (and thus
232
       infinitely asks for the repr of itself). This should only be
233
       possible within a type. */
234
0
    int res = Py_ReprEnter((PyObject *)v);
235
0
    if (res != 0) {
236
0
        return res > 0 ? PyUnicode_FromString("(...)") : NULL;
237
0
    }
238
239
0
    Py_ssize_t prealloc;
240
0
    if (n > 1) {
241
        // "(" + "1" + ", 2" * (len - 1) + ")"
242
0
        prealloc = 1 + 1 + (2 + 1) * (n - 1) + 1;
243
0
    }
244
0
    else {
245
        // "(1,)"
246
0
        prealloc = 4;
247
0
    }
248
0
    PyUnicodeWriter *writer = PyUnicodeWriter_Create(prealloc);
249
0
    if (writer == NULL) {
250
0
        goto error;
251
0
    }
252
253
0
    if (PyUnicodeWriter_WriteChar(writer, '(') < 0) {
254
0
        goto error;
255
0
    }
256
257
    /* Do repr() on each element. */
258
0
    for (Py_ssize_t i = 0; i < n; ++i) {
259
0
        if (i > 0) {
260
0
            if (PyUnicodeWriter_WriteChar(writer, ',') < 0) {
261
0
                goto error;
262
0
            }
263
0
            if (PyUnicodeWriter_WriteChar(writer, ' ') < 0) {
264
0
                goto error;
265
0
            }
266
0
        }
267
268
0
        if (PyUnicodeWriter_WriteRepr(writer, v->ob_item[i]) < 0) {
269
0
            goto error;
270
0
        }
271
0
    }
272
273
0
    if (n == 1) {
274
0
        if (PyUnicodeWriter_WriteChar(writer, ',') < 0) {
275
0
            goto error;
276
0
        }
277
0
    }
278
0
    if (PyUnicodeWriter_WriteChar(writer, ')') < 0) {
279
0
        goto error;
280
0
    }
281
282
0
    Py_ReprLeave((PyObject *)v);
283
0
    return PyUnicodeWriter_Finish(writer);
284
285
0
error:
286
0
    PyUnicodeWriter_Discard(writer);
287
0
    Py_ReprLeave((PyObject *)v);
288
0
    return NULL;
289
0
}
290
291
292
/* Hash for tuples. This is a slightly simplified version of the xxHash
293
   non-cryptographic hash:
294
   - we do not use any parallelism, there is only 1 accumulator.
295
   - we drop the final mixing since this is just a permutation of the
296
     output space: it does not help against collisions.
297
   - at the end, we mangle the length with a single constant.
298
   For the xxHash specification, see
299
   https://github.com/Cyan4973/xxHash/blob/master/doc/xxhash_spec.md
300
301
   The constants for the hash function are defined in pycore_tuple.h.
302
*/
303
304
static Py_hash_t
305
tuple_hash(PyObject *op)
306
734k
{
307
734k
    PyTupleObject *v = _PyTuple_CAST(op);
308
309
734k
    Py_uhash_t acc = FT_ATOMIC_LOAD_SSIZE_RELAXED(v->ob_hash);
310
734k
    if (acc != (Py_uhash_t)-1) {
311
113k
        return acc;
312
113k
    }
313
314
620k
    Py_ssize_t len = Py_SIZE(v);
315
620k
    PyObject **item = v->ob_item;
316
620k
    acc = _PyTuple_HASH_XXPRIME_5;
317
2.51M
    for (Py_ssize_t i = 0; i < len; i++) {
318
1.89M
        Py_uhash_t lane = PyObject_Hash(item[i]);
319
1.89M
        if (lane == (Py_uhash_t)-1) {
320
0
            return -1;
321
0
        }
322
1.89M
        acc += lane * _PyTuple_HASH_XXPRIME_2;
323
1.89M
        acc = _PyTuple_HASH_XXROTATE(acc);
324
1.89M
        acc *= _PyTuple_HASH_XXPRIME_1;
325
1.89M
    }
326
327
    /* Add input length, mangled to keep the historical value of hash(()). */
328
620k
    acc += len ^ (_PyTuple_HASH_XXPRIME_5 ^ 3527539UL);
329
330
620k
    if (acc == (Py_uhash_t)-1) {
331
0
        acc = 1546275796;
332
0
    }
333
334
620k
    FT_ATOMIC_STORE_SSIZE_RELAXED(v->ob_hash, acc);
335
336
620k
    return acc;
337
620k
}
338
339
static Py_ssize_t
340
tuple_length(PyObject *self)
341
729k
{
342
729k
    PyTupleObject *a = _PyTuple_CAST(self);
343
729k
    return Py_SIZE(a);
344
729k
}
345
346
static int
347
tuple_contains(PyObject *self, PyObject *el)
348
6.45M
{
349
6.45M
    PyTupleObject *a = _PyTuple_CAST(self);
350
6.45M
    int cmp = 0;
351
17.4M
    for (Py_ssize_t i = 0; cmp == 0 && i < Py_SIZE(a); ++i) {
352
10.9M
        cmp = PyObject_RichCompareBool(PyTuple_GET_ITEM(a, i), el, Py_EQ);
353
10.9M
    }
354
6.45M
    return cmp;
355
6.45M
}
356
357
static PyObject *
358
tuple_item(PyObject *op, Py_ssize_t i)
359
19.2M
{
360
19.2M
    PyTupleObject *a = _PyTuple_CAST(op);
361
19.2M
    if (i < 0 || i >= Py_SIZE(a)) {
362
16
        PyErr_SetString(PyExc_IndexError, "tuple index out of range");
363
16
        return NULL;
364
16
    }
365
19.2M
    return Py_NewRef(a->ob_item[i]);
366
19.2M
}
367
368
PyObject *
369
_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
370
235M
{
371
235M
    if (n == 0) {
372
64.7M
        return tuple_get_empty();
373
64.7M
    }
374
375
171M
    PyTupleObject *tuple = tuple_alloc(n);
376
171M
    if (tuple == NULL) {
377
0
        return NULL;
378
0
    }
379
171M
    PyObject **dst = tuple->ob_item;
380
516M
    for (Py_ssize_t i = 0; i < n; i++) {
381
345M
        PyObject *item = src[i];
382
345M
        dst[i] = Py_NewRef(item);
383
345M
    }
384
171M
    _PyObject_GC_TRACK(tuple);
385
171M
    return (PyObject *)tuple;
386
171M
}
387
388
PyObject *
389
_PyTuple_FromStackRefStealOnSuccess(const _PyStackRef *src, Py_ssize_t n)
390
150M
{
391
150M
    if (n == 0) {
392
0
        return tuple_get_empty();
393
0
    }
394
150M
    PyTupleObject *tuple = tuple_alloc(n);
395
150M
    if (tuple == NULL) {
396
0
        return NULL;
397
0
    }
398
150M
    PyObject **dst = tuple->ob_item;
399
487M
    for (Py_ssize_t i = 0; i < n; i++) {
400
337M
        dst[i] = PyStackRef_AsPyObjectSteal(src[i]);
401
337M
    }
402
150M
    _PyObject_GC_TRACK(tuple);
403
150M
    return (PyObject *)tuple;
404
150M
}
405
406
PyObject *
407
_PyTuple_FromArraySteal(PyObject *const *src, Py_ssize_t n)
408
305
{
409
305
    if (n == 0) {
410
0
        return tuple_get_empty();
411
0
    }
412
305
    PyTupleObject *tuple = tuple_alloc(n);
413
305
    if (tuple == NULL) {
414
0
        for (Py_ssize_t i = 0; i < n; i++) {
415
0
            Py_DECREF(src[i]);
416
0
        }
417
0
        return NULL;
418
0
    }
419
305
    PyObject **dst = tuple->ob_item;
420
1.68k
    for (Py_ssize_t i = 0; i < n; i++) {
421
1.37k
        PyObject *item = src[i];
422
1.37k
        dst[i] = item;
423
1.37k
    }
424
305
    _PyObject_GC_TRACK(tuple);
425
305
    return (PyObject *)tuple;
426
305
}
427
428
static PyObject *
429
tuple_slice(PyTupleObject *a, Py_ssize_t ilow,
430
           Py_ssize_t ihigh)
431
28.7M
{
432
28.7M
    if (ilow < 0)
433
0
        ilow = 0;
434
28.7M
    if (ihigh > Py_SIZE(a))
435
0
        ihigh = Py_SIZE(a);
436
28.7M
    if (ihigh < ilow)
437
0
        ihigh = ilow;
438
28.7M
    if (ilow == 0 && ihigh == Py_SIZE(a) && PyTuple_CheckExact(a)) {
439
0
        return Py_NewRef(a);
440
0
    }
441
28.7M
    return _PyTuple_FromArray(a->ob_item + ilow, ihigh - ilow);
442
28.7M
}
443
444
PyObject *
445
PyTuple_GetSlice(PyObject *op, Py_ssize_t i, Py_ssize_t j)
446
28.7M
{
447
28.7M
    if (op == NULL || !PyTuple_Check(op)) {
448
0
        PyErr_BadInternalCall();
449
0
        return NULL;
450
0
    }
451
28.7M
    return tuple_slice((PyTupleObject *)op, i, j);
452
28.7M
}
453
454
static PyObject *
455
tuple_concat(PyObject *aa, PyObject *bb)
456
24
{
457
24
    PyTupleObject *a = _PyTuple_CAST(aa);
458
24
    if (Py_SIZE(a) == 0 && PyTuple_CheckExact(bb)) {
459
0
        return Py_NewRef(bb);
460
0
    }
461
24
    if (!PyTuple_Check(bb)) {
462
0
        PyErr_Format(PyExc_TypeError,
463
0
             "can only concatenate tuple (not \"%.200s\") to tuple",
464
0
                 Py_TYPE(bb)->tp_name);
465
0
        return NULL;
466
0
    }
467
24
    PyTupleObject *b = (PyTupleObject *)bb;
468
469
24
    if (Py_SIZE(b) == 0 && PyTuple_CheckExact(a)) {
470
0
        return Py_NewRef(a);
471
0
    }
472
24
    assert((size_t)Py_SIZE(a) + (size_t)Py_SIZE(b) < PY_SSIZE_T_MAX);
473
24
    Py_ssize_t size = Py_SIZE(a) + Py_SIZE(b);
474
24
    if (size == 0) {
475
0
        return tuple_get_empty();
476
0
    }
477
478
24
    PyTupleObject *np = tuple_alloc(size);
479
24
    if (np == NULL) {
480
0
        return NULL;
481
0
    }
482
483
24
    PyObject **src = a->ob_item;
484
24
    PyObject **dest = np->ob_item;
485
208
    for (Py_ssize_t i = 0; i < Py_SIZE(a); i++) {
486
184
        PyObject *v = src[i];
487
184
        dest[i] = Py_NewRef(v);
488
184
    }
489
490
24
    src = b->ob_item;
491
24
    dest = np->ob_item + Py_SIZE(a);
492
62
    for (Py_ssize_t i = 0; i < Py_SIZE(b); i++) {
493
38
        PyObject *v = src[i];
494
38
        dest[i] = Py_NewRef(v);
495
38
    }
496
497
24
    _PyObject_GC_TRACK(np);
498
24
    return (PyObject *)np;
499
24
}
500
501
static PyObject *
502
tuple_repeat(PyObject *self, Py_ssize_t n)
503
0
{
504
0
    PyTupleObject *a = _PyTuple_CAST(self);
505
0
    const Py_ssize_t input_size = Py_SIZE(a);
506
0
    if (input_size == 0 || n == 1) {
507
0
        if (PyTuple_CheckExact(a)) {
508
            /* Since tuples are immutable, we can return a shared
509
               copy in this case */
510
0
            return Py_NewRef(a);
511
0
        }
512
0
    }
513
0
    if (input_size == 0 || n <= 0) {
514
0
        return tuple_get_empty();
515
0
    }
516
0
    assert(n>0);
517
518
0
    if (input_size > PY_SSIZE_T_MAX / n)
519
0
        return PyErr_NoMemory();
520
0
    Py_ssize_t output_size = input_size * n;
521
522
0
    PyTupleObject *np = tuple_alloc(output_size);
523
0
    if (np == NULL)
524
0
        return NULL;
525
526
0
    PyObject **dest = np->ob_item;
527
0
    if (input_size == 1) {
528
0
        PyObject *elem = a->ob_item[0];
529
0
        _Py_RefcntAdd(elem, n);
530
0
        PyObject **dest_end = dest + output_size;
531
0
        while (dest < dest_end) {
532
0
            *dest++ = elem;
533
0
        }
534
0
    }
535
0
    else {
536
0
        PyObject **src = a->ob_item;
537
0
        PyObject **src_end = src + input_size;
538
0
        while (src < src_end) {
539
0
            _Py_RefcntAdd(*src, n);
540
0
            *dest++ = *src++;
541
0
        }
542
543
0
        _Py_memory_repeat((char *)np->ob_item, sizeof(PyObject *)*output_size,
544
0
                          sizeof(PyObject *)*input_size);
545
0
    }
546
0
    _PyObject_GC_TRACK(np);
547
0
    return (PyObject *) np;
548
0
}
549
550
/*[clinic input]
551
tuple.index
552
553
    value: object
554
    start: slice_index(accept={int}) = 0
555
    stop: slice_index(accept={int}, c_default="PY_SSIZE_T_MAX") = sys.maxsize
556
    /
557
558
Return first index of value.
559
560
Raises ValueError if the value is not present.
561
[clinic start generated code]*/
562
563
static PyObject *
564
tuple_index_impl(PyTupleObject *self, PyObject *value, Py_ssize_t start,
565
                 Py_ssize_t stop)
566
/*[clinic end generated code: output=07b6f9f3cb5c33eb input=fb39e9874a21fe3f]*/
567
0
{
568
0
    Py_ssize_t i;
569
570
0
    if (start < 0) {
571
0
        start += Py_SIZE(self);
572
0
        if (start < 0)
573
0
            start = 0;
574
0
    }
575
0
    if (stop < 0) {
576
0
        stop += Py_SIZE(self);
577
0
    }
578
0
    else if (stop > Py_SIZE(self)) {
579
0
        stop = Py_SIZE(self);
580
0
    }
581
0
    for (i = start; i < stop; i++) {
582
0
        int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ);
583
0
        if (cmp > 0)
584
0
            return PyLong_FromSsize_t(i);
585
0
        else if (cmp < 0)
586
0
            return NULL;
587
0
    }
588
0
    PyErr_SetString(PyExc_ValueError, "tuple.index(x): x not in tuple");
589
0
    return NULL;
590
0
}
591
592
/*[clinic input]
593
tuple.count
594
595
     value: object
596
     /
597
598
Return number of occurrences of value.
599
[clinic start generated code]*/
600
601
static PyObject *
602
tuple_count_impl(PyTupleObject *self, PyObject *value)
603
/*[clinic end generated code: output=cf02888d4bc15d7a input=531721aff65bd772]*/
604
0
{
605
0
    Py_ssize_t count = 0;
606
0
    Py_ssize_t i;
607
608
0
    for (i = 0; i < Py_SIZE(self); i++) {
609
0
        int cmp = PyObject_RichCompareBool(self->ob_item[i], value, Py_EQ);
610
0
        if (cmp > 0)
611
0
            count++;
612
0
        else if (cmp < 0)
613
0
            return NULL;
614
0
    }
615
0
    return PyLong_FromSsize_t(count);
616
0
}
617
618
static int
619
tuple_traverse(PyObject *self, visitproc visit, void *arg)
620
9.35M
{
621
9.35M
    PyTupleObject *o = _PyTuple_CAST(self);
622
42.3M
    for (Py_ssize_t i = Py_SIZE(o); --i >= 0; ) {
623
32.9M
        Py_VISIT(o->ob_item[i]);
624
32.9M
    }
625
9.35M
    return 0;
626
9.35M
}
627
628
static PyObject *
629
tuple_richcompare(PyObject *v, PyObject *w, int op)
630
3.44M
{
631
3.44M
    PyTupleObject *vt, *wt;
632
3.44M
    Py_ssize_t i;
633
3.44M
    Py_ssize_t vlen, wlen;
634
635
3.44M
    if (!PyTuple_Check(v) || !PyTuple_Check(w))
636
0
        Py_RETURN_NOTIMPLEMENTED;
637
638
3.44M
    vt = (PyTupleObject *)v;
639
3.44M
    wt = (PyTupleObject *)w;
640
641
3.44M
    vlen = Py_SIZE(vt);
642
3.44M
    wlen = Py_SIZE(wt);
643
644
    /* Note:  the corresponding code for lists has an "early out" test
645
     * here when op is EQ or NE and the lengths differ.  That pays there,
646
     * but Tim was unable to find any real code where EQ/NE tuple
647
     * compares don't have the same length, so testing for it here would
648
     * have cost without benefit.
649
     */
650
651
    /* Search for the first index where items are different.
652
     * Note that because tuples are immutable, it's safe to reuse
653
     * vlen and wlen across the comparison calls.
654
     */
655
5.04M
    for (i = 0; i < vlen && i < wlen; i++) {
656
4.50M
        int k = PyObject_RichCompareBool(vt->ob_item[i],
657
4.50M
                                         wt->ob_item[i], Py_EQ);
658
4.50M
        if (k < 0)
659
0
            return NULL;
660
4.50M
        if (!k)
661
2.90M
            break;
662
4.50M
    }
663
664
3.44M
    if (i >= vlen || i >= wlen) {
665
        /* No more items to compare -- compare sizes */
666
543k
        Py_RETURN_RICHCOMPARE(vlen, wlen, op);
667
543k
    }
668
669
    /* We have an item that differs -- shortcuts for EQ/NE */
670
2.90M
    if (op == Py_EQ) {
671
268
        Py_RETURN_FALSE;
672
268
    }
673
2.90M
    if (op == Py_NE) {
674
65
        Py_RETURN_TRUE;
675
65
    }
676
677
    /* Compare the final item again using the proper operator */
678
2.90M
    return PyObject_RichCompare(vt->ob_item[i], wt->ob_item[i], op);
679
2.90M
}
680
681
static PyObject *
682
tuple_subtype_new(PyTypeObject *type, PyObject *iterable);
683
684
/*[clinic input]
685
@classmethod
686
tuple.__new__ as tuple_new
687
    iterable: object(c_default="NULL") = ()
688
    /
689
690
Built-in immutable sequence.
691
692
If no argument is given, the constructor returns an empty tuple.
693
If iterable is specified the tuple is initialized from iterable's items.
694
695
If the argument is a tuple, the return value is the same object.
696
[clinic start generated code]*/
697
698
static PyObject *
699
tuple_new_impl(PyTypeObject *type, PyObject *iterable)
700
/*[clinic end generated code: output=4546d9f0d469bce7 input=86963bcde633b5a2]*/
701
3.59M
{
702
3.59M
    if (type != &PyTuple_Type)
703
1.79M
        return tuple_subtype_new(type, iterable);
704
705
1.79M
    if (iterable == NULL) {
706
0
        return tuple_get_empty();
707
0
    }
708
1.79M
    else {
709
1.79M
        return PySequence_Tuple(iterable);
710
1.79M
    }
711
1.79M
}
712
713
static PyObject *
714
tuple_vectorcall(PyObject *type, PyObject * const*args,
715
                 size_t nargsf, PyObject *kwnames)
716
74
{
717
74
    if (!_PyArg_NoKwnames("tuple", kwnames)) {
718
0
        return NULL;
719
0
    }
720
721
74
    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
722
74
    if (!_PyArg_CheckPositional("tuple", nargs, 0, 1)) {
723
0
        return NULL;
724
0
    }
725
726
74
    if (nargs) {
727
74
        return tuple_new_impl(_PyType_CAST(type), args[0]);
728
74
    }
729
0
    else {
730
0
        return tuple_get_empty();
731
0
    }
732
74
}
733
734
static PyObject *
735
tuple_subtype_new(PyTypeObject *type, PyObject *iterable)
736
1.79M
{
737
1.79M
    PyObject *tmp, *newobj, *item;
738
1.79M
    Py_ssize_t i, n;
739
740
1.79M
    assert(PyType_IsSubtype(type, &PyTuple_Type));
741
    // tuple subclasses must implement the GC protocol
742
1.79M
    assert(_PyType_IS_GC(type));
743
744
1.79M
    tmp = tuple_new_impl(&PyTuple_Type, iterable);
745
1.79M
    if (tmp == NULL)
746
0
        return NULL;
747
1.79M
    assert(PyTuple_Check(tmp));
748
    /* This may allocate an empty tuple that is not the global one. */
749
1.79M
    newobj = type->tp_alloc(type, n = PyTuple_GET_SIZE(tmp));
750
1.79M
    if (newobj == NULL) {
751
0
        Py_DECREF(tmp);
752
0
        return NULL;
753
0
    }
754
7.18M
    for (i = 0; i < n; i++) {
755
5.39M
        item = PyTuple_GET_ITEM(tmp, i);
756
5.39M
        PyTuple_SET_ITEM(newobj, i, Py_NewRef(item));
757
5.39M
    }
758
1.79M
    Py_DECREF(tmp);
759
760
1.79M
    _PyTuple_RESET_HASH_CACHE(newobj);
761
762
    // Don't track if a subclass tp_alloc is PyType_GenericAlloc()
763
1.79M
    if (!_PyObject_GC_IS_TRACKED(newobj)) {
764
0
        _PyObject_GC_TRACK(newobj);
765
0
    }
766
1.79M
    return newobj;
767
1.79M
}
768
769
static PySequenceMethods tuple_as_sequence = {
770
    tuple_length,                               /* sq_length */
771
    tuple_concat,                               /* sq_concat */
772
    tuple_repeat,                               /* sq_repeat */
773
    tuple_item,                                 /* sq_item */
774
    0,                                          /* sq_slice */
775
    0,                                          /* sq_ass_item */
776
    0,                                          /* sq_ass_slice */
777
    tuple_contains,                             /* sq_contains */
778
};
779
780
static PyObject*
781
tuple_subscript(PyObject *op, PyObject* item)
782
1.32M
{
783
1.32M
    PyTupleObject *self = _PyTuple_CAST(op);
784
1.32M
    if (_PyIndex_Check(item)) {
785
1.32M
        Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
786
1.32M
        if (i == -1 && PyErr_Occurred())
787
0
            return NULL;
788
1.32M
        if (i < 0)
789
1.32M
            i += PyTuple_GET_SIZE(self);
790
1.32M
        return tuple_item(op, i);
791
1.32M
    }
792
279
    else if (PySlice_Check(item)) {
793
279
        Py_ssize_t start, stop, step, slicelength, i;
794
279
        size_t cur;
795
279
        PyObject* it;
796
279
        PyObject **src, **dest;
797
798
279
        if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
799
0
            return NULL;
800
0
        }
801
279
        slicelength = PySlice_AdjustIndices(PyTuple_GET_SIZE(self), &start,
802
279
                                            &stop, step);
803
804
279
        if (slicelength <= 0) {
805
8
            return tuple_get_empty();
806
8
        }
807
271
        else if (start == 0 && step == 1 &&
808
271
                 slicelength == PyTuple_GET_SIZE(self) &&
809
271
                 PyTuple_CheckExact(self)) {
810
8
            return Py_NewRef(self);
811
8
        }
812
263
        else {
813
263
            PyTupleObject* result = tuple_alloc(slicelength);
814
263
            if (!result) return NULL;
815
816
263
            src = self->ob_item;
817
263
            dest = result->ob_item;
818
1.74k
            for (cur = start, i = 0; i < slicelength;
819
1.48k
                 cur += step, i++) {
820
1.48k
                it = Py_NewRef(src[cur]);
821
1.48k
                dest[i] = it;
822
1.48k
            }
823
824
263
            _PyObject_GC_TRACK(result);
825
263
            return (PyObject *)result;
826
263
        }
827
279
    }
828
0
    else {
829
0
        PyErr_Format(PyExc_TypeError,
830
0
                     "tuple indices must be integers or slices, not %.200s",
831
0
                     Py_TYPE(item)->tp_name);
832
0
        return NULL;
833
0
    }
834
1.32M
}
835
836
/*[clinic input]
837
tuple.__getnewargs__
838
[clinic start generated code]*/
839
840
static PyObject *
841
tuple___getnewargs___impl(PyTupleObject *self)
842
/*[clinic end generated code: output=25e06e3ee56027e2 input=1aeb4b286a21639a]*/
843
0
{
844
0
    return Py_BuildValue("(N)", tuple_slice(self, 0, Py_SIZE(self)));
845
0
}
846
847
static PyMethodDef tuple_methods[] = {
848
    TUPLE___GETNEWARGS___METHODDEF
849
    TUPLE_INDEX_METHODDEF
850
    TUPLE_COUNT_METHODDEF
851
    {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
852
    {NULL,              NULL}           /* sentinel */
853
};
854
855
static PyMappingMethods tuple_as_mapping = {
856
    tuple_length,
857
    tuple_subscript,
858
    0
859
};
860
861
static PyObject *tuple_iter(PyObject *seq);
862
863
PyTypeObject PyTuple_Type = {
864
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
865
    "tuple",
866
    sizeof(PyTupleObject) - sizeof(PyObject *),
867
    sizeof(PyObject *),
868
    tuple_dealloc,                              /* tp_dealloc */
869
    0,                                          /* tp_vectorcall_offset */
870
    0,                                          /* tp_getattr */
871
    0,                                          /* tp_setattr */
872
    0,                                          /* tp_as_async */
873
    tuple_repr,                                 /* tp_repr */
874
    0,                                          /* tp_as_number */
875
    &tuple_as_sequence,                         /* tp_as_sequence */
876
    &tuple_as_mapping,                          /* tp_as_mapping */
877
    tuple_hash,                                 /* tp_hash */
878
    0,                                          /* tp_call */
879
    0,                                          /* tp_str */
880
    PyObject_GenericGetAttr,                    /* tp_getattro */
881
    0,                                          /* tp_setattro */
882
    0,                                          /* tp_as_buffer */
883
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
884
        Py_TPFLAGS_BASETYPE | Py_TPFLAGS_TUPLE_SUBCLASS |
885
        _Py_TPFLAGS_MATCH_SELF | Py_TPFLAGS_SEQUENCE,  /* tp_flags */
886
    tuple_new__doc__,                           /* tp_doc */
887
    tuple_traverse,                             /* tp_traverse */
888
    0,                                          /* tp_clear */
889
    tuple_richcompare,                          /* tp_richcompare */
890
    0,                                          /* tp_weaklistoffset */
891
    tuple_iter,                                 /* tp_iter */
892
    0,                                          /* tp_iternext */
893
    tuple_methods,                              /* tp_methods */
894
    0,                                          /* tp_members */
895
    0,                                          /* tp_getset */
896
    0,                                          /* tp_base */
897
    0,                                          /* tp_dict */
898
    0,                                          /* tp_descr_get */
899
    0,                                          /* tp_descr_set */
900
    0,                                          /* tp_dictoffset */
901
    0,                                          /* tp_init */
902
    0,                                          /* tp_alloc */
903
    tuple_new,                                  /* tp_new */
904
    PyObject_GC_Del,                            /* tp_free */
905
    .tp_vectorcall = tuple_vectorcall,
906
    .tp_version_tag = _Py_TYPE_VERSION_TUPLE,
907
};
908
909
/* The following function breaks the notion that tuples are immutable:
910
   it changes the size of a tuple.  We get away with this only if there
911
   is only one module referencing the object.  You can also think of it
912
   as creating a new tuple object and destroying the old one, only more
913
   efficiently.  In any case, don't use this if the tuple may already be
914
   known to some other part of the code. */
915
916
int
917
_PyTuple_Resize(PyObject **pv, Py_ssize_t newsize)
918
288
{
919
288
    PyTupleObject *v;
920
288
    PyTupleObject *sv;
921
288
    Py_ssize_t i;
922
288
    Py_ssize_t oldsize;
923
924
288
    v = (PyTupleObject *) *pv;
925
288
    if (v == NULL || !Py_IS_TYPE(v, &PyTuple_Type) ||
926
288
        (Py_SIZE(v) != 0 && Py_REFCNT(v) != 1)) {
927
0
        *pv = 0;
928
0
        Py_XDECREF(v);
929
0
        PyErr_BadInternalCall();
930
0
        return -1;
931
0
    }
932
933
288
    oldsize = Py_SIZE(v);
934
288
    if (oldsize == newsize) {
935
272
        return 0;
936
272
    }
937
16
    if (newsize == 0) {
938
0
        Py_DECREF(v);
939
0
        *pv = tuple_get_empty();
940
0
        return 0;
941
0
    }
942
16
    if (oldsize == 0) {
943
#ifdef Py_DEBUG
944
        assert(v == &_Py_SINGLETON(tuple_empty));
945
#endif
946
        /* The empty tuple is statically allocated so we never
947
           resize it in-place. */
948
0
        Py_DECREF(v);
949
0
        *pv = PyTuple_New(newsize);
950
0
        return *pv == NULL ? -1 : 0;
951
0
    }
952
953
16
    if (_PyObject_GC_IS_TRACKED(v)) {
954
16
        _PyObject_GC_UNTRACK(v);
955
16
    }
956
#ifdef Py_TRACE_REFS
957
    _Py_ForgetReference((PyObject *) v);
958
#endif
959
    /* DECREF items deleted by shrinkage */
960
64
    for (i = newsize; i < oldsize; i++) {
961
48
        Py_CLEAR(v->ob_item[i]);
962
48
    }
963
16
    _PyReftracerTrack((PyObject *)v, PyRefTracer_DESTROY);
964
16
    sv = PyObject_GC_Resize(PyTupleObject, v, newsize);
965
16
    if (sv == NULL) {
966
0
        *pv = NULL;
967
#ifdef Py_REF_DEBUG
968
        _Py_DecRefTotal(_PyThreadState_GET());
969
#endif
970
0
        PyObject_GC_Del(v);
971
0
        return -1;
972
0
    }
973
16
    _Py_NewReferenceNoTotal((PyObject *) sv);
974
    /* Zero out items added by growing */
975
16
    if (newsize > oldsize)
976
0
        memset(&sv->ob_item[oldsize], 0,
977
0
               sizeof(*sv->ob_item) * (newsize - oldsize));
978
16
    *pv = (PyObject *) sv;
979
16
    _PyObject_GC_TRACK(sv);
980
16
    return 0;
981
16
}
982
983
/*********************** Tuple Iterator **************************/
984
985
13.1M
#define _PyTupleIterObject_CAST(op) ((_PyTupleIterObject *)(op))
986
987
static void
988
tupleiter_dealloc(PyObject *self)
989
2.62M
{
990
2.62M
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
991
2.62M
    _PyObject_GC_UNTRACK(it);
992
2.62M
    Py_XDECREF(it->it_seq);
993
2.62M
    assert(Py_IS_TYPE(self, &PyTupleIter_Type));
994
2.62M
    _Py_FREELIST_FREE(tuple_iters, it, PyObject_GC_Del);
995
2.62M
}
996
997
static int
998
tupleiter_traverse(PyObject *self, visitproc visit, void *arg)
999
0
{
1000
0
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
1001
0
    Py_VISIT(it->it_seq);
1002
0
    return 0;
1003
0
}
1004
1005
static PyObject *
1006
tupleiter_next(PyObject *self)
1007
10.4M
{
1008
10.4M
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
1009
10.4M
    PyTupleObject *seq;
1010
10.4M
    PyObject *item;
1011
1012
10.4M
    assert(it != NULL);
1013
10.4M
    seq = it->it_seq;
1014
10.4M
#ifndef Py_GIL_DISABLED
1015
10.4M
    if (seq == NULL)
1016
0
        return NULL;
1017
10.4M
#endif
1018
10.4M
    assert(PyTuple_Check(seq));
1019
1020
10.4M
    Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
1021
10.4M
    if (index < PyTuple_GET_SIZE(seq)) {
1022
7.87M
        FT_ATOMIC_STORE_SSIZE_RELAXED(it->it_index, index + 1);
1023
7.87M
        item = PyTuple_GET_ITEM(seq, index);
1024
7.87M
        return Py_NewRef(item);
1025
7.87M
    }
1026
1027
2.62M
#ifndef Py_GIL_DISABLED
1028
2.62M
    it->it_seq = NULL;
1029
2.62M
    Py_DECREF(seq);
1030
2.62M
#endif
1031
2.62M
    return NULL;
1032
10.4M
}
1033
1034
static PyObject *
1035
tupleiter_len(PyObject *self, PyObject *Py_UNUSED(ignored))
1036
0
{
1037
0
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
1038
0
    Py_ssize_t len = 0;
1039
#ifdef Py_GIL_DISABLED
1040
    Py_ssize_t idx = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
1041
    Py_ssize_t seq_len = PyTuple_GET_SIZE(it->it_seq);
1042
    if (idx < seq_len)
1043
        len = seq_len - idx;
1044
#else
1045
0
    if (it->it_seq)
1046
0
        len = PyTuple_GET_SIZE(it->it_seq) - it->it_index;
1047
0
#endif
1048
0
    return PyLong_FromSsize_t(len);
1049
0
}
1050
1051
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
1052
1053
static PyObject *
1054
tupleiter_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
1055
0
{
1056
0
    PyObject *iter = _PyEval_GetBuiltin(&_Py_ID(iter));
1057
1058
    /* _PyEval_GetBuiltin can invoke arbitrary code,
1059
     * call must be before access of iterator pointers.
1060
     * see issue #101765 */
1061
0
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
1062
1063
#ifdef Py_GIL_DISABLED
1064
    Py_ssize_t idx = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
1065
    if (idx < PyTuple_GET_SIZE(it->it_seq))
1066
        return Py_BuildValue("N(O)n", iter, it->it_seq, idx);
1067
#else
1068
0
    if (it->it_seq)
1069
0
        return Py_BuildValue("N(O)n", iter, it->it_seq, it->it_index);
1070
0
#endif
1071
0
    return Py_BuildValue("N(())", iter);
1072
0
}
1073
1074
static PyObject *
1075
tupleiter_setstate(PyObject *self, PyObject *state)
1076
0
{
1077
0
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
1078
0
    Py_ssize_t index = PyLong_AsSsize_t(state);
1079
0
    if (index == -1 && PyErr_Occurred())
1080
0
        return NULL;
1081
0
    if (it->it_seq != NULL) {
1082
0
        if (index < 0)
1083
0
            index = 0;
1084
0
        else if (index > PyTuple_GET_SIZE(it->it_seq))
1085
0
            index = PyTuple_GET_SIZE(it->it_seq); /* exhausted iterator */
1086
0
        FT_ATOMIC_STORE_SSIZE_RELAXED(it->it_index, index);
1087
0
    }
1088
0
    Py_RETURN_NONE;
1089
0
}
1090
1091
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
1092
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
1093
1094
static PyMethodDef tupleiter_methods[] = {
1095
    {"__length_hint__", tupleiter_len, METH_NOARGS, length_hint_doc},
1096
    {"__reduce__", tupleiter_reduce, METH_NOARGS, reduce_doc},
1097
    {"__setstate__", tupleiter_setstate, METH_O, setstate_doc},
1098
    {NULL, NULL, 0, NULL} /* sentinel */
1099
};
1100
1101
PyTypeObject PyTupleIter_Type = {
1102
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
1103
    "tuple_iterator",                           /* tp_name */
1104
    sizeof(_PyTupleIterObject),                 /* tp_basicsize */
1105
    0,                                          /* tp_itemsize */
1106
    /* methods */
1107
    tupleiter_dealloc,                          /* tp_dealloc */
1108
    0,                                          /* tp_vectorcall_offset */
1109
    0,                                          /* tp_getattr */
1110
    0,                                          /* tp_setattr */
1111
    0,                                          /* tp_as_async */
1112
    0,                                          /* tp_repr */
1113
    0,                                          /* tp_as_number */
1114
    0,                                          /* tp_as_sequence */
1115
    0,                                          /* tp_as_mapping */
1116
    0,                                          /* tp_hash */
1117
    0,                                          /* tp_call */
1118
    0,                                          /* tp_str */
1119
    PyObject_GenericGetAttr,                    /* tp_getattro */
1120
    0,                                          /* tp_setattro */
1121
    0,                                          /* tp_as_buffer */
1122
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
1123
    0,                                          /* tp_doc */
1124
    tupleiter_traverse,                         /* tp_traverse */
1125
    0,                                          /* tp_clear */
1126
    0,                                          /* tp_richcompare */
1127
    0,                                          /* tp_weaklistoffset */
1128
    PyObject_SelfIter,                          /* tp_iter */
1129
    tupleiter_next,                             /* tp_iternext */
1130
    tupleiter_methods,                          /* tp_methods */
1131
    0,
1132
};
1133
1134
static PyObject *
1135
tuple_iter(PyObject *seq)
1136
2.62M
{
1137
2.62M
    if (!PyTuple_Check(seq)) {
1138
0
        PyErr_BadInternalCall();
1139
0
        return NULL;
1140
0
    }
1141
2.62M
    _PyTupleIterObject *it = _Py_FREELIST_POP(_PyTupleIterObject, tuple_iters);
1142
2.62M
    if (it == NULL) {
1143
28
        it = PyObject_GC_New(_PyTupleIterObject, &PyTupleIter_Type);
1144
28
        if (it == NULL)
1145
0
            return NULL;
1146
28
    }
1147
2.62M
    it->it_index = 0;
1148
2.62M
    it->it_seq = (PyTupleObject *)Py_NewRef(seq);
1149
2.62M
    _PyObject_GC_TRACK(it);
1150
2.62M
    return (PyObject *)it;
1151
2.62M
}
1152
1153
1154
/*************
1155
 * freelists *
1156
 *************/
1157
1158
static inline int
1159
maybe_freelist_push(PyTupleObject *op)
1160
394M
{
1161
394M
    if (!Py_IS_TYPE(op, &PyTuple_Type)) {
1162
1.79M
        return 0;
1163
1.79M
    }
1164
392M
    Py_ssize_t index = Py_SIZE(op) - 1;
1165
392M
    if (index < PyTuple_MAXSAVESIZE) {
1166
392M
        return _Py_FREELIST_PUSH(tuples[index], op, Py_tuple_MAXFREELIST);
1167
392M
    }
1168
2.71k
    return 0;
1169
392M
}
1170
1171
/* Print summary info about the state of the optimized allocator */
1172
void
1173
_PyTuple_DebugMallocStats(FILE *out)
1174
0
{
1175
0
    for (int i = 0; i < PyTuple_MAXSAVESIZE; i++) {
1176
0
        int len = i + 1;
1177
0
        char buf[128];
1178
0
        PyOS_snprintf(buf, sizeof(buf),
1179
0
                      "free %d-sized PyTupleObject", len);
1180
0
        _PyDebugAllocatorStats(out, buf, _Py_FREELIST_SIZE(tuples[i]),
1181
0
                               _PyObject_VAR_SIZE(&PyTuple_Type, len));
1182
0
    }
1183
0
}