Coverage Report

Created: 2026-09-27 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython3/Parser/tokenizer/source.c
Line
Count
Source
1
#include "Python.h"
2
3
#include "source.h"
4
5
void
6
_PyTok_SourceInit(_PyTok_SourceText *source)
7
49.1k
{
8
49.1k
    *source = (_PyTok_SourceText){0};
9
49.1k
}
10
11
void
12
_PyTok_SourceClear(_PyTok_SourceText *source)
13
24.5k
{
14
24.5k
    PyMem_Free(source->bytes);
15
24.5k
    PyMem_Free(source->implicit_lines);
16
24.5k
    _PyTok_SourceInit(source);
17
24.5k
}
18
19
void
20
_PyTok_SourceDiscard(_PyTok_SourceText *source)
21
0
{
22
0
    assert(source->base_offset <= PY_SSIZE_T_MAX - source->len);
23
0
    source->base_offset += source->len;
24
0
    source->len = 0;
25
0
    if (source->bytes != NULL) {
26
0
        source->bytes[0] = '\0';
27
0
    }
28
0
    if (source->implicit_lines != NULL) {
29
0
        Py_ssize_t used = source->nlines / 8 + (source->nlines % 8 != 0);
30
0
        memset(source->implicit_lines, 0, Py_MIN(used, source->implicit_cap));
31
0
    }
32
0
    source->nlines = 0;
33
0
}
34
35
static int
36
reserve_bytes(_PyTok_SourceText *source, Py_ssize_t needed)
37
430k
{
38
430k
    if (needed <= source->cap) {
39
406k
        return 0;
40
406k
    }
41
23.4k
    Py_ssize_t cap = source->cap > 0 ? source->cap : BUFSIZ;
42
24.5k
    while (cap < needed) {
43
1.04k
        if (cap > PY_SSIZE_T_MAX / 2) {
44
0
            cap = needed;
45
0
            break;
46
0
        }
47
1.04k
        cap *= 2;
48
1.04k
    }
49
23.4k
    char *bytes;
50
#ifdef Py_DEBUG
51
    /* Moving on growth makes stale interior pointers fail in debug builds. */
52
    bytes = PyMem_Malloc(cap);
53
    if (bytes != NULL && source->len > 0) {
54
        memcpy(bytes, source->bytes, source->len);
55
    }
56
#else
57
23.4k
    bytes = PyMem_Realloc(source->bytes, cap);
58
23.4k
#endif
59
23.4k
    if (bytes == NULL) {
60
0
        PyErr_NoMemory();
61
0
        return -1;
62
0
    }
63
#ifdef Py_DEBUG
64
    if (source->bytes != NULL) {
65
        memset(source->bytes, 0xDD, source->cap);
66
        PyMem_Free(source->bytes);
67
    }
68
#endif
69
23.4k
    source->bytes = bytes;
70
23.4k
    source->cap = cap;
71
23.4k
    return 0;
72
23.4k
}
73
74
static int
75
reserve_implicit_lines(_PyTok_SourceText *source, int nlines)
76
13.8k
{
77
13.8k
    Py_ssize_t needed = nlines / 8 + (nlines % 8 != 0);
78
13.8k
    if (needed <= source->implicit_cap) {
79
0
        return 0;
80
0
    }
81
13.8k
    Py_ssize_t cap = source->implicit_cap > 0 ? source->implicit_cap : 16;
82
14.6k
    while (cap < needed) {
83
816
        if (cap > PY_SSIZE_T_MAX / 2) {
84
0
            cap = needed;
85
0
            break;
86
0
        }
87
816
        cap *= 2;
88
816
    }
89
13.8k
    unsigned char *lines = PyMem_Realloc(source->implicit_lines, cap);
90
13.8k
    if (lines == NULL) {
91
0
        PyErr_NoMemory();
92
0
        return -1;
93
0
    }
94
13.8k
    memset(lines + source->implicit_cap, 0, cap - source->implicit_cap);
95
13.8k
    source->implicit_lines = lines;
96
13.8k
    source->implicit_cap = cap;
97
13.8k
    return 0;
98
13.8k
}
99
100
static int
101
validate_line(const _PyTok_SourceText *source, const char *bytes,
102
              Py_ssize_t len, int implicit_newline)
103
430k
{
104
430k
    if (len <= 0 || bytes == NULL ||
105
430k
            (source->nlines > 0 &&
106
407k
             (source->len == 0 || source->bytes[source->len - 1] != '\n'))) {
107
0
        PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
108
0
        return -1;
109
0
    }
110
430k
    const char *newline = memchr(bytes, '\n', len);
111
430k
    if ((newline != NULL && newline != bytes + len - 1) ||
112
430k
            (implicit_newline && newline == NULL)) {
113
0
        PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
114
0
        return -1;
115
0
    }
116
430k
    if (source->nlines == INT_MAX ||
117
430k
            (source->nlines == INT_MAX - 1 && newline != NULL)) {
118
0
        PyErr_SetString(PyExc_OverflowError, "too many tokenizer source lines");
119
0
        return -1;
120
0
    }
121
430k
    return 0;
122
430k
}
123
124
_PyTok_Off
125
_PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
126
                        Py_ssize_t len, int implicit_newline)
127
430k
{
128
430k
    if (validate_line(source, bytes, len, implicit_newline) < 0) {
129
0
        return -1;
130
0
    }
131
430k
    if (source->len > PY_SSIZE_T_MAX - len - 1 ||
132
430k
            source->base_offset > PY_SSIZE_T_MAX - source->len - len) {
133
0
        PyErr_NoMemory();
134
0
        return -1;
135
0
    }
136
430k
    int nlines = source->nlines + 1;
137
430k
    if ((implicit_newline && reserve_implicit_lines(source, nlines) < 0) ||
138
430k
            reserve_bytes(source, source->len + len + 1) < 0) {
139
0
        return -1;
140
0
    }
141
142
430k
    _PyTok_Off start = source->len;
143
430k
    memcpy(source->bytes + start, bytes, len);
144
430k
    source->len += len;
145
430k
    source->bytes[source->len] = '\0';
146
430k
    if (implicit_newline) {
147
13.8k
        source->implicit_lines[(nlines - 1) / 8] |=
148
13.8k
            (unsigned char)(1U << ((nlines - 1) & 7));
149
13.8k
    }
150
430k
    source->nlines = nlines;
151
430k
    return source->base_offset + start;
152
430k
}
153
154
const char *
155
_PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno,
156
                      Py_ssize_t *len)
157
295
{
158
295
    assert(len != NULL);
159
295
    const char *line = _PyTok_SourceData(source);
160
295
    const char *end = line + source->len;
161
7.88k
    while (lineno > 1) {
162
7.58k
        const char *newline = memchr(line, '\n', end - line);
163
7.58k
        if (newline == NULL) {
164
0
            break;
165
0
        }
166
7.58k
        line = newline + 1;
167
7.58k
        lineno--;
168
7.58k
    }
169
295
    const char *newline = memchr(line, '\n', end - line);
170
295
    *len = (newline != NULL ? newline : end) - line;
171
295
    return line;
172
295
}
173
174
int
175
_PyTok_SourceLineIsImplicit(const _PyTok_SourceText *source, int lineno)
176
421k
{
177
421k
    if (lineno < 1 || lineno > source->nlines ||
178
421k
            (lineno - 1) / 8 >= source->implicit_cap) {
179
92.3k
        return 0;
180
92.3k
    }
181
329k
    return (source->implicit_lines[(lineno - 1) / 8] >>
182
329k
            ((lineno - 1) & 7)) & 1;
183
421k
}