Skip to content

Commit a64b7ec

Browse files
authored
gh-157710: Avoid resize in PyUnicodeWriter_Finish() for singleton (#157862)
PyBytesWriter_FinishWithSize() and PyUnicodeWriter_Finish() now discard the output string instead of resizing it if the single byte/character singleton is used.
1 parent 6b97452 commit a64b7ec

3 files changed

Lines changed: 31 additions & 22 deletions

File tree

‎Lib/test/test_capi/test_unicode.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2050,12 +2050,19 @@ def test_singletons(self):
20502050
for size in (0, 123):
20512051
with self.subTest(size=size):
20522052
writer = self.create_writer(size)
2053+
writer.write_utf8(b'utf8', 0)
2054+
writer.write_ascii(b'ascii', 0)
2055+
writer.write_widechar(b'wstr', 0)
2056+
writer.write_ucs4(b'ucs4', 0)
2057+
writer.write_substring('text', 0, 0)
20532058
self.assertIs(writer.finish(), '')
20542059

20552060
for ch in range(256):
20562061
with self.subTest(ch=ch):
20572062
ch = chr(ch)
20582063
writer = self.create_writer(0)
2064+
# Use PyUnicodeWriter_WriteSubstring() to avoid the read-only
2065+
# buffer optimization
20592066
writer.write_substring(ch + 'xxx', 0, 1)
20602067
self.assertIs(writer.finish(), ch)
20612068

‎Objects/bytesobject.c‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3938,19 +3938,18 @@ PyBytesWriter_FinishWithSize(PyBytesWriter *writer, Py_ssize_t size)
39383938
}
39393939
}
39403940
else {
3941-
if (size != PyBytes_GET_SIZE(writer->obj)) {
3942-
if (_PyBytes_Resize(&writer->obj, size)) {
3943-
goto error;
3944-
}
3945-
}
3946-
39473941
if (size == 1) {
39483942
// Get the single byte singleton
39493943
unsigned char ch = PyBytes_AS_STRING(writer->obj)[0];
39503944
PyObject *op = (PyObject*)CHARACTER(ch);
39513945
assert(_Py_IsImmortal(op));
39523946
Py_SETREF(writer->obj, op);
39533947
}
3948+
else if (size != PyBytes_GET_SIZE(writer->obj)) {
3949+
if (_PyBytes_Resize(&writer->obj, size)) {
3950+
goto error;
3951+
}
3952+
}
39543953
}
39553954

39563955
result = writer->obj;

‎Objects/unicode_writer.c‎

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -609,8 +609,6 @@ _PyUnicodeWriter_WriteLatin1String(_PyUnicodeWriter *writer,
609609
PyObject *
610610
_PyUnicodeWriter_Finish(_PyUnicodeWriter *writer)
611611
{
612-
PyObject *str;
613-
614612
#ifdef Py_DEBUG
615613
// Check for buffer overflow
616614
if (writer->buffer != NULL) {
@@ -625,23 +623,28 @@ _PyUnicodeWriter_Finish(_PyUnicodeWriter *writer)
625623
}
626624
#endif
627625

628-
if (writer->pos == 0) {
629-
Py_CLEAR(writer->buffer);
630-
return _PyUnicode_GetEmpty();
631-
}
632-
633-
str = writer->buffer;
626+
PyObject *str = writer->buffer;
634627
writer->buffer = NULL;
635628

636-
if (writer->readonly) {
637-
assert(PyUnicode_GET_LENGTH(str) == writer->pos);
638-
assert(_PyUnicode_CheckConsistency(str, 1));
639-
return str;
629+
Py_ssize_t final_size = writer->pos;
630+
if (final_size == 0) {
631+
PyObject *empty = _PyUnicode_GetEmpty();
632+
Py_XDECREF(str); // writer->buffer can be NULL if the position is 0
633+
return empty;
640634
}
641635

642-
if (PyUnicode_GET_LENGTH(str) != writer->pos) {
643-
PyObject *str2;
644-
str2 = _PyUnicode_ResizeCompact(str, writer->pos);
636+
Py_ssize_t length = PyUnicode_GET_LENGTH(str);
637+
if (final_size == 1 && PyUnicode_KIND(str) == PyUnicode_1BYTE_KIND) {
638+
assert(length >= 1);
639+
const Py_UCS1 *data = PyUnicode_1BYTE_DATA(str);
640+
Py_UCS1 ch = data[0];
641+
PyObject *latin1_char = _Py_LATIN1_CHR(ch);
642+
Py_DECREF(str);
643+
return latin1_char;
644+
}
645+
646+
if (!writer->readonly && length != final_size) {
647+
PyObject *str2 = _PyUnicode_ResizeCompact(str, final_size);
645648
if (str2 == NULL) {
646649
Py_DECREF(str);
647650
return NULL;
@@ -650,7 +653,7 @@ _PyUnicodeWriter_Finish(_PyUnicodeWriter *writer)
650653
}
651654

652655
assert(_PyUnicode_CheckConsistency(str, 1));
653-
return _PyUnicode_Result(str);
656+
return str;
654657
}
655658

656659

0 commit comments

Comments
 (0)