Kaydet (Commit) c3b39245 authored tarafından Antoine Pitrou's avatar Antoine Pitrou

Issue #4580: slicing of memoryviews when itemsize != 1 is wrong.

Also fix len() to return number of items rather than length in bytes.

I'm sorry it was not possible for me to work on this without reindenting
a bit some stuff around. The indentation in memoryobject.c is a mess,
I'll open a separate bug for it.
üst 8bcddcab
...@@ -144,16 +144,18 @@ typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *); ...@@ -144,16 +144,18 @@ typedef int(*objobjargproc)(PyObject *, PyObject *, PyObject *);
typedef struct bufferinfo { typedef struct bufferinfo {
void *buf; void *buf;
PyObject *obj; /* owned reference */ PyObject *obj; /* owned reference */
Py_ssize_t len; Py_ssize_t len;
Py_ssize_t itemsize; /* This is Py_ssize_t so it can be Py_ssize_t itemsize; /* This is Py_ssize_t so it can be
pointed to by strides in simple case.*/ pointed to by strides in simple case.*/
int readonly; int readonly;
int ndim; int ndim;
char *format; char *format;
Py_ssize_t *shape; Py_ssize_t *shape;
Py_ssize_t *strides; Py_ssize_t *strides;
Py_ssize_t *suboffsets; Py_ssize_t *suboffsets;
void *internal; Py_ssize_t smalltable[2]; /* static store for shape and strides of
mono-dimensional buffers. */
void *internal;
} Py_buffer; } Py_buffer;
typedef int (*getbufferproc)(PyObject *, Py_buffer *, int); typedef int (*getbufferproc)(PyObject *, Py_buffer *, int);
......
...@@ -25,7 +25,10 @@ class Test(unittest.TestCase): ...@@ -25,7 +25,10 @@ class Test(unittest.TestCase):
v = memoryview(ob) v = memoryview(ob)
try: try:
self.failUnlessEqual(normalize(v.format), normalize(fmt)) self.failUnlessEqual(normalize(v.format), normalize(fmt))
self.failUnlessEqual(len(v), sizeof(ob)) if shape is not None:
self.failUnlessEqual(len(v), shape[0])
else:
self.failUnlessEqual(len(v) * sizeof(itemtp), sizeof(ob))
self.failUnlessEqual(v.itemsize, sizeof(itemtp)) self.failUnlessEqual(v.itemsize, sizeof(itemtp))
self.failUnlessEqual(v.shape, shape) self.failUnlessEqual(v.shape, shape)
# ctypes object always have a non-strided memory block # ctypes object always have a non-strided memory block
...@@ -37,7 +40,7 @@ class Test(unittest.TestCase): ...@@ -37,7 +40,7 @@ class Test(unittest.TestCase):
n = 1 n = 1
for dim in v.shape: for dim in v.shape:
n = n * dim n = n * dim
self.failUnlessEqual(v.itemsize * n, len(v)) self.failUnlessEqual(n * v.itemsize, len(v.tobytes()))
except: except:
# so that we can see the failing type # so that we can see the failing type
print(tp) print(tp)
...@@ -49,7 +52,10 @@ class Test(unittest.TestCase): ...@@ -49,7 +52,10 @@ class Test(unittest.TestCase):
v = memoryview(ob) v = memoryview(ob)
try: try:
self.failUnlessEqual(v.format, fmt) self.failUnlessEqual(v.format, fmt)
self.failUnlessEqual(len(v), sizeof(ob)) if shape is not None:
self.failUnlessEqual(len(v), shape[0])
else:
self.failUnlessEqual(len(v) * sizeof(itemtp), sizeof(ob))
self.failUnlessEqual(v.itemsize, sizeof(itemtp)) self.failUnlessEqual(v.itemsize, sizeof(itemtp))
self.failUnlessEqual(v.shape, shape) self.failUnlessEqual(v.shape, shape)
# ctypes object always have a non-strided memory block # ctypes object always have a non-strided memory block
...@@ -61,7 +67,7 @@ class Test(unittest.TestCase): ...@@ -61,7 +67,7 @@ class Test(unittest.TestCase):
n = 1 n = 1
for dim in v.shape: for dim in v.shape:
n = n * dim n = n * dim
self.failUnlessEqual(v.itemsize * n, len(v)) self.failUnlessEqual(n, len(v))
except: except:
# so that we can see the failing type # so that we can see the failing type
print(tp) print(tp)
......
...@@ -260,7 +260,7 @@ class IOTest(unittest.TestCase): ...@@ -260,7 +260,7 @@ class IOTest(unittest.TestCase):
def test_array_writes(self): def test_array_writes(self):
a = array.array('i', range(10)) a = array.array('i', range(10))
n = len(memoryview(a)) n = len(a.tostring())
f = io.open(support.TESTFN, "wb", 0) f = io.open(support.TESTFN, "wb", 0)
self.assertEqual(f.write(a), n) self.assertEqual(f.write(a), n)
f.close() f.close()
......
This diff is collapsed.
...@@ -559,7 +559,7 @@ class SizeofTest(unittest.TestCase): ...@@ -559,7 +559,7 @@ class SizeofTest(unittest.TestCase):
check(32768*32768-1, size(vh) + 2*self.H) check(32768*32768-1, size(vh) + 2*self.H)
check(32768*32768, size(vh) + 3*self.H) check(32768*32768, size(vh) + 3*self.H)
# memory # memory
check(memoryview(b''), size(h + 'P PP2P2i5P')) check(memoryview(b''), size(h + 'P PP2P2i7P'))
# module # module
check(unittest, size(h + '3P')) check(unittest, size(h + '3P'))
# None # None
......
...@@ -12,6 +12,10 @@ What's New in Python 3.1 alpha 0 ...@@ -12,6 +12,10 @@ What's New in Python 3.1 alpha 0
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #4580: Fix slicing of memoryviews when the item size is greater than
one byte. Also fixes the meaning of len() so that it returns the number of
items, rather than the size in bytes.
- Issue #4075: Use OutputDebugStringW in Py_FatalError. - Issue #4075: Use OutputDebugStringW in Py_FatalError.
- Issue #4747: When the terminal does not use utf-8, executing a script with - Issue #4747: When the terminal does not use utf-8, executing a script with
......
...@@ -3,26 +3,31 @@ ...@@ -3,26 +3,31 @@
#include "Python.h" #include "Python.h"
static void
dup_buffer(Py_buffer *dest, Py_buffer *src)
{
*dest = *src;
if (src->shape == &(src->len))
dest->shape = &(dest->len);
if (src->strides == &(src->itemsize))
dest->strides = &(dest->itemsize);
}
/* XXX The buffer API should mandate that the shape array be non-NULL, but
it would complicate some code since the (de)allocation semantics of shape
are not specified. */
static Py_ssize_t static Py_ssize_t
get_shape0(Py_buffer *buf) get_shape0(Py_buffer *buf)
{ {
if (buf->shape != NULL) if (buf->shape != NULL)
return buf->shape[0]; return buf->shape[0];
assert(buf->ndim == 1 && buf->itemsize > 0); if (buf->ndim == 0)
return buf->len / buf->itemsize; return 1;
PyErr_SetString(PyExc_TypeError,
"exported buffer does not have any shape information associated "
"to it");
return -1;
}
static void
dup_buffer(Py_buffer *dest, Py_buffer *src)
{
*dest = *src;
if (src->ndim == 1 && src->shape != NULL) {
dest->shape = &(dest->smalltable[0]);
dest->shape[0] = get_shape0(src);
}
if (src->ndim == 1 && src->strides != NULL) {
dest->strides = &(dest->smalltable[1]);
dest->strides[0] = src->strides[0];
}
} }
static int static int
...@@ -449,8 +454,6 @@ memory_tolist(PyMemoryViewObject *mem, PyObject *noargs) ...@@ -449,8 +454,6 @@ memory_tolist(PyMemoryViewObject *mem, PyObject *noargs)
return res; return res;
} }
static PyMethodDef memory_methods[] = { static PyMethodDef memory_methods[] = {
{"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL}, {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL},
{"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL}, {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL},
...@@ -474,19 +477,19 @@ memory_dealloc(PyMemoryViewObject *self) ...@@ -474,19 +477,19 @@ memory_dealloc(PyMemoryViewObject *self)
PyObject_CopyData(PyTuple_GET_ITEM(self->base,0), PyObject_CopyData(PyTuple_GET_ITEM(self->base,0),
PyTuple_GET_ITEM(self->base,1)); PyTuple_GET_ITEM(self->base,1));
/* The view member should have readonly == -1 in /* The view member should have readonly == -1 in
this instance indicating that the memory can this instance indicating that the memory can
be "locked" and was locked and will be unlocked be "locked" and was locked and will be unlocked
again after this call. again after this call.
*/ */
PyBuffer_Release(&(self->view)); PyBuffer_Release(&(self->view));
}
else {
PyBuffer_Release(&(self->view));
}
Py_CLEAR(self->base);
} }
PyObject_GC_Del(self); else {
PyBuffer_Release(&(self->view));
}
Py_CLEAR(self->base);
}
PyObject_GC_Del(self);
} }
static PyObject * static PyObject *
...@@ -512,16 +515,10 @@ memory_str(PyMemoryViewObject *self) ...@@ -512,16 +515,10 @@ memory_str(PyMemoryViewObject *self)
} }
/* Sequence methods */ /* Sequence methods */
static Py_ssize_t static Py_ssize_t
memory_length(PyMemoryViewObject *self) memory_length(PyMemoryViewObject *self)
{ {
Py_buffer view; return get_shape0(&self->view);
if (PyObject_GetBuffer((PyObject *)self, &view, PyBUF_FULL) < 0)
return -1;
PyBuffer_Release(&view);
return view.len;
} }
/* /*
...@@ -589,40 +586,38 @@ memory_subscript(PyMemoryViewObject *self, PyObject *key) ...@@ -589,40 +586,38 @@ memory_subscript(PyMemoryViewObject *self, PyObject *key)
} }
} }
else if (PySlice_Check(key)) { else if (PySlice_Check(key)) {
Py_ssize_t start, stop, step, slicelength; Py_ssize_t start, stop, step, slicelength;
if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view), if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
&start, &stop, &step, &slicelength) < 0) { &start, &stop, &step, &slicelength) < 0) {
return NULL; return NULL;
} }
if (step == 1 && view->ndim == 1) { if (step == 1 && view->ndim == 1) {
Py_buffer newview; Py_buffer newview;
void *newbuf = (char *) view->buf void *newbuf = (char *) view->buf
+ start * view->itemsize; + start * view->itemsize;
int newflags = view->readonly int newflags = view->readonly
? PyBUF_CONTIG_RO : PyBUF_CONTIG; ? PyBUF_CONTIG_RO : PyBUF_CONTIG;
/* XXX There should be an API to create a subbuffer */ /* XXX There should be an API to create a subbuffer */
if (view->obj != NULL) { if (view->obj != NULL) {
if (PyObject_GetBuffer(view->obj, if (PyObject_GetBuffer(view->obj, &newview, newflags) == -1)
&newview, newflags) == -1) return NULL;
return NULL; }
} else {
else { newview = *view;
newview = *view; }
} newview.buf = newbuf;
newview.buf = newbuf; newview.len = slicelength * newview.itemsize;
newview.len = slicelength; newview.format = view->format;
newview.format = view->format; newview.shape = &(newview.smalltable[0]);
if (view->shape == &(view->len)) newview.shape[0] = slicelength;
newview.shape = &(newview.len); newview.strides = &(newview.itemsize);
if (view->strides == &(view->itemsize)) return PyMemoryView_FromBuffer(&newview);
newview.strides = &(newview.itemsize); }
return PyMemoryView_FromBuffer(&newview); PyErr_SetNone(PyExc_NotImplementedError);
} return NULL;
PyErr_SetNone(PyExc_NotImplementedError);
return NULL;
} }
PyErr_Format(PyExc_TypeError, PyErr_Format(PyExc_TypeError,
"cannot index memory using \"%.200s\"", "cannot index memory using \"%.200s\"",
...@@ -747,7 +742,7 @@ memory_richcompare(PyObject *v, PyObject *w, int op) ...@@ -747,7 +742,7 @@ memory_richcompare(PyObject *v, PyObject *w, int op)
if (vv.itemsize != ww.itemsize || vv.len != ww.len) if (vv.itemsize != ww.itemsize || vv.len != ww.len)
goto _end; goto _end;
equal = !memcmp(vv.buf, ww.buf, vv.len * vv.itemsize); equal = !memcmp(vv.buf, ww.buf, vv.len);
_end: _end:
PyBuffer_Release(&vv); PyBuffer_Release(&vv);
......
...@@ -1205,7 +1205,7 @@ PyObject *PyUnicode_Decode(const char *s, ...@@ -1205,7 +1205,7 @@ PyObject *PyUnicode_Decode(const char *s,
/* Decode via the codec registry */ /* Decode via the codec registry */
buffer = NULL; buffer = NULL;
if (PyBuffer_FillInfo(&info, NULL, (void *)s, size, 1, PyBUF_SIMPLE) < 0) if (PyBuffer_FillInfo(&info, NULL, (void *)s, size, 1, PyBUF_FULL_RO) < 0)
goto onError; goto onError;
buffer = PyMemoryView_FromBuffer(&info); buffer = PyMemoryView_FromBuffer(&info);
if (buffer == NULL) if (buffer == NULL)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment