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