Coverage Report

Created: 2026-10-17 06:15

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
284
#define SOURCE_BUFFER_GROWTH_FACTOR 2
7
8
void
9
_PyTok_SourceInit(_PyTok_SourceText *source)
10
130k
{
11
130k
    *source = (_PyTok_SourceText){0};
12
130k
}
13
14
void
15
_PyTok_SourceClear(_PyTok_SourceText *source)
16
65.0k
{
17
65.0k
    PyMem_Free(source->bytes);
18
65.0k
    _PyTok_SourceInit(source);
19
65.0k
}
20
21
void
22
_PyTok_SourceDiscard(_PyTok_SourceText *source)
23
324
{
24
324
    assert(source->base_offset <= PY_SSIZE_T_MAX - source->len);
25
324
    source->base_offset += source->len;
26
324
    source->len = 0;
27
324
    if (source->bytes != NULL) {
28
304
        source->bytes[0] = '\0';
29
304
    }
30
324
    source->nlines = 0;
31
324
}
32
33
static int
34
reserve_bytes(_PyTok_SourceText *source, Py_ssize_t needed)
35
258k
{
36
258k
    if (needed <= source->cap) {
37
195k
        return 0;
38
195k
    }
39
63.2k
    Py_ssize_t cap = source->cap > 0 ? source->cap : BUFSIZ;
40
63.3k
    while (cap < needed) {
41
142
        if (cap > PY_SSIZE_T_MAX / SOURCE_BUFFER_GROWTH_FACTOR) {
42
0
            cap = needed;
43
0
            break;
44
0
        }
45
142
        cap *= SOURCE_BUFFER_GROWTH_FACTOR;
46
142
    }
47
63.2k
    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
63.2k
    bytes = PyMem_Realloc(source->bytes, cap);
56
63.2k
#endif
57
63.2k
    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
63.2k
    source->bytes = bytes;
68
63.2k
    source->cap = cap;
69
63.2k
    return 0;
70
63.2k
}
71
72
static int
73
validate_line(const _PyTok_SourceText *source, const char *bytes,
74
              Py_ssize_t len)
75
258k
{
76
258k
    if (len <= 0 || bytes == NULL ||
77
258k
            (source->nlines > 0 &&
78
195k
             (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
258k
    const char *newline = memchr(bytes, '\n', len);
83
258k
    if (newline != NULL && newline != bytes + len - 1) {
84
0
        PyErr_SetString(PyExc_SystemError, "invalid tokenizer source line");
85
0
        return -1;
86
0
    }
87
258k
    if (source->nlines == INT_MAX ||
88
258k
            (source->nlines == INT_MAX - 1 && newline != NULL)) {
89
0
        PyErr_SetString(PyExc_OverflowError, "too many tokenizer source lines");
90
0
        return -1;
91
0
    }
92
258k
    return 0;
93
258k
}
94
95
_PyTok_Off
96
_PyTok_SourceAppendLine(_PyTok_SourceText *source, const char *bytes,
97
                        Py_ssize_t len)
98
258k
{
99
258k
    if (validate_line(source, bytes, len) < 0) {
100
0
        return -1;
101
0
    }
102
258k
    if (source->len > PY_SSIZE_T_MAX - len - 1 ||
103
258k
            source->base_offset > PY_SSIZE_T_MAX - source->len - len) {
104
0
        PyErr_NoMemory();
105
0
        return -1;
106
0
    }
107
258k
    if (reserve_bytes(source, source->len + len + 1) < 0) {
108
0
        return -1;
109
0
    }
110
111
258k
    _PyTok_Off start = source->len;
112
258k
    memcpy(source->bytes + start, bytes, len);
113
258k
    source->len += len;
114
258k
    source->bytes[source->len] = '\0';
115
258k
    source->nlines++;
116
258k
    return source->base_offset + start;
117
258k
}
118
119
const char *
120
_PyTok_SourceLineView(const _PyTok_SourceText *source, Py_ssize_t lineno,
121
                      Py_ssize_t *len)
122
257
{
123
257
    assert(len != NULL);
124
257
    const char *line = _PyTok_SourceData(source);
125
257
    const char *end = line + source->len;
126
4.33k
    while (lineno > 1) {
127
4.08k
        const char *newline = memchr(line, '\n', end - line);
128
4.08k
        if (newline == NULL) {
129
0
            break;
130
0
        }
131
4.08k
        line = newline + 1;
132
4.08k
        lineno--;
133
4.08k
    }
134
257
    const char *newline = memchr(line, '\n', end - line);
135
257
    *len = (newline != NULL ? newline : end) - line;
136
257
    return line;
137
257
}