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_Unpack(key, &start, &stop, &step) < 0) {
588             return NULL;
589         }
590         slicelength = _PySlice_AdjustIndices(get_shape0(view), &start, &stop,
591                                             step);
592 
593         if (step == 1 && view->ndim == 1) {
594             Py_buffer newview;
595             void *newbuf = (char *) view->buf
596                                     + start * view->itemsize;
597             int newflags = view->readonly
598                     ? PyBUF_CONTIG_RO : PyBUF_CONTIG;
599 
600             /* XXX There should be an API to create a subbuffer */
601             if (view->obj != NULL) {
602                 if (PyObject_GetBuffer(view->obj, &newview, newflags) == -1)
603                     return NULL;
604             }
605             else {
606                 newview = *view;
607             }
608             newview.buf = newbuf;
609             newview.len = slicelength * newview.itemsize;
610             newview.format = view->format;
611             newview.shape = &(newview.smalltable[0]);
612             newview.shape[0] = slicelength;
613             newview.strides = &(newview.itemsize);
614             return PyMemoryView_FromBuffer(&newview);
615         }
616         PyErr_SetNone(PyExc_NotImplementedError);
617         return NULL;
618     }
619     PyErr_Format(PyExc_TypeError,
620         "cannot index memory using \"%.200s\"",
621         key->ob_type->tp_name);
622     return NULL;
623 }
624 
625 
626 /* Need to support assigning memory if we can */
627 static int
memory_ass_sub(PyMemoryViewObject * self,PyObject * key,PyObject * value)628 memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
629 {
630     Py_ssize_t start, len, bytelen;
631     Py_buffer srcview;
632     Py_buffer *view = &(self->view);
633     char *srcbuf, *destbuf;
634 
635     if (view->readonly) {
636         PyErr_SetString(PyExc_TypeError,
637             "cannot modify read-only memory");
638         return -1;
639     }
640     if (value == NULL) {
641         PyErr_SetString(PyExc_TypeError,
642                         "cannot delete memory");
643         return -1;
644     }
645     if (view->ndim != 1) {
646         PyErr_SetNone(PyExc_NotImplementedError);
647         return -1;
648     }
649     if (PyIndex_Check(key)) {
650         start = PyNumber_AsSsize_t(key, NULL);
651         if (start == -1 && PyErr_Occurred())
652             return -1;
653         if (start < 0) {
654             start += get_shape0(view);
655         }
656         if ((start < 0) || (start >= get_shape0(view))) {
657             PyErr_SetString(PyExc_IndexError,
658                             "index out of bounds");
659             return -1;
660         }
661         len = 1;
662     }
663     else if (PySlice_Check(key)) {
664         Py_ssize_t stop, step;
665 
666         if (_PySlice_Unpack(key, &start, &stop, &step) < 0) {
667             return -1;
668         }
669         len = _PySlice_AdjustIndices(get_shape0(view), &start, &stop, step);
670         if (step != 1) {
671             PyErr_SetNone(PyExc_NotImplementedError);
672             return -1;
673         }
674     }
675     else {
676         PyErr_Format(PyExc_TypeError,
677             "cannot index memory using \"%.200s\"",
678             key->ob_type->tp_name);
679         return -1;
680     }
681     if (PyObject_GetBuffer(value, &srcview, PyBUF_CONTIG_RO) == -1) {
682         return -1;
683     }
684     /* XXX should we allow assignment of different item sizes
685        as long as the byte length is the same?
686        (e.g. assign 2 shorts to a 4-byte slice) */
687     if (srcview.itemsize != view->itemsize) {
688         PyErr_Format(PyExc_TypeError,
689             "mismatching item sizes for \"%.200s\" and \"%.200s\"",
690             view->obj->ob_type->tp_name, srcview.obj->ob_type->tp_name);
691         goto _error;
692     }
693     bytelen = len * view->itemsize;
694     if (bytelen != srcview.len) {
695         PyErr_SetString(PyExc_ValueError,
696             "cannot modify size of memoryview object");
697         goto _error;
698     }
699     /* Do the actual copy */
700     destbuf = (char *) view->buf + start * view->itemsize;
701     srcbuf = (char *) srcview.buf;
702     if (destbuf + bytelen < srcbuf || srcbuf + bytelen < destbuf)
703         /* No overlapping */
704         memcpy(destbuf, srcbuf, bytelen);
705     else
706         memmove(destbuf, srcbuf, bytelen);
707 
708     PyBuffer_Release(&srcview);
709     return 0;
710 
711 _error:
712     PyBuffer_Release(&srcview);
713     return -1;
714 }
715 
716 static PyObject *
memory_richcompare(PyObject * v,PyObject * w,int op)717 memory_richcompare(PyObject *v, PyObject *w, int op)
718 {
719     Py_buffer vv, ww;
720     int equal = 0;
721     PyObject *res;
722 
723     vv.obj = NULL;
724     ww.obj = NULL;
725     if (op != Py_EQ && op != Py_NE)
726         goto _notimpl;
727     if (PyObject_GetBuffer(v, &vv, PyBUF_CONTIG_RO) == -1) {
728         PyErr_Clear();
729         goto _notimpl;
730     }
731     if (PyObject_GetBuffer(w, &ww, PyBUF_CONTIG_RO) == -1) {
732         PyErr_Clear();
733         goto _notimpl;
734     }
735 
736     if (vv.itemsize != ww.itemsize || vv.len != ww.len)
737         goto _end;
738 
739     equal = !memcmp(vv.buf, ww.buf, vv.len);
740 
741 _end:
742     PyBuffer_Release(&vv);
743     PyBuffer_Release(&ww);
744     if ((equal && op == Py_EQ) || (!equal && op == Py_NE))
745         res = Py_True;
746     else
747         res = Py_False;
748     Py_INCREF(res);
749     return res;
750 
751 _notimpl:
752     PyBuffer_Release(&vv);
753     PyBuffer_Release(&ww);
754     Py_INCREF(Py_NotImplemented);
755     return Py_NotImplemented;
756 }
757 
758 
759 static int
memory_traverse(PyMemoryViewObject * self,visitproc visit,void * arg)760 memory_traverse(PyMemoryViewObject *self, visitproc visit, void *arg)
761 {
762     if (self->base != NULL)
763         Py_VISIT(self->base);
764     if (self->view.obj != NULL)
765         Py_VISIT(self->view.obj);
766     return 0;
767 }
768 
769 static int
memory_clear(PyMemoryViewObject * self)770 memory_clear(PyMemoryViewObject *self)
771 {
772     Py_CLEAR(self->base);
773     PyBuffer_Release(&self->view);
774     return 0;
775 }
776 
777 
778 /* As mapping */
779 static PyMappingMethods memory_as_mapping = {
780     (lenfunc)memory_length,               /* mp_length */
781     (binaryfunc)memory_subscript,         /* mp_subscript */
782     (objobjargproc)memory_ass_sub,        /* mp_ass_subscript */
783 };
784 
785 static PySequenceMethods memory_as_sequence = {
786 	0,                                  /* sq_length */
787 	0,                                  /* sq_concat */
788 	0,                                  /* sq_repeat */
789 	(ssizeargfunc)memory_item,          /* sq_item */
790 };
791 
792 /* Buffer methods */
793 static PyBufferProcs memory_as_buffer = {
794     0,                                    /* bf_getreadbuffer */
795     0,                                    /* bf_getwritebuffer */
796     0,                                    /* bf_getsegcount */
797     0,                                    /* bf_getcharbuffer */
798     (getbufferproc)memory_getbuf,         /* bf_getbuffer */
799     (releasebufferproc)memory_releasebuf, /* bf_releasebuffer */
800 };
801 
802 
803 PyTypeObject PyMemoryView_Type = {
804     PyVarObject_HEAD_INIT(&PyType_Type, 0)
805     "memoryview",
806     sizeof(PyMemoryViewObject),
807     0,
808     (destructor)memory_dealloc,               /* tp_dealloc */
809     0,                                        /* tp_print */
810     0,                                        /* tp_getattr */
811     0,                                        /* tp_setattr */
812     0,                                        /* tp_compare */
813     (reprfunc)memory_repr,                    /* tp_repr */
814     0,                                        /* tp_as_number */
815     &memory_as_sequence,                      /* tp_as_sequence */
816     &memory_as_mapping,                       /* tp_as_mapping */
817     0,                                        /* tp_hash */
818     0,                                        /* tp_call */
819     0,                                        /* tp_str */
820     PyObject_GenericGetAttr,                  /* tp_getattro */
821     0,                                        /* tp_setattro */
822     &memory_as_buffer,                        /* tp_as_buffer */
823     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
824         Py_TPFLAGS_HAVE_NEWBUFFER,            /* tp_flags */
825     memory_doc,                               /* tp_doc */
826     (traverseproc)memory_traverse,            /* tp_traverse */
827     (inquiry)memory_clear,                    /* tp_clear */
828     memory_richcompare,                       /* tp_richcompare */
829     0,                                        /* tp_weaklistoffset */
830     0,                                        /* tp_iter */
831     0,                                        /* tp_iternext */
832     memory_methods,                           /* tp_methods */
833     0,                                        /* tp_members */
834     memory_getsetlist,                        /* tp_getset */
835     0,                                        /* tp_base */
836     0,                                        /* tp_dict */
837     0,                                        /* tp_descr_get */
838     0,                                        /* tp_descr_set */
839     0,                                        /* tp_dictoffset */
840     0,                                        /* tp_init */
841     0,                                        /* tp_alloc */
842     memory_new,                               /* tp_new */
843 };
844