/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 | } |