Issue #4509: bugs in bytearray with exports (buffer protocol)

This commit is contained in:
Antoine Pitrou 2008-12-06 21:27:53 +00:00
parent e6d4a9bdbc
commit 5504e893f8
3 changed files with 65 additions and 11 deletions

View File

@ -769,6 +769,37 @@ class ByteArrayTest(BaseBytesTest):
self.assertEqual(b, b"")
self.assertEqual(c, b"")
def test_resize_forbidden(self):
# #4509: can't resize a bytearray when there are buffer exports, even
# if it wouldn't reallocate the underlying buffer.
# Furthermore, no destructive changes to the buffer may be applied
# before raising the error.
b = bytearray(range(10))
v = memoryview(b)
def resize(n):
b[1:-1] = range(n + 1, 2*n - 1)
resize(10)
orig = b[:]
self.assertRaises(BufferError, resize, 11)
self.assertEquals(b, orig)
self.assertRaises(BufferError, resize, 9)
self.assertEquals(b, orig)
self.assertRaises(BufferError, resize, 0)
self.assertEquals(b, orig)
# Other operations implying resize
self.assertRaises(BufferError, b.pop, 0)
self.assertEquals(b, orig)
self.assertRaises(BufferError, b.remove, b[1])
self.assertEquals(b, orig)
def delitem():
del b[1]
self.assertRaises(BufferError, delitem)
self.assertEquals(b, orig)
# deleting a non-contiguous slice
def delslice():
b[1:-1:2] = b""
self.assertRaises(BufferError, delslice)
self.assertEquals(b, orig)
class AssortedBytesTest(unittest.TestCase):
#

View File

@ -21,6 +21,9 @@ Core and Builtins
growing read buffer. Fixed by using the same growth rate algorithm as
Python 2.x.
- Issue #4509: Various issues surrounding resize of bytearray objects to
which there are buffer exports (e.g. memoryview instances).
Library
-------

View File

@ -100,6 +100,17 @@ _getbuffer(PyObject *obj, Py_buffer *view)
return view->len;
}
static int
_canresize(PyByteArrayObject *self)
{
if (self->ob_exports > 0) {
PyErr_SetString(PyExc_BufferError,
"Existing exports of data: object cannot be re-sized");
return 0;
}
return 1;
}
/* Direct API functions */
PyObject *
@ -180,6 +191,13 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t size)
assert(PyByteArray_Check(self));
assert(size >= 0);
if (size == Py_SIZE(self)) {
return 0;
}
if (!_canresize((PyByteArrayObject *)self)) {
return -1;
}
if (size < alloc / 2) {
/* Major downsize; resize down to exact size */
alloc = size + 1;
@ -199,16 +217,6 @@ PyByteArray_Resize(PyObject *self, Py_ssize_t size)
alloc = size + 1;
}
if (((PyByteArrayObject *)self)->ob_exports > 0) {
/*
fprintf(stderr, "%d: %s", ((PyByteArrayObject *)self)->ob_exports,
((PyByteArrayObject *)self)->ob_bytes);
*/
PyErr_SetString(PyExc_BufferError,
"Existing exports of data: object cannot be re-sized");
return -1;
}
sval = PyMem_Realloc(((PyByteArrayObject *)self)->ob_bytes, alloc);
if (sval == NULL) {
PyErr_NoMemory();
@ -473,6 +481,10 @@ bytes_setslice(PyByteArrayObject *self, Py_ssize_t lo, Py_ssize_t hi,
if (avail != needed) {
if (avail > needed) {
if (!_canresize(self)) {
res = -1;
goto finish;
}
/*
0 lo hi old_size
| |<----avail----->|<-----tomove------>|
@ -605,6 +617,8 @@ bytes_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values)
stop = start;
if (step == 1) {
if (slicelen != needed) {
if (!_canresize(self))
return -1;
if (slicelen > needed) {
/*
0 start stop old_size
@ -640,6 +654,8 @@ bytes_ass_subscript(PyByteArrayObject *self, PyObject *index, PyObject *values)
/* Delete slice */
Py_ssize_t cur, i;
if (!_canresize(self))
return -1;
if (step < 0) {
stop = start + 1;
start = stop + step * (slicelen - 1) - 1;
@ -1401,7 +1417,7 @@ bytes_translate(PyByteArrayObject *self, PyObject *args)
}
goto done;
}
for (i = 0; i < 256; i++)
trans_table[i] = Py_CHARMASK(table[i]);
@ -2659,6 +2675,8 @@ bytes_pop(PyByteArrayObject *self, PyObject *args)
PyErr_SetString(PyExc_IndexError, "pop index out of range");
return NULL;
}
if (!_canresize(self))
return NULL;
value = self->ob_bytes[where];
memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - where);
@ -2689,6 +2707,8 @@ bytes_remove(PyByteArrayObject *self, PyObject *arg)
PyErr_SetString(PyExc_ValueError, "value not found in bytes");
return NULL;
}
if (!_canresize(self))
return NULL;
memmove(self->ob_bytes + where, self->ob_bytes + where + 1, n - where);
if (PyByteArray_Resize((PyObject *)self, n - 1) < 0)