Coverage Report

Created: 2025-09-04 06:25

/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
404M
{
39
404M
    if (size < 0) {
40
0
        PyErr_BadInternalCall();
41
0
        return NULL;
42
0
    }
43
404M
    assert(size != 0);    // The empty tuple is statically allocated.
44
404M
    Py_ssize_t index = size - 1;
45
404M
    if (index < PyTuple_MAXSAVESIZE) {
46
404M
        PyTupleObject *op = _Py_FREELIST_POP(PyTupleObject, tuples[index]);
47
404M
        if (op != NULL) {
48
332M
            _PyTuple_RESET_HASH_CACHE(op);
49
332M
            return op;
50
332M
        }
51
404M
    }
52
    /* Check for overflow */
53
71.8M
    if ((size_t)size > ((size_t)PY_SSIZE_T_MAX - (sizeof(PyTupleObject) -
54
71.8M
                sizeof(PyObject *))) / sizeof(PyObject *)) {
55
0
        return (PyTupleObject *)PyErr_NoMemory();
56
0
    }
57
71.8M
    PyTupleObject *result = PyObject_GC_NewVar(PyTupleObject, &PyTuple_Type, size);
58
71.8M
    if (result != NULL) {
59
71.8M
        _PyTuple_RESET_HASH_CACHE(result);
60
71.8M
    }
61
71.8M
    return result;
62
71.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
59.9M
{
71
59.9M
    return (PyObject *)&_Py_SINGLETON(tuple_empty);
72
59.9M
}
73
74
PyObject *
75
PyTuple_New(Py_ssize_t size)
76
76.5M
{
77
76.5M
    PyTupleObject *op;
78
76.5M
    if (size == 0) {
79
13.4k
        return tuple_get_empty();
80
13.4k
    }
81
76.5M
    op = tuple_alloc(size);
82
76.5M
    if (op == NULL) {
83
0
        return NULL;
84
0
    }
85
284M
    for (Py_ssize_t i = 0; i < size; i++) {
86
208M
        op->ob_item[i] = NULL;
87
208M
    }
88
76.5M
    _PyObject_GC_TRACK(op);
89
76.5M
    return (PyObject *) op;
90
76.5M
}
91
92
Py_ssize_t
93
PyTuple_Size(PyObject *op)
94
6.53M
{
95
6.53M
    if (!PyTuple_Check(op)) {
96
0
        PyErr_BadInternalCall();
97
0
        return -1;
98
0
    }
99
6.53M
    else
100
6.53M
        return Py_SIZE(op);
101
6.53M
}
102
103
PyObject *
104
PyTuple_GetItem(PyObject *op, Py_ssize_t i)
105
31.5M
{
106
31.5M
    if (!PyTuple_Check(op)) {
107
0
        PyErr_BadInternalCall();
108
0
        return NULL;
109
0
    }
110
31.5M
    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.5M
    return ((PyTupleObject *)op) -> ob_item[i];
115
31.5M
}
116
117
int
118
PyTuple_SetItem(PyObject *op, Py_ssize_t i, PyObject *newitem)
119
71.4k
{
120
71.4k
    PyObject **p;
121
71.4k
    if (!PyTuple_Check(op) || Py_REFCNT(op) != 1) {
122
0
        Py_XDECREF(newitem);
123
0
        PyErr_BadInternalCall();
124
0
        return -1;
125
0
    }
126
71.4k
    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
71.4k
    p = ((PyTupleObject *)op) -> ob_item + i;
133
71.4k
    Py_XSETREF(*p, newitem);
134
71.4k
    return 0;
135
71.4k
}
136
137
void
138
_PyTuple_MaybeUntrack(PyObject *op)
139
77.8M
{
140
77.8M
    PyTupleObject *t;
141
77.8M
    Py_ssize_t i, n;
142
143
77.8M
    if (!PyTuple_CheckExact(op) || !_PyObject_GC_IS_TRACKED(op))
144
0
        return;
145
77.8M
    t = (PyTupleObject *) op;
146
77.8M
    n = Py_SIZE(t);
147
230M
    for (i = 0; i < n; i++) {
148
154M
        PyObject *elt = PyTuple_GET_ITEM(t, i);
149
        /* Tuple with NULL elements aren't
150
           fully constructed, don't untrack
151
           them yet. */
152
154M
        if (!elt ||
153
154M
            _PyObject_GC_MAY_BE_TRACKED(elt))
154
1.96M
            return;
155
154M
    }
156
75.9M
    _PyObject_GC_UNTRACK(op);
157
75.9M
}
158
159
PyObject *
160
PyTuple_Pack(Py_ssize_t n, ...)
161
6.41M
{
162
6.41M
    Py_ssize_t i;
163
6.41M
    PyObject *o;
164
6.41M
    PyObject **items;
165
6.41M
    va_list vargs;
166
167
6.41M
    if (n == 0) {
168
0
        return tuple_get_empty();
169
0
    }
170
171
6.41M
    va_start(vargs, n);
172
6.41M
    PyTupleObject *result = tuple_alloc(n);
173
6.41M
    if (result == NULL) {
174
0
        va_end(vargs);
175
0
        return NULL;
176
0
    }
177
6.41M
    items = result->ob_item;
178
19.2M
    for (i = 0; i < n; i++) {
179
12.8M
        o = va_arg(vargs, PyObject *);
180
12.8M
        items[i] = Py_NewRef(o);
181
12.8M
    }
182
6.41M
    va_end(vargs);
183
6.41M
    _PyObject_GC_TRACK(result);
184
6.41M
    return (PyObject *)result;
185
6.41M
}
186
187
188
/* Methods */
189
190
static void
191
tuple_dealloc(PyObject *self)
192
405M
{
193
405M
    PyTupleObject *op = _PyTuple_CAST(self);
194
405M
    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
405M
    PyObject_GC_UnTrack(op);
210
211
405M
    Py_ssize_t i = Py_SIZE(op);
212
1.33G
    while (--i >= 0) {
213
930M
        Py_XDECREF(op->ob_item[i]);
214
930M
    }
215
    // This will abort on the empty singleton (if there is one).
216
405M
    if (!maybe_freelist_push(op)) {
217
73.5M
        Py_TYPE(op)->tp_free((PyObject *)op);
218
73.5M
    }
219
405M
}
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
811k
{
307
811k
    PyTupleObject *v = _PyTuple_CAST(op);
308
309
811k
    Py_uhash_t acc = FT_ATOMIC_LOAD_SSIZE_RELAXED(v->ob_hash);
310
811k
    if (acc != (Py_uhash_t)-1) {
311
114k
        return acc;
312
114k
    }
313
314
697k
    Py_ssize_t len = Py_SIZE(v);
315
697k
    PyObject **item = v->ob_item;
316
697k
    acc = _PyTuple_HASH_XXPRIME_5;
317
2.82M
    for (Py_ssize_t i = 0; i < len; i++) {
318
2.12M
        Py_uhash_t lane = PyObject_Hash(item[i]);
319
2.12M
        if (lane == (Py_uhash_t)-1) {
320
0
            return -1;
321
0
        }
322
2.12M
        acc += lane * _PyTuple_HASH_XXPRIME_2;
323
2.12M
        acc = _PyTuple_HASH_XXROTATE(acc);
324
2.12M
        acc *= _PyTuple_HASH_XXPRIME_1;
325
2.12M
    }
326
327
    /* Add input length, mangled to keep the historical value of hash(()). */
328
697k
    acc += len ^ (_PyTuple_HASH_XXPRIME_5 ^ 3527539UL);
329
330
697k
    if (acc == (Py_uhash_t)-1) {
331
0
        acc = 1546275796;
332
0
    }
333
334
697k
    FT_ATOMIC_STORE_SSIZE_RELAXED(v->ob_hash, acc);
335
336
697k
    return acc;
337
697k
}
338
339
static Py_ssize_t
340
tuple_length(PyObject *self)
341
726k
{
342
726k
    PyTupleObject *a = _PyTuple_CAST(self);
343
726k
    return Py_SIZE(a);
344
726k
}
345
346
static int
347
tuple_contains(PyObject *self, PyObject *el)
348
5.67M
{
349
5.67M
    PyTupleObject *a = _PyTuple_CAST(self);
350
5.67M
    int cmp = 0;
351
15.3M
    for (Py_ssize_t i = 0; cmp == 0 && i < Py_SIZE(a); ++i) {
352
9.63M
        cmp = PyObject_RichCompareBool(PyTuple_GET_ITEM(a, i), el, Py_EQ);
353
9.63M
    }
354
5.67M
    return cmp;
355
5.67M
}
356
357
static PyObject *
358
tuple_item(PyObject *op, Py_ssize_t i)
359
16.6M
{
360
16.6M
    PyTupleObject *a = _PyTuple_CAST(op);
361
16.6M
    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
16.6M
    return Py_NewRef(a->ob_item[i]);
366
16.6M
}
367
368
PyObject *
369
_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
370
227M
{
371
227M
    if (n == 0) {
372
59.8M
        return tuple_get_empty();
373
59.8M
    }
374
375
167M
    PyTupleObject *tuple = tuple_alloc(n);
376
167M
    if (tuple == NULL) {
377
0
        return NULL;
378
0
    }
379
167M
    PyObject **dst = tuple->ob_item;
380
525M
    for (Py_ssize_t i = 0; i < n; i++) {
381
358M
        PyObject *item = src[i];
382
358M
        dst[i] = Py_NewRef(item);
383
358M
    }
384
167M
    _PyObject_GC_TRACK(tuple);
385
167M
    return (PyObject *)tuple;
386
167M
}
387
388
PyObject *
389
_PyTuple_FromStackRefStealOnSuccess(const _PyStackRef *src, Py_ssize_t n)
390
153M
{
391
153M
    if (n == 0) {
392
0
        return tuple_get_empty();
393
0
    }
394
153M
    PyTupleObject *tuple = tuple_alloc(n);
395
153M
    if (tuple == NULL) {
396
0
        return NULL;
397
0
    }
398
153M
    PyObject **dst = tuple->ob_item;
399
499M
    for (Py_ssize_t i = 0; i < n; i++) {
400
346M
        dst[i] = PyStackRef_AsPyObjectSteal(src[i]);
401
346M
    }
402
153M
    _PyObject_GC_TRACK(tuple);
403
153M
    return (PyObject *)tuple;
404
153M
}
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
24.0M
{
432
24.0M
    if (ilow < 0)
433
0
        ilow = 0;
434
24.0M
    if (ihigh > Py_SIZE(a))
435
0
        ihigh = Py_SIZE(a);
436
24.0M
    if (ihigh < ilow)
437
0
        ihigh = ilow;
438
24.0M
    if (ilow == 0 && ihigh == Py_SIZE(a) && PyTuple_CheckExact(a)) {
439
0
        return Py_NewRef(a);
440
0
    }
441
24.0M
    return _PyTuple_FromArray(a->ob_item + ilow, ihigh - ilow);
442
24.0M
}
443
444
PyObject *
445
PyTuple_GetSlice(PyObject *op, Py_ssize_t i, Py_ssize_t j)
446
24.0M
{
447
24.0M
    if (op == NULL || !PyTuple_Check(op)) {
448
0
        PyErr_BadInternalCall();
449
0
        return NULL;
450
0
    }
451
24.0M
    return tuple_slice((PyTupleObject *)op, i, j);
452
24.0M
}
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.77M
{
621
9.77M
    PyTupleObject *o = _PyTuple_CAST(self);
622
45.4M
    for (Py_ssize_t i = Py_SIZE(o); --i >= 0; ) {
623
35.6M
        Py_VISIT(o->ob_item[i]);
624
35.6M
    }
625
9.77M
    return 0;
626
9.77M
}
627
628
static PyObject *
629
tuple_richcompare(PyObject *v, PyObject *w, int op)
630
3.57M
{
631
3.57M
    PyTupleObject *vt, *wt;
632
3.57M
    Py_ssize_t i;
633
3.57M
    Py_ssize_t vlen, wlen;
634
635
3.57M
    if (!PyTuple_Check(v) || !PyTuple_Check(w))
636
0
        Py_RETURN_NOTIMPLEMENTED;
637
638
3.57M
    vt = (PyTupleObject *)v;
639
3.57M
    wt = (PyTupleObject *)w;
640
641
3.57M
    vlen = Py_SIZE(vt);
642
3.57M
    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.40M
    for (i = 0; i < vlen && i < wlen; i++) {
656
4.78M
        int k = PyObject_RichCompareBool(vt->ob_item[i],
657
4.78M
                                         wt->ob_item[i], Py_EQ);
658
4.78M
        if (k < 0)
659
0
            return NULL;
660
4.78M
        if (!k)
661
2.95M
            break;
662
4.78M
    }
663
664
3.57M
    if (i >= vlen || i >= wlen) {
665
        /* No more items to compare -- compare sizes */
666
620k
        Py_RETURN_RICHCOMPARE(vlen, wlen, op);
667
620k
    }
668
669
    /* We have an item that differs -- shortcuts for EQ/NE */
670
2.95M
    if (op == Py_EQ) {
671
266
        Py_RETURN_FALSE;
672
266
    }
673
2.95M
    if (op == Py_NE) {
674
65
        Py_RETURN_TRUE;
675
65
    }
676
677
    /* Compare the final item again using the proper operator */
678
2.95M
    return PyObject_RichCompare(vt->ob_item[i], wt->ob_item[i], op);
679
2.95M
}
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.61M
{
702
3.61M
    if (type != &PyTuple_Type)
703
1.80M
        return tuple_subtype_new(type, iterable);
704
705
1.81M
    if (iterable == NULL) {
706
0
        return tuple_get_empty();
707
0
    }
708
1.81M
    else {
709
1.81M
        return PySequence_Tuple(iterable);
710
1.81M
    }
711
1.81M
}
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.80M
{
737
1.80M
    PyObject *tmp, *newobj, *item;
738
1.80M
    Py_ssize_t i, n;
739
740
1.80M
    assert(PyType_IsSubtype(type, &PyTuple_Type));
741
    // tuple subclasses must implement the GC protocol
742
1.80M
    assert(_PyType_IS_GC(type));
743
744
1.80M
    tmp = tuple_new_impl(&PyTuple_Type, iterable);
745
1.80M
    if (tmp == NULL)
746
0
        return NULL;
747
1.80M
    assert(PyTuple_Check(tmp));
748
    /* This may allocate an empty tuple that is not the global one. */
749
1.80M
    newobj = type->tp_alloc(type, n = PyTuple_GET_SIZE(tmp));
750
1.80M
    if (newobj == NULL) {
751
0
        Py_DECREF(tmp);
752
0
        return NULL;
753
0
    }
754
7.24M
    for (i = 0; i < n; i++) {
755
5.43M
        item = PyTuple_GET_ITEM(tmp, i);
756
5.43M
        PyTuple_SET_ITEM(newobj, i, Py_NewRef(item));
757
5.43M
    }
758
1.80M
    Py_DECREF(tmp);
759
760
1.80M
    _PyTuple_RESET_HASH_CACHE(newobj);
761
762
    // Don't track if a subclass tp_alloc is PyType_GenericAlloc()
763
1.80M
    if (!_PyObject_GC_IS_TRACKED(newobj)) {
764
0
        _PyObject_GC_TRACK(newobj);
765
0
    }
766
1.80M
    return newobj;
767
1.80M
}
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.38M
{
783
1.38M
    PyTupleObject *self = _PyTuple_CAST(op);
784
1.38M
    if (_PyIndex_Check(item)) {
785
1.38M
        Py_ssize_t i = PyNumber_AsSsize_t(item, PyExc_IndexError);
786
1.38M
        if (i == -1 && PyErr_Occurred())
787
0
            return NULL;
788
1.38M
        if (i < 0)
789
1.37M
            i += PyTuple_GET_SIZE(self);
790
1.38M
        return tuple_item(op, i);
791
1.38M
    }
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.38M
}
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.3M
#define _PyTupleIterObject_CAST(op) ((_PyTupleIterObject *)(op))
986
987
static void
988
tupleiter_dealloc(PyObject *self)
989
2.65M
{
990
2.65M
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
991
2.65M
    _PyObject_GC_UNTRACK(it);
992
2.65M
    Py_XDECREF(it->it_seq);
993
2.65M
    assert(Py_IS_TYPE(self, &PyTupleIter_Type));
994
2.65M
    _Py_FREELIST_FREE(tuple_iters, it, PyObject_GC_Del);
995
2.65M
}
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.6M
{
1008
10.6M
    _PyTupleIterObject *it = _PyTupleIterObject_CAST(self);
1009
10.6M
    PyTupleObject *seq;
1010
10.6M
    PyObject *item;
1011
1012
10.6M
    assert(it != NULL);
1013
10.6M
    seq = it->it_seq;
1014
10.6M
#ifndef Py_GIL_DISABLED
1015
10.6M
    if (seq == NULL)
1016
0
        return NULL;
1017
10.6M
#endif
1018
10.6M
    assert(PyTuple_Check(seq));
1019
1020
10.6M
    Py_ssize_t index = FT_ATOMIC_LOAD_SSIZE_RELAXED(it->it_index);
1021
10.6M
    if (index < PyTuple_GET_SIZE(seq)) {
1022
7.98M
        FT_ATOMIC_STORE_SSIZE_RELAXED(it->it_index, index + 1);
1023
7.98M
        item = PyTuple_GET_ITEM(seq, index);
1024
7.98M
        return Py_NewRef(item);
1025
7.98M
    }
1026
1027
2.65M
#ifndef Py_GIL_DISABLED
1028
2.65M
    it->it_seq = NULL;
1029
2.65M
    Py_DECREF(seq);
1030
2.65M
#endif
1031
2.65M
    return NULL;
1032
10.6M
}
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.65M
{
1137
2.65M
    if (!PyTuple_Check(seq)) {
1138
0
        PyErr_BadInternalCall();
1139
0
        return NULL;
1140
0
    }
1141
2.65M
    _PyTupleIterObject *it = _Py_FREELIST_POP(_PyTupleIterObject, tuple_iters);
1142
2.65M
    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.65M
    it->it_index = 0;
1148
2.65M
    it->it_seq = (PyTupleObject *)Py_NewRef(seq);
1149
2.65M
    _PyObject_GC_TRACK(it);
1150
2.65M
    return (PyObject *)it;
1151
2.65M
}
1152
1153
1154
/*************
1155
 * freelists *
1156
 *************/
1157
1158
static inline int
1159
maybe_freelist_push(PyTupleObject *op)
1160
405M
{
1161
405M
    if (!Py_IS_TYPE(op, &PyTuple_Type)) {
1162
1.80M
        return 0;
1163
1.80M
    }
1164
404M
    Py_ssize_t index = Py_SIZE(op) - 1;
1165
404M
    if (index < PyTuple_MAXSAVESIZE) {
1166
404M
        return _Py_FREELIST_PUSH(tuples[index], op, Py_tuple_MAXFREELIST);
1167
404M
    }
1168
2.82k
    return 0;
1169
404M
}
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
}