Skip to content

Commit ae1f4ed

Browse files
committed
gh-153568: Reuse the tokenizer newline buffer
1 parent a82c5a4 commit ae1f4ed

1 file changed

Lines changed: 54 additions & 21 deletions

File tree

Parser/tokenizer/decoder.c

Lines changed: 54 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -72,20 +72,11 @@ chunk_set_unicode(struct tok_state *tok, _PyTok_Chunk *chunk,
7272
return 0;
7373
}
7474

75-
char *
76-
_PyTok_NormalizeNewlines(const char *data, Py_ssize_t len, int preserve_crlf,
77-
int add_final_newline, Py_ssize_t *out_len,
78-
int *implicit_newline)
75+
static void
76+
normalize_newlines_into(char *result, const char *data, Py_ssize_t len,
77+
int preserve_crlf, int add_final_newline,
78+
Py_ssize_t *out_len, int *implicit_newline)
7979
{
80-
if (len > PY_SSIZE_T_MAX - 2) {
81-
PyErr_NoMemory();
82-
return NULL;
83-
}
84-
char *result = PyMem_Malloc((size_t)len + 2);
85-
if (result == NULL) {
86-
PyErr_NoMemory();
87-
return NULL;
88-
}
8980
Py_ssize_t write = 0;
9081
for (Py_ssize_t read = 0; read < len; read++) {
9182
char c = data[read];
@@ -104,6 +95,24 @@ _PyTok_NormalizeNewlines(const char *data, Py_ssize_t len, int preserve_crlf,
10495
result[write] = '\0';
10596
*out_len = write;
10697
*implicit_newline = implicit;
98+
}
99+
100+
char *
101+
_PyTok_NormalizeNewlines(const char *data, Py_ssize_t len, int preserve_crlf,
102+
int add_final_newline, Py_ssize_t *out_len,
103+
int *implicit_newline)
104+
{
105+
if (len > PY_SSIZE_T_MAX - 2) {
106+
PyErr_NoMemory();
107+
return NULL;
108+
}
109+
char *result = PyMem_Malloc((size_t)len + 2);
110+
if (result == NULL) {
111+
PyErr_NoMemory();
112+
return NULL;
113+
}
114+
normalize_newlines_into(result, data, len, preserve_crlf,
115+
add_final_newline, out_len, implicit_newline);
107116
return result;
108117
}
109118

@@ -289,6 +298,8 @@ store_prepared_source(struct tok_state *tok, const char *data, Py_ssize_t len,
289298
int preserve_crlf, int add_final_newline)
290299
{
291300
Py_ssize_t pos = 0;
301+
char *normalized = NULL;
302+
Py_ssize_t capacity = 0;
292303
while (pos < len) {
293304
Py_ssize_t raw_line_len;
294305
if (preserve_crlf) {
@@ -311,29 +322,51 @@ store_prepared_source(struct tok_state *tok, const char *data, Py_ssize_t len,
311322

312323
const char *line = data + pos;
313324
Py_ssize_t line_len = raw_line_len;
314-
char *normalized = NULL;
315325
int implicit = 0;
316326
if (normalize) {
317-
normalized = _PyTok_NormalizeNewlines(
318-
line, line_len, preserve_crlf, add_newline,
319-
&line_len, &implicit);
320-
if (normalized == NULL) {
327+
if (line_len > PY_SSIZE_T_MAX - 2) {
328+
PyErr_NoMemory();
321329
tok->done = E_NOMEM;
322-
return -1;
330+
goto error;
323331
}
332+
Py_ssize_t needed = line_len + 2;
333+
if (needed > capacity) {
334+
Py_ssize_t next_capacity = capacity > 0 ? capacity : 256;
335+
while (next_capacity < needed) {
336+
if (next_capacity > PY_SSIZE_T_MAX / 2) {
337+
next_capacity = needed;
338+
break;
339+
}
340+
next_capacity *= 2;
341+
}
342+
char *resized = PyMem_Realloc(normalized, next_capacity);
343+
if (resized == NULL) {
344+
PyErr_NoMemory();
345+
tok->done = E_NOMEM;
346+
goto error;
347+
}
348+
normalized = resized;
349+
capacity = next_capacity;
350+
}
351+
normalize_newlines_into(normalized, line, line_len,
352+
preserve_crlf, add_newline,
353+
&line_len, &implicit);
324354
line = normalized;
325355
}
326356
_PyTok_Off appended = _PyTok_SourceAppendLine(
327357
&tok->source, line, line_len, implicit);
328-
PyMem_Free(normalized);
329358
if (appended < 0) {
330359
tok->done = PyErr_ExceptionMatches(PyExc_MemoryError)
331360
? E_NOMEM : E_ERROR;
332-
return -1;
361+
goto error;
333362
}
334363
pos += raw_line_len;
335364
}
365+
PyMem_Free(normalized);
336366
return 0;
367+
error:
368+
PyMem_Free(normalized);
369+
return -1;
337370
}
338371

339372
int

0 commit comments

Comments
 (0)