Skip to content

Commit dd009c8

Browse files
Write the escaped string directly into the writer
This avoids creating temporary objects. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 2f87bf9 commit dd009c8

1 file changed

Lines changed: 80 additions & 64 deletions

File tree

Objects/unicodeobject.c

Lines changed: 80 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -2552,14 +2552,59 @@ escaped_char_size(Py_UCS4 ch, int flags)
25522552
}
25532553
}
25542554

2555+
/* Write the escaped form of the character.
2556+
The writer buffer must have enough space reserved. */
2557+
static void
2558+
unicode_write_escaped_char(_PyUnicodeWriter *writer, Py_UCS4 ch, int flags)
2559+
{
2560+
int kind = writer->kind;
2561+
void *data = writer->data;
2562+
Py_ssize_t o = writer->pos;
2563+
switch (escaped_char_size(ch, flags)) {
2564+
case 1:
2565+
PyUnicode_WRITE(kind, data, o++, ch);
2566+
break;
2567+
case 2:
2568+
PyUnicode_WRITE(kind, data, o++, '\\');
2569+
switch (ch) {
2570+
case '\t': ch = 't'; break;
2571+
case '\r': ch = 'r'; break;
2572+
case '\n': ch = 'n'; break;
2573+
}
2574+
PyUnicode_WRITE(kind, data, o++, ch);
2575+
break;
2576+
case 4:
2577+
PyUnicode_WRITE(kind, data, o++, '\\');
2578+
PyUnicode_WRITE(kind, data, o++, 'x');
2579+
PyUnicode_WRITE(kind, data, o++, Py_hexdigits[(ch >> 4) & 0xf]);
2580+
PyUnicode_WRITE(kind, data, o++, Py_hexdigits[ch & 0xf]);
2581+
break;
2582+
default:
2583+
{
2584+
int ndigits = (ch < 0x10000) ? 4 : 8;
2585+
PyUnicode_WRITE(kind, data, o++, '\\');
2586+
PyUnicode_WRITE(kind, data, o++, (ch < 0x10000) ? 'u' : 'U');
2587+
while (--ndigits >= 0) {
2588+
PyUnicode_WRITE(kind, data, o++,
2589+
Py_hexdigits[(ch >> (4*ndigits)) & 0xf]);
2590+
}
2591+
break;
2592+
}
2593+
}
2594+
writer->pos = o;
2595+
}
2596+
25552597
/* Escape special and non-printable characters like repr() does, but
2556-
without surrounding quotes. If the F_SIGN flag is set, also escape
2557-
all non-ASCII characters. Characters are escaped with "\uHHHH" and
2558-
"\UHHHHHHHH", raw bytes (see F_RAWBYTES) -- with "\xHH".
2559-
If precision is non-negative, only the first precision characters of
2560-
str are escaped and returned. */
2561-
static PyObject *
2562-
unicode_fromformat_escape(PyObject *str, Py_ssize_t precision, int flags)
2598+
without surrounding quotes, and write the result to the writer.
2599+
If the F_SIGN flag is set, also escape all non-ASCII characters.
2600+
Characters are escaped with "\uHHHH" and "\UHHHHHHHH", raw bytes
2601+
(see F_RAWBYTES) -- with "\xHH".
2602+
The precision applies to the original string, the width -- to the
2603+
escaped one. */
2604+
static int
2605+
unicode_fromformat_write_escaped(_PyUnicodeWriter *writer, PyObject *str,
2606+
Py_ssize_t width, Py_ssize_t precision,
2607+
int flags)
25632608
{
25642609
Py_ssize_t isize = PyUnicode_GET_LENGTH(str);
25652610
const void *idata = PyUnicode_DATA(str);
@@ -2568,7 +2613,7 @@ unicode_fromformat_escape(PyObject *str, Py_ssize_t precision, int flags)
25682613
isize = precision;
25692614
}
25702615

2571-
/* Compute the length and the maximum character of the output */
2616+
/* Compute the length and the maximum character of the escaped string */
25722617
Py_ssize_t osize = 0;
25732618
Py_UCS4 maxch = 127;
25742619
for (Py_ssize_t i = 0; i < isize; i++) {
@@ -2579,56 +2624,36 @@ unicode_fromformat_escape(PyObject *str, Py_ssize_t precision, int flags)
25792624
}
25802625
if (osize > PY_SSIZE_T_MAX - incr) {
25812626
PyErr_SetString(PyExc_OverflowError, "string is too long");
2582-
return NULL;
2627+
return -1;
25832628
}
25842629
osize += incr;
25852630
}
25862631

2587-
PyObject *res = PyUnicode_New(osize, maxch);
2588-
if (res == NULL) {
2589-
return NULL;
2632+
if (_PyUnicodeWriter_Prepare(writer, Py_MAX(osize, width), maxch) == -1)
2633+
return -1;
2634+
2635+
Py_ssize_t fill = Py_MAX(width - osize, 0);
2636+
if (fill && !(flags & F_LJUST)) {
2637+
if (PyUnicode_Fill(writer->buffer, writer->pos, fill, ' ') == -1)
2638+
return -1;
2639+
writer->pos += fill;
25902640
}
2591-
int okind = PyUnicode_KIND(res);
2592-
void *odata = PyUnicode_DATA(res);
25932641

2594-
Py_ssize_t o = 0;
2642+
Py_ssize_t start = writer->pos;
25952643
for (Py_ssize_t i = 0; i < isize; i++) {
25962644
Py_UCS4 ch = PyUnicode_READ(ikind, idata, i);
2597-
switch (escaped_char_size(ch, flags)) {
2598-
case 1:
2599-
PyUnicode_WRITE(okind, odata, o++, ch);
2600-
break;
2601-
case 2:
2602-
PyUnicode_WRITE(okind, odata, o++, '\\');
2603-
switch (ch) {
2604-
case '\t': ch = 't'; break;
2605-
case '\r': ch = 'r'; break;
2606-
case '\n': ch = 'n'; break;
2607-
}
2608-
PyUnicode_WRITE(okind, odata, o++, ch);
2609-
break;
2610-
case 4:
2611-
PyUnicode_WRITE(okind, odata, o++, '\\');
2612-
PyUnicode_WRITE(okind, odata, o++, 'x');
2613-
PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[(ch >> 4) & 0xf]);
2614-
PyUnicode_WRITE(okind, odata, o++, Py_hexdigits[ch & 0xf]);
2615-
break;
2616-
default:
2617-
{
2618-
int ndigits = (ch < 0x10000) ? 4 : 8;
2619-
PyUnicode_WRITE(okind, odata, o++, '\\');
2620-
PyUnicode_WRITE(okind, odata, o++, (ch < 0x10000) ? 'u' : 'U');
2621-
while (--ndigits >= 0) {
2622-
PyUnicode_WRITE(okind, odata, o++,
2623-
Py_hexdigits[(ch >> (4*ndigits)) & 0xf]);
2624-
}
2625-
break;
2626-
}
2627-
}
2645+
unicode_write_escaped_char(writer, ch, flags);
26282646
}
2629-
assert(o == osize);
2630-
assert(_PyUnicode_CheckConsistency(res, 1));
2631-
return res;
2647+
assert(writer->pos - start == osize);
2648+
(void)start;
2649+
2650+
if (fill && (flags & F_LJUST)) {
2651+
if (PyUnicode_Fill(writer->buffer, writer->pos, fill, ' ') == -1)
2652+
return -1;
2653+
writer->pos += fill;
2654+
}
2655+
2656+
return 0;
26322657
}
26332658

26342659
static int
@@ -2639,15 +2664,8 @@ unicode_fromformat_write_str(_PyUnicodeWriter *writer, PyObject *str,
26392664
Py_UCS4 maxchar;
26402665

26412666
if (flags & F_ESCAPE) {
2642-
/* The precision applies to the original string,
2643-
the width -- to the escaped one. */
2644-
PyObject *escaped = unicode_fromformat_escape(str, precision, flags);
2645-
if (escaped == NULL)
2646-
return -1;
2647-
int res = unicode_fromformat_write_str(writer, escaped, width, -1,
2648-
flags & ~F_ESCAPE);
2649-
Py_DECREF(escaped);
2650-
return res;
2667+
return unicode_fromformat_write_escaped(writer, str, width, precision,
2668+
flags);
26512669
}
26522670

26532671
length = PyUnicode_GET_LENGTH(str);
@@ -2915,13 +2933,11 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
29152933
return NULL;
29162934
}
29172935
if (flags & F_ESCAPE) {
2918-
PyObject *str = PyUnicode_FromOrdinal(ordinal);
2919-
if (str == NULL)
2920-
return NULL;
2921-
int res = unicode_fromformat_write_str(writer, str, -1, -1, flags);
2922-
Py_DECREF(str);
2923-
if (res < 0)
2936+
Py_ssize_t len = escaped_char_size(ordinal, flags);
2937+
Py_UCS4 maxch = (len == 1) ? (Py_UCS4)ordinal : 127;
2938+
if (_PyUnicodeWriter_Prepare(writer, len, maxch) < 0)
29242939
return NULL;
2940+
unicode_write_escaped_char(writer, ordinal, flags);
29252941
}
29262942
else if (_PyUnicodeWriter_WriteCharInline(writer, ordinal) < 0)
29272943
return NULL;

0 commit comments

Comments
 (0)