Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions Include/cpython/unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -557,25 +557,33 @@ typedef struct {
_Py_DEPRECATED_EXTERNALLY(3.14) PyAPI_FUNC(void) _PyUnicodeWriter_Init(
_PyUnicodeWriter *writer);

/* Prepare the buffer to write 'length' characters
with the specified maximum character.

Return 0 on success, raise an exception and return -1 on error. */
#define _PyUnicodeWriter_Prepare(WRITER, LENGTH, MAXCHAR) \
(((MAXCHAR) <= (WRITER)->maxchar \
&& (LENGTH) <= (WRITER)->size - (WRITER)->pos) \
? 0 \
: (((LENGTH) == 0) \
? 0 \
: _PyUnicodeWriter_PrepareInternal((WRITER), (LENGTH), (MAXCHAR))))

/* Don't call this function directly, use the _PyUnicodeWriter_Prepare() macro
instead. */
// Don't call this function directly, use _PyUnicodeWriter_Prepare() instead.
_Py_DEPRECATED_EXTERNALLY(3.14) PyAPI_FUNC(int) _PyUnicodeWriter_PrepareInternal(
_PyUnicodeWriter *writer,
Py_ssize_t length,
Py_UCS4 maxchar);

// Prepare the buffer to write 'length' characters
// with the specified maximum character.
//
// Return 0 on success. Set an exception and return -1 on error.
_Py_DEPRECATED_EXTERNALLY(3.14) static inline int
_PyUnicodeWriter_Prepare(_PyUnicodeWriter *writer,
Py_ssize_t length, Py_UCS4 maxchar)
{
assert(0 <= length);
if (maxchar <= writer->maxchar && length <= (writer->size - writer->pos)) {
return 0;
}
if (length == 0) {
return 0;
}
_Py_COMP_DIAG_PUSH
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
return _PyUnicodeWriter_PrepareInternal(writer, length, maxchar);
_Py_COMP_DIAG_POP
}

/* Prepare the buffer to have at least the kind KIND.
For example, kind=PyUnicode_2BYTE_KIND ensures that the writer will
support characters in range U+000-U+FFFF.
Expand Down
6 changes: 2 additions & 4 deletions Include/internal/pycore_unicodeobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,13 @@ _PyUnicodeWriter_SetReadOnly(_PyUnicodeWriter *writer, PyObject *obj,
static inline int
_PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch)
{
if (ch > writer->maxchar || 1 > writer->size - writer->pos) {
if (ch > writer->maxchar || 1 > (writer->size - writer->pos)) {
if (writer->buffer == NULL && ch <= 255) {
// If the first write is a Latin1 character, use the singleton
// as a read-only object
PyObject *obj = _Py_LATIN1_CHR(ch);
// Py_NewRef() is not need on immortal object
// Py_NewRef() is not needed on immortal object
_PyUnicodeWriter_SetReadOnly(writer, obj, 1);

// The next write will create a new buffer and copy the string
return 0;
}

Expand Down
8 changes: 8 additions & 0 deletions Lib/test/test_capi/test_unicode.py
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,14 @@ def check_format(expected, format, *args):
check_format('%abc',
b'%%%s', b'abc')

# test "%s" with empty string
check_format('x=',
b'x=%s', b'')
check_format('x=',
b'x=%0s', b'')
check_format('x=',
b'x=%.3s', b'')

# truncated string
check_format('abc',
b'%.3s', b'abcdef')
Expand Down
7 changes: 4 additions & 3 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2212,7 +2212,7 @@ long_to_decimal_string_internal(PyObject *aa,
}
}
if (writer) {
if (_PyUnicodeWriter_Prepare(writer, strlen, '9') == -1) {
if (_PyUnicodeWriter_Prepare(writer, strlen, 127) == -1) {
Py_DECREF(scratch);
return -1;
}
Expand All @@ -2227,7 +2227,7 @@ long_to_decimal_string_internal(PyObject *aa,
}
}
else {
str = PyUnicode_New(strlen, '9');
str = PyUnicode_New(strlen, 127);
if (str == NULL) {
Py_DECREF(scratch);
return -1;
Expand Down Expand Up @@ -2385,9 +2385,10 @@ long_format_binary(PyObject *aa, int base, int alternate,
/* 2 characters for prefix */
sz += 2;
}
assert(sz >= 1);

if (writer) {
if (_PyUnicodeWriter_Prepare(writer, sz, 'x') == -1) {
if (_PyUnicodeWriter_Prepare(writer, sz, 127) == -1) {
return -1;
}
assert(_PyUnicodeWriter_CanWrite(writer));
Expand Down
3 changes: 3 additions & 0 deletions Objects/unicode_formatter.c
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,7 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
if (n_total == -1) {
goto done;
}
assert(n_total >= 1);

/* Allocate the memory. */
if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1)
Expand Down Expand Up @@ -1503,6 +1504,7 @@ format_float_internal(PyObject *value,
if (n_total == -1) {
goto done;
}
assert(n_total >= 1);

/* Allocate the memory. */
if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1)
Expand Down Expand Up @@ -1714,6 +1716,7 @@ format_complex_internal(PyObject *value,
/* Add 1 for the 'j', and optionally 2 for parens. */
calc_padding(n_re_total + n_im_total + 1 + add_parens * 2,
format->width, format->align, &lpad, &rpad, &total);
assert(total >= 1);

if (lpad || rpad)
maxchar = Py_MAX(maxchar, format->fill_char);
Expand Down
12 changes: 7 additions & 5 deletions Objects/unicode_writer.c
Original file line number Diff line number Diff line change
Expand Up @@ -138,9 +138,10 @@ _PyUnicodeWriter_PrepareInternal(_PyUnicodeWriter *writer,
assert(length >= 0);
assert(maxchar <= _Py_MAX_UNICODE);

/* ensure that the _PyUnicodeWriter_Prepare macro was used */
assert((maxchar > writer->maxchar && length >= 0)
|| length > 0);
// Check that _PyUnicodeWriter_Prepare() or _PyUnicodeWriter_PrepareKind()
// was used
assert(maxchar > writer->maxchar
|| (length > (writer->size - writer->pos) && length >= 1));

if (length > PY_SSIZE_T_MAX - writer->pos) {
PyErr_NoMemory();
Expand Down Expand Up @@ -334,11 +335,12 @@ _PyUnicodeWriter_WriteSubstring(_PyUnicodeWriter *writer, PyObject *str,
Py_ssize_t start, Py_ssize_t end)
{
assert(0 <= start);
assert(end <= PyUnicode_GET_LENGTH(str));
assert(start <= end);
assert(end <= PyUnicode_GET_LENGTH(str));

if (start == 0 && end == PyUnicode_GET_LENGTH(str))
if (start == 0 && end == PyUnicode_GET_LENGTH(str)) {
return _PyUnicodeWriter_WriteStr(writer, str);
}

Py_ssize_t len = end - start;
if (len == 0) {
Expand Down
6 changes: 3 additions & 3 deletions Objects/unicodeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2545,9 +2545,9 @@ unicode_fromformat_write_str(_PyUnicodeWriter *writer, PyObject *str,
Py_UCS4 maxchar;

length = PyUnicode_GET_LENGTH(str);
if ((precision == -1 || precision >= length)
&& width <= length)
if ((precision == -1 || precision >= length) && width <= length) {
return _PyUnicodeWriter_WriteStr(writer, str);
}

if (precision != -1)
length = Py_MIN(precision, length);
Expand Down Expand Up @@ -2837,7 +2837,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
#undef SPRINT
#undef DO_SPRINTS

assert(len >= 0);
assert(len >= 1);

int sign = (buffer[0] == '-');
len -= sign;
Expand Down
Loading