• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /* Memoryview object implementation */
3 
4 #include "Python.h"
5 
6 static Py_ssize_t
get_shape0(Py_buffer * buf)7 get_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 
19 static void
dup_buffer(Py_buffer * dest,Py_buffer * src)20 dup_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 
33 static int
memory_getbuf(PyMemoryViewObject * self,Py_buffer * view,int flags)34 memory_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 
44 static void
memory_releasebuf(PyMemoryViewObject * self,Py_buffer * view)45 memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view)
46 {
47     PyBuffer_Release(view);
48 }
49 
50 PyDoc_STRVAR(memory_doc,
51 "memoryview(object)\n\
52 \n\
53 Create a new memoryview object which references the given object.");
54 
55 PyObject *
PyMemoryView_FromBuffer(Py_buffer * info)56 PyMemoryView_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 
72 PyObject *
PyMemoryView_FromObject(PyObject * base)73 PyMemoryView_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 
99 static PyObject *
memory_new(PyTypeObject * subtype,PyObject * args,PyObject * kwds)100 memory_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 
114 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)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 
172 static int
_indirect_copy_nd(char * dest,Py_buffer * view,char fort)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 
239 PyObject *
PyMemoryView_GetContiguous(PyObject * obj,int buffertype,char fort)240 PyMemoryView_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 
327 static PyObject *
memory_format_get(PyMemoryViewObject * self)328 memory_format_get(PyMemoryViewObject *self)
329 {
330     return PyString_FromString(self->view.format);
331 }
332 
333 static PyObject *
memory_itemsize_get(PyMemoryViewObject * self)334 memory_itemsize_get(PyMemoryViewObject *self)
335 {
336     return PyLong_FromSsize_t(self->view.itemsize);
337 }
338 
339 static PyObject *
_IntTupleFromSsizet(int len,Py_ssize_t * vals)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 
363 static PyObject *
memory_shape_get(PyMemoryViewObject * self)364 memory_shape_get(PyMemoryViewObject *self)
365 {
366     return _IntTupleFromSsizet(self->view.ndim, self->view.shape);
367 }
368 
369 static PyObject *
memory_strides_get(PyMemoryViewObject * self)370 memory_strides_get(PyMemoryViewObject *self)
371 {
372     return _IntTupleFromSsizet(self->view.ndim, self->view.strides);
373 }
374 
375 static PyObject *
memory_suboffsets_get(PyMemoryViewObject * self)376 memory_suboffsets_get(PyMemoryViewObject *self)
377 {
378     return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets);
379 }
380 
381 static PyObject *
memory_readonly_get(PyMemoryViewObject * self)382 memory_readonly_get(PyMemoryViewObject *self)
383 {
384     return PyBool_FromLong(self->view.readonly);
385 }
386 
387 static PyObject *
memory_ndim_get(PyMemoryViewObject * self)388 memory_ndim_get(PyMemoryViewObject *self)
389 {
390     return PyLong_FromLong(self->view.ndim);
391 }
392 
393 static 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 
405 static PyObject *
memory_tobytes(PyMemoryViewObject * self,PyObject * noargs)406 memory_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 
423 static PyObject *
memory_tolist(PyMemoryViewObject * mem,PyObject * noargs)424 memory_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 
457 static 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 
464 static void
memory_dealloc(PyMemoryViewObject * self)465 memory_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 
495 static PyObject *
memory_repr(PyMemoryViewObject * self)496 memory_repr(PyMemoryViewObject *self)
497 {
498     return PyString_FromFormat("<memory at %p>", self);
499 }
500 
501 /* Sequence methods */
502 static Py_ssize_t
memory_length(PyMemoryViewObject * self)503 memory_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 */
511 static PyObject *
memory_item(PyMemoryViewObject * self,Py_ssize_t result)512 memory_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  */
559 static PyObject *
memory_subscript(PyMemoryViewObject * self,PyObject * key)560 memory_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 */
626 static int
memory_ass_sub(PyMemoryViewObject * self,PyObject * key,PyObject * value)627 memory_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 
715 static PyObject *
memory_richcompare(PyObject * v,PyObject * w,int op)716 memory_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 
758 static int
memory_traverse(PyMemoryViewObject * self,visitproc visit,void * arg)759 memory_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 
768 static int
memory_clear(PyMemoryViewObject * self)769 memory_clear(PyMemoryViewObject *self)
770 {
771     Py_CLEAR(self->base);
772     PyBuffer_Release(&self->view);
773     return 0;
774 }
775 
776 
777 /* As mapping */
778 static 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 
784 static 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 */
792 static 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 
802 PyTypeObject 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