Skip to content

Commit bab97c5

Browse files
authored
gh-157242: Allow negative grow in PyBytesWriter_Grow() (#157375)
PyBytesWriter_Resize() doesn't need to call byteswriter_resize() if the buffer is already large enough.
1 parent b1e7554 commit bab97c5

3 files changed

Lines changed: 42 additions & 15 deletions

File tree

Doc/c-api/bytes.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,7 @@ Low-level API
429429
On success, return ``0``.
430430
On error, set an exception and return ``-1``.
431431
432-
*size* can be negative to shrink the writer.
432+
*grow* can be negative to shrink the writer.
433433
434434
.. c:function:: void* PyBytesWriter_GrowAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size, void *buf)
435435

Lib/test/test_capi/test_bytes.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,24 @@ def test_grow(self):
461461
writer.grow(0) # noop
462462
self.assertEqual(writer.finish(), b'number=123')
463463

464+
for size in (self.SMALL_BUFFER, self.LARGE_BUFFER):
465+
with self.subTest(size=size):
466+
# Truncate the last byte
467+
data = b'x' * size
468+
writer = self.create_writer(size)
469+
writer.write(0, data)
470+
self.assertEqual(writer.get_data(), data)
471+
writer.grow(-1)
472+
self.assertEqual(writer.get_data(), data[:-1])
473+
self.assertEqual(writer.finish(), data[:-1])
474+
475+
# Make the buffer empty
476+
writer = self.create_writer(size)
477+
writer.write(0, data)
478+
writer.grow(-size)
479+
self.assertEqual(writer.get_data(), b'')
480+
self.assertEqual(writer.finish(), b'')
481+
464482
# Switch from small buffer to large buffer
465483
writer = self.create_writer()
466484
small, large = self.SMALL_BUFFER, self.LARGE_BUFFER
@@ -476,8 +494,8 @@ def test_grow(self):
476494
with self.subTest(size=size):
477495
writer = self.create_writer()
478496
writer.write_bytes(b'x' * size, -1)
479-
with self.assertRaisesRegex(ValueError, 'size must be >= 0'):
480-
writer.grow(-1)
497+
with self.assertRaisesRegex(ValueError, 'invalid size'):
498+
writer.grow(-size - 1)
481499
with self.assertRaises(MemoryError):
482500
writer.grow(_testcapi.PY_SSIZE_T_MAX)
483501
self.assertEqual(writer.finish(), b'x' * size)

Objects/bytesobject.c

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3845,8 +3845,13 @@ PyBytesWriter_Resize(PyBytesWriter *writer, Py_ssize_t size)
38453845
PyErr_SetString(PyExc_ValueError, "size must be >= 0");
38463846
return -1;
38473847
}
3848-
if (byteswriter_resize(writer, size, 1) < 0) {
3849-
return -1;
3848+
if (writer->size < size) {
3849+
if (byteswriter_resize(writer, size, 1) < 0) {
3850+
return -1;
3851+
}
3852+
}
3853+
else {
3854+
// The buffer is already large enough. Never shrink the buffer.
38503855
}
38513856
writer->size = size;
38523857
return 0;
@@ -3866,22 +3871,26 @@ _PyBytesWriter_ResizeAndUpdatePointer(PyBytesWriter *writer, Py_ssize_t size,
38663871

38673872

38683873
int
3869-
PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t size)
3874+
PyBytesWriter_Grow(PyBytesWriter *writer, Py_ssize_t grow)
38703875
{
3871-
if (size < 0) {
3872-
PyErr_SetString(PyExc_ValueError, "size must be >= 0");
3873-
return -1;
3874-
}
3875-
if (size == 0) {
3876+
if (grow == 0) {
38763877
// Nothing to do
38773878
return 0;
38783879
}
38793880

3880-
if (size > PY_SSIZE_T_MAX - writer->size) {
3881-
PyErr_NoMemory();
3882-
return -1;
3881+
if (grow >= 0) {
3882+
if (grow > PY_SSIZE_T_MAX - writer->size) {
3883+
PyErr_NoMemory();
3884+
return -1;
3885+
}
3886+
}
3887+
else {
3888+
if (writer->size + grow < 0) {
3889+
PyErr_SetString(PyExc_ValueError, "invalid size");
3890+
return -1;
3891+
}
38833892
}
3884-
size = writer->size + size;
3893+
Py_ssize_t size = writer->size + grow;
38853894

38863895
if (byteswriter_resize(writer, size, 1) < 0) {
38873896
return -1;

0 commit comments

Comments
 (0)