2 /* Memoryview object implementation */
7 get_shape0(Py_buffer
*buf
)
9 if (buf
->shape
!= NULL
)
13 PyErr_SetString(PyExc_TypeError
,
14 "exported buffer does not have any shape information associated "
20 dup_buffer(Py_buffer
*dest
, Py_buffer
*src
)
23 if (src
->ndim
== 1 && src
->shape
!= NULL
) {
24 dest
->shape
= &(dest
->smalltable
[0]);
25 dest
->shape
[0] = get_shape0(src
);
27 if (src
->ndim
== 1 && src
->strides
!= NULL
) {
28 dest
->strides
= &(dest
->smalltable
[1]);
29 dest
->strides
[0] = src
->strides
[0];
34 memory_getbuf(PyMemoryViewObject
*self
, Py_buffer
*view
, int flags
)
37 /* XXX for whatever reason fixing the flags seems necessary */
38 if (self
->view
.readonly
)
39 flags
&= ~PyBUF_WRITABLE
;
40 if (self
->view
.obj
!= NULL
)
41 res
= PyObject_GetBuffer(self
->view
.obj
, view
, flags
);
43 dup_buffer(view
, &self
->view
);
48 memory_releasebuf(PyMemoryViewObject
*self
, Py_buffer
*view
)
50 PyBuffer_Release(view
);
53 PyDoc_STRVAR(memory_doc
,
54 "memoryview(object)\n\
56 Create a new memoryview object which references the given object.");
59 PyMemoryView_FromBuffer(Py_buffer
*info
)
61 PyMemoryViewObject
*mview
;
63 mview
= (PyMemoryViewObject
*)
64 PyObject_GC_New(PyMemoryViewObject
, &PyMemoryView_Type
);
68 dup_buffer(&mview
->view
, info
);
69 /* NOTE: mview->view.obj should already have been incref'ed as
70 part of PyBuffer_FillInfo(). */
71 _PyObject_GC_TRACK(mview
);
72 return (PyObject
*)mview
;
76 PyMemoryView_FromObject(PyObject
*base
)
78 PyMemoryViewObject
*mview
;
81 if (!PyObject_CheckBuffer(base
)) {
82 PyErr_SetString(PyExc_TypeError
,
83 "cannot make memory view because object does "
84 "not have the buffer interface");
88 if (PyObject_GetBuffer(base
, &view
, PyBUF_FULL_RO
) < 0)
91 mview
= (PyMemoryViewObject
*)PyMemoryView_FromBuffer(&view
);
93 PyBuffer_Release(&view
);
99 return (PyObject
*)mview
;
103 memory_new(PyTypeObject
*subtype
, PyObject
*args
, PyObject
*kwds
)
106 static char *kwlist
[] = {"object", 0};
108 if (!PyArg_ParseTupleAndKeywords(args
, kwds
, "O:memoryview", kwlist
,
113 return PyMemoryView_FromObject(obj
);
118 _strided_copy_nd(char *dest
, char *src
, int nd
, Py_ssize_t
*shape
,
119 Py_ssize_t
*strides
, Py_ssize_t itemsize
, char fort
)
122 Py_ssize_t outstride
;
125 memcpy(dest
, src
, itemsize
);
128 for (k
= 0; k
<shape
[0]; k
++) {
129 memcpy(dest
, src
, itemsize
);
136 /* Copy first dimension first,
137 second dimension second, etc...
138 Set up the recursive loop backwards so that final
139 dimension is actually copied last.
141 outstride
= itemsize
;
142 for (k
=1; k
<nd
-1;k
++) {
143 outstride
*= shape
[k
];
145 for (k
=0; k
<shape
[nd
-1]; k
++) {
146 _strided_copy_nd(dest
, src
, nd
-1, shape
,
147 strides
, itemsize
, fort
);
149 src
+= strides
[nd
-1];
154 /* Copy last dimension first,
155 second-to-last dimension second, etc.
156 Set up the recursion so that the
157 first dimension is copied last
159 outstride
= itemsize
;
160 for (k
=1; k
< nd
; k
++) {
161 outstride
*= shape
[k
];
163 for (k
=0; k
<shape
[0]; k
++) {
164 _strided_copy_nd(dest
, src
, nd
-1, shape
+1,
175 void _add_one_to_index_F(int nd
, Py_ssize_t
*index
, Py_ssize_t
*shape
);
176 void _add_one_to_index_C(int nd
, Py_ssize_t
*index
, Py_ssize_t
*shape
);
179 _indirect_copy_nd(char *dest
, Py_buffer
*view
, char fort
)
185 void (*func
)(int, Py_ssize_t
*, Py_ssize_t
*);
187 if (view
->ndim
> PY_SSIZE_T_MAX
/ sizeof(Py_ssize_t
)) {
192 indices
= (Py_ssize_t
*)PyMem_Malloc(sizeof(Py_ssize_t
)*view
->ndim
);
193 if (indices
== NULL
) {
197 for (k
=0; k
<view
->ndim
;k
++) {
202 for (k
=0; k
<view
->ndim
; k
++) {
203 elements
*= view
->shape
[k
];
206 func
= _add_one_to_index_F
;
209 func
= _add_one_to_index_C
;
212 func(view
->ndim
, indices
, view
->shape
);
213 ptr
= PyBuffer_GetPointer(view
, indices
);
214 memcpy(dest
, ptr
, view
->itemsize
);
215 dest
+= view
->itemsize
;
223 Get a the data from an object as a contiguous chunk of memory (in
224 either 'C' or 'F'ortran order) even if it means copying it into a
225 separate memory area.
227 Returns a new reference to a Memory view object. If no copy is needed,
228 the memory view object points to the original memory and holds a
229 lock on the original. If a copy is needed, then the memory view object
230 points to a brand-new Bytes object (and holds a memory lock on it).
234 PyBUF_READ buffer only needs to be read-only
235 PyBUF_WRITE buffer needs to be writable (give error if not contiguous)
236 PyBUF_SHADOW buffer needs to be writable so shadow it with
237 a contiguous buffer if it is not. The view will point to
238 the shadow buffer which can be written to and then
239 will be copied back into the other buffer when the memory
240 view is de-allocated. While the shadow buffer is
241 being used, it will have an exclusive write lock on
246 PyMemoryView_GetContiguous(PyObject
*obj
, int buffertype
, char fort
)
248 PyMemoryViewObject
*mem
;
254 if (!PyObject_CheckBuffer(obj
)) {
255 PyErr_SetString(PyExc_TypeError
,
256 "object does not support the buffer interface");
260 mem
= PyObject_GC_New(PyMemoryViewObject
, &PyMemoryView_Type
);
265 flags
= PyBUF_FULL_RO
;
272 if (PyObject_GetBuffer(obj
, view
, flags
) != 0) {
277 if (PyBuffer_IsContiguous(view
, fort
)) {
281 _PyObject_GC_TRACK(mem
);
282 return (PyObject
*)mem
;
284 /* otherwise a copy is needed */
285 if (buffertype
== PyBUF_WRITE
) {
287 PyErr_SetString(PyExc_BufferError
,
288 "writable contiguous buffer requested "
289 "for a non-contiguousobject.");
292 bytes
= PyBytes_FromStringAndSize(NULL
, view
->len
);
297 dest
= PyBytes_AS_STRING(bytes
);
298 /* different copying strategy depending on whether
299 or not any pointer de-referencing is needed
301 /* strided or in-direct copy */
302 if (view
->suboffsets
==NULL
) {
303 _strided_copy_nd(dest
, view
->buf
, view
->ndim
, view
->shape
,
304 view
->strides
, view
->itemsize
, fort
);
307 if (_indirect_copy_nd(dest
, view
, fort
) < 0) {
313 if (buffertype
== PyBUF_SHADOW
) {
314 /* return a shadowed memory-view object */
316 mem
->base
= PyTuple_Pack(2, obj
, bytes
);
318 if (mem
->base
== NULL
) {
324 PyBuffer_Release(view
); /* XXX ? */
325 /* steal the reference */
328 _PyObject_GC_TRACK(mem
);
329 return (PyObject
*)mem
;
334 memory_format_get(PyMemoryViewObject
*self
)
336 return PyUnicode_FromString(self
->view
.format
);
340 memory_itemsize_get(PyMemoryViewObject
*self
)
342 return PyLong_FromSsize_t(self
->view
.itemsize
);
346 _IntTupleFromSsizet(int len
, Py_ssize_t
*vals
)
356 intTuple
= PyTuple_New(len
);
357 if (!intTuple
) return NULL
;
358 for(i
=0; i
<len
; i
++) {
359 o
= PyLong_FromSsize_t(vals
[i
]);
364 PyTuple_SET_ITEM(intTuple
, i
, o
);
370 memory_shape_get(PyMemoryViewObject
*self
)
372 return _IntTupleFromSsizet(self
->view
.ndim
, self
->view
.shape
);
376 memory_strides_get(PyMemoryViewObject
*self
)
378 return _IntTupleFromSsizet(self
->view
.ndim
, self
->view
.strides
);
382 memory_suboffsets_get(PyMemoryViewObject
*self
)
384 return _IntTupleFromSsizet(self
->view
.ndim
, self
->view
.suboffsets
);
388 memory_readonly_get(PyMemoryViewObject
*self
)
390 return PyBool_FromLong(self
->view
.readonly
);
394 memory_ndim_get(PyMemoryViewObject
*self
)
396 return PyLong_FromLong(self
->view
.ndim
);
399 static PyGetSetDef memory_getsetlist
[] ={
400 {"format", (getter
)memory_format_get
, NULL
, NULL
},
401 {"itemsize", (getter
)memory_itemsize_get
, NULL
, NULL
},
402 {"shape", (getter
)memory_shape_get
, NULL
, NULL
},
403 {"strides", (getter
)memory_strides_get
, NULL
, NULL
},
404 {"suboffsets", (getter
)memory_suboffsets_get
, NULL
, NULL
},
405 {"readonly", (getter
)memory_readonly_get
, NULL
, NULL
},
406 {"ndim", (getter
)memory_ndim_get
, NULL
, NULL
},
407 {NULL
, NULL
, NULL
, NULL
},
412 memory_tobytes(PyMemoryViewObject
*mem
, PyObject
*noargs
)
414 return PyObject_CallFunctionObjArgs(
415 (PyObject
*) &PyBytes_Type
, mem
, NULL
);
418 /* TODO: rewrite this function using the struct module to unpack
422 memory_tolist(PyMemoryViewObject
*mem
, PyObject
*noargs
)
424 Py_buffer
*view
= &(mem
->view
);
426 PyObject
*res
, *item
;
429 if (strcmp(view
->format
, "B") || view
->itemsize
!= 1) {
430 PyErr_SetString(PyExc_NotImplementedError
,
431 "tolist() only supports byte views");
434 if (view
->ndim
!= 1) {
435 PyErr_SetString(PyExc_NotImplementedError
,
436 "tolist() only supports one-dimensional objects");
439 res
= PyList_New(view
->len
);
443 for (i
= 0; i
< view
->len
; i
++) {
444 item
= PyLong_FromUnsignedLong((unsigned char) *buf
);
449 PyList_SET_ITEM(res
, i
, item
);
455 static PyMethodDef memory_methods
[] = {
456 {"tobytes", (PyCFunction
)memory_tobytes
, METH_NOARGS
, NULL
},
457 {"tolist", (PyCFunction
)memory_tolist
, METH_NOARGS
, NULL
},
458 {NULL
, NULL
} /* sentinel */
463 memory_dealloc(PyMemoryViewObject
*self
)
465 _PyObject_GC_UNTRACK(self
);
466 if (self
->view
.obj
!= NULL
) {
467 if (self
->base
&& PyTuple_Check(self
->base
)) {
468 /* Special case when first element is generic object
469 with buffer interface and the second element is a
470 contiguous "shadow" that must be copied back into
471 the data areay of the first tuple element before
472 releasing the buffer on the first element.
475 PyObject_CopyData(PyTuple_GET_ITEM(self
->base
,0),
476 PyTuple_GET_ITEM(self
->base
,1));
478 /* The view member should have readonly == -1 in
479 this instance indicating that the memory can
480 be "locked" and was locked and will be unlocked
481 again after this call.
483 PyBuffer_Release(&(self
->view
));
486 PyBuffer_Release(&(self
->view
));
488 Py_CLEAR(self
->base
);
490 PyObject_GC_Del(self
);
494 memory_repr(PyMemoryViewObject
*self
)
496 return PyUnicode_FromFormat("<memory at %p>", self
);
499 /* Sequence methods */
501 memory_length(PyMemoryViewObject
*self
)
503 return get_shape0(&self
->view
);
506 /* Alternate version of memory_subcript that only accepts indices.
507 Used by PySeqIter_New().
510 memory_item(PyMemoryViewObject
*self
, Py_ssize_t result
)
512 Py_buffer
*view
= &(self
->view
);
514 if (view
->ndim
== 0) {
515 PyErr_SetString(PyExc_IndexError
,
516 "invalid indexing of 0-dim memory");
519 if (view
->ndim
== 1) {
520 /* Return a bytes object */
522 ptr
= (char *)view
->buf
;
524 result
+= get_shape0(view
);
526 if ((result
< 0) || (result
>= get_shape0(view
))) {
527 PyErr_SetString(PyExc_IndexError
,
528 "index out of bounds");
531 if (view
->strides
== NULL
)
532 ptr
+= view
->itemsize
* result
;
534 ptr
+= view
->strides
[0] * result
;
535 if (view
->suboffsets
!= NULL
&&
536 view
->suboffsets
[0] >= 0) {
537 ptr
= *((char **)ptr
) + view
->suboffsets
[0];
539 return PyBytes_FromStringAndSize(ptr
, view
->itemsize
);
541 /* Return a new memory-view object */
543 memset(&newview
, 0, sizeof(newview
));
544 /* XXX: This needs to be fixed so it actually returns a sub-view */
545 return PyMemoryView_FromBuffer(&newview
);
550 mem[obj] returns a bytes object holding the data for one element if
551 obj fully indexes the memory view or another memory-view object
554 0-d memory-view objects can be referenced using ... or () but
555 not with anything else.
558 memory_subscript(PyMemoryViewObject
*self
, PyObject
*key
)
561 view
= &(self
->view
);
563 if (view
->ndim
== 0) {
564 if (key
== Py_Ellipsis
||
565 (PyTuple_Check(key
) && PyTuple_GET_SIZE(key
)==0)) {
567 return (PyObject
*)self
;
570 PyErr_SetString(PyExc_IndexError
,
571 "invalid indexing of 0-dim memory");
575 if (PyIndex_Check(key
)) {
577 result
= PyNumber_AsSsize_t(key
, NULL
);
578 if (result
== -1 && PyErr_Occurred())
580 return memory_item(self
, result
);
582 else if (PySlice_Check(key
)) {
583 Py_ssize_t start
, stop
, step
, slicelength
;
585 if (PySlice_GetIndicesEx((PySliceObject
*)key
, get_shape0(view
),
586 &start
, &stop
, &step
, &slicelength
) < 0) {
590 if (step
== 1 && view
->ndim
== 1) {
592 void *newbuf
= (char *) view
->buf
593 + start
* view
->itemsize
;
594 int newflags
= view
->readonly
595 ? PyBUF_CONTIG_RO
: PyBUF_CONTIG
;
597 /* XXX There should be an API to create a subbuffer */
598 if (view
->obj
!= NULL
) {
599 if (PyObject_GetBuffer(view
->obj
, &newview
, newflags
) == -1)
605 newview
.buf
= newbuf
;
606 newview
.len
= slicelength
* newview
.itemsize
;
607 newview
.format
= view
->format
;
608 newview
.shape
= &(newview
.smalltable
[0]);
609 newview
.shape
[0] = slicelength
;
610 newview
.strides
= &(newview
.itemsize
);
611 return PyMemoryView_FromBuffer(&newview
);
613 PyErr_SetNone(PyExc_NotImplementedError
);
616 PyErr_Format(PyExc_TypeError
,
617 "cannot index memory using \"%.200s\"",
618 key
->ob_type
->tp_name
);
623 /* Need to support assigning memory if we can */
625 memory_ass_sub(PyMemoryViewObject
*self
, PyObject
*key
, PyObject
*value
)
627 Py_ssize_t start
, len
, bytelen
, i
;
629 Py_buffer
*view
= &(self
->view
);
630 char *srcbuf
, *destbuf
;
632 if (view
->readonly
) {
633 PyErr_SetString(PyExc_TypeError
,
634 "cannot modify read-only memory");
637 if (view
->ndim
!= 1) {
638 PyErr_SetNone(PyExc_NotImplementedError
);
641 if (PyIndex_Check(key
)) {
642 start
= PyNumber_AsSsize_t(key
, NULL
);
643 if (start
== -1 && PyErr_Occurred())
646 start
+= get_shape0(view
);
648 if ((start
< 0) || (start
>= get_shape0(view
))) {
649 PyErr_SetString(PyExc_IndexError
,
650 "index out of bounds");
655 else if (PySlice_Check(key
)) {
656 Py_ssize_t stop
, step
;
658 if (PySlice_GetIndicesEx((PySliceObject
*)key
, get_shape0(view
),
659 &start
, &stop
, &step
, &len
) < 0) {
663 PyErr_SetNone(PyExc_NotImplementedError
);
668 PyErr_Format(PyExc_TypeError
,
669 "cannot index memory using \"%.200s\"",
670 key
->ob_type
->tp_name
);
673 if (PyObject_GetBuffer(value
, &srcview
, PyBUF_CONTIG_RO
) == -1) {
676 /* XXX should we allow assignment of different item sizes
677 as long as the byte length is the same?
678 (e.g. assign 2 shorts to a 4-byte slice) */
679 if (srcview
.itemsize
!= view
->itemsize
) {
680 PyErr_Format(PyExc_TypeError
,
681 "mismatching item sizes for \"%.200s\" and \"%.200s\"",
682 view
->obj
->ob_type
->tp_name
, srcview
.obj
->ob_type
->tp_name
);
685 bytelen
= len
* view
->itemsize
;
686 if (bytelen
!= srcview
.len
) {
687 PyErr_SetString(PyExc_ValueError
,
688 "cannot modify size of memoryview object");
691 /* Do the actual copy */
692 destbuf
= (char *) view
->buf
+ start
* view
->itemsize
;
693 srcbuf
= (char *) srcview
.buf
;
694 if (destbuf
+ bytelen
< srcbuf
|| srcbuf
+ bytelen
< destbuf
)
696 memcpy(destbuf
, srcbuf
, bytelen
);
697 else if (destbuf
< srcbuf
) {
698 /* Copy in ascending order */
699 for (i
= 0; i
< bytelen
; i
++)
700 destbuf
[i
] = srcbuf
[i
];
703 /* Copy in descencing order */
704 for (i
= bytelen
- 1; i
>= 0; i
--)
705 destbuf
[i
] = srcbuf
[i
];
708 PyBuffer_Release(&srcview
);
712 PyBuffer_Release(&srcview
);
717 memory_richcompare(PyObject
*v
, PyObject
*w
, int op
)
725 if (op
!= Py_EQ
&& op
!= Py_NE
)
727 if (PyObject_GetBuffer(v
, &vv
, PyBUF_CONTIG_RO
) == -1) {
731 if (PyObject_GetBuffer(w
, &ww
, PyBUF_CONTIG_RO
) == -1) {
736 if (vv
.itemsize
!= ww
.itemsize
|| vv
.len
!= ww
.len
)
739 equal
= !memcmp(vv
.buf
, ww
.buf
, vv
.len
);
742 PyBuffer_Release(&vv
);
743 PyBuffer_Release(&ww
);
744 if ((equal
&& op
== Py_EQ
) || (!equal
&& op
== Py_NE
))
752 PyBuffer_Release(&vv
);
753 PyBuffer_Release(&ww
);
754 Py_INCREF(Py_NotImplemented
);
755 return Py_NotImplemented
;
760 memory_traverse(PyMemoryViewObject
*self
, visitproc visit
, void *arg
)
762 if (self
->base
!= NULL
)
763 Py_VISIT(self
->base
);
764 if (self
->view
.obj
!= NULL
)
765 Py_VISIT(self
->view
.obj
);
770 memory_clear(PyMemoryViewObject
*self
)
772 Py_CLEAR(self
->base
);
773 PyBuffer_Release(&self
->view
);
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 */
785 static PySequenceMethods memory_as_sequence
= {
789 (ssizeargfunc
)memory_item
, /* sq_item */
794 static PyBufferProcs memory_as_buffer
= {
795 (getbufferproc
)memory_getbuf
, /* bf_getbuffer */
796 (releasebufferproc
)memory_releasebuf
, /* bf_releasebuffer */
800 PyTypeObject PyMemoryView_Type
= {
801 PyVarObject_HEAD_INIT(&PyType_Type
, 0)
803 sizeof(PyMemoryViewObject
),
805 (destructor
)memory_dealloc
, /* tp_dealloc */
810 (reprfunc
)memory_repr
, /* tp_repr */
811 0, /* tp_as_number */
812 &memory_as_sequence
, /* tp_as_sequence */
813 &memory_as_mapping
, /* tp_as_mapping */
817 PyObject_GenericGetAttr
, /* tp_getattro */
819 &memory_as_buffer
, /* tp_as_buffer */
820 Py_TPFLAGS_DEFAULT
| Py_TPFLAGS_HAVE_GC
, /* tp_flags */
821 memory_doc
, /* tp_doc */
822 (traverseproc
)memory_traverse
, /* tp_traverse */
823 (inquiry
)memory_clear
, /* tp_clear */
824 memory_richcompare
, /* tp_richcompare */
825 0, /* tp_weaklistoffset */
828 memory_methods
, /* tp_methods */
830 memory_getsetlist
, /* tp_getset */
833 0, /* tp_descr_get */
834 0, /* tp_descr_set */
835 0, /* tp_dictoffset */
838 memory_new
, /* tp_new */