Coverage Report

Created: 2026-10-31 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/cpython/Parser/tokenizer/source.c
Line
Count
Source
1
#include "Python.h"
2
#include "pycore_pymem.h"  // PYMEM_DEADBYTE
3
4
#include "source.h"
5
6
296
#define SOURCE_BUFFER_GROWTH_FACTOR 2
7
8
void
9
_PyTok_SourceInit(_PyTok_SourceText *source)
10
152k
{
11
152k
    *source = (_PyTok_SourceText){0};
12
152k
}
13
14
void
15
_PyTok_SourceClear(_PyTok_SourceText *source)
16
76.0k
{
17
76.0k
    PyMem_Free(source->bytes);
18
76.0k
    _PyTok_SourceInit(source);
19
76.0k
}
20
21
void
22
_PyTok_SourceDiscard(_PyTok_SourceText *source)
23
316
{
24
316
    assert(source->base_offset <= PY_SSIZE_T_MAX - source->len);
25
316
    source->base_offset += source->len;
26
316
    source->len = 0;
27
316
    if (source->bytes != NULL) {
28
300
        source->bytes[0] = '\0';
29
300
    }
30
316
    source->nlines = 0;
31
316
}
32
33
static int
34
reserve_bytes(_PyTok_SourceText *source, Py_ssize_t needed)
35
275k
{
36
275k
    if (needed <= source->cap) {
37
201k
        return 0;
38
201k
    }
39
74.1k
    Py_ssize_t cap = source->cap > 0 ? source->cap : BUFSIZ;
40
74.2k
    while (cap < needed) {
41
148
        if (cap > PY_SSIZE_T_MAX / SOURCE_BUFFER_GROWTH_FACTOR) {
42
0
            cap = needed;
43
0
            break;
44
0
        }
45
148
        cap *= SOURCE_BUFFER_GROWTH_FACTOR;
46
148
    }
47
74.1k
    char *bytes;
48
#ifdef Py_DEBUG
49
    /* Moving on growth makes stale interior pointers fail in debug builds. */
50
    bytes = PyMem_Malloc(cap);
51
    if (bytes != NULL && source->len > 0) {
52
        memcpy(bytes, source->bytes, source->len);
53
    }
54
#else
55
74.1k
    bytes = PyMem_Realloc(source->bytes, cap);
56
74.1k
#endif
57
74.1k
    if (bytes == NULL) {
58
0
        PyErr_NoMemory();
59
0
        return -1;
60
0
    }
61
#ifdef Py_DEBUG
62
    if (source->bytes != NULL) {
63
        memset(source->bytes, PYMEM_DEADBYTE, source->cap);
64
        PyMem_Free(source->bytes);
65
    }
66
#endif
67
74.1k
    source->bytes = bytes;
68
74.1k
    source->cap = cap;
69
74.1k
    return 0;
70
74.1k
}
71
72
static int
73
validate_line(const _PyTok_SourceText *source, const char *bytes,
74
              Py_ssize_t len, int append_newline)
75
275k
{
76
275k
    if (len < 0 || (len == 0 && !append_newline) || bytes == NULL ||
77
275k
            (source->nlines > 0 &&
78
200k
             (source->len == 0 || source->bytes[source->len - 1] != '\n'))) {
79
0
        PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
80
0
        return -1;
81
0
    }
82
275k
    const char *newline = memchr(bytes, '\n', len);
83
275k
    if (newline != NULL && (append_newline || newline != bytes + len - 1)) {
84
0
        PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
85
0
        return -1;
86
0
    }
87
275k
    if (source->nlines == INT_MAX ||
88
275k
            (source->nlines == INT_MAX - 1 && (newline != NULL || append_newline))) {
89
0
        PyErr_SetString(PyExc_OverflowError, "too many tokenizer source lines");
90
0
        return -1;
91
0
    }
92
275k
    return 0;
93
275k
}
94
95
static _PyTok_Off
96
append_line(_PyTok_SourceText *source, const char *bytes,
97
            Py_ssize_t len, int append_newline)
98
275k
{
99
275k
    if (validate_line(source, bytes, len, append_newline) < 0) {
100
0
        return -1;
101
0
    }
102
275k
    if (source->len > PY_SSIZE_T_MAX - len - append_newline - 1 ||
103
275k
            source->base_offset > PY_SSIZE_T_MAX - source->len - len - append_newline) {
104
0
        PyErr_NoMemory();
105
0
        return -1;
106
0
    }
107
275k
    if (reserve_bytes(source, source->len + len + append_newline + 1) < 0) {
108
0
        return -1;
109
0
    }
110
111
275k
    _PyTok_Off start = source->len;
112
275k
    memcpy(source->bytes + start, bytes, len);
113
275k
    source->len += len;
114
275k
    if (append_newline) {
115
88.4k
        source->bytes[source->len++] = '\n';
116
88.4k
    }
117
275k
    source->bytes[source->len] = '\0';
118
275k
    source->nlines++;
119
275k
    return source->base_offset + start;
120
275k
}
121
122
_PyTok_Off
123
_PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
124
                        Py_ssize_t len)
125
186k
{
126
186k
    return append_line(source, bytes, len, 0);
127
186k
}
128
129
_PyTok_Off
130
_PyTok_SourceAppendLineWithNewline(_PyTok_SourceText *source, const char *bytes,
131
                                   Py_ssize_t len)
132
88.4k
{
133
88.4k
    return append_line(source, bytes, len, 1);
134
88.4k
}
135
136
const char *
137
_PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno,
138
                      Py_ssize_t *len)
139
310
{
140
310
    assert(len != NULL);
141
310
    const char *line = _PyTok_SourceData(source);
142
310
    const char *end = line + source->len;
143
6.06k
    while (lineno > 1) {
144
5.75k
        const char *newline = memchr(line, '\n', end - line);
145
5.75k
        if (newline == NULL) {
146
0
            break;
147
0
        }
148
5.75k
        line = newline + 1;
149
5.75k
        lineno--;
150
5.75k
    }
151
310
    const char *newline = memchr(line, '\n', end - line);
152
310
    *len = (newline != NULL ? newline : end) - line;
153
310
    return line;
154
310
}