memoryobject.c revision 1fcdba84be218fa2952315bd95e21339422df922
1
2/* Memoryview object implementation */
3
4#include "Python.h"
5
6static Py_ssize_t
7get_shape0(Py_buffer *buf)
8{
9    if (buf->shape != NULL)
10        return buf->shape[0];
11    if (buf->ndim == 0)
12        return 1;
13    PyErr_SetString(PyExc_TypeError,
14        "exported buffer does not have any shape information associated "
15        "to it");
16    return -1;
17}
18
19static void
20dup_buffer(Py_buffer *dest, Py_buffer *src)
21{
22    *dest = *src;
23    if (src->ndim == 1 && src->shape != NULL) {
24        dest->shape = &(dest->smalltable[0]);
25        dest->shape[0] = get_shape0(src);
26    }
27    if (src->ndim == 1 && src->strides != NULL) {
28        dest->strides = &(dest->smalltable[1]);
29        dest->strides[0] = src->strides[0];
30    }
31}
32
33static int
34memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
35{
36    int res = 0;
37    /* XXX for whatever reason fixing the flags seems necessary */
38    if (self->view.readonly)
39        flags &= ~PyBUF_WRITABLE;
40    if (self->view.obj != NULL)
41        res = PyObject_GetBuffer(self->view.obj, view, flags);
42    if (view)
43        dup_buffer(view, &self->view);
44    return res;
45}
46
47static void
48memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view)
49{
50    PyBuffer_Release(view);
51}
52
53PyDoc_STRVAR(memory_doc,
54"memoryview(object)\n\
55\n\
56Create a new memoryview object which references the given object.");
57
58PyObject *
59PyMemoryView_FromBuffer(Py_buffer *info)
60{
61    PyMemoryViewObject *mview;
62
63    mview = (PyMemoryViewObject *)
64        PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
65    if (mview == NULL)
66        return NULL;
67    mview->base = NULL;
68    dup_buffer(&mview->view, info);
69    /* NOTE: mview->view.obj should already have been incref'ed as
70       part of PyBuffer_FillInfo(). */
71    _PyObject_GC_TRACK(mview);
72    return (PyObject *)mview;
73}
74
75PyObject *
76PyMemoryView_FromObject(PyObject *base)
77{
78    PyMemoryViewObject *mview;
79    Py_buffer view;
80
81    if (!PyObject_CheckBuffer(base)) {
82        PyErr_SetString(PyExc_TypeError,
83            "cannot make memory view because object does "
84            "not have the buffer interface");
85        return NULL;
86    }
87
88    if (PyObject_GetBuffer(base, &view, PyBUF_FULL_RO) < 0)
89        return NULL;
90
91    mview = (PyMemoryViewObject *)PyMemoryView_FromBuffer(&view);
92    if (mview == NULL) {
93        PyBuffer_Release(&view);
94        return NULL;
95    }
96
97    mview->base = base;
98    Py_INCREF(base);
99    return (PyObject *)mview;
100}
101
102static PyObject *
103memory_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
104{
105    PyObject *obj;
106    static char *kwlist[] = {"object", 0};
107
108    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O:memoryview", kwlist,
109                                     &obj)) {
110        return NULL;
111    }
112
113    return PyMemoryView_FromObject(obj);
114}
115
116
117static void
118_strided_copy_nd(char *dest, char *src, int nd, Py_ssize_t *shape,
119                 Py_ssize_t *strides, Py_ssize_t itemsize, char fort)
120{
121    int k;
122    Py_ssize_t outstride;
123
124    if (nd==0) {
125        memcpy(dest, src, itemsize);
126    }
127    else if (nd == 1) {
128        for (k = 0; k<shape[0]; k++) {
129            memcpy(dest, src, itemsize);
130            dest += itemsize;
131            src += strides[0];
132        }
133    }
134    else {
135        if (fort == 'F') {
136            /* Copy first dimension first,
137               second dimension second, etc...
138               Set up the recursive loop backwards so that final
139               dimension is actually copied last.
140            */
141            outstride = itemsize;
142            for (k=1; k<nd-1;k++) {
143                outstride *= shape[k];
144            }
145            for (k=0; k<shape[nd-1]; k++) {
146                _strided_copy_nd(dest, src, nd-1, shape,
147                                 strides, itemsize, fort);
148                dest += outstride;
149                src += strides[nd-1];
150            }
151        }
152
153        else {
154            /* Copy last dimension first,
155               second-to-last dimension second, etc.
156               Set up the recursion so that the
157               first dimension is copied last
158            */
159            outstride = itemsize;
160            for (k=1; k < nd; k++) {
161                outstride *= shape[k];
162            }
163            for (k=0; k<shape[0]; k++) {
164                _strided_copy_nd(dest, src, nd-1, shape+1,
165                                 strides+1, itemsize,
166                                 fort);
167                dest += outstride;
168                src += strides[0];
169            }
170        }
171    }
172    return;
173}
174
175static int
176_indirect_copy_nd(char *dest, Py_buffer *view, char fort)
177{
178    Py_ssize_t *indices;
179    int k;
180    Py_ssize_t elements;
181    char *ptr;
182    void (*func)(int, Py_ssize_t *, Py_ssize_t *);
183
184    if (view->ndim > PY_SSIZE_T_MAX / sizeof(Py_ssize_t)) {
185        PyErr_NoMemory();
186        return -1;
187    }
188
189    indices = (Py_ssize_t *)PyMem_Malloc(sizeof(Py_ssize_t)*view->ndim);
190    if (indices == NULL) {
191        PyErr_NoMemory();
192        return -1;
193    }
194    for (k=0; k<view->ndim;k++) {
195        indices[k] = 0;
196    }
197
198    elements = 1;
199    for (k=0; k<view->ndim; k++) {
200        elements *= view->shape[k];
201    }
202    if (fort == 'F') {
203        func = _Py_add_one_to_index_F;
204    }
205    else {
206        func = _Py_add_one_to_index_C;
207    }
208    while (elements--) {
209        func(view->ndim, indices, view->shape);
210        ptr = PyBuffer_GetPointer(view, indices);
211        memcpy(dest, ptr, view->itemsize);
212        dest += view->itemsize;
213    }
214
215    PyMem_Free(indices);
216    return 0;
217}
218
219/*
220   Get a the data from an object as a contiguous chunk of memory (in
221   either 'C' or 'F'ortran order) even if it means copying it into a
222   separate memory area.
223
224   Returns a new reference to a Memory view object.  If no copy is needed,
225   the memory view object points to the original memory and holds a
226   lock on the original.  If a copy is needed, then the memory view object
227   points to a brand-new Bytes object (and holds a memory lock on it).
228
229   buffertype
230
231   PyBUF_READ  buffer only needs to be read-only
232   PyBUF_WRITE buffer needs to be writable (give error if not contiguous)
233   PyBUF_SHADOW buffer needs to be writable so shadow it with
234                a contiguous buffer if it is not. The view will point to
235                the shadow buffer which can be written to and then
236                will be copied back into the other buffer when the memory
237                view is de-allocated.  While the shadow buffer is
238                being used, it will have an exclusive write lock on
239                the original buffer.
240 */
241
242PyObject *
243PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char fort)
244{
245    PyMemoryViewObject *mem;
246    PyObject *bytes;
247    Py_buffer *view;
248    int flags;
249    char *dest;
250
251    if (!PyObject_CheckBuffer(obj)) {
252        PyErr_SetString(PyExc_TypeError,
253                        "object does not have the buffer interface");
254        return NULL;
255    }
256
257    mem = PyObject_GC_New(PyMemoryViewObject, &PyMemoryView_Type);
258    if (mem == NULL)
259        return NULL;
260
261    view = &mem->view;
262    flags = PyBUF_FULL_RO;
263    switch(buffertype) {
264    case PyBUF_WRITE:
265        flags = PyBUF_FULL;
266        break;
267    }
268
269    if (PyObject_GetBuffer(obj, view, flags) != 0) {
270        Py_DECREF(mem);
271        return NULL;
272    }
273
274    if (PyBuffer_IsContiguous(view, fort)) {
275        /* no copy needed */
276        Py_INCREF(obj);
277        mem->base = obj;
278        _PyObject_GC_TRACK(mem);
279        return (PyObject *)mem;
280    }
281    /* otherwise a copy is needed */
282    if (buffertype == PyBUF_WRITE) {
283        Py_DECREF(mem);
284        PyErr_SetString(PyExc_BufferError,
285                        "writable contiguous buffer requested "
286                        "for a non-contiguousobject.");
287        return NULL;
288    }
289    bytes = PyBytes_FromStringAndSize(NULL, view->len);
290    if (bytes == NULL) {
291        Py_DECREF(mem);
292        return NULL;
293    }
294    dest = PyBytes_AS_STRING(bytes);
295    /* different copying strategy depending on whether
296       or not any pointer de-referencing is needed
297    */
298    /* strided or in-direct copy */
299    if (view->suboffsets==NULL) {
300        _strided_copy_nd(dest, view->buf, view->ndim, view->shape,
301                         view->strides, view->itemsize, fort);
302    }
303    else {
304        if (_indirect_copy_nd(dest, view, fort) < 0) {
305            Py_DECREF(bytes);
306            Py_DECREF(mem);
307            return NULL;
308        }
309    }
310    if (buffertype == PyBUF_SHADOW) {
311        /* return a shadowed memory-view object */
312        view->buf = dest;
313        mem->base = PyTuple_Pack(2, obj, bytes);
314        Py_DECREF(bytes);
315        if (mem->base == NULL) {
316            Py_DECREF(mem);
317            return NULL;
318        }
319    }
320    else {
321        PyBuffer_Release(view);  /* XXX ? */
322        /* steal the reference */
323        mem->base = bytes;
324    }
325    _PyObject_GC_TRACK(mem);
326    return (PyObject *)mem;
327}
328
329
330static PyObject *
331memory_format_get(PyMemoryViewObject *self)
332{
333    return PyString_FromString(self->view.format);
334}
335
336static PyObject *
337memory_itemsize_get(PyMemoryViewObject *self)
338{
339    return PyLong_FromSsize_t(self->view.itemsize);
340}
341
342static PyObject *
343_IntTupleFromSsizet(int len, Py_ssize_t *vals)
344{
345    int i;
346    PyObject *o;
347    PyObject *intTuple;
348
349    if (vals == NULL) {
350        Py_INCREF(Py_None);
351        return Py_None;
352    }
353    intTuple = PyTuple_New(len);
354    if (!intTuple) return NULL;
355    for(i=0; i<len; i++) {
356        o = PyLong_FromSsize_t(vals[i]);
357        if (!o) {
358            Py_DECREF(intTuple);
359            return NULL;
360        }
361        PyTuple_SET_ITEM(intTuple, i, o);
362    }
363    return intTuple;
364}
365
366static PyObject *
367memory_shape_get(PyMemoryViewObject *self)
368{
369    return _IntTupleFromSsizet(self->view.ndim, self->view.shape);
370}
371
372static PyObject *
373memory_strides_get(PyMemoryViewObject *self)
374{
375    return _IntTupleFromSsizet(self->view.ndim, self->view.strides);
376}
377
378static PyObject *
379memory_suboffsets_get(PyMemoryViewObject *self)
380{
381    return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets);
382}
383
384static PyObject *
385memory_readonly_get(PyMemoryViewObject *self)
386{
387    return PyBool_FromLong(self->view.readonly);
388}
389
390static PyObject *
391memory_ndim_get(PyMemoryViewObject *self)
392{
393    return PyLong_FromLong(self->view.ndim);
394}
395
396static PyGetSetDef memory_getsetlist[] ={
397    {"format",          (getter)memory_format_get,      NULL, NULL},
398    {"itemsize",        (getter)memory_itemsize_get,    NULL, NULL},
399    {"shape",           (getter)memory_shape_get,       NULL, NULL},
400    {"strides",         (getter)memory_strides_get,     NULL, NULL},
401    {"suboffsets",      (getter)memory_suboffsets_get,  NULL, NULL},
402    {"readonly",        (getter)memory_readonly_get,    NULL, NULL},
403    {"ndim",            (getter)memory_ndim_get,        NULL, NULL},
404    {NULL, NULL, NULL, NULL},
405};
406
407
408static PyObject *
409memory_tobytes(PyMemoryViewObject *self, PyObject *noargs)
410{
411    Py_buffer view;
412    PyObject *res;
413
414    if (PyObject_GetBuffer((PyObject *)self, &view, PyBUF_FULL) < 0)
415        return NULL;
416
417    res = PyBytes_FromStringAndSize(NULL, view.len);
418    PyBuffer_ToContiguous(PyBytes_AS_STRING(res), &view, view.len, 'C');
419    PyBuffer_Release(&view);
420    return res;
421}
422
423/* TODO: rewrite this function using the struct module to unpack
424   each buffer item */
425
426static PyObject *
427memory_tolist(PyMemoryViewObject *mem, PyObject *noargs)
428{
429    Py_buffer *view = &(mem->view);
430    Py_ssize_t i;
431    PyObject *res, *item;
432    char *buf;
433
434    if (strcmp(view->format, "B") || view->itemsize != 1) {
435        PyErr_SetString(PyExc_NotImplementedError,
436                "tolist() only supports byte views");
437        return NULL;
438    }
439    if (view->ndim != 1) {
440        PyErr_SetString(PyExc_NotImplementedError,
441                "tolist() only supports one-dimensional objects");
442        return NULL;
443    }
444    res = PyList_New(view->len);
445    if (res == NULL)
446        return NULL;
447    buf = view->buf;
448    for (i = 0; i < view->len; i++) {
449        item = PyInt_FromLong((unsigned char) *buf);
450        if (item == NULL) {
451            Py_DECREF(res);
452            return NULL;
453        }
454        PyList_SET_ITEM(res, i, item);
455        buf++;
456    }
457    return res;
458}
459
460static PyMethodDef memory_methods[] = {
461    {"tobytes", (PyCFunction)memory_tobytes, METH_NOARGS, NULL},
462    {"tolist", (PyCFunction)memory_tolist, METH_NOARGS, NULL},
463    {NULL,          NULL}           /* sentinel */
464};
465
466
467static void
468memory_dealloc(PyMemoryViewObject *self)
469{
470    _PyObject_GC_UNTRACK(self);
471    if (self->view.obj != NULL) {
472        if (self->base && PyTuple_Check(self->base)) {
473            /* Special case when first element is generic object
474               with buffer interface and the second element is a
475               contiguous "shadow" that must be copied back into
476               the data areay of the first tuple element before
477               releasing the buffer on the first element.
478            */
479
480            PyObject_CopyData(PyTuple_GET_ITEM(self->base,0),
481                              PyTuple_GET_ITEM(self->base,1));
482
483            /* The view member should have readonly == -1 in
484               this instance indicating that the memory can
485               be "locked" and was locked and will be unlocked
486               again after this call.
487            */
488            PyBuffer_Release(&(self->view));
489        }
490        else {
491            PyBuffer_Release(&(self->view));
492        }
493        Py_CLEAR(self->base);
494    }
495    PyObject_GC_Del(self);
496}
497
498static PyObject *
499memory_repr(PyMemoryViewObject *self)
500{
501    return PyString_FromFormat("<memory at %p>", self);
502}
503
504/* Sequence methods */
505static Py_ssize_t
506memory_length(PyMemoryViewObject *self)
507{
508    return get_shape0(&self->view);
509}
510
511/* Alternate version of memory_subcript that only accepts indices.
512   Used by PySeqIter_New().
513*/
514static PyObject *
515memory_item(PyMemoryViewObject *self, Py_ssize_t result)
516{
517    Py_buffer *view = &(self->view);
518
519    if (view->ndim == 0) {
520        PyErr_SetString(PyExc_IndexError,
521                        "invalid indexing of 0-dim memory");
522        return NULL;
523    }
524    if (view->ndim == 1) {
525        /* Return a bytes object */
526        char *ptr;
527        ptr = (char *)view->buf;
528        if (result < 0) {
529            result += get_shape0(view);
530        }
531        if ((result < 0) || (result >= get_shape0(view))) {
532            PyErr_SetString(PyExc_IndexError,
533                                "index out of bounds");
534            return NULL;
535        }
536        if (view->strides == NULL)
537            ptr += view->itemsize * result;
538        else
539            ptr += view->strides[0] * result;
540        if (view->suboffsets != NULL &&
541            view->suboffsets[0] >= 0) {
542            ptr = *((char **)ptr) + view->suboffsets[0];
543        }
544        return PyBytes_FromStringAndSize(ptr, view->itemsize);
545    } else {
546        /* Return a new memory-view object */
547        Py_buffer newview;
548        memset(&newview, 0, sizeof(newview));
549        /* XXX:  This needs to be fixed so it actually returns a sub-view */
550        return PyMemoryView_FromBuffer(&newview);
551    }
552}
553
554/*
555  mem[obj] returns a bytes object holding the data for one element if
556           obj fully indexes the memory view or another memory-view object
557           if it does not.
558
559           0-d memory-view objects can be referenced using ... or () but
560           not with anything else.
561 */
562static PyObject *
563memory_subscript(PyMemoryViewObject *self, PyObject *key)
564{
565    Py_buffer *view;
566    view = &(self->view);
567
568    if (view->ndim == 0) {
569        if (key == Py_Ellipsis ||
570            (PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
571            Py_INCREF(self);
572            return (PyObject *)self;
573        }
574        else {
575            PyErr_SetString(PyExc_IndexError,
576                                "invalid indexing of 0-dim memory");
577            return NULL;
578        }
579    }
580    if (PyIndex_Check(key)) {
581        Py_ssize_t result;
582        result = PyNumber_AsSsize_t(key, NULL);
583        if (result == -1 && PyErr_Occurred())
584                return NULL;
585        return memory_item(self, result);
586    }
587    else if (PySlice_Check(key)) {
588        Py_ssize_t start, stop, step, slicelength;
589
590        if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
591                                 &start, &stop, &step, &slicelength) < 0) {
592            return NULL;
593        }
594
595        if (step == 1 && view->ndim == 1) {
596            Py_buffer newview;
597            void *newbuf = (char *) view->buf
598                                    + start * view->itemsize;
599            int newflags = view->readonly
600                    ? PyBUF_CONTIG_RO : PyBUF_CONTIG;
601
602            /* XXX There should be an API to create a subbuffer */
603            if (view->obj != NULL) {
604                if (PyObject_GetBuffer(view->obj, &newview, newflags) == -1)
605                    return NULL;
606            }
607            else {
608                newview = *view;
609            }
610            newview.buf = newbuf;
611            newview.len = slicelength * newview.itemsize;
612            newview.format = view->format;
613            newview.shape = &(newview.smalltable[0]);
614            newview.shape[0] = slicelength;
615            newview.strides = &(newview.itemsize);
616            return PyMemoryView_FromBuffer(&newview);
617        }
618        PyErr_SetNone(PyExc_NotImplementedError);
619        return NULL;
620    }
621    PyErr_Format(PyExc_TypeError,
622        "cannot index memory using \"%.200s\"",
623        key->ob_type->tp_name);
624    return NULL;
625}
626
627
628/* Need to support assigning memory if we can */
629static int
630memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
631{
632    Py_ssize_t start, len, bytelen;
633    Py_buffer srcview;
634    Py_buffer *view = &(self->view);
635    char *srcbuf, *destbuf;
636
637    if (view->readonly) {
638        PyErr_SetString(PyExc_TypeError,
639            "cannot modify read-only memory");
640        return -1;
641    }
642    if (view->ndim != 1) {
643        PyErr_SetNone(PyExc_NotImplementedError);
644        return -1;
645    }
646    if (PyIndex_Check(key)) {
647        start = PyNumber_AsSsize_t(key, NULL);
648        if (start == -1 && PyErr_Occurred())
649            return -1;
650        if (start < 0) {
651            start += get_shape0(view);
652        }
653        if ((start < 0) || (start >= get_shape0(view))) {
654            PyErr_SetString(PyExc_IndexError,
655                            "index out of bounds");
656            return -1;
657        }
658        len = 1;
659    }
660    else if (PySlice_Check(key)) {
661        Py_ssize_t stop, step;
662
663        if (PySlice_GetIndicesEx((PySliceObject*)key, get_shape0(view),
664                         &start, &stop, &step, &len) < 0) {
665            return -1;
666        }
667        if (step != 1) {
668            PyErr_SetNone(PyExc_NotImplementedError);
669            return -1;
670        }
671    }
672    else {
673        PyErr_Format(PyExc_TypeError,
674            "cannot index memory using \"%.200s\"",
675            key->ob_type->tp_name);
676        return -1;
677    }
678    if (PyObject_GetBuffer(value, &srcview, PyBUF_CONTIG_RO) == -1) {
679        return -1;
680    }
681    /* XXX should we allow assignment of different item sizes
682       as long as the byte length is the same?
683       (e.g. assign 2 shorts to a 4-byte slice) */
684    if (srcview.itemsize != view->itemsize) {
685        PyErr_Format(PyExc_TypeError,
686            "mismatching item sizes for \"%.200s\" and \"%.200s\"",
687            view->obj->ob_type->tp_name, srcview.obj->ob_type->tp_name);
688        goto _error;
689    }
690    bytelen = len * view->itemsize;
691    if (bytelen != srcview.len) {
692        PyErr_SetString(PyExc_ValueError,
693            "cannot modify size of memoryview object");
694        goto _error;
695    }
696    /* Do the actual copy */
697    destbuf = (char *) view->buf + start * view->itemsize;
698    srcbuf = (char *) srcview.buf;
699    if (destbuf + bytelen < srcbuf || srcbuf + bytelen < destbuf)
700        /* No overlapping */
701        memcpy(destbuf, srcbuf, bytelen);
702    else
703        memmove(destbuf, srcbuf, bytelen);
704
705    PyBuffer_Release(&srcview);
706    return 0;
707
708_error:
709    PyBuffer_Release(&srcview);
710    return -1;
711}
712
713static PyObject *
714memory_richcompare(PyObject *v, PyObject *w, int op)
715{
716    Py_buffer vv, ww;
717    int equal = 0;
718    PyObject *res;
719
720    vv.obj = NULL;
721    ww.obj = NULL;
722    if (op != Py_EQ && op != Py_NE)
723        goto _notimpl;
724    if (PyObject_GetBuffer(v, &vv, PyBUF_CONTIG_RO) == -1) {
725        PyErr_Clear();
726        goto _notimpl;
727    }
728    if (PyObject_GetBuffer(w, &ww, PyBUF_CONTIG_RO) == -1) {
729        PyErr_Clear();
730        goto _notimpl;
731    }
732
733    if (vv.itemsize != ww.itemsize || vv.len != ww.len)
734        goto _end;
735
736    equal = !memcmp(vv.buf, ww.buf, vv.len);
737
738_end:
739    PyBuffer_Release(&vv);
740    PyBuffer_Release(&ww);
741    if ((equal && op == Py_EQ) || (!equal && op == Py_NE))
742        res = Py_True;
743    else
744        res = Py_False;
745    Py_INCREF(res);
746    return res;
747
748_notimpl:
749    PyBuffer_Release(&vv);
750    PyBuffer_Release(&ww);
751    Py_INCREF(Py_NotImplemented);
752    return Py_NotImplemented;
753}
754
755
756static int
757memory_traverse(PyMemoryViewObject *self, visitproc visit, void *arg)
758{
759    if (self->base != NULL)
760        Py_VISIT(self->base);
761    if (self->view.obj != NULL)
762        Py_VISIT(self->view.obj);
763    return 0;
764}
765
766static int
767memory_clear(PyMemoryViewObject *self)
768{
769    Py_CLEAR(self->base);
770    PyBuffer_Release(&self->view);
771    return 0;
772}
773
774
775/* As mapping */
776static PyMappingMethods memory_as_mapping = {
777    (lenfunc)memory_length,               /* mp_length */
778    (binaryfunc)memory_subscript,         /* mp_subscript */
779    (objobjargproc)memory_ass_sub,        /* mp_ass_subscript */
780};
781
782static PySequenceMethods memory_as_sequence = {
783	0,                                  /* sq_length */
784	0,                                  /* sq_concat */
785	0,                                  /* sq_repeat */
786	(ssizeargfunc)memory_item,          /* sq_item */
787};
788
789/* Buffer methods */
790static PyBufferProcs memory_as_buffer = {
791    0,                                    /* bf_getreadbuffer */
792    0,                                    /* bf_getwritebuffer */
793    0,                                    /* bf_getsegcount */
794    0,                                    /* bf_getcharbuffer */
795    (getbufferproc)memory_getbuf,         /* bf_getbuffer */
796    (releasebufferproc)memory_releasebuf, /* bf_releasebuffer */
797};
798
799
800PyTypeObject PyMemoryView_Type = {
801    PyVarObject_HEAD_INIT(&PyType_Type, 0)
802    "memoryview",
803    sizeof(PyMemoryViewObject),
804    0,
805    (destructor)memory_dealloc,               /* tp_dealloc */
806    0,                                        /* tp_print */
807    0,                                        /* tp_getattr */
808    0,                                        /* tp_setattr */
809    0,                                        /* tp_compare */
810    (reprfunc)memory_repr,                    /* tp_repr */
811    0,                                        /* tp_as_number */
812    &memory_as_sequence,                      /* tp_as_sequence */
813    &memory_as_mapping,                       /* tp_as_mapping */
814    0,                                        /* tp_hash */
815    0,                                        /* tp_call */
816    0,                                        /* tp_str */
817    PyObject_GenericGetAttr,                  /* tp_getattro */
818    0,                                        /* tp_setattro */
819    &memory_as_buffer,                        /* tp_as_buffer */
820    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
821        Py_TPFLAGS_HAVE_NEWBUFFER,            /* tp_flags */
822    memory_doc,                               /* tp_doc */
823    (traverseproc)memory_traverse,            /* tp_traverse */
824    (inquiry)memory_clear,                    /* tp_clear */
825    memory_richcompare,                       /* tp_richcompare */
826    0,                                        /* tp_weaklistoffset */
827    0,                                        /* tp_iter */
828    0,                                        /* tp_iternext */
829    memory_methods,                           /* tp_methods */
830    0,                                        /* tp_members */
831    memory_getsetlist,                        /* tp_getset */
832    0,                                        /* tp_base */
833    0,                                        /* tp_dict */
834    0,                                        /* tp_descr_get */
835    0,                                        /* tp_descr_set */
836    0,                                        /* tp_dictoffset */
837    0,                                        /* tp_init */
838    0,                                        /* tp_alloc */
839    memory_new,                               /* tp_new */
840};
841