#include "Python.h"
#include "pycore_abstract.h"
#include "pycore_object.h"
#include "pycore_strhex.h"
#include <stddef.h>
#include "clinic/memoryobject.c.h"
static inline _PyManagedBufferObject *
mbuf_alloc(void)
{
_PyManagedBufferObject *mbuf;
mbuf = (_PyManagedBufferObject *)
PyObject_GC_New(_PyManagedBufferObject, &_PyManagedBuffer_Type);
if (mbuf == NULL)
return NULL;
mbuf->flags = 0;
mbuf->exports = 0;
mbuf->master.obj = NULL;
_PyObject_GC_TRACK(mbuf);
return mbuf;
}
static PyObject *
_PyManagedBuffer_FromObject(PyObject *base, int flags)
{
_PyManagedBufferObject *mbuf;
mbuf = mbuf_alloc();
if (mbuf == NULL)
return NULL;
if (PyObject_GetBuffer(base, &mbuf->master, flags) < 0) {
mbuf->master.obj = NULL;
Py_DECREF(mbuf);
return NULL;
}
return (PyObject *)mbuf;
}
static void
mbuf_release(_PyManagedBufferObject *self)
{
if (self->flags&_Py_MANAGED_BUFFER_RELEASED)
return;
self->flags |= _Py_MANAGED_BUFFER_RELEASED;
_PyObject_GC_UNTRACK(self);
PyBuffer_Release(&self->master);
}
static void
mbuf_dealloc(_PyManagedBufferObject *self)
{
assert(self->exports == 0);
mbuf_release(self);
if (self->flags&_Py_MANAGED_BUFFER_FREE_FORMAT)
PyMem_Free(self->master.format);
PyObject_GC_Del(self);
}
static int
mbuf_traverse(_PyManagedBufferObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->master.obj);
return 0;
}
static int
mbuf_clear(_PyManagedBufferObject *self)
{
assert(self->exports >= 0);
mbuf_release(self);
return 0;
}
PyTypeObject _PyManagedBuffer_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"managedbuffer",
sizeof(_PyManagedBufferObject),
0,
(destructor)mbuf_dealloc,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
PyObject_GenericGetAttr,
0,
0,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
0,
(traverseproc)mbuf_traverse,
(inquiry)mbuf_clear
};
#define BASE_INACCESSIBLE(mv) \
(((PyMemoryViewObject *)mv)->flags&_Py_MEMORYVIEW_RELEASED || \
((PyMemoryViewObject *)mv)->mbuf->flags&_Py_MANAGED_BUFFER_RELEASED)
#define CHECK_RELEASED(mv) \
if (BASE_INACCESSIBLE(mv)) { \
PyErr_SetString(PyExc_ValueError, \
"operation forbidden on released memoryview object"); \
return NULL; \
}
#define CHECK_RELEASED_INT(mv) \
if (BASE_INACCESSIBLE(mv)) { \
PyErr_SetString(PyExc_ValueError, \
"operation forbidden on released memoryview object"); \
return -1; \
}
#define CHECK_RESTRICTED(mv) \
if (((PyMemoryViewObject *)(mv))->flags & _Py_MEMORYVIEW_RESTRICTED) { \
PyErr_SetString(PyExc_ValueError, \
"cannot create new view on restricted memoryview"); \
return NULL; \
}
#define CHECK_RESTRICTED_INT(mv) \
if (((PyMemoryViewObject *)(mv))->flags & _Py_MEMORYVIEW_RESTRICTED) { \
PyErr_SetString(PyExc_ValueError, \
"cannot create new view on restricted memoryview"); \
return -1; \
}
#define CHECK_RELEASED_AGAIN(mv) CHECK_RELEASED(mv)
#define CHECK_RELEASED_INT_AGAIN(mv) CHECK_RELEASED_INT(mv)
#define CHECK_LIST_OR_TUPLE(v) \
if (!PyList_Check(v) && !PyTuple_Check(v)) { \
PyErr_SetString(PyExc_TypeError, \
#v " must be a list or a tuple"); \
return NULL; \
}
#define VIEW_ADDR(mv) (&((PyMemoryViewObject *)mv)->view)
#define HAVE_PTR(suboffsets, dim) (suboffsets && suboffsets[dim] >= 0)
#define ADJUST_PTR(ptr, suboffsets, dim) \
(HAVE_PTR(suboffsets, dim) ? *((char**)ptr) + suboffsets[dim] : ptr)
#define MV_C_CONTIGUOUS(flags) (flags&(_Py_MEMORYVIEW_SCALAR|_Py_MEMORYVIEW_C))
#define MV_F_CONTIGUOUS(flags) \
(flags&(_Py_MEMORYVIEW_SCALAR|_Py_MEMORYVIEW_FORTRAN))
#define MV_ANY_CONTIGUOUS(flags) \
(flags&(_Py_MEMORYVIEW_SCALAR|_Py_MEMORYVIEW_C|_Py_MEMORYVIEW_FORTRAN))
#define MV_CONTIGUOUS_NDIM1(view) \
((view)->shape[0] == 1 || (view)->strides[0] == (view)->itemsize)
#define REQ_INDIRECT(flags) ((flags&PyBUF_INDIRECT) == PyBUF_INDIRECT)
#define REQ_C_CONTIGUOUS(flags) ((flags&PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS)
#define REQ_F_CONTIGUOUS(flags) ((flags&PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS)
#define REQ_ANY_CONTIGUOUS(flags) ((flags&PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS)
#define REQ_STRIDES(flags) ((flags&PyBUF_STRIDES) == PyBUF_STRIDES)
#define REQ_SHAPE(flags) ((flags&PyBUF_ND) == PyBUF_ND)
#define REQ_WRITABLE(flags) (flags&PyBUF_WRITABLE)
#define REQ_FORMAT(flags) (flags&PyBUF_FORMAT)
#define HAVE_SUBOFFSETS_IN_LAST_DIM(view) \
(view->suboffsets && view->suboffsets[dest->ndim-1] >= 0)
static inline int
last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
{
assert(dest->ndim > 0 && src->ndim > 0);
return (!HAVE_SUBOFFSETS_IN_LAST_DIM(dest) &&
!HAVE_SUBOFFSETS_IN_LAST_DIM(src) &&
dest->strides[dest->ndim-1] == dest->itemsize &&
src->strides[src->ndim-1] == src->itemsize);
}
static inline int
equiv_format(const Py_buffer *dest, const Py_buffer *src)
{
const char *dfmt, *sfmt;
assert(dest->format && src->format);
dfmt = dest->format[0] == '@' ? dest->format+1 : dest->format;
sfmt = src->format[0] == '@' ? src->format+1 : src->format;
if (strcmp(dfmt, sfmt) != 0 ||
dest->itemsize != src->itemsize) {
return 0;
}
return 1;
}
static inline int
equiv_shape(const Py_buffer *dest, const Py_buffer *src)
{
int i;
if (dest->ndim != src->ndim)
return 0;
for (i = 0; i < dest->ndim; i++) {
if (dest->shape[i] != src->shape[i])
return 0;
if (dest->shape[i] == 0)
break;
}
return 1;
}
static int
equiv_structure(const Py_buffer *dest, const Py_buffer *src)
{
if (!equiv_format(dest, src) ||
!equiv_shape(dest, src)) {
PyErr_SetString(PyExc_ValueError,
"memoryview assignment: lvalue and rvalue have different "
"structures");
return 0;
}
return 1;
}
static void
copy_base(const Py_ssize_t *shape, Py_ssize_t itemsize,
char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets,
char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets,
char *mem)
{
if (mem == NULL) {
Py_ssize_t size = shape[0] * itemsize;
if (dptr + size < sptr || sptr + size < dptr)
memcpy(dptr, sptr, size);
else
memmove(dptr, sptr, size);
}
else {
char *p;
Py_ssize_t i;
for (i=0, p=mem; i < shape[0]; p+=itemsize, sptr+=sstrides[0], i++) {
char *xsptr = ADJUST_PTR(sptr, ssuboffsets, 0);
memcpy(p, xsptr, itemsize);
}
for (i=0, p=mem; i < shape[0]; p+=itemsize, dptr+=dstrides[0], i++) {
char *xdptr = ADJUST_PTR(dptr, dsuboffsets, 0);
memcpy(xdptr, p, itemsize);
}
}
}
static void
copy_rec(const Py_ssize_t *shape, Py_ssize_t ndim, Py_ssize_t itemsize,
char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets,
char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets,
char *mem)
{
Py_ssize_t i;
assert(ndim >= 1);
if (ndim == 1) {
copy_base(shape, itemsize,
dptr, dstrides, dsuboffsets,
sptr, sstrides, ssuboffsets,
mem);
return;
}
for (i = 0; i < shape[0]; dptr+=dstrides[0], sptr+=sstrides[0], i++) {
char *xdptr = ADJUST_PTR(dptr, dsuboffsets, 0);
char *xsptr = ADJUST_PTR(sptr, ssuboffsets, 0);
copy_rec(shape+1, ndim-1, itemsize,
xdptr, dstrides+1, dsuboffsets ? dsuboffsets+1 : NULL,
xsptr, sstrides+1, ssuboffsets ? ssuboffsets+1 : NULL,
mem);
}
}
static int
copy_single(PyMemoryViewObject *self, const Py_buffer *dest, const Py_buffer *src)
{
CHECK_RELEASED_INT_AGAIN(self);
char *mem = NULL;
assert(dest->ndim == 1);
if (!equiv_structure(dest, src))
return -1;
if (!last_dim_is_contiguous(dest, src)) {
mem = PyMem_Malloc(dest->shape[0] * dest->itemsize);
if (mem == NULL) {
PyErr_NoMemory();
return -1;
}
}
copy_base(dest->shape, dest->itemsize,
dest->buf, dest->strides, dest->suboffsets,
src->buf, src->strides, src->suboffsets,
mem);
if (mem)
PyMem_Free(mem);
return 0;
}
static int
copy_buffer(const Py_buffer *dest, const Py_buffer *src)
{
char *mem = NULL;
assert(dest->ndim > 0);
if (!equiv_structure(dest, src))
return -1;
if (!last_dim_is_contiguous(dest, src)) {
mem = PyMem_Malloc(dest->shape[dest->ndim-1] * dest->itemsize);
if (mem == NULL) {
PyErr_NoMemory();
return -1;
}
}
copy_rec(dest->shape, dest->ndim, dest->itemsize,
dest->buf, dest->strides, dest->suboffsets,
src->buf, src->strides, src->suboffsets,
mem);
if (mem)
PyMem_Free(mem);
return 0;
}
static inline void
init_strides_from_shape(Py_buffer *view)
{
Py_ssize_t i;
assert(view->ndim > 0);
view->strides[view->ndim-1] = view->itemsize;
for (i = view->ndim-2; i >= 0; i--)
view->strides[i] = view->strides[i+1] * view->shape[i+1];
}
static inline void
init_fortran_strides_from_shape(Py_buffer *view)
{
Py_ssize_t i;
assert(view->ndim > 0);
view->strides[0] = view->itemsize;
for (i = 1; i < view->ndim; i++)
view->strides[i] = view->strides[i-1] * view->shape[i-1];
}
static int
buffer_to_contiguous(char *mem, const Py_buffer *src, char order)
{
Py_buffer dest;
Py_ssize_t *strides;
int ret;
assert(src->ndim >= 1);
assert(src->shape != NULL);
assert(src->strides != NULL);
strides = PyMem_Malloc(src->ndim * (sizeof *src->strides));
if (strides == NULL) {
PyErr_NoMemory();
return -1;
}
dest = *src;
dest.buf = mem;
dest.strides = strides;
if (order == 'C' || order == 'A') {
init_strides_from_shape(&dest);
}
else {
init_fortran_strides_from_shape(&dest);
}
dest.suboffsets = NULL;
ret = copy_buffer(&dest, src);
PyMem_Free(strides);
return ret;
}
static inline void
init_shared_values(Py_buffer *dest, const Py_buffer *src)
{
dest->obj = src->obj;
dest->buf = src->buf;
dest->len = src->len;
dest->itemsize = src->itemsize;
dest->readonly = src->readonly;
dest->format = src->format ? src->format : "B";
dest->internal = src->internal;
}
static void
init_shape_strides(Py_buffer *dest, const Py_buffer *src)
{
Py_ssize_t i;
if (src->ndim == 0) {
dest->shape = NULL;
dest->strides = NULL;
return;
}
if (src->ndim == 1) {
dest->shape[0] = src->shape ? src->shape[0] : src->len / src->itemsize;
dest->strides[0] = src->strides ? src->strides[0] : src->itemsize;
return;
}
for (i = 0; i < src->ndim; i++)
dest->shape[i] = src->shape[i];
if (src->strides) {
for (i = 0; i < src->ndim; i++)
dest->strides[i] = src->strides[i];
}
else {
init_strides_from_shape(dest);
}
}
static inline void
init_suboffsets(Py_buffer *dest, const Py_buffer *src)
{
Py_ssize_t i;
if (src->suboffsets == NULL) {
dest->suboffsets = NULL;
return;
}
for (i = 0; i < src->ndim; i++)
dest->suboffsets[i] = src->suboffsets[i];
}
static inline void
init_len(Py_buffer *view)
{
Py_ssize_t i, len;
len = 1;
for (i = 0; i < view->ndim; i++)
len *= view->shape[i];
len *= view->itemsize;
view->len = len;
}
static void
init_flags(PyMemoryViewObject *mv)
{
const Py_buffer *view = &mv->view;
int flags = 0;
switch (view->ndim) {
case 0:
flags |= (_Py_MEMORYVIEW_SCALAR|_Py_MEMORYVIEW_C|
_Py_MEMORYVIEW_FORTRAN);
break;
case 1:
if (MV_CONTIGUOUS_NDIM1(view))
flags |= (_Py_MEMORYVIEW_C|_Py_MEMORYVIEW_FORTRAN);
break;
default:
if (PyBuffer_IsContiguous(view, 'C'))
flags |= _Py_MEMORYVIEW_C;
if (PyBuffer_IsContiguous(view, 'F'))
flags |= _Py_MEMORYVIEW_FORTRAN;
break;
}
if (view->suboffsets) {
flags |= _Py_MEMORYVIEW_PIL;
flags &= ~(_Py_MEMORYVIEW_C|_Py_MEMORYVIEW_FORTRAN);
}
mv->flags = flags;
}
static inline PyMemoryViewObject *
memory_alloc(int ndim)
{
PyMemoryViewObject *mv;
mv = (PyMemoryViewObject *)
PyObject_GC_NewVar(PyMemoryViewObject, &PyMemoryView_Type, 3*ndim);
if (mv == NULL)
return NULL;
mv->mbuf = NULL;
mv->hash = -1;
mv->flags = 0;
mv->exports = 0;
mv->view.ndim = ndim;
mv->view.shape = mv->ob_array;
mv->view.strides = mv->ob_array + ndim;
mv->view.suboffsets = mv->ob_array + 2 * ndim;
mv->weakreflist = NULL;
_PyObject_GC_TRACK(mv);
return mv;
}
static PyObject *
mbuf_add_view(_PyManagedBufferObject *mbuf, const Py_buffer *src)
{
PyMemoryViewObject *mv;
Py_buffer *dest;
if (src == NULL)
src = &mbuf->master;
if (src->ndim > PyBUF_MAX_NDIM) {
PyErr_SetString(PyExc_ValueError,
"memoryview: number of dimensions must not exceed "
Py_STRINGIFY(PyBUF_MAX_NDIM));
return NULL;
}
mv = memory_alloc(src->ndim);
if (mv == NULL)
return NULL;
dest = &mv->view;
init_shared_values(dest, src);
init_shape_strides(dest, src);
init_suboffsets(dest, src);
init_flags(mv);
mv->mbuf = (_PyManagedBufferObject*)Py_NewRef(mbuf);
mbuf->exports++;
return (PyObject *)mv;
}
static PyObject *
mbuf_add_incomplete_view(_PyManagedBufferObject *mbuf, const Py_buffer *src,
int ndim)
{
PyMemoryViewObject *mv;
Py_buffer *dest;
if (src == NULL)
src = &mbuf->master;
assert(ndim <= PyBUF_MAX_NDIM);
mv = memory_alloc(ndim);
if (mv == NULL)
return NULL;
dest = &mv->view;
init_shared_values(dest, src);
mv->mbuf = (_PyManagedBufferObject*)Py_NewRef(mbuf);
mbuf->exports++;
return (PyObject *)mv;
}
PyObject *
PyMemoryView_FromMemory(char *mem, Py_ssize_t size, int flags)
{
_PyManagedBufferObject *mbuf;
PyObject *mv;
int readonly;
assert(mem != NULL);
assert(flags == PyBUF_READ || flags == PyBUF_WRITE);
mbuf = mbuf_alloc();
if (mbuf == NULL)
return NULL;
readonly = (flags == PyBUF_WRITE) ? 0 : 1;
(void)PyBuffer_FillInfo(&mbuf->master, NULL, mem, size, readonly,
PyBUF_FULL_RO);
mv = mbuf_add_view(mbuf, NULL);
Py_DECREF(mbuf);
return mv;
}
PyObject *
PyMemoryView_FromBuffer(const Py_buffer *info)
{
_PyManagedBufferObject *mbuf;
PyObject *mv;
if (info->buf == NULL) {
PyErr_SetString(PyExc_ValueError,
"PyMemoryView_FromBuffer(): info->buf must not be NULL");
return NULL;
}
mbuf = mbuf_alloc();
if (mbuf == NULL)
return NULL;
mbuf->master = *info;
mbuf->master.obj = NULL;
mv = mbuf_add_view(mbuf, NULL);
Py_DECREF(mbuf);
return mv;
}
static PyObject *
PyMemoryView_FromObjectAndFlags(PyObject *v, int flags)
{
_PyManagedBufferObject *mbuf;
if (PyMemoryView_Check(v)) {
PyMemoryViewObject *mv = (PyMemoryViewObject *)v;
CHECK_RELEASED(mv);
CHECK_RESTRICTED(mv);
return mbuf_add_view(mv->mbuf, &mv->view);
}
else if (PyObject_CheckBuffer(v)) {
PyObject *ret;
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(v, flags);
if (mbuf == NULL)
return NULL;
ret = mbuf_add_view(mbuf, NULL);
Py_DECREF(mbuf);
return ret;
}
PyErr_Format(PyExc_TypeError,
"memoryview: a bytes-like object is required, not '%.200s'",
Py_TYPE(v)->tp_name);
return NULL;
}
PyObject *
_PyMemoryView_FromBufferProc(PyObject *v, int flags, getbufferproc bufferproc)
{
_PyManagedBufferObject *mbuf = mbuf_alloc();
if (mbuf == NULL)
return NULL;
int res = bufferproc(v, &mbuf->master, flags);
if (res < 0) {
mbuf->master.obj = NULL;
Py_DECREF(mbuf);
return NULL;
}
PyObject *ret = mbuf_add_view(mbuf, NULL);
Py_DECREF(mbuf);
return ret;
}
PyObject *
PyMemoryView_FromObject(PyObject *v)
{
return PyMemoryView_FromObjectAndFlags(v, PyBUF_FULL_RO);
}
static int
mbuf_copy_format(_PyManagedBufferObject *mbuf, const char *fmt)
{
if (fmt != NULL) {
char *cp = PyMem_Malloc(strlen(fmt)+1);
if (cp == NULL) {
PyErr_NoMemory();
return -1;
}
mbuf->master.format = strcpy(cp, fmt);
mbuf->flags |= _Py_MANAGED_BUFFER_FREE_FORMAT;
}
return 0;
}
static PyObject *
memory_from_contiguous_copy(const Py_buffer *src, char order)
{
_PyManagedBufferObject *mbuf;
PyMemoryViewObject *mv;
PyObject *bytes;
Py_buffer *dest;
int i;
assert(src->ndim > 0);
assert(src->shape != NULL);
bytes = PyBytes_FromStringAndSize(NULL, src->len);
if (bytes == NULL)
return NULL;
mbuf = (_PyManagedBufferObject *)_PyManagedBuffer_FromObject(bytes, PyBUF_FULL_RO);
Py_DECREF(bytes);
if (mbuf == NULL)
return NULL;
if (mbuf_copy_format(mbuf, src->format) < 0) {
Py_DECREF(mbuf);
return NULL;
}
mv = (PyMemoryViewObject *)mbuf_add_incomplete_view(mbuf, NULL, src->ndim);
Py_DECREF(mbuf);
if (mv == NULL)
return NULL;
dest = &mv->view;
dest->itemsize = src->itemsize;
for (i = 0; i < src->ndim; i++) {
dest->shape[i] = src->shape[i];
}
if (order == 'C' || order == 'A') {
init_strides_from_shape(dest);
}
else {
init_fortran_strides_from_shape(dest);
}
dest->suboffsets = NULL;
init_flags(mv);
if (copy_buffer(dest, src) < 0) {
Py_DECREF(mv);
return NULL;
}
return (PyObject *)mv;
}
PyObject *
PyMemoryView_GetContiguous(PyObject *obj, int buffertype, char order)
{
PyMemoryViewObject *mv;
PyObject *ret;
Py_buffer *view;
assert(buffertype == PyBUF_READ || buffertype == PyBUF_WRITE);
assert(order == 'C' || order == 'F' || order == 'A');
mv = (PyMemoryViewObject *)PyMemoryView_FromObject(obj);
if (mv == NULL)
return NULL;
view = &mv->view;
if (buffertype == PyBUF_WRITE && view->readonly) {
PyErr_SetString(PyExc_BufferError,
"underlying buffer is not writable");
Py_DECREF(mv);
return NULL;
}
if (PyBuffer_IsContiguous(view, order))
return (PyObject *)mv;
if (buffertype == PyBUF_WRITE) {
PyErr_SetString(PyExc_BufferError,
"writable contiguous buffer requested "
"for a non-contiguous object.");
Py_DECREF(mv);
return NULL;
}
ret = memory_from_contiguous_copy(view, order);
Py_DECREF(mv);
return ret;
}
static PyObject *
memoryview_impl(PyTypeObject *type, PyObject *object)
{
return PyMemoryView_FromObject(object);
}
static PyObject *
memoryview__from_flags_impl(PyTypeObject *type, PyObject *object, int flags)
{
return PyMemoryView_FromObjectAndFlags(object, flags);
}
typedef struct {
Py_buffer view;
Py_ssize_t array[1];
} Py_buffer_full;
int
PyBuffer_ToContiguous(void *buf, const Py_buffer *src, Py_ssize_t len, char order)
{
Py_buffer_full *fb = NULL;
int ret;
assert(order == 'C' || order == 'F' || order == 'A');
if (len != src->len) {
PyErr_SetString(PyExc_ValueError,
"PyBuffer_ToContiguous: len != view->len");
return -1;
}
if (PyBuffer_IsContiguous(src, order)) {
memcpy((char *)buf, src->buf, len);
return 0;
}
fb = PyMem_Malloc(sizeof *fb + 3 * src->ndim * (sizeof *fb->array));
if (fb == NULL) {
PyErr_NoMemory();
return -1;
}
fb->view.ndim = src->ndim;
fb->view.shape = fb->array;
fb->view.strides = fb->array + src->ndim;
fb->view.suboffsets = fb->array + 2 * src->ndim;
init_shared_values(&fb->view, src);
init_shape_strides(&fb->view, src);
init_suboffsets(&fb->view, src);
src = &fb->view;
ret = buffer_to_contiguous(buf, src, order);
PyMem_Free(fb);
return ret;
}
static int
_memory_release(PyMemoryViewObject *self)
{
if (self->flags & _Py_MEMORYVIEW_RELEASED)
return 0;
if (self->exports == 0) {
self->flags |= _Py_MEMORYVIEW_RELEASED;
assert(self->mbuf->exports > 0);
if (--self->mbuf->exports == 0)
mbuf_release(self->mbuf);
return 0;
}
if (self->exports > 0) {
PyErr_Format(PyExc_BufferError,
"memoryview has %zd exported buffer%s", self->exports,
self->exports==1 ? "" : "s");
return -1;
}
PyErr_SetString(PyExc_SystemError,
"_memory_release(): negative export count");
return -1;
}
static PyObject *
memoryview_release_impl(PyMemoryViewObject *self)
{
if (_memory_release(self) < 0)
return NULL;
Py_RETURN_NONE;
}
static void
memory_dealloc(PyMemoryViewObject *self)
{
assert(self->exports == 0);
_PyObject_GC_UNTRACK(self);
(void)_memory_release(self);
Py_CLEAR(self->mbuf);
if (self->weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *) self);
PyObject_GC_Del(self);
}
static int
memory_traverse(PyMemoryViewObject *self, visitproc visit, void *arg)
{
Py_VISIT(self->mbuf);
return 0;
}
static int
memory_clear(PyMemoryViewObject *self)
{
(void)_memory_release(self);
Py_CLEAR(self->mbuf);
return 0;
}
static PyObject *
memory_enter(PyObject *self, PyObject *args)
{
CHECK_RELEASED(self);
return Py_NewRef(self);
}
static PyObject *
memory_exit(PyObject *self, PyObject *args)
{
return memoryview_release_impl((PyMemoryViewObject *)self);
}
#define IS_BYTE_FORMAT(f) (f == 'b' || f == 'B' || f == 'c')
static inline Py_ssize_t
get_native_fmtchar(char *result, const char *fmt)
{
Py_ssize_t size = -1;
if (fmt[0] == '@') fmt++;
switch (fmt[0]) {
case 'c': case 'b': case 'B': size = sizeof(char); break;
case 'h': case 'H': size = sizeof(short); break;
case 'i': case 'I': size = sizeof(int); break;
case 'l': case 'L': size = sizeof(long); break;
case 'q': case 'Q': size = sizeof(long long); break;
case 'n': case 'N': size = sizeof(Py_ssize_t); break;
case 'f': size = sizeof(float); break;
case 'd': size = sizeof(double); break;
case 'e': size = sizeof(float) / 2; break;
case '?': size = sizeof(_Bool); break;
case 'P': size = sizeof(void *); break;
}
if (size > 0 && fmt[1] == '\0') {
*result = fmt[0];
return size;
}
return -1;
}
static inline const char *
get_native_fmtstr(const char *fmt)
{
int at = 0;
if (fmt[0] == '@') {
at = 1;
fmt++;
}
if (fmt[0] == '\0' || fmt[1] != '\0') {
return NULL;
}
#define RETURN(s) do { return at ? "@" s : s; } while (0)
switch (fmt[0]) {
case 'c': RETURN("c");
case 'b': RETURN("b");
case 'B': RETURN("B");
case 'h': RETURN("h");
case 'H': RETURN("H");
case 'i': RETURN("i");
case 'I': RETURN("I");
case 'l': RETURN("l");
case 'L': RETURN("L");
case 'q': RETURN("q");
case 'Q': RETURN("Q");
case 'n': RETURN("n");
case 'N': RETURN("N");
case 'f': RETURN("f");
case 'd': RETURN("d");
case 'e': RETURN("e");
case '?': RETURN("?");
case 'P': RETURN("P");
}
return NULL;
}
static int
cast_to_1D(PyMemoryViewObject *mv, PyObject *format)
{
Py_buffer *view = &mv->view;
PyObject *asciifmt;
char srcchar, destchar;
Py_ssize_t itemsize;
int ret = -1;
assert(view->ndim >= 1);
assert(Py_SIZE(mv) == 3*view->ndim);
assert(view->shape == mv->ob_array);
assert(view->strides == mv->ob_array + view->ndim);
assert(view->suboffsets == mv->ob_array + 2*view->ndim);
asciifmt = PyUnicode_AsASCIIString(format);
if (asciifmt == NULL)
return ret;
itemsize = get_native_fmtchar(&destchar, PyBytes_AS_STRING(asciifmt));
if (itemsize < 0) {
PyErr_SetString(PyExc_ValueError,
"memoryview: destination format must be a native single "
"character format prefixed with an optional '@'");
goto out;
}
if ((get_native_fmtchar(&srcchar, view->format) < 0 ||
!IS_BYTE_FORMAT(srcchar)) && !IS_BYTE_FORMAT(destchar)) {
PyErr_SetString(PyExc_TypeError,
"memoryview: cannot cast between two non-byte formats");
goto out;
}
if (view->len % itemsize) {
PyErr_SetString(PyExc_TypeError,
"memoryview: length is not a multiple of itemsize");
goto out;
}
view->format = (char *)get_native_fmtstr(PyBytes_AS_STRING(asciifmt));
if (view->format == NULL) {
PyErr_SetString(PyExc_RuntimeError,
"memoryview: internal error");
goto out;
}
view->itemsize = itemsize;
view->ndim = 1;
view->shape[0] = view->len / view->itemsize;
view->strides[0] = view->itemsize;
view->suboffsets = NULL;
init_flags(mv);
ret = 0;
out:
Py_DECREF(asciifmt);
return ret;
}
static Py_ssize_t
copy_shape(Py_ssize_t *shape, const PyObject *seq, Py_ssize_t ndim,
Py_ssize_t itemsize)
{
Py_ssize_t x, i;
Py_ssize_t len = itemsize;
for (i = 0; i < ndim; i++) {
PyObject *tmp = PySequence_Fast_GET_ITEM(seq, i);
if (!PyLong_Check(tmp)) {
PyErr_SetString(PyExc_TypeError,
"memoryview.cast(): elements of shape must be integers");
return -1;
}
x = PyLong_AsSsize_t(tmp);
if (x == -1 && PyErr_Occurred()) {
return -1;
}
if (x <= 0) {
PyErr_Format(PyExc_ValueError,
"memoryview.cast(): elements of shape must be integers > 0");
return -1;
}
if (x > PY_SSIZE_T_MAX / len) {
PyErr_Format(PyExc_ValueError,
"memoryview.cast(): product(shape) > SSIZE_MAX");
return -1;
}
len *= x;
shape[i] = x;
}
return len;
}
static int
cast_to_ND(PyMemoryViewObject *mv, const PyObject *shape, int ndim)
{
Py_buffer *view = &mv->view;
Py_ssize_t len;
assert(view->ndim == 1);
assert(Py_SIZE(mv) == 3*(ndim==0?1:ndim));
assert(view->shape == mv->ob_array);
assert(view->strides == mv->ob_array + (ndim==0?1:ndim));
assert(view->suboffsets == NULL);
view->ndim = ndim;
if (view->ndim == 0) {
view->shape = NULL;
view->strides = NULL;
len = view->itemsize;
}
else {
len = copy_shape(view->shape, shape, ndim, view->itemsize);
if (len < 0)
return -1;
init_strides_from_shape(view);
}
if (view->len != len) {
PyErr_SetString(PyExc_TypeError,
"memoryview: product(shape) * itemsize != buffer size");
return -1;
}
init_flags(mv);
return 0;
}
static int
zero_in_shape(PyMemoryViewObject *mv)
{
Py_buffer *view = &mv->view;
Py_ssize_t i;
for (i = 0; i < view->ndim; i++)
if (view->shape[i] == 0)
return 1;
return 0;
}
static PyObject *
memoryview_cast_impl(PyMemoryViewObject *self, PyObject *format,
PyObject *shape)
{
PyMemoryViewObject *mv = NULL;
Py_ssize_t ndim = 1;
CHECK_RELEASED(self);
CHECK_RESTRICTED(self);
if (!MV_C_CONTIGUOUS(self->flags)) {
PyErr_SetString(PyExc_TypeError,
"memoryview: casts are restricted to C-contiguous views");
return NULL;
}
if ((shape || self->view.ndim != 1) && zero_in_shape(self)) {
PyErr_SetString(PyExc_TypeError,
"memoryview: cannot cast view with zeros in shape or strides");
return NULL;
}
if (shape) {
CHECK_LIST_OR_TUPLE(shape)
ndim = PySequence_Fast_GET_SIZE(shape);
if (ndim > PyBUF_MAX_NDIM) {
PyErr_SetString(PyExc_ValueError,
"memoryview: number of dimensions must not exceed "
Py_STRINGIFY(PyBUF_MAX_NDIM));
return NULL;
}
if (self->view.ndim != 1 && ndim != 1) {
PyErr_SetString(PyExc_TypeError,
"memoryview: cast must be 1D -> ND or ND -> 1D");
return NULL;
}
}
mv = (PyMemoryViewObject *)
mbuf_add_incomplete_view(self->mbuf, &self->view, ndim==0 ? 1 : (int)ndim);
if (mv == NULL)
return NULL;
if (cast_to_1D(mv, format) < 0)
goto error;
if (shape && cast_to_ND(mv, shape, (int)ndim) < 0)
goto error;
return (PyObject *)mv;
error:
Py_DECREF(mv);
return NULL;
}
static PyObject *
memoryview_toreadonly_impl(PyMemoryViewObject *self)
{
CHECK_RELEASED(self);
CHECK_RESTRICTED(self);
self = (PyMemoryViewObject *) mbuf_add_view(self->mbuf, &self->view);
if (self != NULL) {
self->view.readonly = 1;
};
return (PyObject *) self;
}
static int
memory_getbuf(PyMemoryViewObject *self, Py_buffer *view, int flags)
{
Py_buffer *base = &self->view;
int baseflags = self->flags;
CHECK_RELEASED_INT(self);
CHECK_RESTRICTED_INT(self);
*view = *base;
view->obj = NULL;
if (REQ_WRITABLE(flags) && base->readonly) {
PyErr_SetString(PyExc_BufferError,
"memoryview: underlying buffer is not writable");
return -1;
}
if (!REQ_FORMAT(flags)) {
view->format = NULL;
}
if (REQ_C_CONTIGUOUS(flags) && !MV_C_CONTIGUOUS(baseflags)) {
PyErr_SetString(PyExc_BufferError,
"memoryview: underlying buffer is not C-contiguous");
return -1;
}
if (REQ_F_CONTIGUOUS(flags) && !MV_F_CONTIGUOUS(baseflags)) {
PyErr_SetString(PyExc_BufferError,
"memoryview: underlying buffer is not Fortran contiguous");
return -1;
}
if (REQ_ANY_CONTIGUOUS(flags) && !MV_ANY_CONTIGUOUS(baseflags)) {
PyErr_SetString(PyExc_BufferError,
"memoryview: underlying buffer is not contiguous");
return -1;
}
if (!REQ_INDIRECT(flags) && (baseflags & _Py_MEMORYVIEW_PIL)) {
PyErr_SetString(PyExc_BufferError,
"memoryview: underlying buffer requires suboffsets");
return -1;
}
if (!REQ_STRIDES(flags)) {
if (!MV_C_CONTIGUOUS(baseflags)) {
PyErr_SetString(PyExc_BufferError,
"memoryview: underlying buffer is not C-contiguous");
return -1;
}
view->strides = NULL;
}
if (!REQ_SHAPE(flags)) {
if (view->format != NULL) {
PyErr_Format(PyExc_BufferError,
"memoryview: cannot cast to unsigned bytes if the format flag "
"is present");
return -1;
}
view->ndim = 1;
view->shape = NULL;
}
view->obj = Py_NewRef(self);
self->exports++;
return 0;
}
static void
memory_releasebuf(PyMemoryViewObject *self, Py_buffer *view)
{
self->exports--;
return;
}
static PyBufferProcs memory_as_buffer = {
(getbufferproc)memory_getbuf,
(releasebufferproc)memory_releasebuf,
};
static int
type_error_int(const char *fmt)
{
PyErr_Format(PyExc_TypeError,
"memoryview: invalid type for format '%s'", fmt);
return -1;
}
static int
value_error_int(const char *fmt)
{
PyErr_Format(PyExc_ValueError,
"memoryview: invalid value for format '%s'", fmt);
return -1;
}
static int
fix_error_int(const char *fmt)
{
assert(PyErr_Occurred());
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
PyErr_Clear();
return type_error_int(fmt);
}
else if (PyErr_ExceptionMatches(PyExc_OverflowError) ||
PyErr_ExceptionMatches(PyExc_ValueError)) {
PyErr_Clear();
return value_error_int(fmt);
}
return -1;
}
static long
pylong_as_ld(PyObject *item)
{
PyObject *tmp;
long ld;
tmp = _PyNumber_Index(item);
if (tmp == NULL)
return -1;
ld = PyLong_AsLong(tmp);
Py_DECREF(tmp);
return ld;
}
static unsigned long
pylong_as_lu(PyObject *item)
{
PyObject *tmp;
unsigned long lu;
tmp = _PyNumber_Index(item);
if (tmp == NULL)
return (unsigned long)-1;
lu = PyLong_AsUnsignedLong(tmp);
Py_DECREF(tmp);
return lu;
}
static long long
pylong_as_lld(PyObject *item)
{
PyObject *tmp;
long long lld;
tmp = _PyNumber_Index(item);
if (tmp == NULL)
return -1;
lld = PyLong_AsLongLong(tmp);
Py_DECREF(tmp);
return lld;
}
static unsigned long long
pylong_as_llu(PyObject *item)
{
PyObject *tmp;
unsigned long long llu;
tmp = _PyNumber_Index(item);
if (tmp == NULL)
return (unsigned long long)-1;
llu = PyLong_AsUnsignedLongLong(tmp);
Py_DECREF(tmp);
return llu;
}
static Py_ssize_t
pylong_as_zd(PyObject *item)
{
PyObject *tmp;
Py_ssize_t zd;
tmp = _PyNumber_Index(item);
if (tmp == NULL)
return -1;
zd = PyLong_AsSsize_t(tmp);
Py_DECREF(tmp);
return zd;
}
static size_t
pylong_as_zu(PyObject *item)
{
PyObject *tmp;
size_t zu;
tmp = _PyNumber_Index(item);
if (tmp == NULL)
return (size_t)-1;
zu = PyLong_AsSize_t(tmp);
Py_DECREF(tmp);
return zu;
}
#define UNPACK_SINGLE(dest, ptr, type) \
do { \
type x; \
memcpy((char *)&x, ptr, sizeof x); \
dest = x; \
} while (0)
static inline PyObject *
unpack_single(PyMemoryViewObject *self, const char *ptr, const char *fmt)
{
unsigned long long llu;
unsigned long lu;
size_t zu;
long long lld;
long ld;
Py_ssize_t zd;
double d;
unsigned char uc;
void *p;
CHECK_RELEASED_AGAIN(self);
#if PY_LITTLE_ENDIAN
int endian = 1;
#else
int endian = 0;
#endif
switch (fmt[0]) {
case 'B': uc = *((const unsigned char *)ptr); goto convert_uc;
case 'b': ld = *((const signed char *)ptr); goto convert_ld;
case 'h': UNPACK_SINGLE(ld, ptr, short); goto convert_ld;
case 'i': UNPACK_SINGLE(ld, ptr, int); goto convert_ld;
case 'l': UNPACK_SINGLE(ld, ptr, long); goto convert_ld;
case '?': UNPACK_SINGLE(ld, ptr, _Bool); goto convert_bool;
case 'H': UNPACK_SINGLE(lu, ptr, unsigned short); goto convert_lu;
case 'I': UNPACK_SINGLE(lu, ptr, unsigned int); goto convert_lu;
case 'L': UNPACK_SINGLE(lu, ptr, unsigned long); goto convert_lu;
case 'q': UNPACK_SINGLE(lld, ptr, long long); goto convert_lld;
case 'Q': UNPACK_SINGLE(llu, ptr, unsigned long long); goto convert_llu;
case 'n': UNPACK_SINGLE(zd, ptr, Py_ssize_t); goto convert_zd;
case 'N': UNPACK_SINGLE(zu, ptr, size_t); goto convert_zu;
case 'f': UNPACK_SINGLE(d, ptr, float); goto convert_double;
case 'd': UNPACK_SINGLE(d, ptr, double); goto convert_double;
case 'e': d = PyFloat_Unpack2(ptr, endian); goto convert_double;
case 'c': goto convert_bytes;
case 'P': UNPACK_SINGLE(p, ptr, void *); goto convert_pointer;
default: goto err_format;
}
convert_uc:
return PyLong_FromLong(uc);
convert_ld:
return PyLong_FromLong(ld);
convert_lu:
return PyLong_FromUnsignedLong(lu);
convert_lld:
return PyLong_FromLongLong(lld);
convert_llu:
return PyLong_FromUnsignedLongLong(llu);
convert_zd:
return PyLong_FromSsize_t(zd);
convert_zu:
return PyLong_FromSize_t(zu);
convert_double:
return PyFloat_FromDouble(d);
convert_bool:
return PyBool_FromLong(ld);
convert_bytes:
return PyBytes_FromStringAndSize(ptr, 1);
convert_pointer:
return PyLong_FromVoidPtr(p);
err_format:
PyErr_Format(PyExc_NotImplementedError,
"memoryview: format %s not supported", fmt);
return NULL;
}
#define PACK_SINGLE(ptr, src, type) \
do { \
type x; \
x = (type)src; \
memcpy(ptr, (char *)&x, sizeof x); \
} while (0)
static int
pack_single(PyMemoryViewObject *self, char *ptr, PyObject *item, const char *fmt)
{
unsigned long long llu;
unsigned long lu;
size_t zu;
long long lld;
long ld;
Py_ssize_t zd;
double d;
void *p;
#if PY_LITTLE_ENDIAN
int endian = 1;
#else
int endian = 0;
#endif
switch (fmt[0]) {
case 'b': case 'h': case 'i': case 'l':
ld = pylong_as_ld(item);
if (ld == -1 && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
switch (fmt[0]) {
case 'b':
if (ld < SCHAR_MIN || ld > SCHAR_MAX) goto err_range;
*((signed char *)ptr) = (signed char)ld; break;
case 'h':
if (ld < SHRT_MIN || ld > SHRT_MAX) goto err_range;
PACK_SINGLE(ptr, ld, short); break;
case 'i':
if (ld < INT_MIN || ld > INT_MAX) goto err_range;
PACK_SINGLE(ptr, ld, int); break;
default:
PACK_SINGLE(ptr, ld, long); break;
}
break;
case 'B': case 'H': case 'I': case 'L':
lu = pylong_as_lu(item);
if (lu == (unsigned long)-1 && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
switch (fmt[0]) {
case 'B':
if (lu > UCHAR_MAX) goto err_range;
*((unsigned char *)ptr) = (unsigned char)lu; break;
case 'H':
if (lu > USHRT_MAX) goto err_range;
PACK_SINGLE(ptr, lu, unsigned short); break;
case 'I':
if (lu > UINT_MAX) goto err_range;
PACK_SINGLE(ptr, lu, unsigned int); break;
default:
PACK_SINGLE(ptr, lu, unsigned long); break;
}
break;
case 'q':
lld = pylong_as_lld(item);
if (lld == -1 && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
PACK_SINGLE(ptr, lld, long long);
break;
case 'Q':
llu = pylong_as_llu(item);
if (llu == (unsigned long long)-1 && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
PACK_SINGLE(ptr, llu, unsigned long long);
break;
case 'n':
zd = pylong_as_zd(item);
if (zd == -1 && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
PACK_SINGLE(ptr, zd, Py_ssize_t);
break;
case 'N':
zu = pylong_as_zu(item);
if (zu == (size_t)-1 && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
PACK_SINGLE(ptr, zu, size_t);
break;
case 'f': case 'd': case 'e':
d = PyFloat_AsDouble(item);
if (d == -1.0 && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
if (fmt[0] == 'f') {
PACK_SINGLE(ptr, d, float);
}
else if (fmt[0] == 'd') {
PACK_SINGLE(ptr, d, double);
}
else {
if (PyFloat_Pack2(d, ptr, endian) < 0) {
goto err_occurred;
}
}
break;
case '?':
ld = PyObject_IsTrue(item);
if (ld < 0)
return -1;
CHECK_RELEASED_INT_AGAIN(self);
PACK_SINGLE(ptr, ld, _Bool);
break;
case 'c':
if (!PyBytes_Check(item))
return type_error_int(fmt);
if (PyBytes_GET_SIZE(item) != 1)
return value_error_int(fmt);
*ptr = PyBytes_AS_STRING(item)[0];
break;
case 'P':
p = PyLong_AsVoidPtr(item);
if (p == NULL && PyErr_Occurred())
goto err_occurred;
CHECK_RELEASED_INT_AGAIN(self);
PACK_SINGLE(ptr, p, void *);
break;
default: goto err_format;
}
return 0;
err_occurred:
return fix_error_int(fmt);
err_range:
return value_error_int(fmt);
err_format:
PyErr_Format(PyExc_NotImplementedError,
"memoryview: format %s not supported", fmt);
return -1;
}
struct unpacker {
PyObject *unpack_from;
PyObject *mview;
char *item;
Py_ssize_t itemsize;
};
static struct unpacker *
unpacker_new(void)
{
struct unpacker *x = PyMem_Malloc(sizeof *x);
if (x == NULL) {
PyErr_NoMemory();
return NULL;
}
x->unpack_from = NULL;
x->mview = NULL;
x->item = NULL;
x->itemsize = 0;
return x;
}
static void
unpacker_free(struct unpacker *x)
{
if (x) {
Py_XDECREF(x->unpack_from);
Py_XDECREF(x->mview);
PyMem_Free(x->item);
PyMem_Free(x);
}
}
static struct unpacker *
struct_get_unpacker(const char *fmt, Py_ssize_t itemsize)
{
PyObject *Struct = NULL;
PyObject *structobj = NULL;
PyObject *format = NULL;
struct unpacker *x = NULL;
Struct = _PyImport_GetModuleAttrString("struct", "Struct");
if (Struct == NULL)
return NULL;
x = unpacker_new();
if (x == NULL)
goto error;
format = PyBytes_FromString(fmt);
if (format == NULL)
goto error;
structobj = PyObject_CallOneArg(Struct, format);
if (structobj == NULL)
goto error;
x->unpack_from = PyObject_GetAttrString(structobj, "unpack_from");
if (x->unpack_from == NULL)
goto error;
x->item = PyMem_Malloc(itemsize);
if (x->item == NULL) {
PyErr_NoMemory();
goto error;
}
x->itemsize = itemsize;
x->mview = PyMemoryView_FromMemory(x->item, itemsize, PyBUF_WRITE);
if (x->mview == NULL)
goto error;
out:
Py_XDECREF(Struct);
Py_XDECREF(format);
Py_XDECREF(structobj);
return x;
error:
unpacker_free(x);
x = NULL;
goto out;
}
static PyObject *
struct_unpack_single(const char *ptr, struct unpacker *x)
{
PyObject *v;
memcpy(x->item, ptr, x->itemsize);
v = PyObject_CallOneArg(x->unpack_from, x->mview);
if (v == NULL)
return NULL;
if (PyTuple_GET_SIZE(v) == 1) {
PyObject *res = Py_NewRef(PyTuple_GET_ITEM(v, 0));
Py_DECREF(v);
return res;
}
return v;
}
static inline const char *
adjust_fmt(const Py_buffer *view)
{
const char *fmt;
fmt = (view->format[0] == '@') ? view->format+1 : view->format;
if (fmt[0] && fmt[1] == '\0')
return fmt;
PyErr_Format(PyExc_NotImplementedError,
"memoryview: unsupported format %s", view->format);
return NULL;
}
static PyObject *
tolist_base(PyMemoryViewObject *self, const char *ptr, const Py_ssize_t *shape,
const Py_ssize_t *strides, const Py_ssize_t *suboffsets,
const char *fmt)
{
PyObject *lst, *item;
Py_ssize_t i;
lst = PyList_New(shape[0]);
if (lst == NULL)
return NULL;
for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
item = unpack_single(self, xptr, fmt);
if (item == NULL) {
Py_DECREF(lst);
return NULL;
}
PyList_SET_ITEM(lst, i, item);
}
return lst;
}
static PyObject *
tolist_rec(PyMemoryViewObject *self, const char *ptr, Py_ssize_t ndim, const Py_ssize_t *shape,
const Py_ssize_t *strides, const Py_ssize_t *suboffsets,
const char *fmt)
{
PyObject *lst, *item;
Py_ssize_t i;
assert(ndim >= 1);
assert(shape != NULL);
assert(strides != NULL);
if (ndim == 1)
return tolist_base(self, ptr, shape, strides, suboffsets, fmt);
lst = PyList_New(shape[0]);
if (lst == NULL)
return NULL;
for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
const char *xptr = ADJUST_PTR(ptr, suboffsets, 0);
item = tolist_rec(self, xptr, ndim-1, shape+1,
strides+1, suboffsets ? suboffsets+1 : NULL,
fmt);
if (item == NULL) {
Py_DECREF(lst);
return NULL;
}
PyList_SET_ITEM(lst, i, item);
}
return lst;
}
static PyObject *
memoryview_tolist_impl(PyMemoryViewObject *self)
{
const Py_buffer *view = &self->view;
const char *fmt;
CHECK_RELEASED(self);
fmt = adjust_fmt(view);
if (fmt == NULL)
return NULL;
if (view->ndim == 0) {
return unpack_single(self, view->buf, fmt);
}
else if (view->ndim == 1) {
return tolist_base(self, view->buf, view->shape,
view->strides, view->suboffsets,
fmt);
}
else {
return tolist_rec(self, view->buf, view->ndim, view->shape,
view->strides, view->suboffsets,
fmt);
}
}
static PyObject *
memoryview_tobytes_impl(PyMemoryViewObject *self, const char *order)
{
Py_buffer *src = VIEW_ADDR(self);
char ord = 'C';
PyObject *bytes;
CHECK_RELEASED(self);
if (order) {
if (strcmp(order, "F") == 0) {
ord = 'F';
}
else if (strcmp(order, "A") == 0) {
ord = 'A';
}
else if (strcmp(order, "C") != 0) {
PyErr_SetString(PyExc_ValueError,
"order must be 'C', 'F' or 'A'");
return NULL;
}
}
bytes = PyBytes_FromStringAndSize(NULL, src->len);
if (bytes == NULL)
return NULL;
if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, ord) < 0) {
Py_DECREF(bytes);
return NULL;
}
return bytes;
}
static PyObject *
memoryview_hex_impl(PyMemoryViewObject *self, PyObject *sep,
int bytes_per_sep)
{
Py_buffer *src = VIEW_ADDR(self);
PyObject *bytes;
PyObject *ret;
CHECK_RELEASED(self);
if (MV_C_CONTIGUOUS(self->flags)) {
return _Py_strhex_with_sep(src->buf, src->len, sep, bytes_per_sep);
}
bytes = PyBytes_FromStringAndSize(NULL, src->len);
if (bytes == NULL)
return NULL;
if (PyBuffer_ToContiguous(PyBytes_AS_STRING(bytes), src, src->len, 'C') < 0) {
Py_DECREF(bytes);
return NULL;
}
ret = _Py_strhex_with_sep(
PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes),
sep, bytes_per_sep);
Py_DECREF(bytes);
return ret;
}
static PyObject *
memory_repr(PyMemoryViewObject *self)
{
if (self->flags & _Py_MEMORYVIEW_RELEASED)
return PyUnicode_FromFormat("<released memory at %p>", self);
else
return PyUnicode_FromFormat("<memory at %p>", self);
}
static char *
lookup_dimension(const Py_buffer *view, char *ptr, int dim, Py_ssize_t index)
{
Py_ssize_t nitems;
assert(view->shape);
assert(view->strides);
nitems = view->shape[dim];
if (index < 0) {
index += nitems;
}
if (index < 0 || index >= nitems) {
PyErr_Format(PyExc_IndexError,
"index out of bounds on dimension %d", dim + 1);
return NULL;
}
ptr += view->strides[dim] * index;
ptr = ADJUST_PTR(ptr, view->suboffsets, dim);
return ptr;
}
static char *
ptr_from_index(const Py_buffer *view, Py_ssize_t index)
{
char *ptr = (char *)view->buf;
return lookup_dimension(view, ptr, 0, index);
}
static char *
ptr_from_tuple(const Py_buffer *view, PyObject *tup)
{
char *ptr = (char *)view->buf;
Py_ssize_t dim, nindices = PyTuple_GET_SIZE(tup);
if (nindices > view->ndim) {
PyErr_Format(PyExc_TypeError,
"cannot index %zd-dimension view with %zd-element tuple",
view->ndim, nindices);
return NULL;
}
for (dim = 0; dim < nindices; dim++) {
Py_ssize_t index;
index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(tup, dim),
PyExc_IndexError);
if (index == -1 && PyErr_Occurred())
return NULL;
ptr = lookup_dimension(view, ptr, (int)dim, index);
if (ptr == NULL)
return NULL;
}
return ptr;
}
static PyObject *
memory_item(PyMemoryViewObject *self, Py_ssize_t index)
{
Py_buffer *view = &(self->view);
const char *fmt;
CHECK_RELEASED(self);
fmt = adjust_fmt(view);
if (fmt == NULL)
return NULL;
if (view->ndim == 0) {
PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
return NULL;
}
if (view->ndim == 1) {
char *ptr = ptr_from_index(view, index);
if (ptr == NULL)
return NULL;
return unpack_single(self, ptr, fmt);
}
PyErr_SetString(PyExc_NotImplementedError,
"multi-dimensional sub-views are not implemented");
return NULL;
}
static PyObject *
memory_item_multi(PyMemoryViewObject *self, PyObject *tup)
{
Py_buffer *view = &(self->view);
const char *fmt;
Py_ssize_t nindices = PyTuple_GET_SIZE(tup);
char *ptr;
CHECK_RELEASED(self);
fmt = adjust_fmt(view);
if (fmt == NULL)
return NULL;
if (nindices < view->ndim) {
PyErr_SetString(PyExc_NotImplementedError,
"sub-views are not implemented");
return NULL;
}
ptr = ptr_from_tuple(view, tup);
if (ptr == NULL)
return NULL;
return unpack_single(self, ptr, fmt);
}
static inline int
init_slice(Py_buffer *base, PyObject *key, int dim)
{
Py_ssize_t start, stop, step, slicelength;
if (PySlice_Unpack(key, &start, &stop, &step) < 0) {
return -1;
}
slicelength = PySlice_AdjustIndices(base->shape[dim], &start, &stop, step);
if (base->suboffsets == NULL || dim == 0) {
adjust_buf:
base->buf = (char *)base->buf + base->strides[dim] * start;
}
else {
Py_ssize_t n = dim-1;
while (n >= 0 && base->suboffsets[n] < 0)
n--;
if (n < 0)
goto adjust_buf;
base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
}
base->shape[dim] = slicelength;
base->strides[dim] = base->strides[dim] * step;
return 0;
}
static int
is_multislice(PyObject *key)
{
Py_ssize_t size, i;
if (!PyTuple_Check(key))
return 0;
size = PyTuple_GET_SIZE(key);
if (size == 0)
return 0;
for (i = 0; i < size; i++) {
PyObject *x = PyTuple_GET_ITEM(key, i);
if (!PySlice_Check(x))
return 0;
}
return 1;
}
static Py_ssize_t
is_multiindex(PyObject *key)
{
Py_ssize_t size, i;
if (!PyTuple_Check(key))
return 0;
size = PyTuple_GET_SIZE(key);
for (i = 0; i < size; i++) {
PyObject *x = PyTuple_GET_ITEM(key, i);
if (!_PyIndex_Check(x)) {
return 0;
}
}
return 1;
}
static PyObject *
memory_subscript(PyMemoryViewObject *self, PyObject *key)
{
Py_buffer *view;
view = &(self->view);
CHECK_RELEASED(self);
if (view->ndim == 0) {
if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
const char *fmt = adjust_fmt(view);
if (fmt == NULL)
return NULL;
return unpack_single(self, view->buf, fmt);
}
else if (key == Py_Ellipsis) {
return Py_NewRef(self);
}
else {
PyErr_SetString(PyExc_TypeError,
"invalid indexing of 0-dim memory");
return NULL;
}
}
if (_PyIndex_Check(key)) {
Py_ssize_t index;
index = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (index == -1 && PyErr_Occurred())
return NULL;
return memory_item(self, index);
}
else if (PySlice_Check(key)) {
CHECK_RESTRICTED(self);
PyMemoryViewObject *sliced;
sliced = (PyMemoryViewObject *)mbuf_add_view(self->mbuf, view);
if (sliced == NULL)
return NULL;
if (init_slice(&sliced->view, key, 0) < 0) {
Py_DECREF(sliced);
return NULL;
}
init_len(&sliced->view);
init_flags(sliced);
return (PyObject *)sliced;
}
else if (is_multiindex(key)) {
return memory_item_multi(self, key);
}
else if (is_multislice(key)) {
PyErr_SetString(PyExc_NotImplementedError,
"multi-dimensional slicing is not implemented");
return NULL;
}
PyErr_SetString(PyExc_TypeError, "memoryview: invalid slice key");
return NULL;
}
static int
memory_ass_sub(PyMemoryViewObject *self, PyObject *key, PyObject *value)
{
Py_buffer *view = &(self->view);
Py_buffer src;
const char *fmt;
char *ptr;
CHECK_RELEASED_INT(self);
fmt = adjust_fmt(view);
if (fmt == NULL)
return -1;
if (view->readonly) {
PyErr_SetString(PyExc_TypeError, "cannot modify read-only memory");
return -1;
}
if (value == NULL) {
PyErr_SetString(PyExc_TypeError, "cannot delete memory");
return -1;
}
if (view->ndim == 0) {
if (key == Py_Ellipsis ||
(PyTuple_Check(key) && PyTuple_GET_SIZE(key)==0)) {
ptr = (char *)view->buf;
return pack_single(self, ptr, value, fmt);
}
else {
PyErr_SetString(PyExc_TypeError,
"invalid indexing of 0-dim memory");
return -1;
}
}
if (_PyIndex_Check(key)) {
Py_ssize_t index;
if (1 < view->ndim) {
PyErr_SetString(PyExc_NotImplementedError,
"sub-views are not implemented");
return -1;
}
index = PyNumber_AsSsize_t(key, PyExc_IndexError);
if (index == -1 && PyErr_Occurred())
return -1;
ptr = ptr_from_index(view, index);
if (ptr == NULL)
return -1;
return pack_single(self, ptr, value, fmt);
}
if (PySlice_Check(key) && view->ndim == 1) {
Py_buffer dest;
Py_ssize_t arrays[3];
int ret = -1;
if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) < 0)
return ret;
dest = *view;
dest.shape = &arrays[0]; dest.shape[0] = view->shape[0];
dest.strides = &arrays[1]; dest.strides[0] = view->strides[0];
if (view->suboffsets) {
dest.suboffsets = &arrays[2]; dest.suboffsets[0] = view->suboffsets[0];
}
if (init_slice(&dest, key, 0) < 0)
goto end_block;
dest.len = dest.shape[0] * dest.itemsize;
ret = copy_single(self, &dest, &src);
end_block:
PyBuffer_Release(&src);
return ret;
}
if (is_multiindex(key)) {
char *ptr;
if (PyTuple_GET_SIZE(key) < view->ndim) {
PyErr_SetString(PyExc_NotImplementedError,
"sub-views are not implemented");
return -1;
}
ptr = ptr_from_tuple(view, key);
if (ptr == NULL)
return -1;
return pack_single(self, ptr, value, fmt);
}
if (PySlice_Check(key) || is_multislice(key)) {
PyErr_SetString(PyExc_NotImplementedError,
"memoryview slice assignments are currently restricted "
"to ndim = 1");
return -1;
}
PyErr_SetString(PyExc_TypeError, "memoryview: invalid slice key");
return -1;
}
static Py_ssize_t
memory_length(PyMemoryViewObject *self)
{
CHECK_RELEASED_INT(self);
if (self->view.ndim == 0) {
PyErr_SetString(PyExc_TypeError, "0-dim memory has no length");
return -1;
}
return self->view.shape[0];
}
static PyMappingMethods memory_as_mapping = {
(lenfunc)memory_length,
(binaryfunc)memory_subscript,
(objobjargproc)memory_ass_sub,
};
static PySequenceMethods memory_as_sequence = {
(lenfunc)memory_length,
0,
0,
(ssizeargfunc)memory_item,
};
#define MV_COMPARE_EX -1
#define MV_COMPARE_NOT_IMPL -2
static int
fix_struct_error_int(void)
{
assert(PyErr_Occurred());
if (PyErr_ExceptionMatches(PyExc_ImportError) ||
PyErr_ExceptionMatches(PyExc_MemoryError)) {
return MV_COMPARE_EX;
}
PyErr_Clear();
return 0;
}
static int
struct_unpack_cmp(const char *p, const char *q,
struct unpacker *unpack_p, struct unpacker *unpack_q)
{
PyObject *v, *w;
int ret;
v = struct_unpack_single(p, unpack_p);
if (v == NULL)
return MV_COMPARE_EX;
w = struct_unpack_single(q, unpack_q);
if (w == NULL) {
Py_DECREF(v);
return MV_COMPARE_EX;
}
ret = PyObject_RichCompareBool(v, w, Py_EQ);
Py_DECREF(v);
Py_DECREF(w);
return ret;
}
#define CMP_SINGLE(p, q, type) \
do { \
type x; \
type y; \
memcpy((char *)&x, p, sizeof x); \
memcpy((char *)&y, q, sizeof y); \
equal = (x == y); \
} while (0)
static inline int
unpack_cmp(const char *p, const char *q, char fmt,
struct unpacker *unpack_p, struct unpacker *unpack_q)
{
int equal;
switch (fmt) {
case 'B': return *((const unsigned char *)p) == *((const unsigned char *)q);
case 'b': return *((const signed char *)p) == *((const signed char *)q);
case 'h': CMP_SINGLE(p, q, short); return equal;
case 'i': CMP_SINGLE(p, q, int); return equal;
case 'l': CMP_SINGLE(p, q, long); return equal;
case '?': CMP_SINGLE(p, q, _Bool); return equal;
case 'H': CMP_SINGLE(p, q, unsigned short); return equal;
case 'I': CMP_SINGLE(p, q, unsigned int); return equal;
case 'L': CMP_SINGLE(p, q, unsigned long); return equal;
case 'q': CMP_SINGLE(p, q, long long); return equal;
case 'Q': CMP_SINGLE(p, q, unsigned long long); return equal;
case 'n': CMP_SINGLE(p, q, Py_ssize_t); return equal;
case 'N': CMP_SINGLE(p, q, size_t); return equal;
case 'f': CMP_SINGLE(p, q, float); return equal;
case 'd': CMP_SINGLE(p, q, double); return equal;
case 'e': {
#if PY_LITTLE_ENDIAN
int endian = 1;
#else
int endian = 0;
#endif
double u = PyFloat_Unpack2(p, endian);
double v = PyFloat_Unpack2(q, endian);
return (u == v);
}
case 'c': return *p == *q;
case 'P': CMP_SINGLE(p, q, void *); return equal;
case '_':
assert(unpack_p);
assert(unpack_q);
return struct_unpack_cmp(p, q, unpack_p, unpack_q);
}
PyErr_SetString(PyExc_RuntimeError,
"memoryview: internal error in richcompare");
return MV_COMPARE_EX;
}
static int
cmp_base(const char *p, const char *q, const Py_ssize_t *shape,
const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets,
const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets,
char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q)
{
Py_ssize_t i;
int equal;
for (i = 0; i < shape[0]; p+=pstrides[0], q+=qstrides[0], i++) {
const char *xp = ADJUST_PTR(p, psuboffsets, 0);
const char *xq = ADJUST_PTR(q, qsuboffsets, 0);
equal = unpack_cmp(xp, xq, fmt, unpack_p, unpack_q);
if (equal <= 0)
return equal;
}
return 1;
}
static int
cmp_rec(const char *p, const char *q,
Py_ssize_t ndim, const Py_ssize_t *shape,
const Py_ssize_t *pstrides, const Py_ssize_t *psuboffsets,
const Py_ssize_t *qstrides, const Py_ssize_t *qsuboffsets,
char fmt, struct unpacker *unpack_p, struct unpacker *unpack_q)
{
Py_ssize_t i;
int equal;
assert(ndim >= 1);
assert(shape != NULL);
assert(pstrides != NULL);
assert(qstrides != NULL);
if (ndim == 1) {
return cmp_base(p, q, shape,
pstrides, psuboffsets,
qstrides, qsuboffsets,
fmt, unpack_p, unpack_q);
}
for (i = 0; i < shape[0]; p+=pstrides[0], q+=qstrides[0], i++) {
const char *xp = ADJUST_PTR(p, psuboffsets, 0);
const char *xq = ADJUST_PTR(q, qsuboffsets, 0);
equal = cmp_rec(xp, xq, ndim-1, shape+1,
pstrides+1, psuboffsets ? psuboffsets+1 : NULL,
qstrides+1, qsuboffsets ? qsuboffsets+1 : NULL,
fmt, unpack_p, unpack_q);
if (equal <= 0)
return equal;
}
return 1;
}
static PyObject *
memory_richcompare(PyObject *v, PyObject *w, int op)
{
PyObject *res;
Py_buffer wbuf, *vv;
Py_buffer *ww = NULL;
struct unpacker *unpack_v = NULL;
struct unpacker *unpack_w = NULL;
char vfmt, wfmt;
int equal = MV_COMPARE_NOT_IMPL;
if (op != Py_EQ && op != Py_NE)
goto result;
assert(PyMemoryView_Check(v));
if (BASE_INACCESSIBLE(v)) {
equal = (v == w);
goto result;
}
vv = VIEW_ADDR(v);
if (PyMemoryView_Check(w)) {
if (BASE_INACCESSIBLE(w)) {
equal = (v == w);
goto result;
}
ww = VIEW_ADDR(w);
}
else {
if (PyObject_GetBuffer(w, &wbuf, PyBUF_FULL_RO) < 0) {
PyErr_Clear();
goto result;
}
ww = &wbuf;
}
if (!equiv_shape(vv, ww)) {
PyErr_Clear();
equal = 0;
goto result;
}
if (get_native_fmtchar(&vfmt, vv->format) < 0)
vfmt = '_';
if (get_native_fmtchar(&wfmt, ww->format) < 0)
wfmt = '_';
if (vfmt == '_' || wfmt == '_' || vfmt != wfmt) {
vfmt = '_';
unpack_v = struct_get_unpacker(vv->format, vv->itemsize);
if (unpack_v == NULL) {
equal = fix_struct_error_int();
goto result;
}
unpack_w = struct_get_unpacker(ww->format, ww->itemsize);
if (unpack_w == NULL) {
equal = fix_struct_error_int();
goto result;
}
}
if (vv->ndim == 0) {
equal = unpack_cmp(vv->buf, ww->buf,
vfmt, unpack_v, unpack_w);
}
else if (vv->ndim == 1) {
equal = cmp_base(vv->buf, ww->buf, vv->shape,
vv->strides, vv->suboffsets,
ww->strides, ww->suboffsets,
vfmt, unpack_v, unpack_w);
}
else {
equal = cmp_rec(vv->buf, ww->buf, vv->ndim, vv->shape,
vv->strides, vv->suboffsets,
ww->strides, ww->suboffsets,
vfmt, unpack_v, unpack_w);
}
result:
if (equal < 0) {
if (equal == MV_COMPARE_NOT_IMPL)
res = Py_NotImplemented;
else
res = NULL;
}
else if ((equal && op == Py_EQ) || (!equal && op == Py_NE))
res = Py_True;
else
res = Py_False;
if (ww == &wbuf)
PyBuffer_Release(ww);
unpacker_free(unpack_v);
unpacker_free(unpack_w);
return Py_XNewRef(res);
}
static Py_hash_t
memory_hash(PyMemoryViewObject *self)
{
if (self->hash == -1) {
Py_buffer *view = &self->view;
char *mem = view->buf;
Py_ssize_t ret;
char fmt;
CHECK_RELEASED_INT(self);
if (!view->readonly) {
PyErr_SetString(PyExc_ValueError,
"cannot hash writable memoryview object");
return -1;
}
ret = get_native_fmtchar(&fmt, view->format);
if (ret < 0 || !IS_BYTE_FORMAT(fmt)) {
PyErr_SetString(PyExc_ValueError,
"memoryview: hashing is restricted to formats 'B', 'b' or 'c'");
return -1;
}
if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
return -1;
}
if (!MV_C_CONTIGUOUS(self->flags)) {
mem = PyMem_Malloc(view->len);
if (mem == NULL) {
PyErr_NoMemory();
return -1;
}
if (buffer_to_contiguous(mem, view, 'C') < 0) {
PyMem_Free(mem);
return -1;
}
}
self->hash = _Py_HashBytes(mem, view->len);
if (mem != view->buf)
PyMem_Free(mem);
}
return self->hash;
}
static PyObject *
_IntTupleFromSsizet(int len, Py_ssize_t *vals)
{
int i;
PyObject *o;
PyObject *intTuple;
if (vals == NULL)
return PyTuple_New(0);
intTuple = PyTuple_New(len);
if (!intTuple)
return NULL;
for (i=0; i<len; i++) {
o = PyLong_FromSsize_t(vals[i]);
if (!o) {
Py_DECREF(intTuple);
return NULL;
}
PyTuple_SET_ITEM(intTuple, i, o);
}
return intTuple;
}
static PyObject *
memory_obj_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
Py_buffer *view = &self->view;
CHECK_RELEASED(self);
if (view->obj == NULL) {
Py_RETURN_NONE;
}
return Py_NewRef(view->obj);
}
static PyObject *
memory_nbytes_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return PyLong_FromSsize_t(self->view.len);
}
static PyObject *
memory_format_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return PyUnicode_FromString(self->view.format);
}
static PyObject *
memory_itemsize_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return PyLong_FromSsize_t(self->view.itemsize);
}
static PyObject *
memory_shape_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return _IntTupleFromSsizet(self->view.ndim, self->view.shape);
}
static PyObject *
memory_strides_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return _IntTupleFromSsizet(self->view.ndim, self->view.strides);
}
static PyObject *
memory_suboffsets_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return _IntTupleFromSsizet(self->view.ndim, self->view.suboffsets);
}
static PyObject *
memory_readonly_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return PyBool_FromLong(self->view.readonly);
}
static PyObject *
memory_ndim_get(PyMemoryViewObject *self, void *Py_UNUSED(ignored))
{
CHECK_RELEASED(self);
return PyLong_FromLong(self->view.ndim);
}
static PyObject *
memory_c_contiguous(PyMemoryViewObject *self, PyObject *dummy)
{
CHECK_RELEASED(self);
return PyBool_FromLong(MV_C_CONTIGUOUS(self->flags));
}
static PyObject *
memory_f_contiguous(PyMemoryViewObject *self, PyObject *dummy)
{
CHECK_RELEASED(self);
return PyBool_FromLong(MV_F_CONTIGUOUS(self->flags));
}
static PyObject *
memory_contiguous(PyMemoryViewObject *self, PyObject *dummy)
{
CHECK_RELEASED(self);
return PyBool_FromLong(MV_ANY_CONTIGUOUS(self->flags));
}
PyDoc_STRVAR(memory_obj_doc,
"The underlying object of the memoryview.");
PyDoc_STRVAR(memory_nbytes_doc,
"The amount of space in bytes that the array would use in\n"
" a contiguous representation.");
PyDoc_STRVAR(memory_readonly_doc,
"A bool indicating whether the memory is read only.");
PyDoc_STRVAR(memory_itemsize_doc,
"The size in bytes of each element of the memoryview.");
PyDoc_STRVAR(memory_format_doc,
"A string containing the format (in struct module style)\n"
" for each element in the view.");
PyDoc_STRVAR(memory_ndim_doc,
"An integer indicating how many dimensions of a multi-dimensional\n"
" array the memory represents.");
PyDoc_STRVAR(memory_shape_doc,
"A tuple of ndim integers giving the shape of the memory\n"
" as an N-dimensional array.");
PyDoc_STRVAR(memory_strides_doc,
"A tuple of ndim integers giving the size in bytes to access\n"
" each element for each dimension of the array.");
PyDoc_STRVAR(memory_suboffsets_doc,
"A tuple of integers used internally for PIL-style arrays.");
PyDoc_STRVAR(memory_c_contiguous_doc,
"A bool indicating whether the memory is C contiguous.");
PyDoc_STRVAR(memory_f_contiguous_doc,
"A bool indicating whether the memory is Fortran contiguous.");
PyDoc_STRVAR(memory_contiguous_doc,
"A bool indicating whether the memory is contiguous.");
static PyGetSetDef memory_getsetlist[] = {
{"obj", (getter)memory_obj_get, NULL, memory_obj_doc},
{"nbytes", (getter)memory_nbytes_get, NULL, memory_nbytes_doc},
{"readonly", (getter)memory_readonly_get, NULL, memory_readonly_doc},
{"itemsize", (getter)memory_itemsize_get, NULL, memory_itemsize_doc},
{"format", (getter)memory_format_get, NULL, memory_format_doc},
{"ndim", (getter)memory_ndim_get, NULL, memory_ndim_doc},
{"shape", (getter)memory_shape_get, NULL, memory_shape_doc},
{"strides", (getter)memory_strides_get, NULL, memory_strides_doc},
{"suboffsets", (getter)memory_suboffsets_get, NULL, memory_suboffsets_doc},
{"c_contiguous", (getter)memory_c_contiguous, NULL, memory_c_contiguous_doc},
{"f_contiguous", (getter)memory_f_contiguous, NULL, memory_f_contiguous_doc},
{"contiguous", (getter)memory_contiguous, NULL, memory_contiguous_doc},
{NULL, NULL, NULL, NULL},
};
static PyMethodDef memory_methods[] = {
MEMORYVIEW_RELEASE_METHODDEF
MEMORYVIEW_TOBYTES_METHODDEF
MEMORYVIEW_HEX_METHODDEF
MEMORYVIEW_TOLIST_METHODDEF
MEMORYVIEW_CAST_METHODDEF
MEMORYVIEW_TOREADONLY_METHODDEF
MEMORYVIEW__FROM_FLAGS_METHODDEF
{"__enter__", memory_enter, METH_NOARGS, NULL},
{"__exit__", memory_exit, METH_VARARGS, NULL},
{NULL, NULL}
};
PyTypeObject _PyMemoryIter_Type;
typedef struct {
PyObject_HEAD
Py_ssize_t it_index;
PyMemoryViewObject *it_seq;
Py_ssize_t it_length;
const char *it_fmt;
} memoryiterobject;
static void
memoryiter_dealloc(memoryiterobject *it)
{
_PyObject_GC_UNTRACK(it);
Py_XDECREF(it->it_seq);
PyObject_GC_Del(it);
}
static int
memoryiter_traverse(memoryiterobject *it, visitproc visit, void *arg)
{
Py_VISIT(it->it_seq);
return 0;
}
static PyObject *
memoryiter_next(memoryiterobject *it)
{
PyMemoryViewObject *seq;
seq = it->it_seq;
if (seq == NULL) {
return NULL;
}
if (it->it_index < it->it_length) {
CHECK_RELEASED(seq);
Py_buffer *view = &(seq->view);
char *ptr = (char *)seq->view.buf;
ptr += view->strides[0] * it->it_index++;
ptr = ADJUST_PTR(ptr, view->suboffsets, 0);
if (ptr == NULL) {
return NULL;
}
return unpack_single(seq, ptr, it->it_fmt);
}
it->it_seq = NULL;
Py_DECREF(seq);
return NULL;
}
static PyObject *
memory_iter(PyObject *seq)
{
if (!PyMemoryView_Check(seq)) {
PyErr_BadInternalCall();
return NULL;
}
PyMemoryViewObject *obj = (PyMemoryViewObject *)seq;
int ndims = obj->view.ndim;
if (ndims == 0) {
PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
return NULL;
}
if (ndims != 1) {
PyErr_SetString(PyExc_NotImplementedError,
"multi-dimensional sub-views are not implemented");
return NULL;
}
const char *fmt = adjust_fmt(&obj->view);
if (fmt == NULL) {
return NULL;
}
memoryiterobject *it;
it = PyObject_GC_New(memoryiterobject, &_PyMemoryIter_Type);
if (it == NULL) {
return NULL;
}
it->it_fmt = fmt;
it->it_length = memory_length(obj);
it->it_index = 0;
it->it_seq = (PyMemoryViewObject*)Py_NewRef(obj);
_PyObject_GC_TRACK(it);
return (PyObject *)it;
}
PyTypeObject _PyMemoryIter_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
.tp_name = "memory_iterator",
.tp_basicsize = sizeof(memoryiterobject),
.tp_dealloc = (destructor)memoryiter_dealloc,
.tp_getattro = PyObject_GenericGetAttr,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
.tp_traverse = (traverseproc)memoryiter_traverse,
.tp_iter = PyObject_SelfIter,
.tp_iternext = (iternextfunc)memoryiter_next,
};
PyTypeObject PyMemoryView_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"memoryview",
offsetof(PyMemoryViewObject, ob_array),
sizeof(Py_ssize_t),
(destructor)memory_dealloc,
0,
0,
0,
0,
(reprfunc)memory_repr,
0,
&memory_as_sequence,
&memory_as_mapping,
(hashfunc)memory_hash,
0,
0,
PyObject_GenericGetAttr,
0,
&memory_as_buffer,
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_SEQUENCE,
memoryview__doc__,
(traverseproc)memory_traverse,
(inquiry)memory_clear,
memory_richcompare,
offsetof(PyMemoryViewObject, weakreflist),
memory_iter,
0,
memory_methods,
0,
memory_getsetlist,
0,
0,
0,
0,
0,
0,
0,
memoryview,
};