/* 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; | |
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 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 PyString_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_SIMPLE) < 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 PyString_FromFormat("<memory at %p>", self); | |
} | |
/* Sequence methods */ | |
static Py_ssize_t | |
memory_length(PyMemoryViewObject *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); | |
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); | |
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; | |
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 (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 = { | |
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 */ | |
&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 | | |
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 */ | |
}; |