Coverage Report

Created: 2022-07-08 09:39

/home/mdboom/Work/builds/cpython/Objects/enumobject.c
Line
Count
Source (jump to first uncovered line)
1
/* enumerate object */
2
3
#include "Python.h"
4
#include "pycore_call.h"          // _PyObject_CallNoArgs()
5
#include "pycore_long.h"          // _PyLong_GetOne()
6
#include "pycore_object.h"        // _PyObject_GC_TRACK()
7
8
#include "clinic/enumobject.c.h"
9
10
/*[clinic input]
11
class enumerate "enumobject *" "&PyEnum_Type"
12
class reversed "reversedobject *" "&PyReversed_Type"
13
[clinic start generated code]*/
14
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=d2dfdf1a88c88975]*/
15
16
typedef struct {
17
    PyObject_HEAD
18
    Py_ssize_t en_index;           /* current index of enumeration */
19
    PyObject* en_sit;              /* secondary iterator of enumeration */
20
    PyObject* en_result;           /* result tuple  */
21
    PyObject* en_longindex;        /* index for sequences >= PY_SSIZE_T_MAX */
22
    PyObject* one;                 /* borrowed reference */
23
} enumobject;
24
25
26
/*[clinic input]
27
@classmethod
28
enumerate.__new__ as enum_new
29
30
    iterable: object
31
        an object supporting iteration
32
    start: object = 0
33
34
Return an enumerate object.
35
36
The enumerate object yields pairs containing a count (from start, which
37
defaults to zero) and a value yielded by the iterable argument.
38
39
enumerate is useful for obtaining an indexed list:
40
    (0, seq[0]), (1, seq[1]), (2, seq[2]), ...
41
[clinic start generated code]*/
42
43
static PyObject *
44
enum_new_impl(PyTypeObject *type, PyObject *iterable, PyObject *start)
45
/*[clinic end generated code: output=e95e6e439f812c10 input=782e4911efcb8acf]*/
46
{
47
    enumobject *en;
48
49
    en = (enumobject *)type->tp_alloc(type, 0);
50
    if (en == NULL)
  Branch (50:9): [True: 0, False: 129k]
51
        return NULL;
52
    if (start != NULL) {
  Branch (52:9): [True: 3.63k, False: 125k]
53
        start = PyNumber_Index(start);
54
        if (start == NULL) {
  Branch (54:13): [True: 7, False: 3.63k]
55
            Py_DECREF(en);
56
            return NULL;
57
        }
58
        assert(PyLong_Check(start));
59
        en->en_index = PyLong_AsSsize_t(start);
60
        if (en->en_index == -1 && 
PyErr_Occurred()109
) {
  Branch (60:13): [True: 109, False: 3.52k]
  Branch (60:35): [True: 33, False: 76]
61
            PyErr_Clear();
62
            en->en_index = PY_SSIZE_T_MAX;
63
            en->en_longindex = start;
64
        } else {
65
            en->en_longindex = NULL;
66
            Py_DECREF(start);
67
        }
68
    } else {
69
        en->en_index = 0;
70
        en->en_longindex = NULL;
71
    }
72
    en->en_sit = PyObject_GetIter(iterable);
73
    if (en->en_sit == NULL) {
  Branch (73:9): [True: 26, False: 129k]
74
        Py_DECREF(en);
75
        return NULL;
76
    }
77
    en->en_result = PyTuple_Pack(2, Py_None, Py_None);
78
    if (en->en_result == NULL) {
  Branch (78:9): [True: 0, False: 129k]
79
        Py_DECREF(en);
80
        return NULL;
81
    }
82
    en->one = _PyLong_GetOne();    /* borrowed reference */
83
    return (PyObject *)en;
84
}
85
86
static int check_keyword(PyObject *kwnames, int index,
87
                         const char *name)
88
{
89
    PyObject *kw = PyTuple_GET_ITEM(kwnames, index);
90
    if (!_PyUnicode_EqualToASCIIString(kw, name)) {
  Branch (90:9): [True: 16, False: 704]
91
        PyErr_Format(PyExc_TypeError,
92
            "'%S' is an invalid keyword argument for enumerate()", kw);
93
        return 0;
94
    }
95
    return 1;
96
}
97
98
// TODO: Use AC when bpo-43447 is supported
99
static PyObject *
100
enumerate_vectorcall(PyObject *type, PyObject *const *args,
101
                     size_t nargsf, PyObject *kwnames)
102
{
103
    PyTypeObject *tp = _PyType_CAST(type);
104
    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
105
    Py_ssize_t nkwargs = 0;
106
    if (kwnames != NULL) {
  Branch (106:9): [True: 712, False: 128k]
107
        nkwargs = PyTuple_GET_SIZE(kwnames);
108
    }
109
110
    // Manually implement enumerate(iterable, start=...)
111
    if (nargs + nkwargs == 2) {
  Branch (111:9): [True: 3.62k, False: 125k]
112
        if (nkwargs == 1) {
  Branch (112:13): [True: 684, False: 2.94k]
113
            if (!check_keyword(kwnames, 0, "start")) {
  Branch (113:17): [True: 0, False: 684]
114
                return NULL;
115
            }
116
        } else if (nkwargs == 2) {
  Branch (116:20): [True: 20, False: 2.92k]
117
            PyObject *kw0 = PyTuple_GET_ITEM(kwnames, 0);
118
            if (_PyUnicode_EqualToASCIIString(kw0, "start")) {
  Branch (118:17): [True: 8, False: 12]
119
                if (!check_keyword(kwnames, 1, "iterable")) {
  Branch (119:21): [True: 4, False: 4]
120
                    return NULL;
121
                }
122
                return enum_new_impl(tp, args[1], args[0]);
123
            }
124
            if (!check_keyword(kwnames, 0, "iterable") ||
  Branch (124:17): [True: 4, False: 8]
125
                
!check_keyword(kwnames, 1, "start")8
) {
  Branch (125:17): [True: 4, False: 4]
126
                return NULL;
127
            }
128
129
        }
130
        return enum_new_impl(tp, args[0], args[1]);
131
    }
132
133
    if (nargs + nkwargs == 1) {
  Branch (133:9): [True: 125k, False: 8]
134
        if (nkwargs == 1 && 
!check_keyword(kwnames, 0, "iterable")8
) {
  Branch (134:13): [True: 8, False: 125k]
  Branch (134:29): [True: 4, False: 4]
135
            return NULL;
136
        }
137
        return enum_new_impl(tp, args[0], NULL);
138
    }
139
140
    if (nargs == 0) {
  Branch (140:9): [True: 4, False: 4]
141
        PyErr_SetString(PyExc_TypeError,
142
            "enumerate() missing required argument 'iterable'");
143
        return NULL;
144
    }
145
146
    PyErr_Format(PyExc_TypeError,
147
        "enumerate() takes at most 2 arguments (%d given)", nargs + nkwargs);
148
    return NULL;
149
}
150
151
static void
152
enum_dealloc(enumobject *en)
153
{
154
    PyObject_GC_UnTrack(en);
155
    Py_XDECREF(en->en_sit);
156
    Py_XDECREF(en->en_result);
157
    Py_XDECREF(en->en_longindex);
158
    Py_TYPE(en)->tp_free(en);
159
}
160
161
static int
162
enum_traverse(enumobject *en, visitproc visit, void *arg)
163
{
164
    Py_VISIT(en->en_sit);
165
    Py_VISIT(en->en_result);
166
    Py_VISIT(en->en_longindex);
167
    return 0;
168
}
169
170
static PyObject *
171
enum_next_long(enumobject *en, PyObject* next_item)
172
{
173
    PyObject *result = en->en_result;
174
    PyObject *next_index;
175
    PyObject *stepped_up;
176
    PyObject *old_index;
177
    PyObject *old_item;
178
179
    if (en->en_longindex == NULL) {
  Branch (179:9): [True: 0, False: 52]
180
        en->en_longindex = PyLong_FromSsize_t(PY_SSIZE_T_MAX);
181
        if (en->en_longindex == NULL) {
  Branch (181:13): [True: 0, False: 0]
182
            Py_DECREF(next_item);
183
            return NULL;
184
        }
185
    }
186
    next_index = en->en_longindex;
187
    assert(next_index != NULL);
188
    stepped_up = PyNumber_Add(next_index, en->one);
189
    if (stepped_up == NULL) {
  Branch (189:9): [True: 0, False: 52]
190
        Py_DECREF(next_item);
191
        return NULL;
192
    }
193
    en->en_longindex = stepped_up;
194
195
    if (Py_REFCNT(result) == 1) {
  Branch (195:9): [True: 24, False: 28]
196
        Py_INCREF(result);
197
        old_index = PyTuple_GET_ITEM(result, 0);
198
        old_item = PyTuple_GET_ITEM(result, 1);
199
        PyTuple_SET_ITEM(result, 0, next_index);
200
        PyTuple_SET_ITEM(result, 1, next_item);
201
        Py_DECREF(old_index);
202
        Py_DECREF(old_item);
203
        // bpo-42536: The GC may have untracked this result tuple. Since we're
204
        // recycling it, make sure it's tracked again:
205
        if (!_PyObject_GC_IS_TRACKED(result)) {
  Branch (205:13): [True: 1, False: 23]
206
            _PyObject_GC_TRACK(result);
207
        }
208
        return result;
209
    }
210
    result = PyTuple_New(2);
211
    if (result == NULL) {
  Branch (211:9): [True: 0, False: 28]
212
        Py_DECREF(next_index);
213
        Py_DECREF(next_item);
214
        return NULL;
215
    }
216
    PyTuple_SET_ITEM(result, 0, next_index);
217
    PyTuple_SET_ITEM(result, 1, next_item);
218
    return result;
219
}
220
221
static PyObject *
222
enum_next(enumobject *en)
223
{
224
    PyObject *next_index;
225
    PyObject *next_item;
226
    PyObject *result = en->en_result;
227
    PyObject *it = en->en_sit;
228
    PyObject *old_index;
229
    PyObject *old_item;
230
231
    next_item = (*Py_TYPE(it)->tp_iternext)(it);
232
    if (next_item == NULL)
  Branch (232:9): [True: 127k, False: 2.87M]
233
        return NULL;
234
235
    if (en->en_index == PY_SSIZE_T_MAX)
  Branch (235:9): [True: 52, False: 2.87M]
236
        return enum_next_long(en, next_item);
237
238
    next_index = PyLong_FromSsize_t(en->en_index);
239
    if (next_index == NULL) {
  Branch (239:9): [True: 0, False: 2.87M]
240
        Py_DECREF(next_item);
241
        return NULL;
242
    }
243
    en->en_index++;
244
245
    if (Py_REFCNT(result) == 1) {
  Branch (245:9): [True: 2.66M, False: 210k]
246
        Py_INCREF(result);
247
        old_index = PyTuple_GET_ITEM(result, 0);
248
        old_item = PyTuple_GET_ITEM(result, 1);
249
        PyTuple_SET_ITEM(result, 0, next_index);
250
        PyTuple_SET_ITEM(result, 1, next_item);
251
        Py_DECREF(old_index);
252
        Py_DECREF(old_item);
253
        // bpo-42536: The GC may have untracked this result tuple. Since we're
254
        // recycling it, make sure it's tracked again:
255
        if (!_PyObject_GC_IS_TRACKED(result)) {
  Branch (255:13): [True: 527, False: 2.66M]
256
            _PyObject_GC_TRACK(result);
257
        }
258
        return result;
259
    }
260
    result = PyTuple_New(2);
261
    if (result == NULL) {
  Branch (261:9): [True: 0, False: 210k]
262
        Py_DECREF(next_index);
263
        Py_DECREF(next_item);
264
        return NULL;
265
    }
266
    PyTuple_SET_ITEM(result, 0, next_index);
267
    PyTuple_SET_ITEM(result, 1, next_item);
268
    return result;
269
}
270
271
static PyObject *
272
enum_reduce(enumobject *en, PyObject *Py_UNUSED(ignored))
273
{
274
    if (en->en_longindex != NULL)
  Branch (274:9): [True: 12, False: 66]
275
        return Py_BuildValue("O(OO)", Py_TYPE(en), en->en_sit, en->en_longindex);
276
    else
277
        return Py_BuildValue("O(On)", Py_TYPE(en), en->en_sit, en->en_index);
278
}
279
280
PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
281
282
static PyMethodDef enum_methods[] = {
283
    {"__reduce__", (PyCFunction)enum_reduce, METH_NOARGS, reduce_doc},
284
    {"__class_getitem__",    Py_GenericAlias,
285
    METH_O|METH_CLASS,       PyDoc_STR("See PEP 585")},
286
    {NULL,              NULL}           /* sentinel */
287
};
288
289
PyTypeObject PyEnum_Type = {
290
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
291
    "enumerate",                    /* tp_name */
292
    sizeof(enumobject),             /* tp_basicsize */
293
    0,                              /* tp_itemsize */
294
    /* methods */
295
    (destructor)enum_dealloc,       /* tp_dealloc */
296
    0,                              /* tp_vectorcall_offset */
297
    0,                              /* tp_getattr */
298
    0,                              /* tp_setattr */
299
    0,                              /* tp_as_async */
300
    0,                              /* tp_repr */
301
    0,                              /* tp_as_number */
302
    0,                              /* tp_as_sequence */
303
    0,                              /* tp_as_mapping */
304
    0,                              /* tp_hash */
305
    0,                              /* tp_call */
306
    0,                              /* tp_str */
307
    PyObject_GenericGetAttr,        /* tp_getattro */
308
    0,                              /* tp_setattro */
309
    0,                              /* tp_as_buffer */
310
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
311
        Py_TPFLAGS_BASETYPE,        /* tp_flags */
312
    enum_new__doc__,                /* tp_doc */
313
    (traverseproc)enum_traverse,    /* tp_traverse */
314
    0,                              /* tp_clear */
315
    0,                              /* tp_richcompare */
316
    0,                              /* tp_weaklistoffset */
317
    PyObject_SelfIter,              /* tp_iter */
318
    (iternextfunc)enum_next,        /* tp_iternext */
319
    enum_methods,                   /* tp_methods */
320
    0,                              /* tp_members */
321
    0,                              /* tp_getset */
322
    0,                              /* tp_base */
323
    0,                              /* tp_dict */
324
    0,                              /* tp_descr_get */
325
    0,                              /* tp_descr_set */
326
    0,                              /* tp_dictoffset */
327
    0,                              /* tp_init */
328
    PyType_GenericAlloc,            /* tp_alloc */
329
    enum_new,                       /* tp_new */
330
    PyObject_GC_Del,                /* tp_free */
331
    .tp_vectorcall = (vectorcallfunc)enumerate_vectorcall
332
};
333
334
/* Reversed Object ***************************************************************/
335
336
typedef struct {
337
    PyObject_HEAD
338
    Py_ssize_t      index;
339
    PyObject* seq;
340
} reversedobject;
341
342
/*[clinic input]
343
@classmethod
344
reversed.__new__ as reversed_new
345
346
    sequence as seq: object
347
    /
348
349
Return a reverse iterator over the values of the given sequence.
350
[clinic start generated code]*/
351
352
static PyObject *
353
reversed_new_impl(PyTypeObject *type, PyObject *seq)
354
/*[clinic end generated code: output=f7854cc1df26f570 input=aeb720361e5e3f1d]*/
355
{
356
    Py_ssize_t n;
357
    PyObject *reversed_meth;
358
    reversedobject *ro;
359
360
    reversed_meth = _PyObject_LookupSpecial(seq, &_Py_ID(__reversed__));
361
    if (reversed_meth == Py_None) {
  Branch (361:9): [True: 2, False: 118k]
362
        Py_DECREF(reversed_meth);
363
        PyErr_Format(PyExc_TypeError,
364
                     "'%.200s' object is not reversible",
365
                     Py_TYPE(seq)->tp_name);
366
        return NULL;
367
    }
368
    if (reversed_meth != NULL) {
  Branch (368:9): [True: 114k, False: 4.39k]
369
        PyObject *res = _PyObject_CallNoArgs(reversed_meth);
370
        Py_DECREF(reversed_meth);
371
        return res;
372
    }
373
    else if (PyErr_Occurred())
  Branch (373:14): [True: 1, False: 4.39k]
374
        return NULL;
375
376
    if (!PySequence_Check(seq)) {
  Branch (376:9): [True: 11, False: 4.38k]
377
        PyErr_Format(PyExc_TypeError,
378
                     "'%.200s' object is not reversible",
379
                     Py_TYPE(seq)->tp_name);
380
        return NULL;
381
    }
382
383
    n = PySequence_Size(seq);
384
    if (n == -1)
  Branch (384:9): [True: 1, False: 4.38k]
385
        return NULL;
386
387
    ro = (reversedobject *)type->tp_alloc(type, 0);
388
    if (ro == NULL)
  Branch (388:9): [True: 0, False: 4.38k]
389
        return NULL;
390
391
    ro->index = n-1;
392
    Py_INCREF(seq);
393
    ro->seq = seq;
394
    return (PyObject *)ro;
395
}
396
397
static PyObject *
398
reversed_vectorcall(PyObject *type, PyObject * const*args,
399
                size_t nargsf, PyObject *kwnames)
400
{
401
    if (!_PyArg_NoKwnames("reversed", kwnames)) {
402
        return NULL;
403
    }
404
405
    Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
406
    if (!_PyArg_CheckPositional("reversed", nargs, 1, 1)) {
407
        return NULL;
408
    }
409
410
    return reversed_new_impl(_PyType_CAST(type), args[0]);
411
}
412
413
static void
414
reversed_dealloc(reversedobject *ro)
415
{
416
    PyObject_GC_UnTrack(ro);
417
    Py_XDECREF(ro->seq);
418
    Py_TYPE(ro)->tp_free(ro);
419
}
420
421
static int
422
reversed_traverse(reversedobject *ro, visitproc visit, void *arg)
423
{
424
    Py_VISIT(ro->seq);
425
    return 0;
426
}
427
428
static PyObject *
429
reversed_next(reversedobject *ro)
430
{
431
    PyObject *item;
432
    Py_ssize_t index = ro->index;
433
434
    if (index >= 0) {
  Branch (434:9): [True: 5.40k, False: 3.98k]
435
        item = PySequence_GetItem(ro->seq, index);
436
        if (item != NULL) {
  Branch (436:13): [True: 5.40k, False: 0]
437
            ro->index--;
438
            return item;
439
        }
440
        if (PyErr_ExceptionMatches(PyExc_IndexError) ||
  Branch (440:13): [True: 0, False: 0]
441
            PyErr_ExceptionMatches(PyExc_StopIteration))
  Branch (441:13): [True: 0, False: 0]
442
            PyErr_Clear();
443
    }
444
    ro->index = -1;
445
    Py_CLEAR(ro->seq);
446
    return NULL;
447
}
448
449
static PyObject *
450
reversed_len(reversedobject *ro, PyObject *Py_UNUSED(ignored))
451
{
452
    Py_ssize_t position, seqsize;
453
454
    if (ro->seq == NULL)
  Branch (454:9): [True: 15, False: 1.64k]
455
        return PyLong_FromLong(0);
456
    seqsize = PySequence_Size(ro->seq);
457
    if (seqsize == -1)
  Branch (457:9): [True: 1, False: 1.64k]
458
        return NULL;
459
    position = ro->index + 1;
460
    return PyLong_FromSsize_t((seqsize < position)  ?  
00
: position);
  Branch (460:31): [True: 0, False: 1.64k]
461
}
462
463
PyDoc_STRVAR(length_hint_doc, "Private method returning an estimate of len(list(it)).");
464
465
static PyObject *
466
reversed_reduce(reversedobject *ro, PyObject *Py_UNUSED(ignored))
467
{
468
    if (ro->seq)
  Branch (468:9): [True: 270, False: 78]
469
        return Py_BuildValue("O(O)n", Py_TYPE(ro), ro->seq, ro->index);
470
    else
471
        return Py_BuildValue("O(())", Py_TYPE(ro));
472
}
473
474
static PyObject *
475
reversed_setstate(reversedobject *ro, PyObject *state)
476
{
477
    Py_ssize_t index = PyLong_AsSsize_t(state);
478
    if (index == -1 && 
PyErr_Occurred()78
)
  Branch (478:9): [True: 78, False: 204]
  Branch (478:24): [True: 0, False: 78]
479
        return NULL;
480
    if (ro->seq != 0) {
  Branch (480:9): [True: 282, False: 0]
481
        Py_ssize_t n = PySequence_Size(ro->seq);
482
        if (n < 0)
  Branch (482:13): [True: 0, False: 282]
483
            return NULL;
484
        if (index < -1)
  Branch (484:13): [True: 0, False: 282]
485
            index = -1;
486
        else if (index > n-1)
  Branch (486:18): [True: 0, False: 282]
487
            index = n-1;
488
        ro->index = index;
489
    }
490
    Py_RETURN_NONE
0
;
491
}
492
493
PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
494
495
static PyMethodDef reversediter_methods[] = {
496
    {"__length_hint__", (PyCFunction)reversed_len, METH_NOARGS, length_hint_doc},
497
    {"__reduce__", (PyCFunction)reversed_reduce, METH_NOARGS, reduce_doc},
498
    {"__setstate__", (PyCFunction)reversed_setstate, METH_O, setstate_doc},
499
    {NULL,              NULL}           /* sentinel */
500
};
501
502
PyTypeObject PyReversed_Type = {
503
    PyVarObject_HEAD_INIT(&PyType_Type, 0)
504
    "reversed",                     /* tp_name */
505
    sizeof(reversedobject),         /* tp_basicsize */
506
    0,                              /* tp_itemsize */
507
    /* methods */
508
    (destructor)reversed_dealloc,   /* tp_dealloc */
509
    0,                              /* tp_vectorcall_offset */
510
    0,                              /* tp_getattr */
511
    0,                              /* tp_setattr */
512
    0,                              /* tp_as_async */
513
    0,                              /* tp_repr */
514
    0,                              /* tp_as_number */
515
    0,                              /* tp_as_sequence */
516
    0,                              /* tp_as_mapping */
517
    0,                              /* tp_hash */
518
    0,                              /* tp_call */
519
    0,                              /* tp_str */
520
    PyObject_GenericGetAttr,        /* tp_getattro */
521
    0,                              /* tp_setattro */
522
    0,                              /* tp_as_buffer */
523
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
524
        Py_TPFLAGS_BASETYPE,        /* tp_flags */
525
    reversed_new__doc__,            /* tp_doc */
526
    (traverseproc)reversed_traverse,/* tp_traverse */
527
    0,                              /* tp_clear */
528
    0,                              /* tp_richcompare */
529
    0,                              /* tp_weaklistoffset */
530
    PyObject_SelfIter,              /* tp_iter */
531
    (iternextfunc)reversed_next,    /* tp_iternext */
532
    reversediter_methods,           /* tp_methods */
533
    0,                              /* tp_members */
534
    0,                              /* tp_getset */
535
    0,                              /* tp_base */
536
    0,                              /* tp_dict */
537
    0,                              /* tp_descr_get */
538
    0,                              /* tp_descr_set */
539
    0,                              /* tp_dictoffset */
540
    0,                              /* tp_init */
541
    PyType_GenericAlloc,            /* tp_alloc */
542
    reversed_new,                   /* tp_new */
543
    PyObject_GC_Del,                /* tp_free */
544
    .tp_vectorcall = (vectorcallfunc)reversed_vectorcall,
545
};