Skip to content

Commit 909270c

Browse files
committed
Optimize bytearray_realign_data_lock_held()
Always use _PyBytes_ResizeKeepOnError(), but move remaining bytes on error.
1 parent 6762d16 commit 909270c

1 file changed

Lines changed: 23 additions & 28 deletions

File tree

Objects/bytearrayobject.c

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -225,41 +225,36 @@ PyByteArray_AsString(PyObject *self)
225225

226226

227227
static int
228-
bytearray_realign_data_lock_held(PyByteArrayObject *self, Py_ssize_t new_size, Py_ssize_t alloc)
228+
bytearray_realign_data_lock_held(PyByteArrayObject *self,
229+
Py_ssize_t new_size, Py_ssize_t alloc)
229230
{
230231
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
231232
assert(1 <= new_size && new_size <= alloc);
232233

233234
Py_ssize_t size = Py_SIZE(self);
234-
size_t logical_offset = (size_t) (self->ob_start - self->ob_bytes);
235-
236-
if (logical_offset == 0 || new_size >= size) {
237-
/* Re-align data to the start of the allocation. */
238-
if (logical_offset != 0) {
239-
/* optimization tradeoff: This is faster than a new allocation when
240-
the number of bytes being removed in a resize is small; for
241-
large size changes it may be better to just make a new bytes
242-
object as _PyBytes_Resize will do a malloc + memcpy internally.
243-
*/
244-
memmove(self->ob_bytes, self->ob_start, size);
245-
self->ob_start = self->ob_bytes;
246-
}
247235

248-
if (_PyBytes_ResizeKeepOnError(&self->ob_bytes_object, alloc) < 0) {
249-
bytearray_write_trailing_null_byte(self);
250-
return -1;
251-
}
252-
}
253-
else {
254-
// Using memmove() would be unsafe, since _PyBytes_ResizeKeepOnError()
255-
// failure code path would be unable to restore the bytearray to its
256-
// previous state.
257-
PyObject *resized = PyBytes_FromStringAndSize(NULL, alloc);
258-
if (resized == NULL) {
259-
return -1;
236+
/* Re-align data to the start of the allocation. */
237+
char *old_start = self->ob_start;
238+
if (self->ob_start != self->ob_bytes) {
239+
/* optimization tradeoff: This is faster than a new allocation when
240+
the number of bytes being removed in a resize is small; for
241+
large size changes it may be better to just make a new bytes
242+
object as _PyBytes_Resize will do a malloc + memcpy internally.
243+
*/
244+
Py_ssize_t move = Py_MIN(new_size, size);
245+
memmove(self->ob_bytes, self->ob_start, move);
246+
self->ob_start = self->ob_bytes;
247+
}
248+
249+
if (_PyBytes_ResizeKeepOnError(&self->ob_bytes_object, alloc) < 0) {
250+
if (new_size < size) {
251+
// Move remaining bytes
252+
Py_ssize_t moved = new_size;
253+
Py_ssize_t remaining = size - moved;
254+
memmove(self->ob_bytes + moved, old_start + moved, remaining);
260255
}
261-
memcpy(PyBytes_AS_STRING(resized), self->ob_start, new_size);
262-
Py_SETREF(self->ob_bytes_object, resized);
256+
bytearray_write_trailing_null_byte(self);
257+
return -1;
263258
}
264259
return 0;
265260
}

0 commit comments

Comments
 (0)