mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 07:01:21 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			2932 lines
		
	
	
	
		
			83 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2932 lines
		
	
	
	
		
			83 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* C Extension module to test all aspects of PEP-3118.
 | 
						|
   Written by Stefan Krah. */
 | 
						|
 | 
						|
#include "Python.h"
 | 
						|
 | 
						|
 | 
						|
/* struct module */
 | 
						|
static PyObject *structmodule = NULL;
 | 
						|
static PyObject *Struct = NULL;
 | 
						|
static PyObject *calcsize = NULL;
 | 
						|
 | 
						|
/* cache simple format string */
 | 
						|
static const char *simple_fmt = "B";
 | 
						|
static PyObject *simple_format = NULL;
 | 
						|
#define SIMPLE_FORMAT(fmt) (fmt == NULL || strcmp(fmt, "B") == 0)
 | 
						|
#define FIX_FORMAT(fmt) (fmt == NULL ? "B" : fmt)
 | 
						|
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/*                             NDArray Object                             */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
static PyTypeObject NDArray_Type;
 | 
						|
#define NDArray_Check(v) Py_IS_TYPE(v, &NDArray_Type)
 | 
						|
 | 
						|
#define CHECK_LIST_OR_TUPLE(v) \
 | 
						|
    do { \
 | 
						|
        if (!PyList_Check(v) && !PyTuple_Check(v)) { \
 | 
						|
            PyErr_SetString(PyExc_TypeError, \
 | 
						|
                            #v " must be a list or a tuple"); \
 | 
						|
            return NULL; \
 | 
						|
        } \
 | 
						|
    } while (0)
 | 
						|
 | 
						|
#define PyMem_XFree(v) \
 | 
						|
    do { if (v) PyMem_Free(v); } while (0)
 | 
						|
 | 
						|
/* Maximum number of dimensions. */
 | 
						|
#define ND_MAX_NDIM (2 * PyBUF_MAX_NDIM)
 | 
						|
 | 
						|
/* Check for the presence of suboffsets in the first dimension. */
 | 
						|
#define HAVE_PTR(suboffsets) (suboffsets && suboffsets[0] >= 0)
 | 
						|
/* Adjust ptr if suboffsets are present. */
 | 
						|
#define ADJUST_PTR(ptr, suboffsets) \
 | 
						|
    (HAVE_PTR(suboffsets) ? *((char**)ptr) + suboffsets[0] : ptr)
 | 
						|
 | 
						|
/* Default: NumPy style (strides), read-only, no var-export, C-style layout */
 | 
						|
#define ND_DEFAULT          0x000
 | 
						|
/* User configurable flags for the ndarray */
 | 
						|
#define ND_VAREXPORT        0x001   /* change layout while buffers are exported */
 | 
						|
/* User configurable flags for each base buffer */
 | 
						|
#define ND_WRITABLE         0x002   /* mark base buffer as writable */
 | 
						|
#define ND_FORTRAN          0x004   /* Fortran contiguous layout */
 | 
						|
#define ND_SCALAR           0x008   /* scalar: ndim = 0 */
 | 
						|
#define ND_PIL              0x010   /* convert to PIL-style array (suboffsets) */
 | 
						|
#define ND_REDIRECT         0x020   /* redirect buffer requests */
 | 
						|
#define ND_GETBUF_FAIL      0x040   /* trigger getbuffer failure */
 | 
						|
#define ND_GETBUF_UNDEFINED 0x080   /* undefined view.obj */
 | 
						|
/* Internal flags for the base buffer */
 | 
						|
#define ND_C                0x100   /* C contiguous layout (default) */
 | 
						|
#define ND_OWN_ARRAYS       0x200   /* consumer owns arrays */
 | 
						|
 | 
						|
/* ndarray properties */
 | 
						|
#define ND_IS_CONSUMER(nd) \
 | 
						|
    (((NDArrayObject *)nd)->head == &((NDArrayObject *)nd)->staticbuf)
 | 
						|
 | 
						|
/* ndbuf->flags properties */
 | 
						|
#define ND_C_CONTIGUOUS(flags) (!!(flags&(ND_SCALAR|ND_C)))
 | 
						|
#define ND_FORTRAN_CONTIGUOUS(flags) (!!(flags&(ND_SCALAR|ND_FORTRAN)))
 | 
						|
#define ND_ANY_CONTIGUOUS(flags) (!!(flags&(ND_SCALAR|ND_C|ND_FORTRAN)))
 | 
						|
 | 
						|
/* getbuffer() requests */
 | 
						|
#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)
 | 
						|
 | 
						|
 | 
						|
/* Single node of a list of base buffers. The list is needed to implement
 | 
						|
   changes in memory layout while exported buffers are active. */
 | 
						|
static PyTypeObject NDArray_Type;
 | 
						|
 | 
						|
struct ndbuf;
 | 
						|
typedef struct ndbuf {
 | 
						|
    struct ndbuf *next;
 | 
						|
    struct ndbuf *prev;
 | 
						|
    Py_ssize_t len;     /* length of data */
 | 
						|
    Py_ssize_t offset;  /* start of the array relative to data */
 | 
						|
    char *data;         /* raw data */
 | 
						|
    int flags;          /* capabilities of the base buffer */
 | 
						|
    Py_ssize_t exports; /* number of exports */
 | 
						|
    Py_buffer base;     /* base buffer */
 | 
						|
} ndbuf_t;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    PyObject_HEAD
 | 
						|
    int flags;          /* ndarray flags */
 | 
						|
    ndbuf_t staticbuf;  /* static buffer for re-exporting mode */
 | 
						|
    ndbuf_t *head;      /* currently active base buffer */
 | 
						|
} NDArrayObject;
 | 
						|
 | 
						|
 | 
						|
static ndbuf_t *
 | 
						|
ndbuf_new(Py_ssize_t nitems, Py_ssize_t itemsize, Py_ssize_t offset, int flags)
 | 
						|
{
 | 
						|
    ndbuf_t *ndbuf;
 | 
						|
    Py_buffer *base;
 | 
						|
    Py_ssize_t len;
 | 
						|
 | 
						|
    len = nitems * itemsize;
 | 
						|
    if (offset % itemsize) {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "offset must be a multiple of itemsize");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (offset < 0 || offset+itemsize > len) {
 | 
						|
        PyErr_SetString(PyExc_ValueError, "offset out of bounds");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ndbuf = PyMem_Malloc(sizeof *ndbuf);
 | 
						|
    if (ndbuf == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ndbuf->next = NULL;
 | 
						|
    ndbuf->prev = NULL;
 | 
						|
    ndbuf->len = len;
 | 
						|
    ndbuf->offset= offset;
 | 
						|
 | 
						|
    ndbuf->data = PyMem_Malloc(len);
 | 
						|
    if (ndbuf->data == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        PyMem_Free(ndbuf);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ndbuf->flags = flags;
 | 
						|
    ndbuf->exports = 0;
 | 
						|
 | 
						|
    base = &ndbuf->base;
 | 
						|
    base->obj = NULL;
 | 
						|
    base->buf = ndbuf->data;
 | 
						|
    base->len = len;
 | 
						|
    base->itemsize = 1;
 | 
						|
    base->readonly = 0;
 | 
						|
    base->format = NULL;
 | 
						|
    base->ndim = 1;
 | 
						|
    base->shape = NULL;
 | 
						|
    base->strides = NULL;
 | 
						|
    base->suboffsets = NULL;
 | 
						|
    base->internal = ndbuf;
 | 
						|
 | 
						|
    return ndbuf;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ndbuf_free(ndbuf_t *ndbuf)
 | 
						|
{
 | 
						|
    Py_buffer *base = &ndbuf->base;
 | 
						|
 | 
						|
    PyMem_XFree(ndbuf->data);
 | 
						|
    PyMem_XFree(base->format);
 | 
						|
    PyMem_XFree(base->shape);
 | 
						|
    PyMem_XFree(base->strides);
 | 
						|
    PyMem_XFree(base->suboffsets);
 | 
						|
 | 
						|
    PyMem_Free(ndbuf);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ndbuf_push(NDArrayObject *nd, ndbuf_t *elt)
 | 
						|
{
 | 
						|
    elt->next = nd->head;
 | 
						|
    if (nd->head) nd->head->prev = elt;
 | 
						|
    nd->head = elt;
 | 
						|
    elt->prev = NULL;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ndbuf_delete(NDArrayObject *nd, ndbuf_t *elt)
 | 
						|
{
 | 
						|
    if (elt->prev)
 | 
						|
        elt->prev->next = elt->next;
 | 
						|
    else
 | 
						|
        nd->head = elt->next;
 | 
						|
 | 
						|
    if (elt->next)
 | 
						|
        elt->next->prev = elt->prev;
 | 
						|
 | 
						|
    ndbuf_free(elt);
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ndbuf_pop(NDArrayObject *nd)
 | 
						|
{
 | 
						|
    ndbuf_delete(nd, nd->head);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
    NDArrayObject *nd;
 | 
						|
 | 
						|
    nd = PyObject_New(NDArrayObject, &NDArray_Type);
 | 
						|
    if (nd == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    nd->flags = 0;
 | 
						|
    nd->head = NULL;
 | 
						|
    return (PyObject *)nd;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ndarray_dealloc(PyObject *op)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    if (self->head) {
 | 
						|
        if (ND_IS_CONSUMER(self)) {
 | 
						|
            Py_buffer *base = &self->head->base;
 | 
						|
            if (self->head->flags & ND_OWN_ARRAYS) {
 | 
						|
                PyMem_XFree(base->shape);
 | 
						|
                PyMem_XFree(base->strides);
 | 
						|
                PyMem_XFree(base->suboffsets);
 | 
						|
            }
 | 
						|
            PyBuffer_Release(base);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            while (self->head)
 | 
						|
                ndbuf_pop(self);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    PyObject_Free(self);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
ndarray_init_staticbuf(PyObject *exporter, NDArrayObject *nd, int flags)
 | 
						|
{
 | 
						|
    Py_buffer *base = &nd->staticbuf.base;
 | 
						|
 | 
						|
    if (PyObject_GetBuffer(exporter, base, flags) < 0)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    nd->head = &nd->staticbuf;
 | 
						|
 | 
						|
    nd->head->next = NULL;
 | 
						|
    nd->head->prev = NULL;
 | 
						|
    nd->head->len = -1;
 | 
						|
    nd->head->offset = -1;
 | 
						|
    nd->head->data = NULL;
 | 
						|
 | 
						|
    nd->head->flags = base->readonly ? 0 : ND_WRITABLE;
 | 
						|
    nd->head->exports = 0;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
init_flags(ndbuf_t *ndbuf)
 | 
						|
{
 | 
						|
    if (ndbuf->base.ndim == 0)
 | 
						|
        ndbuf->flags |= ND_SCALAR;
 | 
						|
    if (ndbuf->base.suboffsets)
 | 
						|
        ndbuf->flags |= ND_PIL;
 | 
						|
    if (PyBuffer_IsContiguous(&ndbuf->base, 'C'))
 | 
						|
        ndbuf->flags |= ND_C;
 | 
						|
    if (PyBuffer_IsContiguous(&ndbuf->base, 'F'))
 | 
						|
        ndbuf->flags |= ND_FORTRAN;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/****************************************************************************/
 | 
						|
/*                          Buffer/List conversions                         */
 | 
						|
/****************************************************************************/
 | 
						|
 | 
						|
static Py_ssize_t *strides_from_shape(const ndbuf_t *, int flags);
 | 
						|
 | 
						|
/* Get number of members in a struct: see issue #12740 */
 | 
						|
typedef struct {
 | 
						|
    PyObject_HEAD
 | 
						|
    Py_ssize_t s_size;
 | 
						|
    Py_ssize_t s_len;
 | 
						|
} PyPartialStructObject;
 | 
						|
 | 
						|
static Py_ssize_t
 | 
						|
get_nmemb(PyObject *s)
 | 
						|
{
 | 
						|
    return ((PyPartialStructObject *)s)->s_len;
 | 
						|
}
 | 
						|
 | 
						|
/* Pack all items into the buffer of 'obj'. The 'format' parameter must be
 | 
						|
   in struct module syntax. For standard C types, a single item is an integer.
 | 
						|
   For compound types, a single item is a tuple of integers. */
 | 
						|
static int
 | 
						|
pack_from_list(PyObject *obj, PyObject *items, PyObject *format,
 | 
						|
               Py_ssize_t itemsize)
 | 
						|
{
 | 
						|
    PyObject *structobj, *pack_into;
 | 
						|
    PyObject *args, *offset;
 | 
						|
    PyObject *item, *tmp;
 | 
						|
    Py_ssize_t nitems; /* number of items */
 | 
						|
    Py_ssize_t nmemb;  /* number of members in a single item */
 | 
						|
    Py_ssize_t i, j;
 | 
						|
    int ret = 0;
 | 
						|
 | 
						|
    assert(PyObject_CheckBuffer(obj));
 | 
						|
    assert(PyList_Check(items) || PyTuple_Check(items));
 | 
						|
 | 
						|
    structobj = PyObject_CallFunctionObjArgs(Struct, format, NULL);
 | 
						|
    if (structobj == NULL)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    nitems = PySequence_Fast_GET_SIZE(items);
 | 
						|
    nmemb = get_nmemb(structobj);
 | 
						|
    assert(nmemb >= 1);
 | 
						|
 | 
						|
    pack_into = PyObject_GetAttrString(structobj, "pack_into");
 | 
						|
    if (pack_into == NULL) {
 | 
						|
        Py_DECREF(structobj);
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    /* nmemb >= 1 */
 | 
						|
    args = PyTuple_New(2 + nmemb);
 | 
						|
    if (args == NULL) {
 | 
						|
        Py_DECREF(pack_into);
 | 
						|
        Py_DECREF(structobj);
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    offset = NULL;
 | 
						|
    for (i = 0; i < nitems; i++) {
 | 
						|
        /* Loop invariant: args[j] are borrowed references or NULL. */
 | 
						|
        PyTuple_SET_ITEM(args, 0, obj);
 | 
						|
        for (j = 1; j < 2+nmemb; j++)
 | 
						|
            PyTuple_SET_ITEM(args, j, NULL);
 | 
						|
 | 
						|
        Py_XDECREF(offset);
 | 
						|
        offset = PyLong_FromSsize_t(i*itemsize);
 | 
						|
        if (offset == NULL) {
 | 
						|
            ret = -1;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        PyTuple_SET_ITEM(args, 1, offset);
 | 
						|
 | 
						|
        item = PySequence_Fast_GET_ITEM(items, i);
 | 
						|
        if ((PyBytes_Check(item) || PyLong_Check(item) ||
 | 
						|
             PyFloat_Check(item)) && nmemb == 1) {
 | 
						|
            PyTuple_SET_ITEM(args, 2, item);
 | 
						|
        }
 | 
						|
        else if ((PyList_Check(item) || PyTuple_Check(item)) &&
 | 
						|
                 PySequence_Length(item) == nmemb) {
 | 
						|
            for (j = 0; j < nmemb; j++) {
 | 
						|
                tmp = PySequence_Fast_GET_ITEM(item, j);
 | 
						|
                PyTuple_SET_ITEM(args, 2+j, tmp);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            PyErr_SetString(PyExc_ValueError,
 | 
						|
                "mismatch between initializer element and format string");
 | 
						|
            ret = -1;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
 | 
						|
        tmp = PyObject_CallObject(pack_into, args);
 | 
						|
        if (tmp == NULL) {
 | 
						|
            ret = -1;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
        Py_DECREF(tmp);
 | 
						|
    }
 | 
						|
 | 
						|
    Py_INCREF(obj); /* args[0] */
 | 
						|
    /* args[1]: offset is either NULL or should be dealloc'd */
 | 
						|
    for (i = 2; i < 2+nmemb; i++) {
 | 
						|
        tmp = PyTuple_GET_ITEM(args, i);
 | 
						|
        Py_XINCREF(tmp);
 | 
						|
    }
 | 
						|
    Py_DECREF(args);
 | 
						|
 | 
						|
    Py_DECREF(pack_into);
 | 
						|
    Py_DECREF(structobj);
 | 
						|
    return ret;
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* Pack single element */
 | 
						|
static int
 | 
						|
pack_single(char *ptr, PyObject *item, const char *fmt, Py_ssize_t itemsize)
 | 
						|
{
 | 
						|
    PyObject *structobj = NULL, *pack_into = NULL, *args = NULL;
 | 
						|
    PyObject *format = NULL, *mview = NULL, *zero = NULL;
 | 
						|
    Py_ssize_t i, nmemb;
 | 
						|
    int ret = -1;
 | 
						|
    PyObject *x;
 | 
						|
 | 
						|
    if (fmt == NULL) fmt = "B";
 | 
						|
 | 
						|
    format = PyUnicode_FromString(fmt);
 | 
						|
    if (format == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    structobj = PyObject_CallFunctionObjArgs(Struct, format, NULL);
 | 
						|
    if (structobj == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    nmemb = get_nmemb(structobj);
 | 
						|
    assert(nmemb >= 1);
 | 
						|
 | 
						|
    mview = PyMemoryView_FromMemory(ptr, itemsize, PyBUF_WRITE);
 | 
						|
    if (mview == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    zero = PyLong_FromLong(0);
 | 
						|
    if (zero == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    pack_into = PyObject_GetAttrString(structobj, "pack_into");
 | 
						|
    if (pack_into == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    args = PyTuple_New(2+nmemb);
 | 
						|
    if (args == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    PyTuple_SET_ITEM(args, 0, mview);
 | 
						|
    PyTuple_SET_ITEM(args, 1, zero);
 | 
						|
 | 
						|
    if ((PyBytes_Check(item) || PyLong_Check(item) ||
 | 
						|
         PyFloat_Check(item)) && nmemb == 1) {
 | 
						|
         PyTuple_SET_ITEM(args, 2, item);
 | 
						|
    }
 | 
						|
    else if ((PyList_Check(item) || PyTuple_Check(item)) &&
 | 
						|
             PySequence_Length(item) == nmemb) {
 | 
						|
        for (i = 0; i < nmemb; i++) {
 | 
						|
            x = PySequence_Fast_GET_ITEM(item, i);
 | 
						|
            PyTuple_SET_ITEM(args, 2+i, x);
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "mismatch between initializer element and format string");
 | 
						|
        goto args_out;
 | 
						|
    }
 | 
						|
 | 
						|
    x = PyObject_CallObject(pack_into, args);
 | 
						|
    if (x != NULL) {
 | 
						|
        Py_DECREF(x);
 | 
						|
        ret = 0;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
args_out:
 | 
						|
    for (i = 0; i < 2+nmemb; i++)
 | 
						|
        Py_XINCREF(PyTuple_GET_ITEM(args, i));
 | 
						|
    Py_XDECREF(args);
 | 
						|
out:
 | 
						|
    Py_XDECREF(pack_into);
 | 
						|
    Py_XDECREF(zero);
 | 
						|
    Py_XDECREF(mview);
 | 
						|
    Py_XDECREF(structobj);
 | 
						|
    Py_XDECREF(format);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
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) {
 | 
						|
        if (!HAVE_PTR(dsuboffsets) && !HAVE_PTR(ssuboffsets) &&
 | 
						|
            dstrides[0] == itemsize && sstrides[0] == itemsize) {
 | 
						|
            memmove(dptr, sptr, shape[0] * itemsize);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            char *p;
 | 
						|
            assert(mem != NULL);
 | 
						|
            for (i=0, p=mem; i<shape[0]; p+=itemsize, sptr+=sstrides[0], i++) {
 | 
						|
                char *xsptr = ADJUST_PTR(sptr, ssuboffsets);
 | 
						|
                memcpy(p, xsptr, itemsize);
 | 
						|
            }
 | 
						|
            for (i=0, p=mem; i<shape[0]; p+=itemsize, dptr+=dstrides[0], i++) {
 | 
						|
                char *xdptr = ADJUST_PTR(dptr, dsuboffsets);
 | 
						|
                memcpy(xdptr, p, itemsize);
 | 
						|
            }
 | 
						|
        }
 | 
						|
        return;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < shape[0]; dptr+=dstrides[0], sptr+=sstrides[0], i++) {
 | 
						|
        char *xdptr = ADJUST_PTR(dptr, dsuboffsets);
 | 
						|
        char *xsptr = ADJUST_PTR(sptr, ssuboffsets);
 | 
						|
 | 
						|
        copy_rec(shape+1, ndim-1, itemsize,
 | 
						|
                 xdptr, dstrides+1, dsuboffsets ? dsuboffsets+1 : NULL,
 | 
						|
                 xsptr, sstrides+1, ssuboffsets ? ssuboffsets+1 : NULL,
 | 
						|
                 mem);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
cmp_structure(Py_buffer *dest, Py_buffer *src)
 | 
						|
{
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    if (strcmp(FIX_FORMAT(dest->format), FIX_FORMAT(src->format)) != 0 ||
 | 
						|
        dest->itemsize != src->itemsize ||
 | 
						|
        dest->ndim != src->ndim)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    for (i = 0; i < dest->ndim; i++) {
 | 
						|
        if (dest->shape[i] != src->shape[i])
 | 
						|
            return -1;
 | 
						|
        if (dest->shape[i] == 0)
 | 
						|
            break;
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
/* Copy src to dest. Both buffers must have the same format, itemsize,
 | 
						|
   ndim and shape. Copying is atomic, the function never fails with
 | 
						|
   a partial copy. */
 | 
						|
static int
 | 
						|
copy_buffer(Py_buffer *dest, Py_buffer *src)
 | 
						|
{
 | 
						|
    char *mem = NULL;
 | 
						|
 | 
						|
    assert(dest->ndim > 0);
 | 
						|
 | 
						|
    if (cmp_structure(dest, src) < 0) {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "ndarray assignment: lvalue and rvalue have different structures");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((dest->suboffsets && dest->suboffsets[dest->ndim-1] >= 0) ||
 | 
						|
        (src->suboffsets && src->suboffsets[src->ndim-1] >= 0) ||
 | 
						|
        dest->strides[dest->ndim-1] != dest->itemsize ||
 | 
						|
        src->strides[src->ndim-1] != src->itemsize) {
 | 
						|
        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);
 | 
						|
 | 
						|
    PyMem_XFree(mem);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/* Unpack single element */
 | 
						|
static PyObject *
 | 
						|
unpack_single(char *ptr, const char *fmt, Py_ssize_t itemsize)
 | 
						|
{
 | 
						|
    PyObject *x, *unpack_from, *mview;
 | 
						|
 | 
						|
    if (fmt == NULL) {
 | 
						|
        fmt = "B";
 | 
						|
        itemsize = 1;
 | 
						|
    }
 | 
						|
 | 
						|
    unpack_from = PyObject_GetAttrString(structmodule, "unpack_from");
 | 
						|
    if (unpack_from == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    mview = PyMemoryView_FromMemory(ptr, itemsize, PyBUF_READ);
 | 
						|
    if (mview == NULL) {
 | 
						|
        Py_DECREF(unpack_from);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    x = PyObject_CallFunction(unpack_from, "sO", fmt, mview);
 | 
						|
    Py_DECREF(unpack_from);
 | 
						|
    Py_DECREF(mview);
 | 
						|
    if (x == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    if (PyTuple_GET_SIZE(x) == 1) {
 | 
						|
        PyObject *tmp = PyTuple_GET_ITEM(x, 0);
 | 
						|
        Py_INCREF(tmp);
 | 
						|
        Py_DECREF(x);
 | 
						|
        return tmp;
 | 
						|
    }
 | 
						|
 | 
						|
    return x;
 | 
						|
}
 | 
						|
 | 
						|
/* Unpack a multi-dimensional matrix into a nested list. Return a scalar
 | 
						|
   for ndim = 0. */
 | 
						|
static PyObject *
 | 
						|
unpack_rec(PyObject *unpack_from, char *ptr, PyObject *mview, char *item,
 | 
						|
           const Py_ssize_t *shape, const Py_ssize_t *strides,
 | 
						|
           const Py_ssize_t *suboffsets, Py_ssize_t ndim, Py_ssize_t itemsize)
 | 
						|
{
 | 
						|
    PyObject *lst, *x;
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    assert(ndim >= 0);
 | 
						|
    assert(shape != NULL);
 | 
						|
    assert(strides != NULL);
 | 
						|
 | 
						|
    if (ndim == 0) {
 | 
						|
        memcpy(item, ptr, itemsize);
 | 
						|
        x = PyObject_CallFunctionObjArgs(unpack_from, mview, NULL);
 | 
						|
        if (x == NULL)
 | 
						|
            return NULL;
 | 
						|
        if (PyTuple_GET_SIZE(x) == 1) {
 | 
						|
            PyObject *tmp = PyTuple_GET_ITEM(x, 0);
 | 
						|
            Py_INCREF(tmp);
 | 
						|
            Py_DECREF(x);
 | 
						|
            return tmp;
 | 
						|
        }
 | 
						|
        return x;
 | 
						|
    }
 | 
						|
 | 
						|
    lst = PyList_New(shape[0]);
 | 
						|
    if (lst == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
 | 
						|
        char *nextptr = ADJUST_PTR(ptr, suboffsets);
 | 
						|
 | 
						|
        x = unpack_rec(unpack_from, nextptr, mview, item,
 | 
						|
                       shape+1, strides+1, suboffsets ? suboffsets+1 : NULL,
 | 
						|
                       ndim-1, itemsize);
 | 
						|
        if (x == NULL) {
 | 
						|
            Py_DECREF(lst);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
 | 
						|
        PyList_SET_ITEM(lst, i, x);
 | 
						|
    }
 | 
						|
 | 
						|
    return lst;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_as_list(NDArrayObject *nd)
 | 
						|
{
 | 
						|
    PyObject *structobj = NULL, *unpack_from = NULL;
 | 
						|
    PyObject *lst = NULL, *mview = NULL;
 | 
						|
    Py_buffer *base = &nd->head->base;
 | 
						|
    Py_ssize_t *shape = base->shape;
 | 
						|
    Py_ssize_t *strides = base->strides;
 | 
						|
    Py_ssize_t simple_shape[1];
 | 
						|
    Py_ssize_t simple_strides[1];
 | 
						|
    char *item = NULL;
 | 
						|
    PyObject *format;
 | 
						|
    char *fmt = base->format;
 | 
						|
 | 
						|
    base = &nd->head->base;
 | 
						|
 | 
						|
    if (fmt == NULL) {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "ndarray: tolist() does not support format=NULL, use "
 | 
						|
            "tobytes()");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (shape == NULL) {
 | 
						|
        assert(ND_C_CONTIGUOUS(nd->head->flags));
 | 
						|
        assert(base->strides == NULL);
 | 
						|
        assert(base->ndim <= 1);
 | 
						|
        shape = simple_shape;
 | 
						|
        shape[0] = base->len;
 | 
						|
        strides = simple_strides;
 | 
						|
        strides[0] = base->itemsize;
 | 
						|
    }
 | 
						|
    else if (strides == NULL) {
 | 
						|
        assert(ND_C_CONTIGUOUS(nd->head->flags));
 | 
						|
        strides = strides_from_shape(nd->head, 0);
 | 
						|
        if (strides == NULL)
 | 
						|
            return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    format = PyUnicode_FromString(fmt);
 | 
						|
    if (format == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    structobj = PyObject_CallFunctionObjArgs(Struct, format, NULL);
 | 
						|
    Py_DECREF(format);
 | 
						|
    if (structobj == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    unpack_from = PyObject_GetAttrString(structobj, "unpack_from");
 | 
						|
    if (unpack_from == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    item = PyMem_Malloc(base->itemsize);
 | 
						|
    if (item == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    mview = PyMemoryView_FromMemory(item, base->itemsize, PyBUF_WRITE);
 | 
						|
    if (mview == NULL)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    lst = unpack_rec(unpack_from, base->buf, mview, item,
 | 
						|
                     shape, strides, base->suboffsets,
 | 
						|
                     base->ndim, base->itemsize);
 | 
						|
 | 
						|
out:
 | 
						|
    Py_XDECREF(mview);
 | 
						|
    PyMem_XFree(item);
 | 
						|
    Py_XDECREF(unpack_from);
 | 
						|
    Py_XDECREF(structobj);
 | 
						|
    if (strides != base->strides && strides != simple_strides)
 | 
						|
        PyMem_XFree(strides);
 | 
						|
 | 
						|
    return lst;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
/****************************************************************************/
 | 
						|
/*                            Initialize ndbuf                              */
 | 
						|
/****************************************************************************/
 | 
						|
 | 
						|
/*
 | 
						|
   State of a new ndbuf during initialization. 'OK' means that initialization
 | 
						|
   is complete. 'PTR' means that a pointer has been initialized, but the
 | 
						|
   state of the memory is still undefined and ndbuf->offset is disregarded.
 | 
						|
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  |                 | ndbuf_new | init_simple | init_structure |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | next            | OK (NULL) |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | prev            | OK (NULL) |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | len             |    OK     |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | offset          |    OK     |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | data            |    PTR    |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | flags           |    user   |    user     |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | exports         |   OK (0)  |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.obj        | OK (NULL) |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.buf        |    PTR    |     PTR     |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.len        | len(data) |  len(data)  |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.itemsize   |     1     |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.readonly   |     0     |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.format     |    NULL   |     OK      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.ndim       |     1     |      1      |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.shape      |    NULL   |    NULL     |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.strides    |    NULL   |    NULL     |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.suboffsets |    NULL   |    NULL     |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
  | base.internal   |    OK     |    OK       |       OK       |
 | 
						|
  +-----------------+-----------+-------------+----------------+
 | 
						|
 | 
						|
*/
 | 
						|
 | 
						|
static Py_ssize_t
 | 
						|
get_itemsize(PyObject *format)
 | 
						|
{
 | 
						|
    PyObject *tmp;
 | 
						|
    Py_ssize_t itemsize;
 | 
						|
 | 
						|
    tmp = PyObject_CallFunctionObjArgs(calcsize, format, NULL);
 | 
						|
    if (tmp == NULL)
 | 
						|
        return -1;
 | 
						|
    itemsize = PyLong_AsSsize_t(tmp);
 | 
						|
    Py_DECREF(tmp);
 | 
						|
 | 
						|
    return itemsize;
 | 
						|
}
 | 
						|
 | 
						|
static char *
 | 
						|
get_format(PyObject *format)
 | 
						|
{
 | 
						|
    PyObject *tmp;
 | 
						|
    char *fmt;
 | 
						|
 | 
						|
    tmp = PyUnicode_AsASCIIString(format);
 | 
						|
    if (tmp == NULL)
 | 
						|
        return NULL;
 | 
						|
    fmt = PyMem_Malloc(PyBytes_GET_SIZE(tmp)+1);
 | 
						|
    if (fmt == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        Py_DECREF(tmp);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    strcpy(fmt, PyBytes_AS_STRING(tmp));
 | 
						|
    Py_DECREF(tmp);
 | 
						|
 | 
						|
    return fmt;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
init_simple(ndbuf_t *ndbuf, PyObject *items, PyObject *format,
 | 
						|
            Py_ssize_t itemsize)
 | 
						|
{
 | 
						|
    PyObject *mview;
 | 
						|
    Py_buffer *base = &ndbuf->base;
 | 
						|
    int ret;
 | 
						|
 | 
						|
    mview = PyMemoryView_FromBuffer(base);
 | 
						|
    if (mview == NULL)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    ret = pack_from_list(mview, items, format, itemsize);
 | 
						|
    Py_DECREF(mview);
 | 
						|
    if (ret < 0)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    base->readonly = !(ndbuf->flags & ND_WRITABLE);
 | 
						|
    base->itemsize = itemsize;
 | 
						|
    base->format = get_format(format);
 | 
						|
    if (base->format == NULL)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static Py_ssize_t *
 | 
						|
seq_as_ssize_array(PyObject *seq, Py_ssize_t len, int is_shape)
 | 
						|
{
 | 
						|
    Py_ssize_t *dest;
 | 
						|
    Py_ssize_t x, i;
 | 
						|
 | 
						|
    /* ndim = len <= ND_MAX_NDIM, so PyMem_New() is actually not needed. */
 | 
						|
    dest = PyMem_New(Py_ssize_t, len);
 | 
						|
    if (dest == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < len; i++) {
 | 
						|
        PyObject *tmp = PySequence_Fast_GET_ITEM(seq, i);
 | 
						|
        if (!PyLong_Check(tmp)) {
 | 
						|
            PyErr_Format(PyExc_ValueError,
 | 
						|
                "elements of %s must be integers",
 | 
						|
                is_shape ? "shape" : "strides");
 | 
						|
            PyMem_Free(dest);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        x = PyLong_AsSsize_t(tmp);
 | 
						|
        if (PyErr_Occurred()) {
 | 
						|
            PyMem_Free(dest);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        if (is_shape && x < 0) {
 | 
						|
            PyErr_Format(PyExc_ValueError,
 | 
						|
                "elements of shape must be integers >= 0");
 | 
						|
            PyMem_Free(dest);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        dest[i] = x;
 | 
						|
    }
 | 
						|
 | 
						|
    return dest;
 | 
						|
}
 | 
						|
 | 
						|
static Py_ssize_t *
 | 
						|
strides_from_shape(const ndbuf_t *ndbuf, int flags)
 | 
						|
{
 | 
						|
    const Py_buffer *base = &ndbuf->base;
 | 
						|
    Py_ssize_t *s, i;
 | 
						|
 | 
						|
    s = PyMem_Malloc(base->ndim * (sizeof *s));
 | 
						|
    if (s == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (flags & ND_FORTRAN) {
 | 
						|
        s[0] = base->itemsize;
 | 
						|
        for (i = 1; i < base->ndim; i++)
 | 
						|
            s[i] = s[i-1] * base->shape[i-1];
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        s[base->ndim-1] = base->itemsize;
 | 
						|
        for (i = base->ndim-2; i >= 0; i--)
 | 
						|
            s[i] = s[i+1] * base->shape[i+1];
 | 
						|
    }
 | 
						|
 | 
						|
    return s;
 | 
						|
}
 | 
						|
 | 
						|
/* Bounds check:
 | 
						|
 | 
						|
     len := complete length of allocated memory
 | 
						|
     offset := start of the array
 | 
						|
 | 
						|
     A single array element is indexed by:
 | 
						|
 | 
						|
       i = indices[0] * strides[0] + indices[1] * strides[1] + ...
 | 
						|
 | 
						|
     imin is reached when all indices[n] combined with positive strides are 0
 | 
						|
     and all indices combined with negative strides are shape[n]-1, which is
 | 
						|
     the maximum index for the nth dimension.
 | 
						|
 | 
						|
     imax is reached when all indices[n] combined with negative strides are 0
 | 
						|
     and all indices combined with positive strides are shape[n]-1.
 | 
						|
*/
 | 
						|
static int
 | 
						|
verify_structure(Py_ssize_t len, Py_ssize_t itemsize, Py_ssize_t offset,
 | 
						|
                 const Py_ssize_t *shape, const Py_ssize_t *strides,
 | 
						|
                 Py_ssize_t ndim)
 | 
						|
{
 | 
						|
    Py_ssize_t imin, imax;
 | 
						|
    Py_ssize_t n;
 | 
						|
 | 
						|
    assert(ndim >= 0);
 | 
						|
 | 
						|
    if (ndim == 0 && (offset < 0 || offset+itemsize > len))
 | 
						|
        goto invalid_combination;
 | 
						|
 | 
						|
    for (n = 0; n < ndim; n++)
 | 
						|
        if (strides[n] % itemsize) {
 | 
						|
            PyErr_SetString(PyExc_ValueError,
 | 
						|
            "strides must be a multiple of itemsize");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
    for (n = 0; n < ndim; n++)
 | 
						|
        if (shape[n] == 0)
 | 
						|
            return 0;
 | 
						|
 | 
						|
    imin = imax = 0;
 | 
						|
    for (n = 0; n < ndim; n++)
 | 
						|
        if (strides[n] <= 0)
 | 
						|
            imin += (shape[n]-1) * strides[n];
 | 
						|
        else
 | 
						|
            imax += (shape[n]-1) * strides[n];
 | 
						|
 | 
						|
    if (imin + offset < 0 || imax + offset + itemsize > len)
 | 
						|
        goto invalid_combination;
 | 
						|
 | 
						|
    return 0;
 | 
						|
 | 
						|
 | 
						|
invalid_combination:
 | 
						|
    PyErr_SetString(PyExc_ValueError,
 | 
						|
        "invalid combination of buffer, shape and strides");
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
   Convert a NumPy-style array to an array using suboffsets to stride in
 | 
						|
   the first dimension. Requirements: ndim > 0.
 | 
						|
 | 
						|
   Contiguous example
 | 
						|
   ==================
 | 
						|
 | 
						|
     Input:
 | 
						|
     ------
 | 
						|
       shape      = {2, 2, 3};
 | 
						|
       strides    = {6, 3, 1};
 | 
						|
       suboffsets = NULL;
 | 
						|
       data       = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
 | 
						|
       buf        = &data[0]
 | 
						|
 | 
						|
     Output:
 | 
						|
     -------
 | 
						|
       shape      = {2, 2, 3};
 | 
						|
       strides    = {sizeof(char *), 3, 1};
 | 
						|
       suboffsets = {0, -1, -1};
 | 
						|
       data       = {p1, p2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
 | 
						|
                     |   |   ^                 ^
 | 
						|
                     `---'---'                 |
 | 
						|
                         |                     |
 | 
						|
                         `---------------------'
 | 
						|
       buf        = &data[0]
 | 
						|
 | 
						|
     So, in the example the input resembles the three-dimensional array
 | 
						|
     char v[2][2][3], while the output resembles an array of two pointers
 | 
						|
     to two-dimensional arrays: char (*v[2])[2][3].
 | 
						|
 | 
						|
 | 
						|
   Non-contiguous example:
 | 
						|
   =======================
 | 
						|
 | 
						|
     Input (with offset and negative strides):
 | 
						|
     -----------------------------------------
 | 
						|
       shape      = {2, 2, 3};
 | 
						|
       strides    = {-6, 3, -1};
 | 
						|
       offset     = 8
 | 
						|
       suboffsets = NULL;
 | 
						|
       data       = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
 | 
						|
 | 
						|
     Output:
 | 
						|
     -------
 | 
						|
       shape      = {2, 2, 3};
 | 
						|
       strides    = {-sizeof(char *), 3, -1};
 | 
						|
       suboffsets = {2, -1, -1};
 | 
						|
       newdata    = {p1, p2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
 | 
						|
                     |   |   ^     ^           ^     ^
 | 
						|
                     `---'---'     |           |     `- p2+suboffsets[0]
 | 
						|
                         |         `-----------|--- p1+suboffsets[0]
 | 
						|
                         `---------------------'
 | 
						|
       buf        = &newdata[1]  # striding backwards over the pointers.
 | 
						|
 | 
						|
     suboffsets[0] is the same as the offset that one would specify if
 | 
						|
     the two {2, 3} subarrays were created directly, hence the name.
 | 
						|
*/
 | 
						|
static int
 | 
						|
init_suboffsets(ndbuf_t *ndbuf)
 | 
						|
{
 | 
						|
    Py_buffer *base = &ndbuf->base;
 | 
						|
    Py_ssize_t start, step;
 | 
						|
    Py_ssize_t imin, suboffset0;
 | 
						|
    Py_ssize_t addsize;
 | 
						|
    Py_ssize_t n;
 | 
						|
    char *data;
 | 
						|
 | 
						|
    assert(base->ndim > 0);
 | 
						|
    assert(base->suboffsets == NULL);
 | 
						|
 | 
						|
    /* Allocate new data with additional space for shape[0] pointers. */
 | 
						|
    addsize = base->shape[0] * (sizeof (char *));
 | 
						|
 | 
						|
    /* Align array start to a multiple of 8. */
 | 
						|
    addsize = 8 * ((addsize + 7) / 8);
 | 
						|
 | 
						|
    data = PyMem_Malloc(ndbuf->len + addsize);
 | 
						|
    if (data == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    memcpy(data + addsize, ndbuf->data, ndbuf->len);
 | 
						|
 | 
						|
    PyMem_Free(ndbuf->data);
 | 
						|
    ndbuf->data = data;
 | 
						|
    ndbuf->len += addsize;
 | 
						|
    base->buf = ndbuf->data;
 | 
						|
 | 
						|
    /* imin: minimum index of the input array relative to ndbuf->offset.
 | 
						|
       suboffset0: offset for each sub-array of the output. This is the
 | 
						|
                   same as calculating -imin' for a sub-array of ndim-1. */
 | 
						|
    imin = suboffset0 = 0;
 | 
						|
    for (n = 0; n < base->ndim; n++) {
 | 
						|
        if (base->shape[n] == 0)
 | 
						|
            break;
 | 
						|
        if (base->strides[n] <= 0) {
 | 
						|
            Py_ssize_t x = (base->shape[n]-1) * base->strides[n];
 | 
						|
            imin += x;
 | 
						|
            suboffset0 += (n >= 1) ? -x : 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* Initialize the array of pointers to the sub-arrays. */
 | 
						|
    start = addsize + ndbuf->offset + imin;
 | 
						|
    step = base->strides[0] < 0 ? -base->strides[0] : base->strides[0];
 | 
						|
 | 
						|
    for (n = 0; n < base->shape[0]; n++)
 | 
						|
        ((char **)base->buf)[n] = (char *)base->buf + start + n*step;
 | 
						|
 | 
						|
    /* Initialize suboffsets. */
 | 
						|
    base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
 | 
						|
    if (base->suboffsets == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    base->suboffsets[0] = suboffset0;
 | 
						|
    for (n = 1; n < base->ndim; n++)
 | 
						|
        base->suboffsets[n] = -1;
 | 
						|
 | 
						|
    /* Adjust strides for the first (zeroth) dimension. */
 | 
						|
    if (base->strides[0] >= 0) {
 | 
						|
        base->strides[0] = sizeof(char *);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        /* Striding backwards. */
 | 
						|
        base->strides[0] = -(Py_ssize_t)sizeof(char *);
 | 
						|
        if (base->shape[0] > 0)
 | 
						|
            base->buf = (char *)base->buf + (base->shape[0]-1) * sizeof(char *);
 | 
						|
    }
 | 
						|
 | 
						|
    ndbuf->flags &= ~(ND_C|ND_FORTRAN);
 | 
						|
    ndbuf->offset = 0;
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
init_len(Py_buffer *base)
 | 
						|
{
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    base->len = 1;
 | 
						|
    for (i = 0; i < base->ndim; i++)
 | 
						|
        base->len *= base->shape[i];
 | 
						|
    base->len *= base->itemsize;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
init_structure(ndbuf_t *ndbuf, PyObject *shape, PyObject *strides,
 | 
						|
               Py_ssize_t ndim)
 | 
						|
{
 | 
						|
    Py_buffer *base = &ndbuf->base;
 | 
						|
 | 
						|
    base->ndim = (int)ndim;
 | 
						|
    if (ndim == 0) {
 | 
						|
        if (ndbuf->flags & ND_PIL) {
 | 
						|
            PyErr_SetString(PyExc_TypeError,
 | 
						|
                "ndim = 0 cannot be used in conjunction with ND_PIL");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        ndbuf->flags |= (ND_SCALAR|ND_C|ND_FORTRAN);
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    /* shape */
 | 
						|
    base->shape = seq_as_ssize_array(shape, ndim, 1);
 | 
						|
    if (base->shape == NULL)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    /* strides */
 | 
						|
    if (strides) {
 | 
						|
        base->strides = seq_as_ssize_array(strides, ndim, 0);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        base->strides = strides_from_shape(ndbuf, ndbuf->flags);
 | 
						|
    }
 | 
						|
    if (base->strides == NULL)
 | 
						|
        return -1;
 | 
						|
    if (verify_structure(base->len, base->itemsize, ndbuf->offset,
 | 
						|
                         base->shape, base->strides, ndim) < 0)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    /* buf */
 | 
						|
    base->buf = ndbuf->data + ndbuf->offset;
 | 
						|
 | 
						|
    /* len */
 | 
						|
    init_len(base);
 | 
						|
 | 
						|
    /* ndbuf->flags */
 | 
						|
    if (PyBuffer_IsContiguous(base, 'C'))
 | 
						|
        ndbuf->flags |= ND_C;
 | 
						|
    if (PyBuffer_IsContiguous(base, 'F'))
 | 
						|
        ndbuf->flags |= ND_FORTRAN;
 | 
						|
 | 
						|
 | 
						|
    /* convert numpy array to suboffset representation */
 | 
						|
    if (ndbuf->flags & ND_PIL) {
 | 
						|
        /* modifies base->buf, base->strides and base->suboffsets **/
 | 
						|
        return init_suboffsets(ndbuf);
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static ndbuf_t *
 | 
						|
init_ndbuf(PyObject *items, PyObject *shape, PyObject *strides,
 | 
						|
           Py_ssize_t offset, PyObject *format, int flags)
 | 
						|
{
 | 
						|
    ndbuf_t *ndbuf;
 | 
						|
    Py_ssize_t ndim;
 | 
						|
    Py_ssize_t nitems;
 | 
						|
    Py_ssize_t itemsize;
 | 
						|
 | 
						|
    /* ndim = len(shape) */
 | 
						|
    CHECK_LIST_OR_TUPLE(shape);
 | 
						|
    ndim = PySequence_Fast_GET_SIZE(shape);
 | 
						|
    if (ndim > ND_MAX_NDIM) {
 | 
						|
        PyErr_Format(PyExc_ValueError,
 | 
						|
            "ndim must not exceed %d", ND_MAX_NDIM);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    /* len(strides) = len(shape) */
 | 
						|
    if (strides) {
 | 
						|
        CHECK_LIST_OR_TUPLE(strides);
 | 
						|
        if (PySequence_Fast_GET_SIZE(strides) == 0)
 | 
						|
            strides = NULL;
 | 
						|
        else if (flags & ND_FORTRAN) {
 | 
						|
            PyErr_SetString(PyExc_TypeError,
 | 
						|
                "ND_FORTRAN cannot be used together with strides");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        else if (PySequence_Fast_GET_SIZE(strides) != ndim) {
 | 
						|
            PyErr_SetString(PyExc_ValueError,
 | 
						|
                "len(shape) != len(strides)");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    /* itemsize */
 | 
						|
    itemsize = get_itemsize(format);
 | 
						|
    if (itemsize <= 0) {
 | 
						|
        if (itemsize == 0) {
 | 
						|
            PyErr_SetString(PyExc_ValueError,
 | 
						|
                "itemsize must not be zero");
 | 
						|
        }
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    /* convert scalar to list */
 | 
						|
    if (ndim == 0) {
 | 
						|
        items = PyTuple_Pack(1, items);
 | 
						|
        if (items == NULL)
 | 
						|
            return NULL;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        CHECK_LIST_OR_TUPLE(items);
 | 
						|
        Py_INCREF(items);
 | 
						|
    }
 | 
						|
 | 
						|
    /* number of items */
 | 
						|
    nitems = PySequence_Fast_GET_SIZE(items);
 | 
						|
    if (nitems == 0) {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "initializer list or tuple must not be empty");
 | 
						|
        Py_DECREF(items);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ndbuf = ndbuf_new(nitems, itemsize, offset, flags);
 | 
						|
    if (ndbuf == NULL) {
 | 
						|
        Py_DECREF(items);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    if (init_simple(ndbuf, items, format, itemsize) < 0)
 | 
						|
        goto error;
 | 
						|
    if (init_structure(ndbuf, shape, strides, ndim) < 0)
 | 
						|
        goto error;
 | 
						|
 | 
						|
    Py_DECREF(items);
 | 
						|
    return ndbuf;
 | 
						|
 | 
						|
error:
 | 
						|
    Py_DECREF(items);
 | 
						|
    ndbuf_free(ndbuf);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
/* initialize and push a new base onto the linked list */
 | 
						|
static int
 | 
						|
ndarray_push_base(NDArrayObject *nd, PyObject *items,
 | 
						|
                  PyObject *shape, PyObject *strides,
 | 
						|
                  Py_ssize_t offset, PyObject *format, int flags)
 | 
						|
{
 | 
						|
    ndbuf_t *ndbuf;
 | 
						|
 | 
						|
    ndbuf = init_ndbuf(items, shape, strides, offset, format, flags);
 | 
						|
    if (ndbuf == NULL)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    ndbuf_push(nd, ndbuf);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
#define PyBUF_UNUSED 0x10000
 | 
						|
static int
 | 
						|
ndarray_init(PyObject *self, PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
    NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    static char *kwlist[] = {
 | 
						|
        "obj", "shape", "strides", "offset", "format", "flags", "getbuf", NULL
 | 
						|
    };
 | 
						|
    PyObject *v = NULL;  /* initializer: scalar, list, tuple or base object */
 | 
						|
    PyObject *shape = NULL;   /* size of each dimension */
 | 
						|
    PyObject *strides = NULL; /* number of bytes to the next elt in each dim */
 | 
						|
    Py_ssize_t offset = 0;            /* buffer offset */
 | 
						|
    PyObject *format = simple_format; /* struct module specifier: "B" */
 | 
						|
    int flags = ND_DEFAULT;           /* base buffer and ndarray flags */
 | 
						|
 | 
						|
    int getbuf = PyBUF_UNUSED; /* re-exporter: getbuffer request flags */
 | 
						|
 | 
						|
 | 
						|
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOnOii", kwlist,
 | 
						|
            &v, &shape, &strides, &offset, &format, &flags, &getbuf))
 | 
						|
        return -1;
 | 
						|
 | 
						|
    /* NDArrayObject is re-exporter */
 | 
						|
    if (PyObject_CheckBuffer(v) && shape == NULL) {
 | 
						|
        if (strides || offset || format != simple_format ||
 | 
						|
            !(flags == ND_DEFAULT || flags == ND_REDIRECT)) {
 | 
						|
            PyErr_SetString(PyExc_TypeError,
 | 
						|
               "construction from exporter object only takes 'obj', 'getbuf' "
 | 
						|
               "and 'flags' arguments");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
 | 
						|
        getbuf = (getbuf == PyBUF_UNUSED) ? PyBUF_FULL_RO : getbuf;
 | 
						|
 | 
						|
        if (ndarray_init_staticbuf(v, nd, getbuf) < 0)
 | 
						|
            return -1;
 | 
						|
 | 
						|
        init_flags(nd->head);
 | 
						|
        nd->head->flags |= flags;
 | 
						|
 | 
						|
        return 0;
 | 
						|
    }
 | 
						|
 | 
						|
    /* NDArrayObject is the original base object. */
 | 
						|
    if (getbuf != PyBUF_UNUSED) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "getbuf argument only valid for construction from exporter "
 | 
						|
            "object");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (shape == NULL) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "shape is a required argument when constructing from "
 | 
						|
            "list, tuple or scalar");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (flags & ND_VAREXPORT) {
 | 
						|
        nd->flags |= ND_VAREXPORT;
 | 
						|
        flags &= ~ND_VAREXPORT;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Initialize and push the first base buffer onto the linked list. */
 | 
						|
    return ndarray_push_base(nd, v, shape, strides, offset, format, flags);
 | 
						|
}
 | 
						|
 | 
						|
/* Push an additional base onto the linked list. */
 | 
						|
static PyObject *
 | 
						|
ndarray_push(PyObject *self, PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
    NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    static char *kwlist[] = {
 | 
						|
        "items", "shape", "strides", "offset", "format", "flags", NULL
 | 
						|
    };
 | 
						|
    PyObject *items = NULL;   /* initializer: scalar, list or tuple */
 | 
						|
    PyObject *shape = NULL;   /* size of each dimension */
 | 
						|
    PyObject *strides = NULL; /* number of bytes to the next elt in each dim */
 | 
						|
    PyObject *format = simple_format;  /* struct module specifier: "B" */
 | 
						|
    Py_ssize_t offset = 0;             /* buffer offset */
 | 
						|
    int flags = ND_DEFAULT;            /* base buffer flags */
 | 
						|
 | 
						|
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OnOi", kwlist,
 | 
						|
            &items, &shape, &strides, &offset, &format, &flags))
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    if (flags & ND_VAREXPORT) {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "ND_VAREXPORT flag can only be used during object creation");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (ND_IS_CONSUMER(nd)) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "structure of re-exporting object is immutable");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (!(nd->flags&ND_VAREXPORT) && nd->head->exports > 0) {
 | 
						|
        PyErr_Format(PyExc_BufferError,
 | 
						|
            "cannot change structure: %zd exported buffer%s",
 | 
						|
            nd->head->exports, nd->head->exports==1 ? "" : "s");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (ndarray_push_base(nd, items, shape, strides,
 | 
						|
                          offset, format, flags) < 0)
 | 
						|
        return NULL;
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/* Pop a base from the linked list (if possible). */
 | 
						|
static PyObject *
 | 
						|
ndarray_pop(PyObject *self, PyObject *dummy)
 | 
						|
{
 | 
						|
    NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    if (ND_IS_CONSUMER(nd)) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "structure of re-exporting object is immutable");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (nd->head->exports > 0) {
 | 
						|
        PyErr_Format(PyExc_BufferError,
 | 
						|
            "cannot change structure: %zd exported buffer%s",
 | 
						|
            nd->head->exports, nd->head->exports==1 ? "" : "s");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (nd->head->next == NULL) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "list only has a single base");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ndbuf_pop(nd);
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/*                               getbuffer                                */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
static int
 | 
						|
ndarray_getbuf(PyObject *op, Py_buffer *view, int flags)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    ndbuf_t *ndbuf = self->head;
 | 
						|
    Py_buffer *base = &ndbuf->base;
 | 
						|
    int baseflags = ndbuf->flags;
 | 
						|
 | 
						|
    /* redirect mode */
 | 
						|
    if (base->obj != NULL && (baseflags&ND_REDIRECT)) {
 | 
						|
        return PyObject_GetBuffer(base->obj, view, flags);
 | 
						|
    }
 | 
						|
 | 
						|
    /* start with complete information */
 | 
						|
    *view = *base;
 | 
						|
    view->obj = NULL;
 | 
						|
 | 
						|
    /* reconstruct format */
 | 
						|
    if (view->format == NULL)
 | 
						|
        view->format = "B";
 | 
						|
 | 
						|
    if (base->ndim != 0 &&
 | 
						|
        ((REQ_SHAPE(flags) && base->shape == NULL) ||
 | 
						|
         (REQ_STRIDES(flags) && base->strides == NULL))) {
 | 
						|
        /* The ndarray is a re-exporter that has been created without full
 | 
						|
           information for testing purposes. In this particular case the
 | 
						|
           ndarray is not a PEP-3118 compliant buffer provider. */
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "re-exporter does not provide format, shape or strides");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (baseflags & ND_GETBUF_FAIL) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "ND_GETBUF_FAIL: forced test exception");
 | 
						|
        if (baseflags & ND_GETBUF_UNDEFINED)
 | 
						|
            view->obj = (PyObject *)0x1; /* wrong but permitted in <= 3.2 */
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    if (REQ_WRITABLE(flags) && base->readonly) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "ndarray is not writable");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (!REQ_FORMAT(flags)) {
 | 
						|
        /* NULL indicates that the buffer's data type has been cast to 'B'.
 | 
						|
           view->itemsize is the _previous_ itemsize. If shape is present,
 | 
						|
           the equality product(shape) * itemsize = len still holds at this
 | 
						|
           point. The equality calcsize(format) = itemsize does _not_ hold
 | 
						|
           from here on! */
 | 
						|
        view->format = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (REQ_C_CONTIGUOUS(flags) && !ND_C_CONTIGUOUS(baseflags)) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "ndarray is not C-contiguous");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (REQ_F_CONTIGUOUS(flags) && !ND_FORTRAN_CONTIGUOUS(baseflags)) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "ndarray is not Fortran contiguous");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (REQ_ANY_CONTIGUOUS(flags) && !ND_ANY_CONTIGUOUS(baseflags)) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "ndarray is not contiguous");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (!REQ_INDIRECT(flags) && (baseflags & ND_PIL)) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "ndarray cannot be represented without suboffsets");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (!REQ_STRIDES(flags)) {
 | 
						|
        if (!ND_C_CONTIGUOUS(baseflags)) {
 | 
						|
            PyErr_SetString(PyExc_BufferError,
 | 
						|
                "ndarray is not C-contiguous");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        view->strides = NULL;
 | 
						|
    }
 | 
						|
    if (!REQ_SHAPE(flags)) {
 | 
						|
        /* PyBUF_SIMPLE or PyBUF_WRITABLE: at this point buf is C-contiguous,
 | 
						|
           so base->buf = ndbuf->data. */
 | 
						|
        if (view->format != NULL) {
 | 
						|
            /* PyBUF_SIMPLE|PyBUF_FORMAT and PyBUF_WRITABLE|PyBUF_FORMAT do
 | 
						|
               not make sense. */
 | 
						|
            PyErr_Format(PyExc_BufferError,
 | 
						|
                "ndarray: cannot cast to unsigned bytes if the format flag "
 | 
						|
                "is present");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
        /* product(shape) * itemsize = len and calcsize(format) = itemsize
 | 
						|
           do _not_ hold from here on! */
 | 
						|
        view->ndim = 1;
 | 
						|
        view->shape = NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    /* Ascertain that the new buffer has the same contiguity as the exporter */
 | 
						|
    if (ND_C_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'C') ||
 | 
						|
        /* skip cast to 1-d */
 | 
						|
        (view->format != NULL && view->shape != NULL &&
 | 
						|
         ND_FORTRAN_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'F')) ||
 | 
						|
        /* cast to 1-d */
 | 
						|
        (view->format == NULL && view->shape == NULL &&
 | 
						|
         !PyBuffer_IsContiguous(view, 'F'))) {
 | 
						|
        PyErr_SetString(PyExc_BufferError,
 | 
						|
            "ndarray: contiguity mismatch in getbuf()");
 | 
						|
            return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    view->obj = Py_NewRef(self);
 | 
						|
    self->head->exports++;
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
ndarray_releasebuf(PyObject *op, Py_buffer *view)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    if (!ND_IS_CONSUMER(self)) {
 | 
						|
        ndbuf_t *ndbuf = view->internal;
 | 
						|
        if (--ndbuf->exports == 0 && ndbuf != self->head)
 | 
						|
            ndbuf_delete(self, ndbuf);
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
static PyBufferProcs ndarray_as_buffer = {
 | 
						|
    ndarray_getbuf,         /* bf_getbuffer */
 | 
						|
    ndarray_releasebuf,     /* bf_releasebuffer */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/*                           indexing/slicing                             */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
static char *
 | 
						|
ptr_from_index(Py_buffer *base, Py_ssize_t index)
 | 
						|
{
 | 
						|
    char *ptr;
 | 
						|
    Py_ssize_t nitems; /* items in the first dimension */
 | 
						|
 | 
						|
    if (base->shape)
 | 
						|
        nitems = base->shape[0];
 | 
						|
    else {
 | 
						|
        assert(base->ndim == 1 && SIMPLE_FORMAT(base->format));
 | 
						|
        nitems = base->len;
 | 
						|
    }
 | 
						|
 | 
						|
    if (index < 0) {
 | 
						|
        index += nitems;
 | 
						|
    }
 | 
						|
    if (index < 0 || index >= nitems) {
 | 
						|
        PyErr_SetString(PyExc_IndexError, "index out of bounds");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ptr = (char *)base->buf;
 | 
						|
 | 
						|
    if (base->strides == NULL)
 | 
						|
         ptr += base->itemsize * index;
 | 
						|
    else
 | 
						|
         ptr += base->strides[0] * index;
 | 
						|
 | 
						|
    ptr = ADJUST_PTR(ptr, base->suboffsets);
 | 
						|
 | 
						|
    return ptr;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_item(PyObject *op, Py_ssize_t index)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject *)op;
 | 
						|
    ndbuf_t *ndbuf = self->head;
 | 
						|
    Py_buffer *base = &ndbuf->base;
 | 
						|
    char *ptr;
 | 
						|
 | 
						|
    if (base->ndim == 0) {
 | 
						|
        PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ptr = ptr_from_index(base, index);
 | 
						|
    if (ptr == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    if (base->ndim == 1) {
 | 
						|
        return unpack_single(ptr, base->format, base->itemsize);
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        NDArrayObject *nd;
 | 
						|
        Py_buffer *subview;
 | 
						|
 | 
						|
        nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
 | 
						|
        if (nd == NULL)
 | 
						|
            return NULL;
 | 
						|
 | 
						|
        if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
 | 
						|
            Py_DECREF(nd);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
 | 
						|
        subview = &nd->staticbuf.base;
 | 
						|
 | 
						|
        subview->buf = ptr;
 | 
						|
        subview->len /= subview->shape[0];
 | 
						|
 | 
						|
        subview->ndim--;
 | 
						|
        subview->shape++;
 | 
						|
        if (subview->strides) subview->strides++;
 | 
						|
        if (subview->suboffsets) subview->suboffsets++;
 | 
						|
 | 
						|
        init_flags(&nd->staticbuf);
 | 
						|
 | 
						|
        return (PyObject *)nd;
 | 
						|
    }
 | 
						|
}
 | 
						|
 | 
						|
/*
 | 
						|
  For each dimension, we get valid (start, stop, step, slicelength) quadruples
 | 
						|
  from PySlice_GetIndicesEx().
 | 
						|
 | 
						|
  Slicing NumPy arrays
 | 
						|
  ====================
 | 
						|
 | 
						|
    A pointer to an element in a NumPy array is defined by:
 | 
						|
 | 
						|
      ptr = (char *)buf + indices[0] * strides[0] +
 | 
						|
                          ... +
 | 
						|
                          indices[ndim-1] * strides[ndim-1]
 | 
						|
 | 
						|
    Adjust buf:
 | 
						|
    -----------
 | 
						|
      Adding start[n] for each dimension effectively adds the constant:
 | 
						|
 | 
						|
        c = start[0] * strides[0] + ... + start[ndim-1] * strides[ndim-1]
 | 
						|
 | 
						|
      Therefore init_slice() adds all start[n] directly to buf.
 | 
						|
 | 
						|
    Adjust shape:
 | 
						|
    -------------
 | 
						|
      Obviously shape[n] = slicelength[n]
 | 
						|
 | 
						|
    Adjust strides:
 | 
						|
    ---------------
 | 
						|
      In the original array, the next element in a dimension is reached
 | 
						|
      by adding strides[n] to the pointer. In the sliced array, elements
 | 
						|
      may be skipped, so the next element is reached by adding:
 | 
						|
 | 
						|
        strides[n] * step[n]
 | 
						|
 | 
						|
  Slicing PIL arrays
 | 
						|
  ==================
 | 
						|
 | 
						|
    Layout:
 | 
						|
    -------
 | 
						|
      In the first (zeroth) dimension, PIL arrays have an array of pointers
 | 
						|
      to sub-arrays of ndim-1. Striding in the first dimension is done by
 | 
						|
      getting the index of the nth pointer, dereference it and then add a
 | 
						|
      suboffset to it. The arrays pointed to can best be seen a regular
 | 
						|
      NumPy arrays.
 | 
						|
 | 
						|
    Adjust buf:
 | 
						|
    -----------
 | 
						|
      In the original array, buf points to a location (usually the start)
 | 
						|
      in the array of pointers. For the sliced array, start[0] can be
 | 
						|
      added to buf in the same manner as for NumPy arrays.
 | 
						|
 | 
						|
    Adjust suboffsets:
 | 
						|
    ------------------
 | 
						|
      Due to the dereferencing step in the addressing scheme, it is not
 | 
						|
      possible to adjust buf for higher dimensions. Recall that the
 | 
						|
      sub-arrays pointed to are regular NumPy arrays, so for each of
 | 
						|
      those arrays adding start[n] effectively adds the constant:
 | 
						|
 | 
						|
        c = start[1] * strides[1] + ... + start[ndim-1] * strides[ndim-1]
 | 
						|
 | 
						|
      This constant is added to suboffsets[0]. suboffsets[0] in turn is
 | 
						|
      added to each pointer right after dereferencing.
 | 
						|
 | 
						|
    Adjust shape and strides:
 | 
						|
    -------------------------
 | 
						|
      Shape and strides are not influenced by the dereferencing step, so
 | 
						|
      they are adjusted in the same manner as for NumPy arrays.
 | 
						|
 | 
						|
  Multiple levels of suboffsets
 | 
						|
  =============================
 | 
						|
 | 
						|
      For a construct like an array of pointers to array of pointers to
 | 
						|
      sub-arrays of ndim-2:
 | 
						|
 | 
						|
        suboffsets[0] = start[1] * strides[1]
 | 
						|
        suboffsets[1] = start[2] * strides[2] + ...
 | 
						|
*/
 | 
						|
static 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; /* all suboffsets are negative */
 | 
						|
        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
 | 
						|
copy_structure(Py_buffer *base)
 | 
						|
{
 | 
						|
    Py_ssize_t *shape = NULL, *strides = NULL, *suboffsets = NULL;
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    shape = PyMem_Malloc(base->ndim * (sizeof *shape));
 | 
						|
    strides = PyMem_Malloc(base->ndim * (sizeof *strides));
 | 
						|
    if (shape == NULL || strides == NULL)
 | 
						|
        goto err_nomem;
 | 
						|
 | 
						|
    suboffsets = NULL;
 | 
						|
    if (base->suboffsets) {
 | 
						|
        suboffsets = PyMem_Malloc(base->ndim * (sizeof *suboffsets));
 | 
						|
        if (suboffsets == NULL)
 | 
						|
            goto err_nomem;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < base->ndim; i++) {
 | 
						|
        shape[i] = base->shape[i];
 | 
						|
        strides[i] = base->strides[i];
 | 
						|
        if (suboffsets)
 | 
						|
            suboffsets[i] = base->suboffsets[i];
 | 
						|
    }
 | 
						|
 | 
						|
    base->shape = shape;
 | 
						|
    base->strides = strides;
 | 
						|
    base->suboffsets = suboffsets;
 | 
						|
 | 
						|
    return 0;
 | 
						|
 | 
						|
err_nomem:
 | 
						|
    PyErr_NoMemory();
 | 
						|
    PyMem_XFree(shape);
 | 
						|
    PyMem_XFree(strides);
 | 
						|
    PyMem_XFree(suboffsets);
 | 
						|
    return -1;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_subscript(PyObject *op, PyObject *key)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    NDArrayObject *nd;
 | 
						|
    ndbuf_t *ndbuf;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
 | 
						|
    if (base->ndim == 0) {
 | 
						|
        if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
 | 
						|
            return unpack_single(base->buf, base->format, base->itemsize);
 | 
						|
        }
 | 
						|
        else if (key == Py_Ellipsis) {
 | 
						|
            return Py_NewRef(self);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (PyIndex_Check(key)) {
 | 
						|
        Py_ssize_t index = PyLong_AsSsize_t(key);
 | 
						|
        if (index == -1 && PyErr_Occurred())
 | 
						|
            return NULL;
 | 
						|
        return ndarray_item(op, index);
 | 
						|
    }
 | 
						|
 | 
						|
    nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
 | 
						|
    if (nd == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    /* new ndarray is a consumer */
 | 
						|
    if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
 | 
						|
        Py_DECREF(nd);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    /* copy shape, strides and suboffsets */
 | 
						|
    ndbuf = nd->head;
 | 
						|
    base = &ndbuf->base;
 | 
						|
    if (copy_structure(base) < 0) {
 | 
						|
        Py_DECREF(nd);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    ndbuf->flags |= ND_OWN_ARRAYS;
 | 
						|
 | 
						|
    if (PySlice_Check(key)) {
 | 
						|
        /* one-dimensional slice */
 | 
						|
        if (init_slice(base, key, 0) < 0)
 | 
						|
            goto err_occurred;
 | 
						|
    }
 | 
						|
    else if (PyTuple_Check(key)) {
 | 
						|
        /* multi-dimensional slice */
 | 
						|
        PyObject *tuple = key;
 | 
						|
        Py_ssize_t i, n;
 | 
						|
 | 
						|
        n = PyTuple_GET_SIZE(tuple);
 | 
						|
        for (i = 0; i < n; i++) {
 | 
						|
            key = PyTuple_GET_ITEM(tuple, i);
 | 
						|
            if (!PySlice_Check(key))
 | 
						|
                goto type_error;
 | 
						|
            if (init_slice(base, key, (int)i) < 0)
 | 
						|
                goto err_occurred;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        goto type_error;
 | 
						|
    }
 | 
						|
 | 
						|
    init_len(base);
 | 
						|
    init_flags(ndbuf);
 | 
						|
 | 
						|
    return (PyObject *)nd;
 | 
						|
 | 
						|
 | 
						|
type_error:
 | 
						|
    PyErr_Format(PyExc_TypeError,
 | 
						|
        "cannot index memory using \"%.200s\"",
 | 
						|
        Py_TYPE(key)->tp_name);
 | 
						|
err_occurred:
 | 
						|
    Py_DECREF(nd);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static int
 | 
						|
ndarray_ass_subscript(PyObject *op, PyObject *key, PyObject *value)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    NDArrayObject *nd;
 | 
						|
    Py_buffer *dest = &self->head->base;
 | 
						|
    Py_buffer src;
 | 
						|
    char *ptr;
 | 
						|
    Py_ssize_t index;
 | 
						|
    int ret = -1;
 | 
						|
 | 
						|
    if (dest->readonly) {
 | 
						|
        PyErr_SetString(PyExc_TypeError, "ndarray is not writable");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (value == NULL) {
 | 
						|
        PyErr_SetString(PyExc_TypeError, "ndarray data cannot be deleted");
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (dest->ndim == 0) {
 | 
						|
        if (key == Py_Ellipsis ||
 | 
						|
            (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
 | 
						|
            ptr = (char *)dest->buf;
 | 
						|
            return pack_single(ptr, value, dest->format, dest->itemsize);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
 | 
						|
            return -1;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    if (dest->ndim == 1 && PyIndex_Check(key)) {
 | 
						|
        /* rvalue must be a single item */
 | 
						|
        index = PyLong_AsSsize_t(key);
 | 
						|
        if (index == -1 && PyErr_Occurred())
 | 
						|
            return -1;
 | 
						|
        else {
 | 
						|
            ptr = ptr_from_index(dest, index);
 | 
						|
            if (ptr == NULL)
 | 
						|
                return -1;
 | 
						|
        }
 | 
						|
        return pack_single(ptr, value, dest->format, dest->itemsize);
 | 
						|
    }
 | 
						|
 | 
						|
    /* rvalue must be an exporter */
 | 
						|
    if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) == -1)
 | 
						|
        return -1;
 | 
						|
 | 
						|
    nd = (NDArrayObject *)ndarray_subscript((PyObject*)self, key);
 | 
						|
    if (nd != NULL) {
 | 
						|
        dest = &nd->head->base;
 | 
						|
        ret = copy_buffer(dest, &src);
 | 
						|
        Py_DECREF(nd);
 | 
						|
    }
 | 
						|
 | 
						|
    PyBuffer_Release(&src);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
slice_indices(PyObject *self, PyObject *args)
 | 
						|
{
 | 
						|
    PyObject *ret, *key, *tmp;
 | 
						|
    Py_ssize_t s[4]; /* start, stop, step, slicelength */
 | 
						|
    Py_ssize_t i, len;
 | 
						|
 | 
						|
    if (!PyArg_ParseTuple(args, "On", &key, &len)) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (!PySlice_Check(key)) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "first argument must be a slice object");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (PySlice_Unpack(key, &s[0], &s[1], &s[2]) < 0) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    s[3] = PySlice_AdjustIndices(len, &s[0], &s[1], s[2]);
 | 
						|
 | 
						|
    ret = PyTuple_New(4);
 | 
						|
    if (ret == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    for (i = 0; i < 4; i++) {
 | 
						|
        tmp = PyLong_FromSsize_t(s[i]);
 | 
						|
        if (tmp == NULL)
 | 
						|
            goto error;
 | 
						|
        PyTuple_SET_ITEM(ret, i, tmp);
 | 
						|
    }
 | 
						|
 | 
						|
    return ret;
 | 
						|
 | 
						|
error:
 | 
						|
    Py_DECREF(ret);
 | 
						|
    return NULL;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static PyMappingMethods ndarray_as_mapping = {
 | 
						|
    NULL,                                 /* mp_length */
 | 
						|
    ndarray_subscript,                    /* mp_subscript */
 | 
						|
    ndarray_ass_subscript                 /* mp_ass_subscript */
 | 
						|
};
 | 
						|
 | 
						|
static PySequenceMethods ndarray_as_sequence = {
 | 
						|
    0,              /* sq_length */
 | 
						|
    0,              /* sq_concat */
 | 
						|
    0,              /* sq_repeat */
 | 
						|
    ndarray_item,   /* sq_item */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/*                                 getters                                */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ssize_array_as_tuple(Py_ssize_t *array, Py_ssize_t len)
 | 
						|
{
 | 
						|
    PyObject *tuple, *x;
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    if (array == NULL)
 | 
						|
        return PyTuple_New(0);
 | 
						|
 | 
						|
    tuple = PyTuple_New(len);
 | 
						|
    if (tuple == NULL)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    for (i = 0; i < len; i++) {
 | 
						|
        x = PyLong_FromSsize_t(array[i]);
 | 
						|
        if (x == NULL) {
 | 
						|
            Py_DECREF(tuple);
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        PyTuple_SET_ITEM(tuple, i, x);
 | 
						|
    }
 | 
						|
 | 
						|
    return tuple;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_flags(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    return PyLong_FromLong(self->head->flags);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_offset(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    ndbuf_t *ndbuf = self->head;
 | 
						|
    return PyLong_FromSsize_t(ndbuf->offset);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_obj(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
 | 
						|
    if (base->obj == NULL) {
 | 
						|
        Py_RETURN_NONE;
 | 
						|
    }
 | 
						|
    return Py_NewRef(base->obj);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_nbytes(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    return PyLong_FromSsize_t(base->len);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_readonly(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    return PyBool_FromLong(base->readonly);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_itemsize(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    return PyLong_FromSsize_t(base->itemsize);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_format(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    const char *fmt = base->format ? base->format : "";
 | 
						|
    return PyUnicode_FromString(fmt);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_ndim(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    return PyLong_FromSsize_t(base->ndim);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_shape(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    return ssize_array_as_tuple(base->shape, base->ndim);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_strides(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    return ssize_array_as_tuple(base->strides, base->ndim);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_get_suboffsets(PyObject *op, void *closure)
 | 
						|
{
 | 
						|
    NDArrayObject *self = (NDArrayObject*)op;
 | 
						|
    Py_buffer *base = &self->head->base;
 | 
						|
    return ssize_array_as_tuple(base->suboffsets, base->ndim);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_c_contig(PyObject *self, void *dummy)
 | 
						|
{
 | 
						|
    NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    int ret = PyBuffer_IsContiguous(&nd->head->base, 'C');
 | 
						|
 | 
						|
    if (ret != ND_C_CONTIGUOUS(nd->head->flags)) {
 | 
						|
        PyErr_SetString(PyExc_RuntimeError,
 | 
						|
            "results from PyBuffer_IsContiguous() and flags differ");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return PyBool_FromLong(ret);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_fortran_contig(PyObject *self, void *dummy)
 | 
						|
{
 | 
						|
    NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    int ret = PyBuffer_IsContiguous(&nd->head->base, 'F');
 | 
						|
 | 
						|
    if (ret != ND_FORTRAN_CONTIGUOUS(nd->head->flags)) {
 | 
						|
        PyErr_SetString(PyExc_RuntimeError,
 | 
						|
            "results from PyBuffer_IsContiguous() and flags differ");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return PyBool_FromLong(ret);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_contig(PyObject *self, void *dummy)
 | 
						|
{
 | 
						|
    NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    int ret = PyBuffer_IsContiguous(&nd->head->base, 'A');
 | 
						|
 | 
						|
    if (ret != ND_ANY_CONTIGUOUS(nd->head->flags)) {
 | 
						|
        PyErr_SetString(PyExc_RuntimeError,
 | 
						|
            "results from PyBuffer_IsContiguous() and flags differ");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return PyBool_FromLong(ret);
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static PyGetSetDef ndarray_getset [] =
 | 
						|
{
 | 
						|
  /* ndbuf */
 | 
						|
  { "flags",        ndarray_get_flags,      NULL, NULL, NULL},
 | 
						|
  { "offset",       ndarray_get_offset,     NULL, NULL, NULL},
 | 
						|
  /* ndbuf.base */
 | 
						|
  { "obj",          ndarray_get_obj,        NULL, NULL, NULL},
 | 
						|
  { "nbytes",       ndarray_get_nbytes,     NULL, NULL, NULL},
 | 
						|
  { "readonly",     ndarray_get_readonly,   NULL, NULL, NULL},
 | 
						|
  { "itemsize",     ndarray_get_itemsize,   NULL, NULL, NULL},
 | 
						|
  { "format",       ndarray_get_format,     NULL, NULL, NULL},
 | 
						|
  { "ndim",         ndarray_get_ndim,       NULL, NULL, NULL},
 | 
						|
  { "shape",        ndarray_get_shape,      NULL, NULL, NULL},
 | 
						|
  { "strides",      ndarray_get_strides,    NULL, NULL, NULL},
 | 
						|
  { "suboffsets",   ndarray_get_suboffsets, NULL, NULL, NULL},
 | 
						|
  { "c_contiguous", ndarray_c_contig,       NULL, NULL, NULL},
 | 
						|
  { "f_contiguous", ndarray_fortran_contig, NULL, NULL, NULL},
 | 
						|
  { "contiguous",   ndarray_contig,         NULL, NULL, NULL},
 | 
						|
  {NULL}
 | 
						|
};
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_tolist(PyObject *self, PyObject *dummy)
 | 
						|
{
 | 
						|
    return ndarray_as_list((NDArrayObject *)self);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
ndarray_tobytes(PyObject *self, PyObject *dummy)
 | 
						|
{
 | 
						|
    ndbuf_t *ndbuf = ((NDArrayObject *)self)->head;
 | 
						|
    Py_buffer *src = &ndbuf->base;
 | 
						|
    Py_buffer dest;
 | 
						|
    PyObject *ret = NULL;
 | 
						|
    char *mem;
 | 
						|
 | 
						|
    if (ND_C_CONTIGUOUS(ndbuf->flags))
 | 
						|
        return PyBytes_FromStringAndSize(src->buf, src->len);
 | 
						|
 | 
						|
    assert(src->shape != NULL);
 | 
						|
    assert(src->strides != NULL);
 | 
						|
    assert(src->ndim > 0);
 | 
						|
 | 
						|
    mem = PyMem_Malloc(src->len);
 | 
						|
    if (mem == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    dest = *src;
 | 
						|
    dest.buf = mem;
 | 
						|
    dest.suboffsets = NULL;
 | 
						|
    dest.strides = strides_from_shape(ndbuf, 0);
 | 
						|
    if (dest.strides == NULL)
 | 
						|
        goto out;
 | 
						|
    if (copy_buffer(&dest, src) < 0)
 | 
						|
        goto out;
 | 
						|
 | 
						|
    ret = PyBytes_FromStringAndSize(mem, src->len);
 | 
						|
 | 
						|
out:
 | 
						|
    PyMem_XFree(dest.strides);
 | 
						|
    PyMem_Free(mem);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
/* add redundant (negative) suboffsets for testing */
 | 
						|
static PyObject *
 | 
						|
ndarray_add_suboffsets(PyObject *self, PyObject *dummy)
 | 
						|
{
 | 
						|
    NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    Py_buffer *base = &nd->head->base;
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
    if (base->suboffsets != NULL) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "cannot add suboffsets to PIL-style array");
 | 
						|
            return NULL;
 | 
						|
    }
 | 
						|
    if (base->strides == NULL) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "cannot add suboffsets to array without strides");
 | 
						|
            return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
 | 
						|
    if (base->suboffsets == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < base->ndim; i++)
 | 
						|
        base->suboffsets[i] = -1;
 | 
						|
 | 
						|
    nd->head->flags &= ~(ND_C|ND_FORTRAN);
 | 
						|
 | 
						|
    Py_RETURN_NONE;
 | 
						|
}
 | 
						|
 | 
						|
/* Test PyMemoryView_FromBuffer(): return a memoryview from a static buffer.
 | 
						|
   Obviously this is fragile and only one such view may be active at any
 | 
						|
   time. Never use anything like this in real code! */
 | 
						|
static char *infobuf = NULL;
 | 
						|
static PyObject *
 | 
						|
ndarray_memoryview_from_buffer(PyObject *self, PyObject *dummy)
 | 
						|
{
 | 
						|
    const NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    const Py_buffer *view = &nd->head->base;
 | 
						|
    const ndbuf_t *ndbuf;
 | 
						|
    static char format[ND_MAX_NDIM+1];
 | 
						|
    static Py_ssize_t shape[ND_MAX_NDIM];
 | 
						|
    static Py_ssize_t strides[ND_MAX_NDIM];
 | 
						|
    static Py_ssize_t suboffsets[ND_MAX_NDIM];
 | 
						|
    static Py_buffer info;
 | 
						|
    char *p;
 | 
						|
 | 
						|
    if (!ND_IS_CONSUMER(nd))
 | 
						|
        ndbuf = nd->head; /* self is ndarray/original exporter */
 | 
						|
    else if (NDArray_Check(view->obj) && !ND_IS_CONSUMER(view->obj))
 | 
						|
        /* self is ndarray and consumer from ndarray/original exporter */
 | 
						|
        ndbuf = ((NDArrayObject *)view->obj)->head;
 | 
						|
    else {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
        "memoryview_from_buffer(): ndarray must be original exporter or "
 | 
						|
        "consumer from ndarray/original exporter");
 | 
						|
         return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    info = *view;
 | 
						|
    p = PyMem_Realloc(infobuf, ndbuf->len);
 | 
						|
    if (p == NULL) {
 | 
						|
        PyMem_Free(infobuf);
 | 
						|
        PyErr_NoMemory();
 | 
						|
        infobuf = NULL;
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        infobuf = p;
 | 
						|
    }
 | 
						|
    /* copy the complete raw data */
 | 
						|
    memcpy(infobuf, ndbuf->data, ndbuf->len);
 | 
						|
    info.buf = infobuf + ((char *)view->buf - ndbuf->data);
 | 
						|
 | 
						|
    if (view->format) {
 | 
						|
        if (strlen(view->format) > ND_MAX_NDIM) {
 | 
						|
            PyErr_Format(PyExc_TypeError,
 | 
						|
                "memoryview_from_buffer: format is limited to %d characters",
 | 
						|
                ND_MAX_NDIM);
 | 
						|
                return NULL;
 | 
						|
        }
 | 
						|
        strcpy(format, view->format);
 | 
						|
        info.format = format;
 | 
						|
    }
 | 
						|
    if (view->ndim > ND_MAX_NDIM) {
 | 
						|
        PyErr_Format(PyExc_TypeError,
 | 
						|
            "memoryview_from_buffer: ndim is limited to %d", ND_MAX_NDIM);
 | 
						|
            return NULL;
 | 
						|
    }
 | 
						|
    if (view->shape) {
 | 
						|
        memcpy(shape, view->shape, view->ndim * sizeof(Py_ssize_t));
 | 
						|
        info.shape = shape;
 | 
						|
    }
 | 
						|
    if (view->strides) {
 | 
						|
        memcpy(strides, view->strides, view->ndim * sizeof(Py_ssize_t));
 | 
						|
        info.strides = strides;
 | 
						|
    }
 | 
						|
    if (view->suboffsets) {
 | 
						|
        memcpy(suboffsets, view->suboffsets, view->ndim * sizeof(Py_ssize_t));
 | 
						|
        info.suboffsets = suboffsets;
 | 
						|
    }
 | 
						|
 | 
						|
    return PyMemoryView_FromBuffer(&info);
 | 
						|
}
 | 
						|
 | 
						|
/* Get a single item from bufobj at the location specified by seq.
 | 
						|
   seq is a list or tuple of indices. The purpose of this function
 | 
						|
   is to check other functions against PyBuffer_GetPointer(). */
 | 
						|
static PyObject *
 | 
						|
get_pointer(PyObject *self, PyObject *args)
 | 
						|
{
 | 
						|
    PyObject *ret = NULL, *bufobj, *seq;
 | 
						|
    Py_buffer view;
 | 
						|
    Py_ssize_t indices[ND_MAX_NDIM];
 | 
						|
    Py_ssize_t i;
 | 
						|
    void *ptr;
 | 
						|
 | 
						|
    if (!PyArg_ParseTuple(args, "OO", &bufobj, &seq)) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    CHECK_LIST_OR_TUPLE(seq);
 | 
						|
    if (PyObject_GetBuffer(bufobj, &view, PyBUF_FULL_RO) < 0)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    if (view.ndim > ND_MAX_NDIM) {
 | 
						|
        PyErr_Format(PyExc_ValueError,
 | 
						|
            "get_pointer(): ndim > %d", ND_MAX_NDIM);
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
    if (PySequence_Fast_GET_SIZE(seq) != view.ndim) {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "get_pointer(): len(indices) != ndim");
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    for (i = 0; i < view.ndim; i++) {
 | 
						|
        PyObject *x = PySequence_Fast_GET_ITEM(seq, i);
 | 
						|
        indices[i] = PyLong_AsSsize_t(x);
 | 
						|
        if (PyErr_Occurred())
 | 
						|
            goto out;
 | 
						|
        if (indices[i] < 0 || indices[i] >= view.shape[i]) {
 | 
						|
            PyErr_Format(PyExc_ValueError,
 | 
						|
                "get_pointer(): invalid index %zd at position %zd",
 | 
						|
                indices[i], i);
 | 
						|
            goto out;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    ptr = PyBuffer_GetPointer(&view, indices);
 | 
						|
    ret = unpack_single(ptr, view.format, view.itemsize);
 | 
						|
 | 
						|
out:
 | 
						|
    PyBuffer_Release(&view);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
get_sizeof_void_p(PyObject *self, PyObject *Py_UNUSED(ignored))
 | 
						|
{
 | 
						|
    return PyLong_FromSize_t(sizeof(void *));
 | 
						|
}
 | 
						|
 | 
						|
static char
 | 
						|
get_ascii_order(PyObject *order)
 | 
						|
{
 | 
						|
    PyObject *ascii_order;
 | 
						|
    char ord;
 | 
						|
 | 
						|
    if (!PyUnicode_Check(order)) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "order must be a string");
 | 
						|
        return CHAR_MAX;
 | 
						|
    }
 | 
						|
 | 
						|
    ascii_order = PyUnicode_AsASCIIString(order);
 | 
						|
    if (ascii_order == NULL) {
 | 
						|
        return CHAR_MAX;
 | 
						|
    }
 | 
						|
 | 
						|
    ord = PyBytes_AS_STRING(ascii_order)[0];
 | 
						|
    Py_DECREF(ascii_order);
 | 
						|
 | 
						|
    if (ord != 'C' && ord != 'F' && ord != 'A') {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "invalid order, must be C, F or A");
 | 
						|
        return CHAR_MAX;
 | 
						|
    }
 | 
						|
 | 
						|
    return ord;
 | 
						|
}
 | 
						|
 | 
						|
/* Get a contiguous memoryview. */
 | 
						|
static PyObject *
 | 
						|
get_contiguous(PyObject *self, PyObject *args)
 | 
						|
{
 | 
						|
    PyObject *obj;
 | 
						|
    PyObject *buffertype;
 | 
						|
    PyObject *order;
 | 
						|
    long type;
 | 
						|
    char ord;
 | 
						|
 | 
						|
    if (!PyArg_ParseTuple(args, "OOO", &obj, &buffertype, &order)) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!PyLong_Check(buffertype)) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "buffertype must be PyBUF_READ or PyBUF_WRITE");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    type = PyLong_AsLong(buffertype);
 | 
						|
    if (type == -1 && PyErr_Occurred()) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (type != PyBUF_READ && type != PyBUF_WRITE) {
 | 
						|
        PyErr_SetString(PyExc_ValueError,
 | 
						|
            "invalid buffer type");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ord = get_ascii_order(order);
 | 
						|
    if (ord == CHAR_MAX)
 | 
						|
        return NULL;
 | 
						|
 | 
						|
    return PyMemoryView_GetContiguous(obj, (int)type, ord);
 | 
						|
}
 | 
						|
 | 
						|
/* PyBuffer_ToContiguous() */
 | 
						|
static PyObject *
 | 
						|
py_buffer_to_contiguous(PyObject *self, PyObject *args)
 | 
						|
{
 | 
						|
    PyObject *obj;
 | 
						|
    PyObject *order;
 | 
						|
    PyObject *ret = NULL;
 | 
						|
    int flags;
 | 
						|
    char ord;
 | 
						|
    Py_buffer view;
 | 
						|
    char *buf = NULL;
 | 
						|
 | 
						|
    if (!PyArg_ParseTuple(args, "OOi", &obj, &order, &flags)) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (PyObject_GetBuffer(obj, &view, flags) < 0) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ord = get_ascii_order(order);
 | 
						|
    if (ord == CHAR_MAX) {
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    buf = PyMem_Malloc(view.len);
 | 
						|
    if (buf == NULL) {
 | 
						|
        PyErr_NoMemory();
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    if (PyBuffer_ToContiguous(buf, &view, view.len, ord) < 0) {
 | 
						|
        goto out;
 | 
						|
    }
 | 
						|
 | 
						|
    ret = PyBytes_FromStringAndSize(buf, view.len);
 | 
						|
 | 
						|
out:
 | 
						|
    PyBuffer_Release(&view);
 | 
						|
    PyMem_XFree(buf);
 | 
						|
    return ret;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
fmtcmp(const char *fmt1, const char *fmt2)
 | 
						|
{
 | 
						|
    if (fmt1 == NULL) {
 | 
						|
        return fmt2 == NULL || strcmp(fmt2, "B") == 0;
 | 
						|
    }
 | 
						|
    if (fmt2 == NULL) {
 | 
						|
        return fmt1 == NULL || strcmp(fmt1, "B") == 0;
 | 
						|
    }
 | 
						|
    return strcmp(fmt1, fmt2) == 0;
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
arraycmp(const Py_ssize_t *a1, const Py_ssize_t *a2, const Py_ssize_t *shape,
 | 
						|
         Py_ssize_t ndim)
 | 
						|
{
 | 
						|
    Py_ssize_t i;
 | 
						|
 | 
						|
 | 
						|
    for (i = 0; i < ndim; i++) {
 | 
						|
        if (shape && shape[i] <= 1) {
 | 
						|
            /* strides can differ if the dimension is less than 2 */
 | 
						|
            continue;
 | 
						|
        }
 | 
						|
        if (a1[i] != a2[i]) {
 | 
						|
            return 0;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    return 1;
 | 
						|
}
 | 
						|
 | 
						|
/* Compare two contiguous buffers for physical equality. */
 | 
						|
static PyObject *
 | 
						|
cmp_contig(PyObject *self, PyObject *args)
 | 
						|
{
 | 
						|
    PyObject *b1, *b2; /* buffer objects */
 | 
						|
    Py_buffer v1, v2;
 | 
						|
    PyObject *ret;
 | 
						|
    int equal = 0;
 | 
						|
 | 
						|
    if (!PyArg_ParseTuple(args, "OO", &b1, &b2)) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (PyObject_GetBuffer(b1, &v1, PyBUF_FULL_RO) < 0) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "cmp_contig: first argument does not implement the buffer "
 | 
						|
            "protocol");
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    if (PyObject_GetBuffer(b2, &v2, PyBUF_FULL_RO) < 0) {
 | 
						|
        PyErr_SetString(PyExc_TypeError,
 | 
						|
            "cmp_contig: second argument does not implement the buffer "
 | 
						|
            "protocol");
 | 
						|
        PyBuffer_Release(&v1);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (!(PyBuffer_IsContiguous(&v1, 'C')&&PyBuffer_IsContiguous(&v2, 'C')) &&
 | 
						|
        !(PyBuffer_IsContiguous(&v1, 'F')&&PyBuffer_IsContiguous(&v2, 'F'))) {
 | 
						|
        goto result;
 | 
						|
    }
 | 
						|
 | 
						|
    /* readonly may differ if created from non-contiguous */
 | 
						|
    if (v1.len != v2.len ||
 | 
						|
        v1.itemsize != v2.itemsize ||
 | 
						|
        v1.ndim != v2.ndim ||
 | 
						|
        !fmtcmp(v1.format, v2.format) ||
 | 
						|
        !!v1.shape != !!v2.shape ||
 | 
						|
        !!v1.strides != !!v2.strides ||
 | 
						|
        !!v1.suboffsets != !!v2.suboffsets) {
 | 
						|
        goto result;
 | 
						|
    }
 | 
						|
 | 
						|
    if ((v1.shape && !arraycmp(v1.shape, v2.shape, NULL, v1.ndim)) ||
 | 
						|
        (v1.strides && !arraycmp(v1.strides, v2.strides, v1.shape, v1.ndim)) ||
 | 
						|
        (v1.suboffsets && !arraycmp(v1.suboffsets, v2.suboffsets, NULL,
 | 
						|
                                    v1.ndim))) {
 | 
						|
        goto result;
 | 
						|
    }
 | 
						|
 | 
						|
    if (memcmp((char *)v1.buf, (char *)v2.buf, v1.len) != 0) {
 | 
						|
        goto result;
 | 
						|
    }
 | 
						|
 | 
						|
    equal = 1;
 | 
						|
 | 
						|
result:
 | 
						|
    PyBuffer_Release(&v1);
 | 
						|
    PyBuffer_Release(&v2);
 | 
						|
 | 
						|
    ret = equal ? Py_True : Py_False;
 | 
						|
    return Py_NewRef(ret);
 | 
						|
}
 | 
						|
 | 
						|
static PyObject *
 | 
						|
is_contiguous(PyObject *self, PyObject *args)
 | 
						|
{
 | 
						|
    PyObject *obj;
 | 
						|
    PyObject *order;
 | 
						|
    PyObject *ret = NULL;
 | 
						|
    Py_buffer view, *base;
 | 
						|
    char ord;
 | 
						|
 | 
						|
    if (!PyArg_ParseTuple(args, "OO", &obj, &order)) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    ord = get_ascii_order(order);
 | 
						|
    if (ord == CHAR_MAX) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
 | 
						|
    if (NDArray_Check(obj)) {
 | 
						|
        /* Skip the buffer protocol to check simple etc. buffers directly. */
 | 
						|
        base = &((NDArrayObject *)obj)->head->base;
 | 
						|
        ret = PyBuffer_IsContiguous(base, ord) ? Py_True : Py_False;
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) {
 | 
						|
            PyErr_SetString(PyExc_TypeError,
 | 
						|
                "is_contiguous: object does not implement the buffer "
 | 
						|
                "protocol");
 | 
						|
            return NULL;
 | 
						|
        }
 | 
						|
        ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
 | 
						|
        PyBuffer_Release(&view);
 | 
						|
    }
 | 
						|
 | 
						|
    return Py_NewRef(ret);
 | 
						|
}
 | 
						|
 | 
						|
static Py_hash_t
 | 
						|
ndarray_hash(PyObject *self)
 | 
						|
{
 | 
						|
    const NDArrayObject *nd = (NDArrayObject *)self;
 | 
						|
    const Py_buffer *view = &nd->head->base;
 | 
						|
    PyObject *bytes;
 | 
						|
    Py_hash_t hash;
 | 
						|
 | 
						|
    if (!view->readonly) {
 | 
						|
         PyErr_SetString(PyExc_ValueError,
 | 
						|
             "cannot hash writable ndarray object");
 | 
						|
         return -1;
 | 
						|
    }
 | 
						|
    if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
 | 
						|
         return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    bytes = ndarray_tobytes(self, NULL);
 | 
						|
    if (bytes == NULL) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    hash = PyObject_Hash(bytes);
 | 
						|
    Py_DECREF(bytes);
 | 
						|
    return hash;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
static PyMethodDef ndarray_methods[] =
 | 
						|
{
 | 
						|
    { "tolist", ndarray_tolist, METH_NOARGS, NULL },
 | 
						|
    { "tobytes", ndarray_tobytes, METH_NOARGS, NULL },
 | 
						|
    { "push", _PyCFunction_CAST(ndarray_push), METH_VARARGS|METH_KEYWORDS, NULL },
 | 
						|
    { "pop", ndarray_pop, METH_NOARGS, NULL },
 | 
						|
    { "add_suboffsets", ndarray_add_suboffsets, METH_NOARGS, NULL },
 | 
						|
    { "memoryview_from_buffer", ndarray_memoryview_from_buffer, METH_NOARGS, NULL },
 | 
						|
    {NULL}
 | 
						|
};
 | 
						|
 | 
						|
static PyTypeObject NDArray_Type = {
 | 
						|
    PyVarObject_HEAD_INIT(NULL, 0)
 | 
						|
    "ndarray",                   /* Name of this type */
 | 
						|
    sizeof(NDArrayObject),       /* Basic object size */
 | 
						|
    0,                           /* Item size for varobject */
 | 
						|
    ndarray_dealloc,             /* tp_dealloc */
 | 
						|
    0,                           /* tp_vectorcall_offset */
 | 
						|
    0,                           /* tp_getattr */
 | 
						|
    0,                           /* tp_setattr */
 | 
						|
    0,                           /* tp_as_async */
 | 
						|
    0,                           /* tp_repr */
 | 
						|
    0,                           /* tp_as_number */
 | 
						|
    &ndarray_as_sequence,        /* tp_as_sequence */
 | 
						|
    &ndarray_as_mapping,         /* tp_as_mapping */
 | 
						|
    ndarray_hash,                /* tp_hash */
 | 
						|
    0,                           /* tp_call */
 | 
						|
    0,                           /* tp_str */
 | 
						|
    PyObject_GenericGetAttr,     /* tp_getattro */
 | 
						|
    0,                           /* tp_setattro */
 | 
						|
    &ndarray_as_buffer,          /* tp_as_buffer */
 | 
						|
    Py_TPFLAGS_DEFAULT,          /* tp_flags */
 | 
						|
    0,                           /* tp_doc */
 | 
						|
    0,                           /* tp_traverse */
 | 
						|
    0,                           /* tp_clear */
 | 
						|
    0,                           /* tp_richcompare */
 | 
						|
    0,                           /* tp_weaklistoffset */
 | 
						|
    0,                           /* tp_iter */
 | 
						|
    0,                           /* tp_iternext */
 | 
						|
    ndarray_methods,             /* tp_methods */
 | 
						|
    0,                           /* tp_members */
 | 
						|
    ndarray_getset,              /* tp_getset */
 | 
						|
    0,                           /* tp_base */
 | 
						|
    0,                           /* tp_dict */
 | 
						|
    0,                           /* tp_descr_get */
 | 
						|
    0,                           /* tp_descr_set */
 | 
						|
    0,                           /* tp_dictoffset */
 | 
						|
    ndarray_init,                /* tp_init */
 | 
						|
    0,                           /* tp_alloc */
 | 
						|
    ndarray_new,                 /* tp_new */
 | 
						|
};
 | 
						|
 | 
						|
/**************************************************************************/
 | 
						|
/*                          StaticArray Object                            */
 | 
						|
/**************************************************************************/
 | 
						|
 | 
						|
static PyTypeObject StaticArray_Type;
 | 
						|
 | 
						|
typedef struct {
 | 
						|
    PyObject_HEAD
 | 
						|
    int legacy_mode; /* if true, use the view.obj==NULL hack */
 | 
						|
} StaticArrayObject;
 | 
						|
 | 
						|
static char static_mem[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
 | 
						|
static Py_ssize_t static_shape[1] = {12};
 | 
						|
static Py_ssize_t static_strides[1] = {1};
 | 
						|
static Py_buffer static_buffer = {
 | 
						|
    static_mem,     /* buf */
 | 
						|
    NULL,           /* obj */
 | 
						|
    12,             /* len */
 | 
						|
    1,              /* itemsize */
 | 
						|
    1,              /* readonly */
 | 
						|
    1,              /* ndim */
 | 
						|
    "B",            /* format */
 | 
						|
    static_shape,   /* shape */
 | 
						|
    static_strides, /* strides */
 | 
						|
    NULL,           /* suboffsets */
 | 
						|
    NULL            /* internal */
 | 
						|
};
 | 
						|
 | 
						|
static PyObject *
 | 
						|
staticarray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
    return (PyObject *)PyObject_New(StaticArrayObject, &StaticArray_Type);
 | 
						|
}
 | 
						|
 | 
						|
static int
 | 
						|
staticarray_init(PyObject *self, PyObject *args, PyObject *kwds)
 | 
						|
{
 | 
						|
    StaticArrayObject *a = (StaticArrayObject *)self;
 | 
						|
    static char *kwlist[] = {
 | 
						|
        "legacy_mode", NULL
 | 
						|
    };
 | 
						|
    PyObject *legacy_mode = Py_False;
 | 
						|
 | 
						|
    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &legacy_mode))
 | 
						|
        return -1;
 | 
						|
 | 
						|
    a->legacy_mode = (legacy_mode != Py_False);
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static void
 | 
						|
staticarray_dealloc(PyObject *self)
 | 
						|
{
 | 
						|
    PyObject_Free(self);
 | 
						|
}
 | 
						|
 | 
						|
/* Return a buffer for a PyBUF_FULL_RO request. Flags are not checked,
 | 
						|
   which makes this object a non-compliant exporter! */
 | 
						|
static int
 | 
						|
staticarray_getbuf(PyObject *op, Py_buffer *view, int flags)
 | 
						|
{
 | 
						|
    StaticArrayObject *self = (StaticArrayObject *)op;
 | 
						|
    *view = static_buffer;
 | 
						|
 | 
						|
    if (self->legacy_mode) {
 | 
						|
        view->obj = NULL; /* Don't use this in new code. */
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        view->obj = Py_NewRef(self);
 | 
						|
    }
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
static PyBufferProcs staticarray_as_buffer = {
 | 
						|
    staticarray_getbuf,                /* bf_getbuffer */
 | 
						|
    NULL,                              /* bf_releasebuffer */
 | 
						|
};
 | 
						|
 | 
						|
static PyTypeObject StaticArray_Type = {
 | 
						|
    PyVarObject_HEAD_INIT(NULL, 0)
 | 
						|
    "staticarray",                   /* Name of this type */
 | 
						|
    sizeof(StaticArrayObject),       /* Basic object size */
 | 
						|
    0,                               /* Item size for varobject */
 | 
						|
    staticarray_dealloc,             /* tp_dealloc */
 | 
						|
    0,                               /* tp_vectorcall_offset */
 | 
						|
    0,                               /* tp_getattr */
 | 
						|
    0,                               /* tp_setattr */
 | 
						|
    0,                               /* tp_as_async */
 | 
						|
    0,                               /* tp_repr */
 | 
						|
    0,                               /* tp_as_number */
 | 
						|
    0,                               /* tp_as_sequence */
 | 
						|
    0,                               /* tp_as_mapping */
 | 
						|
    0,                               /* tp_hash */
 | 
						|
    0,                               /* tp_call */
 | 
						|
    0,                               /* tp_str */
 | 
						|
    0,                               /* tp_getattro */
 | 
						|
    0,                               /* tp_setattro */
 | 
						|
    &staticarray_as_buffer,          /* tp_as_buffer */
 | 
						|
    Py_TPFLAGS_DEFAULT,              /* tp_flags */
 | 
						|
    0,                               /* tp_doc */
 | 
						|
    0,                               /* tp_traverse */
 | 
						|
    0,                               /* tp_clear */
 | 
						|
    0,                               /* tp_richcompare */
 | 
						|
    0,                               /* tp_weaklistoffset */
 | 
						|
    0,                               /* tp_iter */
 | 
						|
    0,                               /* tp_iternext */
 | 
						|
    0,                               /* tp_methods */
 | 
						|
    0,                               /* tp_members */
 | 
						|
    0,                               /* tp_getset */
 | 
						|
    0,                               /* tp_base */
 | 
						|
    0,                               /* tp_dict */
 | 
						|
    0,                               /* tp_descr_get */
 | 
						|
    0,                               /* tp_descr_set */
 | 
						|
    0,                               /* tp_dictoffset */
 | 
						|
    staticarray_init,                /* tp_init */
 | 
						|
    0,                               /* tp_alloc */
 | 
						|
    staticarray_new,                 /* tp_new */
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
static struct PyMethodDef _testbuffer_functions[] = {
 | 
						|
    {"slice_indices", slice_indices, METH_VARARGS, NULL},
 | 
						|
    {"get_pointer", get_pointer, METH_VARARGS, NULL},
 | 
						|
    {"get_sizeof_void_p", get_sizeof_void_p, METH_NOARGS, NULL},
 | 
						|
    {"get_contiguous", get_contiguous, METH_VARARGS, NULL},
 | 
						|
    {"py_buffer_to_contiguous", py_buffer_to_contiguous, METH_VARARGS, NULL},
 | 
						|
    {"is_contiguous", is_contiguous, METH_VARARGS, NULL},
 | 
						|
    {"cmp_contig", cmp_contig, METH_VARARGS, NULL},
 | 
						|
    {NULL, NULL}
 | 
						|
};
 | 
						|
 | 
						|
static struct PyModuleDef _testbuffermodule = {
 | 
						|
    PyModuleDef_HEAD_INIT,
 | 
						|
    "_testbuffer",
 | 
						|
    NULL,
 | 
						|
    -1,
 | 
						|
    _testbuffer_functions,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL,
 | 
						|
    NULL
 | 
						|
};
 | 
						|
 | 
						|
static int
 | 
						|
_testbuffer_exec(PyObject *mod)
 | 
						|
{
 | 
						|
    Py_SET_TYPE(&NDArray_Type, &PyType_Type);
 | 
						|
    if (PyType_Ready(&NDArray_Type)) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    if (PyModule_AddType(mod, &NDArray_Type) < 0) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    Py_SET_TYPE(&StaticArray_Type, &PyType_Type);
 | 
						|
    if (PyModule_AddType(mod, &StaticArray_Type) < 0) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    structmodule = PyImport_ImportModule("struct");
 | 
						|
    if (structmodule == NULL) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    Struct = PyObject_GetAttrString(structmodule, "Struct");
 | 
						|
    if (Struct == NULL) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
    calcsize = PyObject_GetAttrString(structmodule, "calcsize");
 | 
						|
    if (calcsize == NULL) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
    simple_format = PyUnicode_FromString(simple_fmt);
 | 
						|
    if (simple_format == NULL) {
 | 
						|
        return -1;
 | 
						|
    }
 | 
						|
 | 
						|
#define ADD_INT_MACRO(mod, macro)                                             \
 | 
						|
    do {                                                                    \
 | 
						|
        if (PyModule_AddIntConstant(mod, #macro, macro) < 0) {                \
 | 
						|
            return -1;                                                      \
 | 
						|
        }                                                                   \
 | 
						|
    } while (0)
 | 
						|
 | 
						|
    ADD_INT_MACRO(mod, ND_MAX_NDIM);
 | 
						|
    ADD_INT_MACRO(mod, ND_VAREXPORT);
 | 
						|
    ADD_INT_MACRO(mod, ND_WRITABLE);
 | 
						|
    ADD_INT_MACRO(mod, ND_FORTRAN);
 | 
						|
    ADD_INT_MACRO(mod, ND_SCALAR);
 | 
						|
    ADD_INT_MACRO(mod, ND_PIL);
 | 
						|
    ADD_INT_MACRO(mod, ND_GETBUF_FAIL);
 | 
						|
    ADD_INT_MACRO(mod, ND_GETBUF_UNDEFINED);
 | 
						|
    ADD_INT_MACRO(mod, ND_REDIRECT);
 | 
						|
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_SIMPLE);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_WRITABLE);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_FORMAT);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_ND);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_STRIDES);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_INDIRECT);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_C_CONTIGUOUS);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_F_CONTIGUOUS);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_ANY_CONTIGUOUS);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_FULL);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_FULL_RO);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_RECORDS);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_RECORDS_RO);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_STRIDED);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_STRIDED_RO);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_CONTIG);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_CONTIG_RO);
 | 
						|
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_READ);
 | 
						|
    ADD_INT_MACRO(mod, PyBUF_WRITE);
 | 
						|
 | 
						|
#undef ADD_INT_MACRO
 | 
						|
 | 
						|
    return 0;
 | 
						|
}
 | 
						|
 | 
						|
PyMODINIT_FUNC
 | 
						|
PyInit__testbuffer(void)
 | 
						|
{
 | 
						|
    PyObject *mod = PyModule_Create(&_testbuffermodule);
 | 
						|
    if (mod == NULL) {
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
#ifdef Py_GIL_DISABLED
 | 
						|
    PyUnstable_Module_SetGIL(mod, Py_MOD_GIL_NOT_USED);
 | 
						|
#endif
 | 
						|
    if (_testbuffer_exec(mod) < 0) {
 | 
						|
        Py_DECREF(mod);
 | 
						|
        return NULL;
 | 
						|
    }
 | 
						|
    return mod;
 | 
						|
}
 |