Skip to content

Commit 70e6f3c

Browse files
authored
gh-158451: Convert _PyUnicodeWriter_Prepare() to static inline function (#158466)
Add assertion to _PyUnicodeWriter_Prepare() to check that length is not negative. Add some assertions to check that _PyUnicodeWriter_Prepare() is called with len >= 1.
1 parent 82a89b3 commit 70e6f3c

7 files changed

Lines changed: 49 additions & 29 deletions

File tree

‎Include/cpython/unicodeobject.h‎

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -557,25 +557,33 @@ typedef struct {
557557
_Py_DEPRECATED_EXTERNALLY(3.14) PyAPI_FUNC(void) _PyUnicodeWriter_Init(
558558
_PyUnicodeWriter *writer);
559559

560-
/* Prepare the buffer to write 'length' characters
561-
with the specified maximum character.
562-
563-
Return 0 on success, raise an exception and return -1 on error. */
564-
#define _PyUnicodeWriter_Prepare(WRITER, LENGTH, MAXCHAR) \
565-
(((MAXCHAR) <= (WRITER)->maxchar \
566-
&& (LENGTH) <= (WRITER)->size - (WRITER)->pos) \
567-
? 0 \
568-
: (((LENGTH) == 0) \
569-
? 0 \
570-
: _PyUnicodeWriter_PrepareInternal((WRITER), (LENGTH), (MAXCHAR))))
571-
572-
/* Don't call this function directly, use the _PyUnicodeWriter_Prepare() macro
573-
instead. */
560+
// Don't call this function directly, use _PyUnicodeWriter_Prepare() instead.
574561
_Py_DEPRECATED_EXTERNALLY(3.14) PyAPI_FUNC(int) _PyUnicodeWriter_PrepareInternal(
575562
_PyUnicodeWriter *writer,
576563
Py_ssize_t length,
577564
Py_UCS4 maxchar);
578565

566+
// Prepare the buffer to write 'length' characters
567+
// with the specified maximum character.
568+
//
569+
// Return 0 on success. Set an exception and return -1 on error.
570+
_Py_DEPRECATED_EXTERNALLY(3.14) static inline int
571+
_PyUnicodeWriter_Prepare(_PyUnicodeWriter *writer,
572+
Py_ssize_t length, Py_UCS4 maxchar)
573+
{
574+
assert(0 <= length);
575+
if (maxchar <= writer->maxchar && length <= (writer->size - writer->pos)) {
576+
return 0;
577+
}
578+
if (length == 0) {
579+
return 0;
580+
}
581+
_Py_COMP_DIAG_PUSH
582+
_Py_COMP_DIAG_IGNORE_DEPR_DECLS
583+
return _PyUnicodeWriter_PrepareInternal(writer, length, maxchar);
584+
_Py_COMP_DIAG_POP
585+
}
586+
579587
/* Prepare the buffer to have at least the kind KIND.
580588
For example, kind=PyUnicode_2BYTE_KIND ensures that the writer will
581589
support characters in range U+000-U+FFFF.

‎Include/internal/pycore_unicodeobject.h‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,13 @@ _PyUnicodeWriter_SetReadOnly(_PyUnicodeWriter *writer, PyObject *obj,
168168
static inline int
169169
_PyUnicodeWriter_WriteCharInline(_PyUnicodeWriter *writer, Py_UCS4 ch)
170170
{
171-
if (ch > writer->maxchar || 1 > writer->size - writer->pos) {
171+
if (ch > writer->maxchar || 1 > (writer->size - writer->pos)) {
172172
if (writer->buffer == NULL && ch <= 255) {
173173
// If the first write is a Latin1 character, use the singleton
174174
// as a read-only object
175175
PyObject *obj = _Py_LATIN1_CHR(ch);
176-
// Py_NewRef() is not need on immortal object
176+
// Py_NewRef() is not needed on immortal object
177177
_PyUnicodeWriter_SetReadOnly(writer, obj, 1);
178-
179-
// The next write will create a new buffer and copy the string
180178
return 0;
181179
}
182180

‎Lib/test/test_capi/test_unicode.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,14 @@ def check_format(expected, format, *args):
461461
check_format('%abc',
462462
b'%%%s', b'abc')
463463

464+
# test "%s" with empty string
465+
check_format('x=',
466+
b'x=%s', b'')
467+
check_format('x=',
468+
b'x=%0s', b'')
469+
check_format('x=',
470+
b'x=%.3s', b'')
471+
464472
# truncated string
465473
check_format('abc',
466474
b'%.3s', b'abcdef')

‎Objects/longobject.c‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2212,7 +2212,7 @@ long_to_decimal_string_internal(PyObject *aa,
22122212
}
22132213
}
22142214
if (writer) {
2215-
if (_PyUnicodeWriter_Prepare(writer, strlen, '9') == -1) {
2215+
if (_PyUnicodeWriter_Prepare(writer, strlen, 127) == -1) {
22162216
Py_DECREF(scratch);
22172217
return -1;
22182218
}
@@ -2227,7 +2227,7 @@ long_to_decimal_string_internal(PyObject *aa,
22272227
}
22282228
}
22292229
else {
2230-
str = PyUnicode_New(strlen, '9');
2230+
str = PyUnicode_New(strlen, 127);
22312231
if (str == NULL) {
22322232
Py_DECREF(scratch);
22332233
return -1;
@@ -2385,9 +2385,10 @@ long_format_binary(PyObject *aa, int base, int alternate,
23852385
/* 2 characters for prefix */
23862386
sz += 2;
23872387
}
2388+
assert(sz >= 1);
23882389

23892390
if (writer) {
2390-
if (_PyUnicodeWriter_Prepare(writer, sz, 'x') == -1) {
2391+
if (_PyUnicodeWriter_Prepare(writer, sz, 127) == -1) {
23912392
return -1;
23922393
}
23932394
assert(_PyUnicodeWriter_CanWrite(writer));

‎Objects/unicode_formatter.c‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1349,6 +1349,7 @@ format_long_internal(PyObject *value, const InternalFormatSpec *format,
13491349
if (n_total == -1) {
13501350
goto done;
13511351
}
1352+
assert(n_total >= 1);
13521353

13531354
/* Allocate the memory. */
13541355
if (_PyUnicodeWriter_Prepare(writer, n_total, maxchar) == -1)
@@ -1503,6 +1504,7 @@ format_float_internal(PyObject *value,
15031504
if (n_total == -1) {
15041505
goto done;
15051506
}
1507+
assert(n_total >= 1);
15061508

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

17181721
if (lpad || rpad)
17191722
maxchar = Py_MAX(maxchar, format->fill_char);

‎Objects/unicode_writer.c‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,10 @@ _PyUnicodeWriter_PrepareInternal(_PyUnicodeWriter *writer,
138138
assert(length >= 0);
139139
assert(maxchar <= _Py_MAX_UNICODE);
140140

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

145146
if (length > PY_SSIZE_T_MAX - writer->pos) {
146147
PyErr_NoMemory();
@@ -336,11 +337,12 @@ _PyUnicodeWriter_WriteSubstring(_PyUnicodeWriter *writer, PyObject *str,
336337
Py_ssize_t start, Py_ssize_t end)
337338
{
338339
assert(0 <= start);
339-
assert(end <= PyUnicode_GET_LENGTH(str));
340340
assert(start <= end);
341+
assert(end <= PyUnicode_GET_LENGTH(str));
341342

342-
if (start == 0 && end == PyUnicode_GET_LENGTH(str))
343+
if (start == 0 && end == PyUnicode_GET_LENGTH(str)) {
343344
return _PyUnicodeWriter_WriteStr(writer, str);
345+
}
344346

345347
Py_ssize_t len = end - start;
346348
if (len == 0) {

‎Objects/unicodeobject.c‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2545,9 +2545,9 @@ unicode_fromformat_write_str(_PyUnicodeWriter *writer, PyObject *str,
25452545
Py_UCS4 maxchar;
25462546

25472547
length = PyUnicode_GET_LENGTH(str);
2548-
if ((precision == -1 || precision >= length)
2549-
&& width <= length)
2548+
if ((precision == -1 || precision >= length) && width <= length) {
25502549
return _PyUnicodeWriter_WriteStr(writer, str);
2550+
}
25512551

25522552
if (precision != -1)
25532553
length = Py_MIN(precision, length);
@@ -2837,7 +2837,7 @@ unicode_fromformat_arg(_PyUnicodeWriter *writer,
28372837
#undef SPRINT
28382838
#undef DO_SPRINTS
28392839

2840-
assert(len >= 0);
2840+
assert(len >= 1);
28412841

28422842
int sign = (buffer[0] == '-');
28432843
len -= sign;

0 commit comments

Comments
 (0)