Skip to content

Commit b6f8403

Browse files
committed
Rely on bytes for end of buffer NULL
1. After __init__ or C construction guarantee ob_bytes_object is set by using empty bytes object. 2. In resize place a null terminator mid-buffer only if required 3. Remove now unneded branches - n == PY_SSIZE_T_MAX checks are redundant with resize checks. - size = 0 is handled by PyBytes_FromStringAndSize - No more alloc + 1; exact resize is exact and bytes does +1 for null - No downsize to 0 special case since alloc == size there.
1 parent 6e4b910 commit b6f8403

File tree

1 file changed

+27
-59
lines changed

1 file changed

+27
-59
lines changed

Objects/bytearrayobject.c

Lines changed: 27 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -127,15 +127,14 @@ PyObject *
127127
PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size)
128128
{
129129
PyByteArrayObject *new;
130-
Py_ssize_t alloc;
131130

132131
if (size < 0) {
133132
PyErr_SetString(PyExc_SystemError,
134133
"Negative size passed to PyByteArray_FromStringAndSize");
135134
return NULL;
136135
}
137136

138-
/* Prevent buffer overflow when setting alloc to size+1. */
137+
/* Prevent buffer overflow when setting alloc to size. */
139138
if (size == PY_SSIZE_T_MAX) {
140139
return PyErr_NoMemory();
141140
}
@@ -145,27 +144,18 @@ PyByteArray_FromStringAndSize(const char *bytes, Py_ssize_t size)
145144
return NULL;
146145
}
147146

148-
if (size == 0) {
149-
new->ob_bytes_object = NULL;
150-
new->ob_bytes = NULL;
151-
alloc = 0;
147+
new->ob_bytes_object = PyBytes_FromStringAndSize(NULL, size);
148+
if (new->ob_bytes_object == NULL) {
149+
Py_DECREF(new);
150+
return NULL;
152151
}
153-
else {
154-
alloc = size + 1;
155-
new->ob_bytes_object = PyBytes_FromStringAndSize(NULL, alloc);
156-
if (new->ob_bytes_object == NULL) {
157-
Py_DECREF(new);
158-
return NULL;
159-
}
160-
new->ob_bytes = PyBytes_AS_STRING(new->ob_bytes_object);
161-
assert(new->ob_bytes);
162-
if (bytes != NULL && size > 0) {
163-
memcpy(new->ob_bytes, bytes, size);
164-
}
165-
new->ob_bytes[size] = '\0'; /* Trailing null byte */
152+
new->ob_bytes = PyBytes_AS_STRING(new->ob_bytes_object);
153+
assert(new->ob_bytes);
154+
if (bytes != NULL && size > 0) {
155+
memcpy(new->ob_bytes, bytes, size);
166156
}
167157
Py_SET_SIZE(new, size);
168-
new->ob_alloc = alloc;
158+
new->ob_alloc = size;
169159
new->ob_start = new->ob_bytes;
170160
new->ob_exports = 0;
171161

@@ -218,21 +208,17 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size)
218208
return -1;
219209
}
220210

221-
if (size + logical_offset + 1 <= alloc) {
211+
if (size + logical_offset <= alloc) {
222212
/* Current buffer is large enough to host the requested size,
223213
decide on a strategy. */
224214
if (size < alloc / 2) {
225215
/* Major downsize; resize down to exact size */
226-
alloc = size + 1;
227-
228-
/* If new size is 0; don't need to allocate one byte for null. */
229-
if (size == 0) {
230-
alloc = 0;
231-
}
216+
alloc = size;
232217
}
233218
else {
234219
/* Minor downsize; quick exit */
235220
Py_SET_SIZE(self, size);
221+
/* Add mid-buffer null; end provided by bytes. */
236222
PyByteArray_AS_STRING(self)[size] = '\0'; /* Trailing null */
237223
return 0;
238224
}
@@ -245,10 +231,11 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size)
245231
}
246232
else {
247233
/* Major upsize; resize up to exact size. Upsize always means size > 0 */
248-
alloc = size + 1;
234+
alloc = size;
249235
}
250236
}
251-
if (alloc > PY_SSIZE_T_MAX) {
237+
// NOTE: offsetof() logic copied from PyBytesObject_SIZE in bytesobject.c
238+
if (alloc > PY_SSIZE_T_MAX - (offsetof(PyBytesObject, ob_sval) + 1)) {
252239
PyErr_NoMemory();
253240
return -1;
254241
}
@@ -277,7 +264,10 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size)
277264
obj->ob_bytes = obj->ob_start = PyBytes_AS_STRING(obj->ob_bytes_object);
278265
Py_SET_SIZE(self, size);
279266
FT_ATOMIC_STORE_SSIZE_RELAXED(obj->ob_alloc, alloc);
280-
obj->ob_bytes[size] = '\0'; /* Trailing null byte */
267+
if (alloc != size) {
268+
/* Add mid-buffer null; end provided by bytes. */
269+
obj->ob_bytes[size] = '\0';
270+
}
281271

282272
return 0;
283273
}
@@ -1550,15 +1540,11 @@ bytearray_take_bytes_impl(PyByteArrayObject *self, PyObject *n)
15501540
}
15511541

15521542
// Copy remaining bytes to a new bytes.
1553-
PyObject *remaining = NULL;
15541543
Py_ssize_t remaining_length = size - to_take;
1555-
if (remaining_length > 0) {
1556-
// +1 to copy across the null which always ends a bytearray.
1557-
remaining = PyBytes_FromStringAndSize(self->ob_start + to_take,
1558-
remaining_length + 1);
1559-
if (remaining == NULL) {
1560-
return NULL;
1561-
}
1544+
PyObject *remaining = PyBytes_FromStringAndSize(self->ob_start + to_take,
1545+
remaining_length);
1546+
if (remaining == NULL) {
1547+
return NULL;
15621548
}
15631549

15641550
// If the bytes are offset inside the buffer must first align.
@@ -1575,17 +1561,9 @@ bytearray_take_bytes_impl(PyByteArrayObject *self, PyObject *n)
15751561
// Point the bytearray towards the buffer with the remaining data.
15761562
PyObject *result = self->ob_bytes_object;
15771563
self->ob_bytes_object = remaining;
1578-
if (remaining) {
1579-
self->ob_bytes = self->ob_start = PyBytes_AS_STRING(self->ob_bytes_object);
1580-
Py_SET_SIZE(self, size - to_take);
1581-
FT_ATOMIC_STORE_SSIZE_RELAXED(self->ob_alloc, size - to_take + 1);
1582-
}
1583-
else {
1584-
self->ob_bytes = self->ob_start = NULL;
1585-
Py_SET_SIZE(self, 0);
1586-
FT_ATOMIC_STORE_SSIZE_RELAXED(self->ob_alloc, 0);
1587-
}
1588-
1564+
self->ob_bytes = self->ob_start = PyBytes_AS_STRING(self->ob_bytes_object);
1565+
Py_SET_SIZE(self, remaining_length);
1566+
FT_ATOMIC_STORE_SSIZE_RELAXED(self->ob_alloc, remaining_length);
15891567
return result;
15901568
}
15911569

@@ -1967,11 +1945,6 @@ bytearray_insert_impl(PyByteArrayObject *self, Py_ssize_t index, int item)
19671945
Py_ssize_t n = Py_SIZE(self);
19681946
char *buf;
19691947

1970-
if (n == PY_SSIZE_T_MAX) {
1971-
PyErr_SetString(PyExc_OverflowError,
1972-
"cannot add more objects to bytearray");
1973-
return NULL;
1974-
}
19751948
if (bytearray_resize_lock_held((PyObject *)self, n + 1) < 0)
19761949
return NULL;
19771950
buf = PyByteArray_AS_STRING(self);
@@ -2086,11 +2059,6 @@ bytearray_append_impl(PyByteArrayObject *self, int item)
20862059
{
20872060
Py_ssize_t n = Py_SIZE(self);
20882061

2089-
if (n == PY_SSIZE_T_MAX) {
2090-
PyErr_SetString(PyExc_OverflowError,
2091-
"cannot add more objects to bytearray");
2092-
return NULL;
2093-
}
20942062
if (bytearray_resize_lock_held((PyObject *)self, n + 1) < 0)
20952063
return NULL;
20962064

0 commit comments

Comments
 (0)