summaryrefslogtreecommitdiffstats
path: root/Objects/memoryobject.c
diff options
context:
space:
mode:
authorAntoine Pitrou <solipsis@pitrou.net>2009-04-02 21:18:34 (GMT)
committerAntoine Pitrou <solipsis@pitrou.net>2009-04-02 21:18:34 (GMT)
commit789be0c0a0656d17f831aa781cf7c5d55e5b4835 (patch)
tree8ce796bd726fcbbb4c9f19ae2d3601517ebbee91 /Objects/memoryobject.c
parent48e3fd240fc6338fbcbbc9c1c8a7d118faca924a (diff)
downloadcpython-789be0c0a0656d17f831aa781cf7c5d55e5b4835.zip
cpython-789be0c0a0656d17f831aa781cf7c5d55e5b4835.tar.gz
cpython-789be0c0a0656d17f831aa781cf7c5d55e5b4835.tar.bz2
Issue #2396: backport the memoryview object.
Diffstat (limited to 'Objects/memoryobject.c')
-rw-r--r--Objects/memoryobject.c834
1 files changed, 834 insertions, 0 deletions
diff --git a/Objects/memoryobject.c b/Objects/memoryobject.c
new file mode 100644
index 0000000..1828cef
--- /dev/null
+++ b/Objects/memoryobject.c
@@ -0,0 +1,834 @@
+
+/* Memoryview object implementation */
+
+#include "Python.h"
+
+static Py_ssize_t
+get_shape0(Py_buffer *buf)
+{
+ if (buf->shape != NULL)
+ return buf->shape[0];
+ if (buf->ndim == 0)
+ 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
+memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
+{
+ int res = 0;
+ /* XXX for whatever reason fixing the flags seems necessary */
+ if (self->view.readonly)
+ flags &= ~PyBUF_WRITABLE;
+ if (self->view.obj != NULL)
+ res = PyObject_GetBuffer(self->view.obj, view, flags);
+ if (view)
+ dup_buffer(view, &self->view);
+ return res;
+}
+
+static void
+memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view)
+{
+ PyBuffer_Release(view);
+}
+
+PyDoc_STRVAR(memory_doc,
+"memoryview(object)\n\
+\n\
+Create a new memoryview object which references the given object.");
+
+PyObject *
+PyMemoryView_FromBuffer(Py_buffer *info)
+{
+ PyMemoryViewObject *mview;
+
+ mview = (PyMemoryViewObject *)
+ PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
+ if (mview == NULL)
+ return NULL;
+ mview->base = NULL;
+ dup_buffer(&mview->view, info);
+ /* NOTE: mview->view.obj should already have been incref'ed as
+ part of PyBuffer_FillInfo(). */
+ _PyObject_GC_TRACK(mview);
+ return (PyObject *)mview;
+}
+
+PyObject *
+PyMemoryView_FromObject(PyObject *base)
+{
+ PyMemoryViewObject *mview;
+
+ if (!PyObject_CheckBuffer(base)) {
+ PyErr_SetString(PyExc_TypeError,
+ "cannot make memory view because object does "
+ "not have the buffer interface");
+ return NULL;
+ }
+
+ mview = (PyMemoryViewObject *)
+ PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
+ if (mview == NULL)
+ return NULL;
+
+ mview->base = NULL;
+ if (PyObject_GetBuffer(base, &(mview->view), PyBUF_FULL_RO) < 0) {
+ Py_DECREF(mview);
+ return NULL;
+ }
+
+ mview->base = base;
+ Py_INCREF(base);
+ _PyObject_GC_TRACK(mview);
+ return (PyObject *)mview;
+}
+
+static PyObject *
+memory_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
+{
+ PyObject *obj;
+ static char *kwlist[] = {"object", 0};
+
+ if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:memoryview", kwlist,
+ &obj)) {
+ return NULL;
+ }
+
+ return PyMemoryView_FromObject(obj);
+}
+
+
+static void
+_strided_copy_nd(char *dest, char *src, int nd, Py_ssize_t *shape,
+ Py_ssize_t *strides, Py_ssize_t itemsize, char fort)
+{
+ int k;
+ Py_ssize_t outstride;
+
+ if (nd==0) {
+ memcpy(dest, src, itemsize);
+ }
+ else if (nd == 1) {
+ for (k = 0; k<shape[0]; k++) {
+ memcpy(dest, src, itemsize);
+ dest += itemsize;
+ src += strides[0];
+ }
+ }
+ else {
+ if (fort == 'F') {
+ /* Copy first dimension first,
+ second dimension second, etc...
+ Set up the recursive loop backwards so that final
+ dimension is actually copied last.
+ */
+ outstride = itemsize;
+ for (k=1; k<nd-1;k++) {
+ outstride *= shape[k];
+ }
+ for (k=0; k<shape[nd-1]; k++) {
+ _strided_copy_nd(dest, src, nd-1, shape,
+ strides, itemsize, fort);
+ dest += outstride;
+ src += strides[nd-1];
+ }
+ }
+
+ else {
+ /* Copy last dimension first,
+ second-to-last dimension second, etc.
+ Set up the recursion so that the
+ first dimension is copied last
+ */
+ outstride = itemsize;
+ for (k=1; k < nd; k++) {
+ outstride *= shape[k];
+ }
+ for (k=0; k<shape[0]; k++) {
+ _strided_copy_nd(dest, src, nd-1, shape+1,
+ strides+1, itemsize,
+ fort);
+ dest += outstride;
+ src += strides[0];
+ }
+ }
+ }
+ return;
+}
+
+void _add_one_to_index_F(int nd, Py_ssize_t *index, Py_ssize_t *shape);
+void _add_one_to_index_C(int nd, Py_ssize_t *index, Py_ssize_t *shape);
+
+static int
+_indirect_copy_nd(char *dest, Py_buffer *view, char fort)
+{
+ Py_ssize_t *indices;
+ int k;
+ Py_ssize_t elements;
+ char *ptr;
+ void (*func)(int, Py_ssize_t *, Py_ssize_t *);
+
+ if (view->ndim > PY_SSIZE_T_MAX / sizeof(Py_ssize_t)) {
+ PyErr_NoMemory();
+ return -1;
+ }
+
+ indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*view->ndim);
+ if (indices == NULL) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ for (k=0; k<view->ndim;k++) {
+ indices[k] = 0;
+ }
+
+ elements = 1;
+ for (k=0; k<view->ndim; k++) {
+ elements *= view->shape[k];
+ }
+ if (fort == 'F') {
+ func = _add_one_to_index_F;
+ }
+ else {
+ func = _add_one_to_index_C;
+ }
+ while (elements--) {
+ func(view->ndim, indices, view->shape);
+ ptr = PyBuffer_GetPointer(view, indices);
+ memcpy(dest, ptr, view->itemsize);
+ dest += view->itemsize;
+ }
+
+ PyMem_Free(indices);
+ return 0;
+}
+
+/*
+ Get a the data from an object as a contiguous chunk of memory (in
+ either 'C' or 'F'ortran order) even if it means copying it into a
+ separate memory area.
+
+ Returns a new reference to a Memory view object. If no copy is needed,
+ the memory view object points to the original memory and holds a
+ lock on the original. If a copy is needed, then the memory view object
+ points to a brand-new Bytes object (and holds a memory lock on it).
+
+ buffertype
+
+ PyBUF_READ buffer only needs to be read-only
+ PyBUF_WRITE buffer needs to be writable (give error if not contiguous)
+ PyBUF_SHADOW buffer needs to be writable so shadow it with
+ a contiguous buffer if it is not. The view will point to
+ the shadow buffer which can be written to and then
+ will be copied back into the other buffer when the memory
+ view is de-allocated. While the shadow buffer is
+ being used, it will have an exclusive write lock on
+ the original buffer.
+ */
+
+PyObject *
+PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char fort)
+{
+ PyMemoryViewObject *mem;
+ PyObject *bytes;
+ Py_buffer *view;
+ int flags;
+ char *dest;
+
+ if (!PyObject_CheckBuffer(obj)) {
+ PyErr_SetString(PyExc_TypeError,
+ "object does not have the buffer interface");
+ return NULL;
+ }
+
+ mem = PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
+ if (mem == NULL)
+ return NULL;
+
+ view = &mem->view;
+ flags = PyBUF_FULL_RO;
+ switch(buffertype) {
+ case PyBUF_WRITE:
+ flags = PyBUF_FULL;
+ break;
+ }
+
+ if (PyObject_GetBuffer(obj, view, flags) != 0) {
+ Py_DECREF(mem);
+ return NULL;
+ }
+
+ if (PyBuffer_IsContiguous(view, fort)) {
+ /* no copy needed */
+ Py_INCREF(obj);
+ mem->base = obj;
+ _PyObject_GC_TRACK(mem);
+ return (PyObject *)mem;
+ }
+ /* otherwise a copy is needed */
+ if (buffertype == PyBUF_WRITE) {
+ Py_DECREF(mem);
+ PyErr_SetString(PyExc_BufferError,
+ "writable contiguous buffer requested "
+ "for a non-contiguousobject.");
+ return NULL;
+ }
+ bytes = PyBytes_FromStringAndSize(NULL, view->len);
+ if (bytes == NULL) {
+ Py_DECREF(mem);
+ return NULL;
+ }
+ dest = PyBytes_AS_STRING(bytes);
+ /* different copying strategy depending on whether
+ or not any pointer de-referencing is needed
+ */
+ /* strided or in-direct copy */
+ if (view->suboffsets==NULL) {
+ _strided_copy_nd(dest, view->buf, view->ndim, view->shape,
+ view->strides, view->itemsize, fort);
+ }
+ else {
+ if (_indirect_copy_nd(dest, view, fort) < 0) {
+ Py_DECREF(bytes);
+ Py_DECREF(mem);
+ return NULL;
+ }
+ }
+ if (buffertype == PyBUF_SHADOW) {
+ /* return a shadowed memory-view object */
+ view->buf = dest;
+ mem->base = PyTuple_Pack(2, obj, bytes);
+ Py_DECREF(bytes);
+ if (mem->base == NULL) {
+ Py_DECREF(mem);
+ return NULL;
+ }
+ }
+ else {
+ PyBuffer_Release(view); /* XXX ? */
+ /* steal the reference */
+ mem->base = bytes;
+ }
+ _PyObject_GC_TRACK(mem);
+ return (PyObject *)mem;
+}
+
+
+static PyObject *
+memory_format_get(PyMemoryViewObject *self)
+{
+ return PyUnicode_FromString(self->view.format);
+}
+
+static PyObject *
+memory_itemsize_get(PyMemoryViewObject *self)
+{
+ return PyLong_FromSsize_t(self->view.itemsize);
+}
+
+static PyObject *
+_IntTupleFromSsizet(int len, Py_ssize_t *vals)
+{
+ int i;
+ PyObject *o;
+ PyObject *intTuple;
+
+ if (vals == NULL) {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+ intTuple = PyTuple_New(len);
+ if (!intTuple) return NULL;
+ for(i=0; i<len; i++) {
+ o = PyLong_FromSsize_t(vals[i]);
+ if (!o) {
+ Py_DECREF(intTuple);
+ return NULL;
+ }
+ PyTuple_SET_ITEM(intTuple, i, o);
+ }
+ return intTuple;
+}
+
+static PyObject *
+memory_shape_get(PyMemoryViewObject *self)
+{
+ return _IntTupleFromSsizet(self->view.ndim, self->view.shape);
+}
+
+static PyObject *
+memory_strides_get(PyMemoryViewObject *self)
+{
+ return _IntTupleFromSsizet(self->view.ndim, self->view.strides);
+}
+
+static PyObject *
+memory_suboffsets_get(PyMemoryViewObject *self)
+{
+ return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets);
+}
+
+static PyObject *
+memory_readonly_get(PyMemoryViewObject *self)
+{
+ return PyBool_FromLong(self->view.readonly);
+}
+
+static PyObject *
+memory_ndim_get(PyMemoryViewObject *self)
+{
+ return PyLong_FromLong(self->view.ndim);
+}
+
+static PyGetSetDef memory_getsetlist[] ={
+ {"format", (getter)memory_format_get, NULL, NULL},
+ {"itemsize", (getter)memory_itemsize_get, NULL, NULL},
+ {"shape", (getter)memory_shape_get, NULL, NULL},
+ {"strides", (getter)memory_strides_get, NULL, NULL},
+ {"suboffsets", (getter)memory_suboffsets_get, NULL, NULL},
+ {"readonly", (getter)memory_readonly_get, NULL, NULL},
+ {"ndim", (getter)memory_ndim_get, NULL, NULL},
+ {NULL, NULL, NULL, NULL},
+};
+
+
+static PyObject *
+memory_tobytes(PyMemoryViewObject *self, PyObject *noargs)
+{
+ Py_buffer view;
+ PyObject *res;
+
+ if (PyObject_GetBuffer((PyObject *)self, &view, PyBUF_FULL) < 0)
+ return NULL;
+
+ res = PyBytes_FromStringAndSize(NULL, view.len);
+ PyBuffer_ToContiguous(PyBytes_AS_STRING(res), &view, view.len, 'C');
+ PyBuffer_Release(&view);
+ return res;
+}
+
+/* TODO: rewrite this function using the struct module to unpack
+ each buffer item */
+
+static PyObject *
+memory_tolist(PyMemoryViewObject *mem, PyObject *noargs)
+{
+ Py_buffer *view = &(mem->view);
+ Py_ssize_t i;
+ PyObject *res, *item;
+ char *buf;
+
+ if (strcmp(view->format, "B") || view->itemsize != 1) {
+ PyErr_SetString(PyExc_NotImplementedError,
+ "tolist() only supports byte views");
+ return NULL;
+ }
+ if (view->ndim != 1) {
+ PyErr_SetString(PyExc_NotImplementedError,
+ "tolist() only supports one-dimensional objects");
+ return NULL;
+ }
+ res = PyList_New(view->len);
+ if (res == NULL)
+ return NULL;
+ buf = view->buf;
+ for (i = 0; i < view->len; i++) {
+ item = PyInt_FromLong((unsigned char) *buf);
+ if (item == NULL) {
+ Py_DECREF(res);
+ return NULL;
+ }
+ PyList_SET_ITEM(res, i, item);
+ buf++;
+ }
+ return res;
+}
+
+static PyMethodDef memory_methods[] = {
+ {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL},
+ {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL},
+ {NULL, NULL} /* sentinel */
+};
+
+
+static void
+memory_dealloc(PyMemoryViewObject *self)
+{
+ _PyObject_GC_UNTRACK(self);
+ if (self->view.obj != NULL) {
+ if (self->base && PyTuple_Check(self->base)) {
+ /* Special case when first element is generic object
+ with buffer interface and the second element is a
+ contiguous "shadow" that must be copied back into
+ the data areay of the first tuple element before
+ releasing the buffer on the first element.
+ */
+
+ PyObject_CopyData(PyTuple_GET_ITEM(self->base,0),
+ PyTuple_GET_ITEM(self->base,1));
+
+ /* The view member should have readonly == -1 in
+ this instance indicating that the memory can
+ be "locked" and was locked and will be unlocked
+ again after this call.
+ */
+ PyBuffer_Release(&(self->view));
+ }
+ else {
+ PyBuffer_Release(&(self->view));
+ }
+ Py_CLEAR(self->base);
+ }
+ PyObject_GC_Del(self);
+}
+
+static PyObject *
+memory_repr(PyMemoryViewObject *self)
+{
+ return PyUnicode_FromFormat("<memory at %p>", self);
+}
+
+/* Sequence methods */
+static Py_ssize_t
+memory_length(PyMemoryViewObject *self)
+{
+ return get_shape0(&self->view);
+}
+
+/*
+ mem[obj] returns a bytes object holding the data for one element if
+ obj fully indexes the memory view or another memory-view object
+ if it does not.
+
+ 0-d memory-view objects can be referenced using ... or () but
+ not with anything else.
+ */
+static PyObject *
+memory_subscript(PyMemoryViewObject *self, PyObject *key)
+{
+ Py_buffer *view;
+ view = &(self->view);
+
+ if (view->ndim == 0) {
+ if (key == Py_Ellipsis ||
+ (PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
+ Py_INCREF(self);
+ return (PyObject *)self;
+ }
+ else {
+ PyErr_SetString(PyExc_IndexError,
+ "invalid indexing of 0-dim memory");
+ return NULL;
+ }
+ }
+ if (PyIndex_Check(key)) {
+ Py_ssize_t result;
+ result = PyNumber_AsSsize_t(key, NULL);
+ if (result == -1 && PyErr_Occurred())
+ return NULL;
+ if (view->ndim == 1) {
+ /* Return a bytes object */
+ char *ptr;
+ ptr = (char *)view->buf;
+ if (result < 0) {
+ result += get_shape0(view);
+ }
+ if ((result < 0) || (result >= get_shape0(view))) {
+ PyErr_SetString(PyExc_IndexError,
+ "index out of bounds");
+ return NULL;
+ }
+ if (view->strides == NULL)
+ ptr += view->itemsize * result;
+ else
+ ptr += view->strides[0] * result;
+ if (view->suboffsets != NULL &&
+ view->suboffsets[0] >= 0) {
+ ptr = *((char **)ptr) + view->suboffsets[0];
+ }
+ return PyBytes_FromStringAndSize(ptr, view->itemsize);
+ }
+ else {
+ /* Return a new memory-view object */
+ Py_buffer newview;
+ memset(&newview, 0, sizeof(newview));
+ /* XXX: This needs to be fixed so it
+ actually returns a sub-view
+ */
+ return PyMemoryView_FromBuffer(&newview);
+ }
+ }
+ else if (PySlice_Check(key)) {
+ Py_ssize_t start, stop, step, slicelength;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
+ &start, &stop, &step, &slicelength) < 0) {
+ return NULL;
+ }
+
+ if (step == 1 && view->ndim == 1) {
+ Py_buffer newview;
+ void *newbuf = (char *) view->buf
+ + start * view->itemsize;
+ int newflags = view->readonly
+ ? PyBUF_CONTIG_RO : PyBUF_CONTIG;
+
+ /* XXX There should be an API to create a subbuffer */
+ if (view->obj != NULL) {
+ if (PyObject_GetBuffer(view->obj, &newview, newflags) == -1)
+ return NULL;
+ }
+ else {
+ newview = *view;
+ }
+ newview.buf = newbuf;
+ newview.len = slicelength * newview.itemsize;
+ newview.format = view->format;
+ newview.shape = &(newview.smalltable[0]);
+ newview.shape[0] = slicelength;
+ newview.strides = &(newview.itemsize);
+ return PyMemoryView_FromBuffer(&newview);
+ }
+ PyErr_SetNone(PyExc_NotImplementedError);
+ return NULL;
+ }
+ PyErr_Format(PyExc_TypeError,
+ "cannot index memory using \"%.200s\"",
+ key->ob_type->tp_name);
+ return NULL;
+}
+
+
+/* Need to support assigning memory if we can */
+static int
+memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
+{
+ Py_ssize_t start, len, bytelen, i;
+ Py_buffer srcview;
+ Py_buffer *view = &(self->view);
+ char *srcbuf, *destbuf;
+
+ if (view->readonly) {
+ PyErr_SetString(PyExc_TypeError,
+ "cannot modify read-only memory");
+ return -1;
+ }
+ if (view->ndim != 1) {
+ PyErr_SetNone(PyExc_NotImplementedError);
+ return -1;
+ }
+ if (PyIndex_Check(key)) {
+ start = PyNumber_AsSsize_t(key, NULL);
+ if (start == -1 && PyErr_Occurred())
+ return -1;
+ if (start < 0) {
+ start += get_shape0(view);
+ }
+ if ((start < 0) || (start >= get_shape0(view))) {
+ PyErr_SetString(PyExc_IndexError,
+ "index out of bounds");
+ return -1;
+ }
+ len = 1;
+ }
+ else if (PySlice_Check(key)) {
+ Py_ssize_t stop, step;
+
+ if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
+ &start, &stop, &step, &len) < 0) {
+ return -1;
+ }
+ if (step != 1) {
+ PyErr_SetNone(PyExc_NotImplementedError);
+ return -1;
+ }
+ }
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "cannot index memory using \"%.200s\"",
+ key->ob_type->tp_name);
+ return -1;
+ }
+ if (PyObject_GetBuffer(value, &srcview, PyBUF_CONTIG_RO) == -1) {
+ return -1;
+ }
+ /* XXX should we allow assignment of different item sizes
+ as long as the byte length is the same?
+ (e.g. assign 2 shorts to a 4-byte slice) */
+ if (srcview.itemsize != view->itemsize) {
+ PyErr_Format(PyExc_TypeError,
+ "mismatching item sizes for \"%.200s\" and \"%.200s\"",
+ view->obj->ob_type->tp_name, srcview.obj->ob_type->tp_name);
+ goto _error;
+ }
+ bytelen = len * view->itemsize;
+ if (bytelen != srcview.len) {
+ PyErr_SetString(PyExc_ValueError,
+ "cannot modify size of memoryview object");
+ goto _error;
+ }
+ /* Do the actual copy */
+ destbuf = (char *) view->buf + start * view->itemsize;
+ srcbuf = (char *) srcview.buf;
+ if (destbuf + bytelen < srcbuf || srcbuf + bytelen < destbuf)
+ /* No overlapping */
+ memcpy(destbuf, srcbuf, bytelen);
+ else if (destbuf < srcbuf) {
+ /* Copy in ascending order */
+ for (i = 0; i < bytelen; i++)
+ destbuf[i] = srcbuf[i];
+ }
+ else {
+ /* Copy in descencing order */
+ for (i = bytelen - 1; i >= 0; i--)
+ destbuf[i] = srcbuf[i];
+ }
+
+ PyBuffer_Release(&srcview);
+ return 0;
+
+_error:
+ PyBuffer_Release(&srcview);
+ return -1;
+}
+
+static PyObject *
+memory_richcompare(PyObject *v, PyObject *w, int op)
+{
+ Py_buffer vv, ww;
+ int equal = 0;
+ PyObject *res;
+
+ vv.obj = NULL;
+ ww.obj = NULL;
+ if (op != Py_EQ && op != Py_NE)
+ goto _notimpl;
+ if (PyObject_GetBuffer(v, &vv, PyBUF_CONTIG_RO) == -1) {
+ PyErr_Clear();
+ goto _notimpl;
+ }
+ if (PyObject_GetBuffer(w, &ww, PyBUF_CONTIG_RO) == -1) {
+ PyErr_Clear();
+ goto _notimpl;
+ }
+
+ if (vv.itemsize != ww.itemsize || vv.len != ww.len)
+ goto _end;
+
+ equal = !memcmp(vv.buf, ww.buf, vv.len);
+
+_end:
+ PyBuffer_Release(&vv);
+ PyBuffer_Release(&ww);
+ if ((equal && op == Py_EQ) || (!equal && op == Py_NE))
+ res = Py_True;
+ else
+ res = Py_False;
+ Py_INCREF(res);
+ return res;
+
+_notimpl:
+ PyBuffer_Release(&vv);
+ PyBuffer_Release(&ww);
+ Py_INCREF(Py_NotImplemented);
+ return Py_NotImplemented;
+}
+
+
+static int
+memory_traverse(PyMemoryViewObject *self, visitproc visit, void *arg)
+{
+ if (self->base != NULL)
+ Py_VISIT(self->base);
+ if (self->view.obj != NULL)
+ Py_VISIT(self->view.obj);
+ return 0;
+}
+
+static int
+memory_clear(PyMemoryViewObject *self)
+{
+ Py_CLEAR(self->base);
+ PyBuffer_Release(&self->view);
+ return 0;
+}
+
+
+/* As mapping */
+static PyMappingMethods memory_as_mapping = {
+ (lenfunc)memory_length, /* mp_length */
+ (binaryfunc)memory_subscript, /* mp_subscript */
+ (objobjargproc)memory_ass_sub, /* mp_ass_subscript */
+};
+
+
+/* Buffer methods */
+static PyBufferProcs memory_as_buffer = {
+ 0, /* bf_getreadbuffer */
+ 0, /* bf_getwritebuffer */
+ 0, /* bf_getsegcount */
+ 0, /* bf_getcharbuffer */
+ (getbufferproc)memory_getbuf, /* bf_getbuffer */
+ (releasebufferproc)memory_releasebuf, /* bf_releasebuffer */
+};
+
+
+PyTypeObject PyMemoryView_Type = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "memoryview",
+ sizeof(PyMemoryViewObject),
+ 0,
+ (destructor)memory_dealloc, /* tp_dealloc */
+ 0, /* tp_print */
+ 0, /* tp_getattr */
+ 0, /* tp_setattr */
+ 0, /* tp_compare */
+ (reprfunc)memory_repr, /* tp_repr */
+ 0, /* tp_as_number */
+ 0, /* tp_as_sequence */
+ &memory_as_mapping, /* tp_as_mapping */
+ 0, /* tp_hash */
+ 0, /* tp_call */
+ 0, /* tp_str */
+ PyObject_GenericGetAttr, /* tp_getattro */
+ 0, /* tp_setattro */
+ &memory_as_buffer, /* tp_as_buffer */
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
+ Py_TPFLAGS_HAVE_NEWBUFFER, /* tp_flags */
+ memory_doc, /* tp_doc */
+ (traverseproc)memory_traverse, /* tp_traverse */
+ (inquiry)memory_clear, /* tp_clear */
+ memory_richcompare, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ memory_methods, /* tp_methods */
+ 0, /* tp_members */
+ memory_getsetlist, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ 0, /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ memory_new, /* tp_new */
+};