| |
| /* Memoryview object implementation */ |
| |
| #include "Python.h" |
| |
| #define IS_RELEASED(memobj) \ |
| (((PyMemoryViewObject *) memobj)->view.buf == NULL) |
| |
| #define CHECK_RELEASED(memobj) \ |
| if (IS_RELEASED(memobj)) { \ |
| PyErr_SetString(PyExc_ValueError, \ |
| "operation forbidden on released memoryview object"); \ |
| return NULL; \ |
| } |
| |
| #define CHECK_RELEASED_INT(memobj) \ |
| if (IS_RELEASED(memobj)) { \ |
| PyErr_SetString(PyExc_ValueError, \ |
| "operation forbidden on released memoryview object"); \ |
| return -1; \ |
| } |
| |
| 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; |
| CHECK_RELEASED_INT(self); |
| /* 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; |
| Py_buffer view; |
| |
| if (!PyObject_CheckBuffer(base)) { |
| PyErr_SetString(PyExc_TypeError, |
| "cannot make memory view because object does " |
| "not have the buffer interface"); |
| return NULL; |
| } |
| |
| if (PyObject_GetBuffer(base, &view, PyBUF_FULL_RO) < 0) |
| return NULL; |
| |
| mview = (PyMemoryViewObject *)PyMemoryView_FromBuffer(&view); |
| if (mview == NULL) { |
| PyBuffer_Release(&view); |
| return NULL; |
| } |
| |
| mview->base = base; |
| Py_INCREF(base); |
| 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; |
| } |
| |
| 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 *, const 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 = _Py_add_one_to_index_F; |
| } |
| else { |
| func = _Py_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 support 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) |
| { |
| CHECK_RELEASED(self); |
| return PyUnicode_FromString(self->view.format); |
| } |
| |
| static PyObject * |
| memory_itemsize_get(PyMemoryViewObject *self) |
| { |
| CHECK_RELEASED(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) |
| { |
| CHECK_RELEASED(self); |
| return _IntTupleFromSsizet(self->view.ndim, self->view.shape); |
| } |
| |
| static PyObject * |
| memory_strides_get(PyMemoryViewObject *self) |
| { |
| CHECK_RELEASED(self); |
| return _IntTupleFromSsizet(self->view.ndim, self->view.strides); |
| } |
| |
| static PyObject * |
| memory_suboffsets_get(PyMemoryViewObject *self) |
| { |
| CHECK_RELEASED(self); |
| return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets); |
| } |
| |
| static PyObject * |
| memory_readonly_get(PyMemoryViewObject *self) |
| { |
| CHECK_RELEASED(self); |
| return PyBool_FromLong(self->view.readonly); |
| } |
| |
| static PyObject * |
| memory_ndim_get(PyMemoryViewObject *self) |
| { |
| CHECK_RELEASED(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 *mem, PyObject *noargs) |
| { |
| CHECK_RELEASED(mem); |
| return PyObject_CallFunctionObjArgs( |
| (PyObject *) &PyBytes_Type, mem, NULL); |
| } |
| |
| /* 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; |
| |
| CHECK_RELEASED(mem); |
| 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 = PyLong_FromUnsignedLong((unsigned char) *buf); |
| if (item == NULL) { |
| Py_DECREF(res); |
| return NULL; |
| } |
| PyList_SET_ITEM(res, i, item); |
| buf++; |
| } |
| return res; |
| } |
| |
| static void |
| do_release(PyMemoryViewObject *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); |
| } |
| self->view.obj = NULL; |
| self->view.buf = NULL; |
| } |
| |
| static PyObject * |
| memory_enter(PyObject *self, PyObject *args) |
| { |
| CHECK_RELEASED(self); |
| Py_INCREF(self); |
| return self; |
| } |
| |
| static PyObject * |
| memory_exit(PyObject *self, PyObject *args) |
| { |
| do_release((PyMemoryViewObject *) self); |
| Py_RETURN_NONE; |
| } |
| |
| static PyMethodDef memory_methods[] = { |
| {"release", memory_exit, METH_NOARGS}, |
| {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL}, |
| {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL}, |
| {"__enter__", memory_enter, METH_NOARGS}, |
| {"__exit__", memory_exit, METH_VARARGS}, |
| {NULL, NULL} /* sentinel */ |
| }; |
| |
| |
| static void |
| memory_dealloc(PyMemoryViewObject *self) |
| { |
| _PyObject_GC_UNTRACK(self); |
| do_release(self); |
| PyObject_GC_Del(self); |
| } |
| |
| static PyObject * |
| memory_repr(PyMemoryViewObject *self) |
| { |
| if (IS_RELEASED(self)) |
| return PyUnicode_FromFormat("<released memory at %p>", self); |
| else |
| return PyUnicode_FromFormat("<memory at %p>", self); |
| } |
| |
| /* Sequence methods */ |
| static Py_ssize_t |
| memory_length(PyMemoryViewObject *self) |
| { |
| CHECK_RELEASED_INT(self); |
| return get_shape0(&self->view); |
| } |
| |
| /* Alternate version of memory_subcript that only accepts indices. |
| Used by PySeqIter_New(). |
| */ |
| static PyObject * |
| memory_item(PyMemoryViewObject *self, Py_ssize_t result) |
| { |
| Py_buffer *view = &(self->view); |
| |
| CHECK_RELEASED(self); |
| if (view->ndim == 0) { |
| PyErr_SetString(PyExc_IndexError, |
| "invalid indexing of 0-dim memory"); |
| 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); |
| } |
| } |
| |
| /* |
| 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); |
| |
| CHECK_RELEASED(self); |
| 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; |
| return memory_item(self, result); |
| } |
| 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; |
| Py_buffer srcview; |
| Py_buffer *view = &(self->view); |
| char *srcbuf, *destbuf; |
| |
| CHECK_RELEASED_INT(self); |
| if (view->readonly) { |
| PyErr_SetString(PyExc_TypeError, |
| "cannot modify read-only memory"); |
| return -1; |
| } |
| if (value == NULL) { |
| PyErr_SetString(PyExc_TypeError, |
| "cannot delete 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 |
| memmove(destbuf, srcbuf, bytelen); |
| |
| 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 ((PyMemoryView_Check(v) && IS_RELEASED(v)) || |
| (PyMemoryView_Check(w) && IS_RELEASED(w))) { |
| equal = (v == w); |
| goto _end; |
| } |
| 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 */ |
| }; |
| |
| static PySequenceMethods memory_as_sequence = { |
| 0, /* sq_length */ |
| 0, /* sq_concat */ |
| 0, /* sq_repeat */ |
| (ssizeargfunc)memory_item, /* sq_item */ |
| }; |
| |
| /* Buffer methods */ |
| |
| static PyBufferProcs memory_as_buffer = { |
| (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_reserved */ |
| (reprfunc)memory_repr, /* tp_repr */ |
| 0, /* tp_as_number */ |
| &memory_as_sequence, /* 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, /* 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 */ |
| }; |