@@ -238,69 +238,55 @@ bytearray_resize_storage(PyByteArrayObject *self,
238238
239239 Py_ssize_t size = Py_SIZE (self );
240240
241- if (self -> ob_start != self -> ob_bytes && new_size < size ) {
242- /* Shrink with a logical offset: copy the bytes to keep into a new
243- allocation. Allocate first, so that the bytearray (including
244- ob_start) is left unchanged on error: bytearray_setslice_linear()
245- relies on it to restore the bytearray after a failed "del b[:n]".
241+ /* Re-align data to the start of the allocation. */
242+ char * old_start = self -> ob_start ;
243+ if (self -> ob_start != self -> ob_bytes ) {
244+ /* optimization tradeoff: This is faster than a new allocation when
245+ the number of bytes being removed in a resize is small; for
246+ large size changes it may be better to just make a new bytes
247+ object as _PyBytes_Resize will do a malloc + memcpy internally.
246248 */
247- PyObject * bytes = PyBytes_FromStringAndSize (NULL , alloc );
248- if (bytes == NULL ) {
249- return -1 ;
250- }
251- memcpy (PyBytes_AS_STRING (bytes ), self -> ob_start , new_size );
252- Py_SETREF (self -> ob_bytes_object , bytes );
249+ Py_ssize_t move = Py_MIN (new_size , size );
250+ memmove (self -> ob_bytes , self -> ob_start , move );
251+ self -> ob_start = self -> ob_bytes ;
253252 }
254- else {
255- /* Re-align data to the start of the allocation. */
256- if (self -> ob_start != self -> ob_bytes ) {
257- /* optimization tradeoff: This is faster than a new allocation
258- when the number of bytes being added in a resize is small. */
259- memmove (self -> ob_bytes , self -> ob_start , size );
260- self -> ob_start = self -> ob_bytes ;
261- }
262253
263- if (_PyBytes_ResizeKeepOnError (& self -> ob_bytes_object , alloc ) < 0 ) {
264- bytearray_write_trailing_null_byte (self );
265- return -1 ;
254+ if (_PyBytes_ResizeKeepOnError (& self -> ob_bytes_object , alloc ) < 0 ) {
255+ if (old_start != self -> ob_bytes && new_size < size ) {
256+ // Move remaining bytes
257+ Py_ssize_t moved = new_size ;
258+ Py_ssize_t remaining = size - moved ;
259+ memmove (self -> ob_bytes + moved , old_start + moved , remaining );
266260 }
261+ bytearray_write_trailing_null_byte (self );
262+ return -1 ;
267263 }
268264 assert (_PyBytes_IsMutable (self -> ob_bytes_object ));
269265 return 0 ;
270266}
271267
272268
269+ /* Resize the storage for requested_size bytes, even if it is already the
270+ size of the bytearray. The caller must have checked that the bytearray
271+ can be resized. */
273272static int
274- bytearray_resize_lock_held ( PyObject * self , Py_ssize_t requested_size )
273+ bytearray_resize_unchecked ( PyByteArrayObject * obj , Py_ssize_t requested_size )
275274{
276- _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (self );
277- PyByteArrayObject * obj = (( PyByteArrayObject * )self ) ;
275+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (obj );
276+ PyObject * self = (PyObject * )obj ;
278277
279278 assert (obj -> ob_bytes_object != NULL );
279+ assert (requested_size >= 0 );
280+ assert (obj -> ob_exports == 0 );
280281
281282 /* All computations are done unsigned to avoid integer overflows
282283 (see issue #22335). */
283284 size_t alloc = (size_t ) obj -> ob_alloc ;
284285 size_t logical_offset = (size_t ) (obj -> ob_start - obj -> ob_bytes );
285286 size_t size = (size_t ) requested_size ;
286287
287- assert (self != NULL );
288- assert (PyByteArray_Check (self ));
289288 assert (logical_offset <= alloc );
290289
291- if (requested_size < 0 ) {
292- PyErr_Format (PyExc_ValueError ,
293- "Can only resize to positive sizes, got %zd" , requested_size );
294- return -1 ;
295- }
296-
297- if (requested_size == Py_SIZE (self )) {
298- return 0 ;
299- }
300- if (!_canresize (obj )) {
301- return -1 ;
302- }
303-
304290 /* Resize to 0 resets to empty bytes (see issue #153419). */
305291 if (requested_size == 0 ) {
306292 Py_SETREF (obj -> ob_bytes_object ,
@@ -352,6 +338,30 @@ bytearray_resize_lock_held(PyObject *self, Py_ssize_t requested_size)
352338 return 0 ;
353339}
354340
341+ static int
342+ bytearray_resize_lock_held (PyObject * self , Py_ssize_t requested_size )
343+ {
344+ _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED (self );
345+ PyByteArrayObject * obj = ((PyByteArrayObject * )self );
346+
347+ assert (self != NULL );
348+ assert (PyByteArray_Check (self ));
349+
350+ if (requested_size < 0 ) {
351+ PyErr_Format (PyExc_ValueError ,
352+ "Can only resize to positive sizes, got %zd" , requested_size );
353+ return -1 ;
354+ }
355+
356+ if (requested_size == Py_SIZE (self )) {
357+ return 0 ;
358+ }
359+ if (!_canresize (obj )) {
360+ return -1 ;
361+ }
362+ return bytearray_resize_unchecked (obj , requested_size );
363+ }
364+
355365int
356366PyByteArray_Resize (PyObject * self , Py_ssize_t requested_size )
357367{
@@ -616,8 +626,12 @@ bytearray_setslice_linear(PyByteArrayObject *self,
616626 return -1 ;
617627
618628 if (lo == 0 ) {
619- /* Shrink the buffer by advancing its logical start */
629+ /* Shrink the buffer by advancing its logical start. The bytes
630+ are removed at this point; the resize below only releases
631+ memory. */
620632 self -> ob_start -= growth ;
633+ Py_SET_SIZE (self , Py_SIZE (self ) + growth );
634+ growth = 0 ;
621635 /*
622636 0 lo hi old_size
623637 | |<----avail----->|<-----tail------>|
@@ -635,23 +649,14 @@ bytearray_setslice_linear(PyByteArrayObject *self,
635649 memmove (buf + lo + bytes_len , buf + hi ,
636650 Py_SIZE (self ) - hi );
637651 }
638- if (bytearray_resize_lock_held ((PyObject * )self ,
639- Py_SIZE (self ) + growth ) < 0 ) {
652+ if (bytearray_resize_unchecked (self , Py_SIZE (self ) + growth ) < 0 ) {
640653 /* Issue #19578: Handling the memory allocation failure here is
641654 tricky here because the bytearray object has already been
642- modified. Depending on growth and lo, the behaviour is
643- different.
644-
645- If growth < 0 and lo != 0, the operation is completed, but a
646- MemoryError is still raised and the memory block is not
647- shrunk. Otherwise, the bytearray is restored in its previous
648- state and a MemoryError is raised. */
649- if (lo == 0 ) {
650- self -> ob_start += growth ;
651- return -1 ;
652- }
653- /* memmove() removed bytes, the bytearray object cannot be
654- restored in its previous state. */
655+ modified: the bytes are removed and the bytearray object
656+ cannot be restored in its previous state.
657+
658+ The operation is completed, but a MemoryError is still
659+ raised and the memory block is not shrunk. */
655660 Py_SET_SIZE (self , Py_SIZE (self ) + growth );
656661 bytearray_write_trailing_null_byte (self );
657662 res = -1 ;
0 commit comments