|  | 
 | /* Memoryview object implementation */ | 
 |  | 
 | #include "Python.h" | 
 |  | 
 | static int | 
 | memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags) | 
 | { | 
 |         if (view != NULL) | 
 | 		*view = self->view; | 
 | 	if (self->base == NULL) | 
 | 		return 0; | 
 |         return self->base->ob_type->tp_as_buffer->bf_getbuffer(self->base, NULL, | 
 |                                                                PyBUF_FULL); | 
 | } | 
 |  | 
 | static void | 
 | memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view) | 
 | { | 
 | 	if (self->base != NULL) | 
 | 		PyObject_ReleaseBuffer(self->base, NULL); | 
 | } | 
 |  | 
 | PyDoc_STRVAR(memory_doc, | 
 | "memoryview(object)\n\ | 
 | \n\ | 
 | Create a new memoryview object which references the given object."); | 
 |  | 
 | PyObject * | 
 | PyMemoryView_FromMemory(Py_buffer *info) | 
 | { | 
 | 	PyMemoryViewObject *mview; | 
 |  | 
 | 	mview = (PyMemoryViewObject *)PyObject_New(PyMemoryViewObject, | 
 | 						   &PyMemoryView_Type); | 
 | 	if (mview == NULL) return NULL; | 
 | 	mview->base = NULL; | 
 | 	mview->view = *info; | 
 | 	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_New(PyMemoryViewObject, | 
 |                                                    &PyMemoryView_Type); | 
 |         if (mview == NULL) return NULL; | 
 |  | 
 |         mview->base = NULL; | 
 |         if (PyObject_GetBuffer(base, &(mview->view), PyBUF_FULL) < 0) { | 
 |                 Py_DECREF(mview); | 
 |                 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; | 
 | } | 
 |  | 
 | 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 *); | 
 |  | 
 |  | 
 |         /* XXX(nnorwitz): need to check for overflow! */ | 
 |         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_New(PyMemoryViewObject, &PyMemoryView_Type); | 
 |         if (mem == NULL) return NULL; | 
 |  | 
 |         view = &PyMemoryView(mem); | 
 |         flags = PyBUF_FULL_RO; | 
 |         switch(buffertype) { | 
 |         case PyBUF_WRITE: | 
 |                 flags = PyBUF_FULL; | 
 |                 break; | 
 |         case PyBUF_SHADOW: | 
 |                 flags = PyBUF_FULL_XLCK; | 
 |                 break; | 
 |         } | 
 |  | 
 |         if (PyObject_GetBuffer(obj, view, flags) != 0) { | 
 |                 PyObject_DEL(mem); | 
 |                 return NULL; | 
 |         } | 
 |  | 
 |         if (PyBuffer_IsContiguous(view, fort)) { | 
 |                 /* no copy needed */ | 
 |                 Py_INCREF(obj); | 
 |                 mem->base = obj; | 
 |                 return (PyObject *)mem; | 
 |         } | 
 |         /* otherwise a copy is needed */ | 
 |         if (buffertype == PyBUF_WRITE) { | 
 |                 PyObject_DEL(mem); | 
 |                 PyErr_SetString(PyExc_BufferError, | 
 |                                 "writable contiguous buffer requested " | 
 |                                 "for a non-contiguousobject."); | 
 |                 return NULL; | 
 |         } | 
 |         bytes = PyBytes_FromStringAndSize(NULL, view->len); | 
 |         if (bytes == NULL) { | 
 |                 PyObject_ReleaseBuffer(obj, view); | 
 |                 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); | 
 |                         PyObject_ReleaseBuffer(obj, view); | 
 |                         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) { | 
 | 			PyObject_ReleaseBuffer(obj, view); | 
 | 			return NULL; | 
 | 		} | 
 |         } | 
 |         else { | 
 |                 PyObject_ReleaseBuffer(obj, view); | 
 |                 /* steal the reference */ | 
 |                 mem->base = bytes; | 
 |         } | 
 |         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_size_get(PyMemoryViewObject *self) | 
 | { | 
 |         return PyLong_FromSsize_t(self->view.len); | 
 | } | 
 |  | 
 | 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}, | 
 |         {"size",	(getter)memory_size_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) | 
 | { | 
 |         return PyBytes_FromObject((PyObject *)mem); | 
 | } | 
 |  | 
 | static PyObject * | 
 | memory_tolist(PyMemoryViewObject *mem, PyObject *noargs) | 
 | { | 
 | 	/* This should construct a (nested) list of unpacked objects | 
 | 	   possibly using the struct module. | 
 | 	 */ | 
 |         Py_INCREF(Py_NotImplemented); | 
 |         return Py_NotImplemented; | 
 | } | 
 |  | 
 |  | 
 |  | 
 | 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) | 
 | { | 
 |         if (self->base != NULL) { | 
 |             if (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. | 
 |                 */ | 
 |                 PyObject_ReleaseBuffer(PyTuple_GET_ITEM(self->base,0), | 
 |                                        &(self->view)); | 
 |             } | 
 |             else { | 
 |                 PyObject_ReleaseBuffer(self->base, &(self->view)); | 
 |             } | 
 |             Py_CLEAR(self->base); | 
 |         } | 
 |         PyObject_DEL(self); | 
 | } | 
 |  | 
 | static PyObject * | 
 | memory_repr(PyMemoryViewObject *self) | 
 | { | 
 | 	return PyUnicode_FromFormat("<memory at %p>", self); | 
 | } | 
 |  | 
 |  | 
 | static PyObject * | 
 | memory_str(PyMemoryViewObject *self) | 
 | { | 
 |         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'); | 
 |         PyObject_ReleaseBuffer((PyObject *)self, &view); | 
 |         return res; | 
 | } | 
 |  | 
 | /* Sequence methods */ | 
 |  | 
 | static Py_ssize_t | 
 | memory_length(PyMemoryViewObject *self) | 
 | { | 
 |         Py_buffer view; | 
 |  | 
 |         if (PyObject_GetBuffer((PyObject *)self, &view, PyBUF_FULL) < 0) | 
 |                 return -1; | 
 |         PyObject_ReleaseBuffer((PyObject *)self, &view); | 
 | 	return view.len; | 
 | } | 
 |  | 
 | /* | 
 |   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 += view->shape[0]; | 
 | 			} | 
 | 			if ((result < 0) || (result > view->shape[0])) { | 
 | 				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_FromMemory(&newview); | 
 | 		} | 
 | 	} | 
 |  | 
 | 	/* Need to support getting a sliced view */ | 
 |         Py_INCREF(Py_NotImplemented); | 
 |         return Py_NotImplemented; | 
 | } | 
 |  | 
 |  | 
 | /* Need to support assigning memory if we can */ | 
 | static int | 
 | memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value) | 
 | { | 
 |         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 = { | 
 | 	(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 */ | 
 | 	(reprfunc)memory_str,			/* tp_str */ | 
 | 	PyObject_GenericGetAttr,		/* tp_getattro */ | 
 | 	0,					/* tp_setattro */ | 
 | 	&memory_as_buffer,			/* tp_as_buffer */ | 
 | 	Py_TPFLAGS_DEFAULT,			/* tp_flags */ | 
 | 	memory_doc,				/* tp_doc */ | 
 | 	0,					/* tp_traverse */ | 
 | 	0,					/* tp_clear */ | 
 | 	0,		                	/* 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 */ | 
 | }; |