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