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