From 051d0703d3c6f3988707ce011831f28a9406eea0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 13 Sep 2025 20:10:09 +0200 Subject: [PATCH 1/6] gh-129813, PEP 782: Use PyBytesWriter in utf8_encoder() Replace the private _PyBytesWriter API with the new public PyBytesWriter API in utf8_encoder() and unicode_encode_ucs1(). --- Objects/stringlib/codecs.h | 69 ++++++++++----------- Objects/unicodeobject.c | 122 +++++++++++++++++-------------------- 2 files changed, 88 insertions(+), 103 deletions(-) diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index 440410d0aef17d..f80344fdd918f3 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -257,16 +257,14 @@ STRINGLIB(utf8_decode)(const char **inptr, const char *end, /* UTF-8 encoder specialized for a Unicode kind to avoid the slow PyUnicode_READ() macro. Delete some parts of the code depending on the kind: UCS-1 strings don't need to handle surrogates for example. */ -Py_LOCAL_INLINE(char *) -STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, - PyObject *unicode, +Py_LOCAL_INLINE(PyBytesWriter*) +STRINGLIB(utf8_encoder)(PyObject *unicode, const STRINGLIB_CHAR *data, Py_ssize_t size, _Py_error_handler error_handler, - const char *errors) + const char *errors, + char **end) { - Py_ssize_t i; /* index into data of next input character */ - char *p; /* next free byte in output buffer */ #if STRINGLIB_SIZEOF_CHAR > 1 PyObject *error_handler_obj = NULL; PyObject *exc = NULL; @@ -284,14 +282,19 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, if (size > PY_SSIZE_T_MAX / max_char_size) { /* integer overflow */ PyErr_NoMemory(); + *end = NULL; return NULL; } - _PyBytesWriter_Init(writer); - p = _PyBytesWriter_Alloc(writer, size * max_char_size); - if (p == NULL) + PyBytesWriter *writer = PyBytesWriter_Create(size * max_char_size); + if (writer == NULL) { + *end = NULL; return NULL; + } + /* next free byte in output buffer */ + char *p = PyBytesWriter_GetData(writer); + Py_ssize_t i; /* index into data of next input character */ for (i = 0; i < size;) { Py_UCS4 ch = data[i++]; @@ -323,9 +326,6 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, while ((endpos < size) && Py_UNICODE_IS_SURROGATE(data[endpos])) endpos++; - /* Only overallocate the buffer if it's not the last write */ - writer->overallocate = (endpos < size); - switch (error_handler) { case _Py_ERROR_REPLACE: @@ -347,8 +347,6 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, break; case _Py_ERROR_BACKSLASHREPLACE: - /* subtract preallocated bytes */ - writer->min_size -= max_char_size * (endpos - startpos); p = backslashreplace(writer, p, unicode, startpos, endpos); if (p == NULL) @@ -357,8 +355,6 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, break; case _Py_ERROR_XMLCHARREFREPLACE: - /* subtract preallocated bytes */ - writer->min_size -= max_char_size * (endpos - startpos); p = xmlcharrefreplace(writer, p, unicode, startpos, endpos); if (p == NULL) @@ -388,23 +384,19 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, goto error; if (newpos < startpos) { - writer->overallocate = 1; - p = _PyBytesWriter_Prepare(writer, p, - max_char_size * (startpos - newpos)); - if (p == NULL) + p = PyBytesWriter_GrowAndUpdatePointer(writer, + max_char_size * (startpos - newpos), + p); + if (p == NULL) { goto error; - } - else { - /* subtract preallocated bytes */ - writer->min_size -= max_char_size * (newpos - startpos); - /* Only overallocate the buffer if it's not the last write */ - writer->overallocate = (newpos < size); + } } + char *rep_str; + Py_ssize_t rep_len; if (PyBytes_Check(rep)) { - p = _PyBytesWriter_WriteBytes(writer, p, - PyBytes_AS_STRING(rep), - PyBytes_GET_SIZE(rep)); + rep_str = PyBytes_AS_STRING(rep); + rep_len = PyBytes_GET_SIZE(rep); } else { /* rep is unicode */ @@ -415,21 +407,20 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, goto error; } - p = _PyBytesWriter_WriteBytes(writer, p, - PyUnicode_DATA(rep), - PyUnicode_GET_LENGTH(rep)); + rep_str = PyUnicode_DATA(rep); + rep_len = PyUnicode_GET_LENGTH(rep); } - if (p == NULL) + p = PyBytesWriter_GrowAndUpdatePointer(writer, rep_len, p); + if (p == NULL) { goto error; + } + memcpy(p, rep_str, rep_len); + p += rep_len; Py_CLEAR(rep); i = newpos; } - - /* If overallocation was disabled, ensure that it was the last - write. Otherwise, we missed an optimization */ - assert(writer->overallocate || i == size); } else #if STRINGLIB_SIZEOF_CHAR > 2 @@ -458,13 +449,15 @@ STRINGLIB(utf8_encoder)(_PyBytesWriter *writer, Py_XDECREF(error_handler_obj); Py_XDECREF(exc); #endif - return p; + *end = p; + return writer; #if STRINGLIB_SIZEOF_CHAR > 1 error: Py_XDECREF(rep); Py_XDECREF(error_handler_obj); Py_XDECREF(exc); + *end = NULL; return NULL; #endif } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index c8d2c68615e13e..d820a3391200ed 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -828,7 +828,7 @@ unicode_result_unchanged(PyObject *unicode) /* Implementation of the "backslashreplace" error handler for 8-bit encodings: ASCII, Latin1, UTF-8, etc. */ static char* -backslashreplace(_PyBytesWriter *writer, char *str, +backslashreplace(PyBytesWriter *writer, char *str, PyObject *unicode, Py_ssize_t collstart, Py_ssize_t collend) { Py_ssize_t size, i; @@ -861,9 +861,10 @@ backslashreplace(_PyBytesWriter *writer, char *str, size += incr; } - str = _PyBytesWriter_Prepare(writer, str, size); - if (str == NULL) + str = PyBytesWriter_GrowAndUpdatePointer(writer, size, str); + if (str == NULL) { return NULL; + } /* generate replacement */ for (i = collstart; i < collend; ++i) { @@ -894,7 +895,7 @@ backslashreplace(_PyBytesWriter *writer, char *str, /* Implementation of the "xmlcharrefreplace" error handler for 8-bit encodings: ASCII, Latin1, UTF-8, etc. */ static char* -xmlcharrefreplace(_PyBytesWriter *writer, char *str, +xmlcharrefreplace(PyBytesWriter *writer, char *str, PyObject *unicode, Py_ssize_t collstart, Py_ssize_t collend) { Py_ssize_t size, i; @@ -935,9 +936,10 @@ xmlcharrefreplace(_PyBytesWriter *writer, char *str, size += incr; } - str = _PyBytesWriter_Prepare(writer, str, size); - if (str == NULL) + str = PyBytesWriter_GrowAndUpdatePointer(writer, size, str); + if (str == NULL) { return NULL; + } /* generate replacement */ for (i = collstart; i < collend; ++i) { @@ -5836,7 +5838,7 @@ unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler, const void *data = PyUnicode_DATA(unicode); Py_ssize_t size = PyUnicode_GET_LENGTH(unicode); - _PyBytesWriter writer; + PyBytesWriter *writer; char *end; switch (kind) { @@ -5845,21 +5847,24 @@ unicode_encode_utf8(PyObject *unicode, _Py_error_handler error_handler, case PyUnicode_1BYTE_KIND: /* the string cannot be ASCII, or PyUnicode_UTF8() would be set */ assert(!PyUnicode_IS_ASCII(unicode)); - end = ucs1lib_utf8_encoder(&writer, unicode, data, size, error_handler, errors); + writer = ucs1lib_utf8_encoder(unicode, data, size, + error_handler, errors, &end); break; case PyUnicode_2BYTE_KIND: - end = ucs2lib_utf8_encoder(&writer, unicode, data, size, error_handler, errors); + writer = ucs2lib_utf8_encoder(unicode, data, size, + error_handler, errors, &end); break; case PyUnicode_4BYTE_KIND: - end = ucs4lib_utf8_encoder(&writer, unicode, data, size, error_handler, errors); + writer = ucs4lib_utf8_encoder(unicode, data, size, + error_handler, errors, &end); break; } - if (end == NULL) { - _PyBytesWriter_Dealloc(&writer); + if (writer == NULL) { + PyBytesWriter_Discard(writer); return NULL; } - return _PyBytesWriter_Finish(&writer, end); + return PyBytesWriter_FinishWithPointer(writer, end); } static int @@ -5873,37 +5878,36 @@ unicode_fill_utf8(PyObject *unicode) const void *data = PyUnicode_DATA(unicode); Py_ssize_t size = PyUnicode_GET_LENGTH(unicode); - _PyBytesWriter writer; + PyBytesWriter *writer; char *end; switch (kind) { default: Py_UNREACHABLE(); case PyUnicode_1BYTE_KIND: - end = ucs1lib_utf8_encoder(&writer, unicode, data, size, - _Py_ERROR_STRICT, NULL); + writer = ucs1lib_utf8_encoder(unicode, data, size, + _Py_ERROR_STRICT, NULL, &end); break; case PyUnicode_2BYTE_KIND: - end = ucs2lib_utf8_encoder(&writer, unicode, data, size, - _Py_ERROR_STRICT, NULL); + writer = ucs2lib_utf8_encoder(unicode, data, size, + _Py_ERROR_STRICT, NULL, &end); break; case PyUnicode_4BYTE_KIND: - end = ucs4lib_utf8_encoder(&writer, unicode, data, size, - _Py_ERROR_STRICT, NULL); + writer = ucs4lib_utf8_encoder(unicode, data, size, + _Py_ERROR_STRICT, NULL, &end); break; } - if (end == NULL) { - _PyBytesWriter_Dealloc(&writer); + if (writer == NULL) { + PyBytesWriter_Discard(writer); return -1; } - const char *start = writer.use_small_buffer ? writer.small_buffer : - PyBytes_AS_STRING(writer.buffer); + const char *start = PyBytesWriter_GetData(writer); Py_ssize_t len = end - start; char *cache = PyMem_Malloc(len + 1); if (cache == NULL) { - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); PyErr_NoMemory(); return -1; } @@ -5911,7 +5915,7 @@ unicode_fill_utf8(PyObject *unicode) cache[len] = '\0'; PyUnicode_SET_UTF8_LENGTH(unicode, len); PyUnicode_SET_UTF8(unicode, cache); - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); return 0; } @@ -7347,16 +7351,12 @@ unicode_encode_ucs1(PyObject *unicode, Py_ssize_t pos=0, size; int kind; const void *data; - /* pointer into the output */ - char *str; const char *encoding = (limit == 256) ? "latin-1" : "ascii"; const char *reason = (limit == 256) ? "ordinal not in range(256)" : "ordinal not in range(128)"; PyObject *error_handler_obj = NULL; PyObject *exc = NULL; _Py_error_handler error_handler = _Py_ERROR_UNKNOWN; PyObject *rep = NULL; - /* output object */ - _PyBytesWriter writer; size = PyUnicode_GET_LENGTH(unicode); kind = PyUnicode_KIND(unicode); @@ -7366,10 +7366,13 @@ unicode_encode_ucs1(PyObject *unicode, if (size == 0) return Py_GetConstant(Py_CONSTANT_EMPTY_BYTES); - _PyBytesWriter_Init(&writer); - str = _PyBytesWriter_Alloc(&writer, size); - if (str == NULL) + /* output object */ + PyBytesWriter *writer = PyBytesWriter_Create(size); + if (writer == NULL) { return NULL; + } + /* pointer into the output */ + char *str = PyBytesWriter_GetData(writer); while (pos < size) { Py_UCS4 ch = PyUnicode_READ(kind, data, pos); @@ -7390,9 +7393,6 @@ unicode_encode_ucs1(PyObject *unicode, while ((collend < size) && (PyUnicode_READ(kind, data, collend) >= limit)) ++collend; - /* Only overallocate the buffer if it's not the last write */ - writer.overallocate = (collend < size); - /* cache callback name lookup (if not done yet, i.e. it's the first error) */ if (error_handler == _Py_ERROR_UNKNOWN) error_handler = _Py_GetErrorHandler(errors); @@ -7411,9 +7411,7 @@ unicode_encode_ucs1(PyObject *unicode, break; case _Py_ERROR_BACKSLASHREPLACE: - /* subtract preallocated bytes */ - writer.min_size -= (collend - collstart); - str = backslashreplace(&writer, str, + str = backslashreplace(writer, str, unicode, collstart, collend); if (str == NULL) goto onError; @@ -7421,9 +7419,7 @@ unicode_encode_ucs1(PyObject *unicode, break; case _Py_ERROR_XMLCHARREFREPLACE: - /* subtract preallocated bytes */ - writer.min_size -= (collend - collstart); - str = xmlcharrefreplace(&writer, str, + str = xmlcharrefreplace(writer, str, unicode, collstart, collend); if (str == NULL) goto onError; @@ -7454,24 +7450,20 @@ unicode_encode_ucs1(PyObject *unicode, goto onError; if (newpos < collstart) { - writer.overallocate = 1; - str = _PyBytesWriter_Prepare(&writer, str, - collstart - newpos); - if (str == NULL) + str = PyBytesWriter_GrowAndUpdatePointer(writer, + collstart - newpos, + str); + if (str == NULL) { goto onError; - } - else { - /* subtract preallocated bytes */ - writer.min_size -= newpos - collstart; - /* Only overallocate the buffer if it's not the last write */ - writer.overallocate = (newpos < size); + } } + char *rep_str; + Py_ssize_t rep_len; if (PyBytes_Check(rep)) { /* Directly copy bytes result to output. */ - str = _PyBytesWriter_WriteBytes(&writer, str, - PyBytes_AS_STRING(rep), - PyBytes_GET_SIZE(rep)); + rep_str = PyBytes_AS_STRING(rep); + rep_len = PyBytes_GET_SIZE(rep); } else { assert(PyUnicode_Check(rep)); @@ -7486,30 +7478,30 @@ unicode_encode_ucs1(PyObject *unicode, goto onError; } assert(PyUnicode_KIND(rep) == PyUnicode_1BYTE_KIND); - str = _PyBytesWriter_WriteBytes(&writer, str, - PyUnicode_DATA(rep), - PyUnicode_GET_LENGTH(rep)); + rep_str = PyUnicode_DATA(rep); + rep_len = PyUnicode_GET_LENGTH(rep); } - if (str == NULL) + + str = PyBytesWriter_GrowAndUpdatePointer(writer, rep_len, str); + if (str == NULL) { goto onError; + } + memcpy(str, rep_str, rep_len); + str += rep_len; pos = newpos; Py_CLEAR(rep); } - - /* If overallocation was disabled, ensure that it was the last - write. Otherwise, we missed an optimization */ - assert(writer.overallocate || pos == size); } } Py_XDECREF(error_handler_obj); Py_XDECREF(exc); - return _PyBytesWriter_Finish(&writer, str); + return PyBytesWriter_FinishWithPointer(writer, str); onError: Py_XDECREF(rep); - _PyBytesWriter_Dealloc(&writer); + PyBytesWriter_Discard(writer); Py_XDECREF(error_handler_obj); Py_XDECREF(exc); return NULL; From 7181b30bb3ad539a575030a101238a83564df612 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 15 Sep 2025 16:36:12 +0200 Subject: [PATCH 2/6] Fix leak: call PyBytesWriter_Discard() --- Objects/stringlib/codecs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index f80344fdd918f3..2ed8ace700efca 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -454,6 +454,7 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, #if STRINGLIB_SIZEOF_CHAR > 1 error: + PyBytesWriter_Discard(writer); Py_XDECREF(rep); Py_XDECREF(error_handler_obj); Py_XDECREF(exc); From ecdece1e9a46221be24e1d9be39f99190c5d26f6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 17 Sep 2025 15:52:09 +0200 Subject: [PATCH 3/6] Restore code to disable overallocation --- Objects/stringlib/codecs.h | 11 +++++++++++ Objects/unicodeobject.c | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index 2ed8ace700efca..c91729174a2e83 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -326,6 +326,9 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, while ((endpos < size) && Py_UNICODE_IS_SURROGATE(data[endpos])) endpos++; + /* Only overallocate the buffer if it's not the last write */ + writer->overallocate = (endpos < size); + switch (error_handler) { case _Py_ERROR_REPLACE: @@ -391,6 +394,10 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, goto error; } } + else { + /* Only overallocate the buffer if it's not the last write */ + writer->overallocate = (newpos < size); + } char *rep_str; Py_ssize_t rep_len; @@ -421,6 +428,10 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, i = newpos; } + + /* If overallocation was disabled, ensure that it was the last + write. Otherwise, we missed an optimization */ + assert(writer->overallocate || i == size); } else #if STRINGLIB_SIZEOF_CHAR > 2 diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index d820a3391200ed..ae8c455afc1866 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7393,6 +7393,9 @@ unicode_encode_ucs1(PyObject *unicode, while ((collend < size) && (PyUnicode_READ(kind, data, collend) >= limit)) ++collend; + /* Only overallocate the buffer if it's not the last write */ + writer->overallocate = (collend < size); + /* cache callback name lookup (if not done yet, i.e. it's the first error) */ if (error_handler == _Py_ERROR_UNKNOWN) error_handler = _Py_GetErrorHandler(errors); @@ -7457,6 +7460,10 @@ unicode_encode_ucs1(PyObject *unicode, goto onError; } } + else { + /* Only overallocate the buffer if it's not the last write */ + writer->overallocate = (newpos < size); + } char *rep_str; Py_ssize_t rep_len; @@ -7492,6 +7499,10 @@ unicode_encode_ucs1(PyObject *unicode, pos = newpos; Py_CLEAR(rep); } + + /* If overallocation was disabled, ensure that it was the last + write. Otherwise, we missed an optimization */ + assert(writer->overallocate || pos == size); } } From 65e90a6838f2261fb8c6227298f24ed4e79eccab Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 18 Sep 2025 17:55:59 +0200 Subject: [PATCH 4/6] Fix test_codeccallbacks --- Objects/stringlib/codecs.h | 1 + Objects/unicodeobject.c | 1 + 2 files changed, 2 insertions(+) diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index c91729174a2e83..fe9a17fd2c1a7f 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -387,6 +387,7 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, goto error; if (newpos < startpos) { + writer->overallocate = 1; p = PyBytesWriter_GrowAndUpdatePointer(writer, max_char_size * (startpos - newpos), p); diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index ae8c455afc1866..0fe681c00862ac 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7453,6 +7453,7 @@ unicode_encode_ucs1(PyObject *unicode, goto onError; if (newpos < collstart) { + writer->overallocate = 1; str = PyBytesWriter_GrowAndUpdatePointer(writer, collstart - newpos, str); From c9abefd57a4083378b9cb1a42fa8deb464d60638 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 18 Sep 2025 23:07:27 +0200 Subject: [PATCH 5/6] Restore min_size optimization --- Objects/stringlib/codecs.h | 6 ++++++ Objects/unicodeobject.c | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/Objects/stringlib/codecs.h b/Objects/stringlib/codecs.h index fe9a17fd2c1a7f..9e53fab842909a 100644 --- a/Objects/stringlib/codecs.h +++ b/Objects/stringlib/codecs.h @@ -350,6 +350,8 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, break; case _Py_ERROR_BACKSLASHREPLACE: + /* subtract preallocated bytes */ + writer->size -= max_char_size * (endpos - startpos); p = backslashreplace(writer, p, unicode, startpos, endpos); if (p == NULL) @@ -358,6 +360,8 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, break; case _Py_ERROR_XMLCHARREFREPLACE: + /* subtract preallocated bytes */ + writer->size -= max_char_size * (endpos - startpos); p = xmlcharrefreplace(writer, p, unicode, startpos, endpos); if (p == NULL) @@ -396,6 +400,8 @@ STRINGLIB(utf8_encoder)(PyObject *unicode, } } else { + /* subtract preallocated bytes */ + writer->size -= max_char_size * (newpos - startpos); /* Only overallocate the buffer if it's not the last write */ writer->overallocate = (newpos < size); } diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index 0fe681c00862ac..ae15caa3120fb2 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -7414,6 +7414,8 @@ unicode_encode_ucs1(PyObject *unicode, break; case _Py_ERROR_BACKSLASHREPLACE: + /* subtract preallocated bytes */ + writer->size -= (collend - collstart); str = backslashreplace(writer, str, unicode, collstart, collend); if (str == NULL) @@ -7422,6 +7424,8 @@ unicode_encode_ucs1(PyObject *unicode, break; case _Py_ERROR_XMLCHARREFREPLACE: + /* subtract preallocated bytes */ + writer->size -= (collend - collstart); str = xmlcharrefreplace(writer, str, unicode, collstart, collend); if (str == NULL) @@ -7462,6 +7466,8 @@ unicode_encode_ucs1(PyObject *unicode, } } else { + /* subtract preallocated bytes */ + writer->size -= newpos - collstart; /* Only overallocate the buffer if it's not the last write */ writer->overallocate = (newpos < size); } From 7212e743729a9fd3c82d95aadb05873feb989306 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 22 Sep 2025 14:11:47 +0200 Subject: [PATCH 6/6] Address Serhiy's review Remove useless PyBytesWriter_Discard() call --- Objects/unicodeobject.c | 1 - 1 file changed, 1 deletion(-) diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c index ae15caa3120fb2..ab0f00e6be0b38 100644 --- a/Objects/unicodeobject.c +++ b/Objects/unicodeobject.c @@ -5898,7 +5898,6 @@ unicode_fill_utf8(PyObject *unicode) break; } if (writer == NULL) { - PyBytesWriter_Discard(writer); return -1; }