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