Home | History | Annotate | Download | only in Modules
      1 /* C Extension module to test all aspects of PEP-3118.
      2    Written by Stefan Krah. */
      3 
      4 
      5 #define PY_SSIZE_T_CLEAN
      6 
      7 #include "Python.h"
      8 
      9 
     10 /* struct module */
     11 PyObject *structmodule = NULL;
     12 PyObject *Struct = NULL;
     13 PyObject *calcsize = NULL;
     14 
     15 /* cache simple format string */
     16 static const char *simple_fmt = "B";
     17 PyObject *simple_format = NULL;
     18 #define SIMPLE_FORMAT(fmt) (fmt == NULL || strcmp(fmt, "B") == 0)
     19 #define FIX_FORMAT(fmt) (fmt == NULL ? "B" : fmt)
     20 
     21 
     22 /**************************************************************************/
     23 /*                             NDArray Object                             */
     24 /**************************************************************************/
     25 
     26 static PyTypeObject NDArray_Type;
     27 #define NDArray_Check(v) (Py_TYPE(v) == &NDArray_Type)
     28 
     29 #define CHECK_LIST_OR_TUPLE(v) \
     30     if (!PyList_Check(v) && !PyTuple_Check(v)) { \
     31         PyErr_SetString(PyExc_TypeError,         \
     32             #v " must be a list or a tuple");    \
     33         return NULL;                             \
     34     }                                            \
     35 
     36 #define PyMem_XFree(v) \
     37     do { if (v) PyMem_Free(v); } while (0)
     38 
     39 /* Maximum number of dimensions. */
     40 #define ND_MAX_NDIM (2 * PyBUF_MAX_NDIM)
     41 
     42 /* Check for the presence of suboffsets in the first dimension. */
     43 #define HAVE_PTR(suboffsets) (suboffsets && suboffsets[0] >= 0)
     44 /* Adjust ptr if suboffsets are present. */
     45 #define ADJUST_PTR(ptr, suboffsets) \
     46     (HAVE_PTR(suboffsets) ? *((char**)ptr) + suboffsets[0] : ptr)
     47 
     48 /* Default: NumPy style (strides), read-only, no var-export, C-style layout */
     49 #define ND_DEFAULT          0x000
     50 /* User configurable flags for the ndarray */
     51 #define ND_VAREXPORT        0x001   /* change layout while buffers are exported */
     52 /* User configurable flags for each base buffer */
     53 #define ND_WRITABLE         0x002   /* mark base buffer as writable */
     54 #define ND_FORTRAN          0x004   /* Fortran contiguous layout */
     55 #define ND_SCALAR           0x008   /* scalar: ndim = 0 */
     56 #define ND_PIL              0x010   /* convert to PIL-style array (suboffsets) */
     57 #define ND_REDIRECT         0x020   /* redirect buffer requests */
     58 #define ND_GETBUF_FAIL      0x040   /* trigger getbuffer failure */
     59 #define ND_GETBUF_UNDEFINED 0x080   /* undefined view.obj */
     60 /* Internal flags for the base buffer */
     61 #define ND_C                0x100   /* C contiguous layout (default) */
     62 #define ND_OWN_ARRAYS       0x200   /* consumer owns arrays */
     63 
     64 /* ndarray properties */
     65 #define ND_IS_CONSUMER(nd) \
     66     (((NDArrayObject *)nd)->head == &((NDArrayObject *)nd)->staticbuf)
     67 
     68 /* ndbuf->flags properties */
     69 #define ND_C_CONTIGUOUS(flags) (!!(flags&(ND_SCALAR|ND_C)))
     70 #define ND_FORTRAN_CONTIGUOUS(flags) (!!(flags&(ND_SCALAR|ND_FORTRAN)))
     71 #define ND_ANY_CONTIGUOUS(flags) (!!(flags&(ND_SCALAR|ND_C|ND_FORTRAN)))
     72 
     73 /* getbuffer() requests */
     74 #define REQ_INDIRECT(flags) ((flags&PyBUF_INDIRECT) == PyBUF_INDIRECT)
     75 #define REQ_C_CONTIGUOUS(flags) ((flags&PyBUF_C_CONTIGUOUS) == PyBUF_C_CONTIGUOUS)
     76 #define REQ_F_CONTIGUOUS(flags) ((flags&PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS)
     77 #define REQ_ANY_CONTIGUOUS(flags) ((flags&PyBUF_ANY_CONTIGUOUS) == PyBUF_ANY_CONTIGUOUS)
     78 #define REQ_STRIDES(flags) ((flags&PyBUF_STRIDES) == PyBUF_STRIDES)
     79 #define REQ_SHAPE(flags) ((flags&PyBUF_ND) == PyBUF_ND)
     80 #define REQ_WRITABLE(flags) (flags&PyBUF_WRITABLE)
     81 #define REQ_FORMAT(flags) (flags&PyBUF_FORMAT)
     82 
     83 
     84 /* Single node of a list of base buffers. The list is needed to implement
     85    changes in memory layout while exported buffers are active. */
     86 static PyTypeObject NDArray_Type;
     87 
     88 struct ndbuf;
     89 typedef struct ndbuf {
     90     struct ndbuf *next;
     91     struct ndbuf *prev;
     92     Py_ssize_t len;     /* length of data */
     93     Py_ssize_t offset;  /* start of the array relative to data */
     94     char *data;         /* raw data */
     95     int flags;          /* capabilities of the base buffer */
     96     Py_ssize_t exports; /* number of exports */
     97     Py_buffer base;     /* base buffer */
     98 } ndbuf_t;
     99 
    100 typedef struct {
    101     PyObject_HEAD
    102     int flags;          /* ndarray flags */
    103     ndbuf_t staticbuf;  /* static buffer for re-exporting mode */
    104     ndbuf_t *head;      /* currently active base buffer */
    105 } NDArrayObject;
    106 
    107 
    108 static ndbuf_t *
    109 ndbuf_new(Py_ssize_t nitems, Py_ssize_t itemsize, Py_ssize_t offset, int flags)
    110 {
    111     ndbuf_t *ndbuf;
    112     Py_buffer *base;
    113     Py_ssize_t len;
    114 
    115     len = nitems * itemsize;
    116     if (offset % itemsize) {
    117         PyErr_SetString(PyExc_ValueError,
    118             "offset must be a multiple of itemsize");
    119         return NULL;
    120     }
    121     if (offset < 0 || offset+itemsize > len) {
    122         PyErr_SetString(PyExc_ValueError, "offset out of bounds");
    123         return NULL;
    124     }
    125 
    126     ndbuf = PyMem_Malloc(sizeof *ndbuf);
    127     if (ndbuf == NULL) {
    128         PyErr_NoMemory();
    129         return NULL;
    130     }
    131 
    132     ndbuf->next = NULL;
    133     ndbuf->prev = NULL;
    134     ndbuf->len = len;
    135     ndbuf->offset= offset;
    136 
    137     ndbuf->data = PyMem_Malloc(len);
    138     if (ndbuf->data == NULL) {
    139         PyErr_NoMemory();
    140         PyMem_Free(ndbuf);
    141         return NULL;
    142     }
    143 
    144     ndbuf->flags = flags;
    145     ndbuf->exports = 0;
    146 
    147     base = &ndbuf->base;
    148     base->obj = NULL;
    149     base->buf = ndbuf->data;
    150     base->len = len;
    151     base->itemsize = 1;
    152     base->readonly = 0;
    153     base->format = NULL;
    154     base->ndim = 1;
    155     base->shape = NULL;
    156     base->strides = NULL;
    157     base->suboffsets = NULL;
    158     base->internal = ndbuf;
    159 
    160     return ndbuf;
    161 }
    162 
    163 static void
    164 ndbuf_free(ndbuf_t *ndbuf)
    165 {
    166     Py_buffer *base = &ndbuf->base;
    167 
    168     PyMem_XFree(ndbuf->data);
    169     PyMem_XFree(base->format);
    170     PyMem_XFree(base->shape);
    171     PyMem_XFree(base->strides);
    172     PyMem_XFree(base->suboffsets);
    173 
    174     PyMem_Free(ndbuf);
    175 }
    176 
    177 static void
    178 ndbuf_push(NDArrayObject *nd, ndbuf_t *elt)
    179 {
    180     elt->next = nd->head;
    181     if (nd->head) nd->head->prev = elt;
    182     nd->head = elt;
    183     elt->prev = NULL;
    184 }
    185 
    186 static void
    187 ndbuf_delete(NDArrayObject *nd, ndbuf_t *elt)
    188 {
    189     if (elt->prev)
    190         elt->prev->next = elt->next;
    191     else
    192         nd->head = elt->next;
    193 
    194     if (elt->next)
    195         elt->next->prev = elt->prev;
    196 
    197     ndbuf_free(elt);
    198 }
    199 
    200 static void
    201 ndbuf_pop(NDArrayObject *nd)
    202 {
    203     ndbuf_delete(nd, nd->head);
    204 }
    205 
    206 
    207 static PyObject *
    208 ndarray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
    209 {
    210     NDArrayObject *nd;
    211 
    212     nd = PyObject_New(NDArrayObject, &NDArray_Type);
    213     if (nd == NULL)
    214         return NULL;
    215 
    216     nd->flags = 0;
    217     nd->head = NULL;
    218     return (PyObject *)nd;
    219 }
    220 
    221 static void
    222 ndarray_dealloc(NDArrayObject *self)
    223 {
    224     if (self->head) {
    225         if (ND_IS_CONSUMER(self)) {
    226             Py_buffer *base = &self->head->base;
    227             if (self->head->flags & ND_OWN_ARRAYS) {
    228                 PyMem_XFree(base->shape);
    229                 PyMem_XFree(base->strides);
    230                 PyMem_XFree(base->suboffsets);
    231             }
    232             PyBuffer_Release(base);
    233         }
    234         else {
    235             while (self->head)
    236                 ndbuf_pop(self);
    237         }
    238     }
    239     PyObject_Del(self);
    240 }
    241 
    242 static int
    243 ndarray_init_staticbuf(PyObject *exporter, NDArrayObject *nd, int flags)
    244 {
    245     Py_buffer *base = &nd->staticbuf.base;
    246 
    247     if (PyObject_GetBuffer(exporter, base, flags) < 0)
    248         return -1;
    249 
    250     nd->head = &nd->staticbuf;
    251 
    252     nd->head->next = NULL;
    253     nd->head->prev = NULL;
    254     nd->head->len = -1;
    255     nd->head->offset = -1;
    256     nd->head->data = NULL;
    257 
    258     nd->head->flags = base->readonly ? 0 : ND_WRITABLE;
    259     nd->head->exports = 0;
    260 
    261     return 0;
    262 }
    263 
    264 static void
    265 init_flags(ndbuf_t *ndbuf)
    266 {
    267     if (ndbuf->base.ndim == 0)
    268         ndbuf->flags |= ND_SCALAR;
    269     if (ndbuf->base.suboffsets)
    270         ndbuf->flags |= ND_PIL;
    271     if (PyBuffer_IsContiguous(&ndbuf->base, 'C'))
    272         ndbuf->flags |= ND_C;
    273     if (PyBuffer_IsContiguous(&ndbuf->base, 'F'))
    274         ndbuf->flags |= ND_FORTRAN;
    275 }
    276 
    277 
    278 /****************************************************************************/
    279 /*                          Buffer/List conversions                         */
    280 /****************************************************************************/
    281 
    282 static Py_ssize_t *strides_from_shape(const ndbuf_t *, int flags);
    283 
    284 /* Get number of members in a struct: see issue #12740 */
    285 typedef struct {
    286     PyObject_HEAD
    287     Py_ssize_t s_size;
    288     Py_ssize_t s_len;
    289 } PyPartialStructObject;
    290 
    291 static Py_ssize_t
    292 get_nmemb(PyObject *s)
    293 {
    294     return ((PyPartialStructObject *)s)->s_len;
    295 }
    296 
    297 /* Pack all items into the buffer of 'obj'. The 'format' parameter must be
    298    in struct module syntax. For standard C types, a single item is an integer.
    299    For compound types, a single item is a tuple of integers. */
    300 static int
    301 pack_from_list(PyObject *obj, PyObject *items, PyObject *format,
    302                Py_ssize_t itemsize)
    303 {
    304     PyObject *structobj, *pack_into;
    305     PyObject *args, *offset;
    306     PyObject *item, *tmp;
    307     Py_ssize_t nitems; /* number of items */
    308     Py_ssize_t nmemb;  /* number of members in a single item */
    309     Py_ssize_t i, j;
    310     int ret = 0;
    311 
    312     assert(PyObject_CheckBuffer(obj));
    313     assert(PyList_Check(items) || PyTuple_Check(items));
    314 
    315     structobj = PyObject_CallFunctionObjArgs(Struct, format, NULL);
    316     if (structobj == NULL)
    317         return -1;
    318 
    319     nitems = PySequence_Fast_GET_SIZE(items);
    320     nmemb = get_nmemb(structobj);
    321     assert(nmemb >= 1);
    322 
    323     pack_into = PyObject_GetAttrString(structobj, "pack_into");
    324     if (pack_into == NULL) {
    325         Py_DECREF(structobj);
    326         return -1;
    327     }
    328 
    329     /* nmemb >= 1 */
    330     args = PyTuple_New(2 + nmemb);
    331     if (args == NULL) {
    332         Py_DECREF(pack_into);
    333         Py_DECREF(structobj);
    334         return -1;
    335     }
    336 
    337     offset = NULL;
    338     for (i = 0; i < nitems; i++) {
    339         /* Loop invariant: args[j] are borrowed references or NULL. */
    340         PyTuple_SET_ITEM(args, 0, obj);
    341         for (j = 1; j < 2+nmemb; j++)
    342             PyTuple_SET_ITEM(args, j, NULL);
    343 
    344         Py_XDECREF(offset);
    345         offset = PyLong_FromSsize_t(i*itemsize);
    346         if (offset == NULL) {
    347             ret = -1;
    348             break;
    349         }
    350         PyTuple_SET_ITEM(args, 1, offset);
    351 
    352         item = PySequence_Fast_GET_ITEM(items, i);
    353         if ((PyBytes_Check(item) || PyLong_Check(item) ||
    354              PyFloat_Check(item)) && nmemb == 1) {
    355             PyTuple_SET_ITEM(args, 2, item);
    356         }
    357         else if ((PyList_Check(item) || PyTuple_Check(item)) &&
    358                  PySequence_Length(item) == nmemb) {
    359             for (j = 0; j < nmemb; j++) {
    360                 tmp = PySequence_Fast_GET_ITEM(item, j);
    361                 PyTuple_SET_ITEM(args, 2+j, tmp);
    362             }
    363         }
    364         else {
    365             PyErr_SetString(PyExc_ValueError,
    366                 "mismatch between initializer element and format string");
    367             ret = -1;
    368             break;
    369         }
    370 
    371         tmp = PyObject_CallObject(pack_into, args);
    372         if (tmp == NULL) {
    373             ret = -1;
    374             break;
    375         }
    376         Py_DECREF(tmp);
    377     }
    378 
    379     Py_INCREF(obj); /* args[0] */
    380     /* args[1]: offset is either NULL or should be dealloc'd */
    381     for (i = 2; i < 2+nmemb; i++) {
    382         tmp = PyTuple_GET_ITEM(args, i);
    383         Py_XINCREF(tmp);
    384     }
    385     Py_DECREF(args);
    386 
    387     Py_DECREF(pack_into);
    388     Py_DECREF(structobj);
    389     return ret;
    390 
    391 }
    392 
    393 /* Pack single element */
    394 static int
    395 pack_single(char *ptr, PyObject *item, const char *fmt, Py_ssize_t itemsize)
    396 {
    397     PyObject *structobj = NULL, *pack_into = NULL, *args = NULL;
    398     PyObject *format = NULL, *mview = NULL, *zero = NULL;
    399     Py_ssize_t i, nmemb;
    400     int ret = -1;
    401     PyObject *x;
    402 
    403     if (fmt == NULL) fmt = "B";
    404 
    405     format = PyUnicode_FromString(fmt);
    406     if (format == NULL)
    407         goto out;
    408 
    409     structobj = PyObject_CallFunctionObjArgs(Struct, format, NULL);
    410     if (structobj == NULL)
    411         goto out;
    412 
    413     nmemb = get_nmemb(structobj);
    414     assert(nmemb >= 1);
    415 
    416     mview = PyMemoryView_FromMemory(ptr, itemsize, PyBUF_WRITE);
    417     if (mview == NULL)
    418         goto out;
    419 
    420     zero = PyLong_FromLong(0);
    421     if (zero == NULL)
    422         goto out;
    423 
    424     pack_into = PyObject_GetAttrString(structobj, "pack_into");
    425     if (pack_into == NULL)
    426         goto out;
    427 
    428     args = PyTuple_New(2+nmemb);
    429     if (args == NULL)
    430         goto out;
    431 
    432     PyTuple_SET_ITEM(args, 0, mview);
    433     PyTuple_SET_ITEM(args, 1, zero);
    434 
    435     if ((PyBytes_Check(item) || PyLong_Check(item) ||
    436          PyFloat_Check(item)) && nmemb == 1) {
    437          PyTuple_SET_ITEM(args, 2, item);
    438     }
    439     else if ((PyList_Check(item) || PyTuple_Check(item)) &&
    440              PySequence_Length(item) == nmemb) {
    441         for (i = 0; i < nmemb; i++) {
    442             x = PySequence_Fast_GET_ITEM(item, i);
    443             PyTuple_SET_ITEM(args, 2+i, x);
    444         }
    445     }
    446     else {
    447         PyErr_SetString(PyExc_ValueError,
    448             "mismatch between initializer element and format string");
    449         goto args_out;
    450     }
    451 
    452     x = PyObject_CallObject(pack_into, args);
    453     if (x != NULL) {
    454         Py_DECREF(x);
    455         ret = 0;
    456     }
    457 
    458 
    459 args_out:
    460     for (i = 0; i < 2+nmemb; i++)
    461         Py_XINCREF(PyTuple_GET_ITEM(args, i));
    462     Py_XDECREF(args);
    463 out:
    464     Py_XDECREF(pack_into);
    465     Py_XDECREF(zero);
    466     Py_XDECREF(mview);
    467     Py_XDECREF(structobj);
    468     Py_XDECREF(format);
    469     return ret;
    470 }
    471 
    472 static void
    473 copy_rec(const Py_ssize_t *shape, Py_ssize_t ndim, Py_ssize_t itemsize,
    474          char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets,
    475          char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets,
    476          char *mem)
    477 {
    478     Py_ssize_t i;
    479 
    480     assert(ndim >= 1);
    481 
    482     if (ndim == 1) {
    483         if (!HAVE_PTR(dsuboffsets) && !HAVE_PTR(ssuboffsets) &&
    484             dstrides[0] == itemsize && sstrides[0] == itemsize) {
    485             memmove(dptr, sptr, shape[0] * itemsize);
    486         }
    487         else {
    488             char *p;
    489             assert(mem != NULL);
    490             for (i=0, p=mem; i<shape[0]; p+=itemsize, sptr+=sstrides[0], i++) {
    491                 char *xsptr = ADJUST_PTR(sptr, ssuboffsets);
    492                 memcpy(p, xsptr, itemsize);
    493             }
    494             for (i=0, p=mem; i<shape[0]; p+=itemsize, dptr+=dstrides[0], i++) {
    495                 char *xdptr = ADJUST_PTR(dptr, dsuboffsets);
    496                 memcpy(xdptr, p, itemsize);
    497             }
    498         }
    499         return;
    500     }
    501 
    502     for (i = 0; i < shape[0]; dptr+=dstrides[0], sptr+=sstrides[0], i++) {
    503         char *xdptr = ADJUST_PTR(dptr, dsuboffsets);
    504         char *xsptr = ADJUST_PTR(sptr, ssuboffsets);
    505 
    506         copy_rec(shape+1, ndim-1, itemsize,
    507                  xdptr, dstrides+1, dsuboffsets ? dsuboffsets+1 : NULL,
    508                  xsptr, sstrides+1, ssuboffsets ? ssuboffsets+1 : NULL,
    509                  mem);
    510     }
    511 }
    512 
    513 static int
    514 cmp_structure(Py_buffer *dest, Py_buffer *src)
    515 {
    516     Py_ssize_t i;
    517 
    518     if (strcmp(FIX_FORMAT(dest->format), FIX_FORMAT(src->format)) != 0 ||
    519         dest->itemsize != src->itemsize ||
    520         dest->ndim != src->ndim)
    521         return -1;
    522 
    523     for (i = 0; i < dest->ndim; i++) {
    524         if (dest->shape[i] != src->shape[i])
    525             return -1;
    526         if (dest->shape[i] == 0)
    527             break;
    528     }
    529 
    530     return 0;
    531 }
    532 
    533 /* Copy src to dest. Both buffers must have the same format, itemsize,
    534    ndim and shape. Copying is atomic, the function never fails with
    535    a partial copy. */
    536 static int
    537 copy_buffer(Py_buffer *dest, Py_buffer *src)
    538 {
    539     char *mem = NULL;
    540 
    541     assert(dest->ndim > 0);
    542 
    543     if (cmp_structure(dest, src) < 0) {
    544         PyErr_SetString(PyExc_ValueError,
    545             "ndarray assignment: lvalue and rvalue have different structures");
    546         return -1;
    547     }
    548 
    549     if ((dest->suboffsets && dest->suboffsets[dest->ndim-1] >= 0) ||
    550         (src->suboffsets && src->suboffsets[src->ndim-1] >= 0) ||
    551         dest->strides[dest->ndim-1] != dest->itemsize ||
    552         src->strides[src->ndim-1] != src->itemsize) {
    553         mem = PyMem_Malloc(dest->shape[dest->ndim-1] * dest->itemsize);
    554         if (mem == NULL) {
    555             PyErr_NoMemory();
    556             return -1;
    557         }
    558     }
    559 
    560     copy_rec(dest->shape, dest->ndim, dest->itemsize,
    561              dest->buf, dest->strides, dest->suboffsets,
    562              src->buf, src->strides, src->suboffsets,
    563              mem);
    564 
    565     PyMem_XFree(mem);
    566     return 0;
    567 }
    568 
    569 
    570 /* Unpack single element */
    571 static PyObject *
    572 unpack_single(char *ptr, const char *fmt, Py_ssize_t itemsize)
    573 {
    574     PyObject *x, *unpack_from, *mview;
    575 
    576     if (fmt == NULL) {
    577         fmt = "B";
    578         itemsize = 1;
    579     }
    580 
    581     unpack_from = PyObject_GetAttrString(structmodule, "unpack_from");
    582     if (unpack_from == NULL)
    583         return NULL;
    584 
    585     mview = PyMemoryView_FromMemory(ptr, itemsize, PyBUF_READ);
    586     if (mview == NULL) {
    587         Py_DECREF(unpack_from);
    588         return NULL;
    589     }
    590 
    591     x = PyObject_CallFunction(unpack_from, "sO", fmt, mview);
    592     Py_DECREF(unpack_from);
    593     Py_DECREF(mview);
    594     if (x == NULL)
    595         return NULL;
    596 
    597     if (PyTuple_GET_SIZE(x) == 1) {
    598         PyObject *tmp = PyTuple_GET_ITEM(x, 0);
    599         Py_INCREF(tmp);
    600         Py_DECREF(x);
    601         return tmp;
    602     }
    603 
    604     return x;
    605 }
    606 
    607 /* Unpack a multi-dimensional matrix into a nested list. Return a scalar
    608    for ndim = 0. */
    609 static PyObject *
    610 unpack_rec(PyObject *unpack_from, char *ptr, PyObject *mview, char *item,
    611            const Py_ssize_t *shape, const Py_ssize_t *strides,
    612            const Py_ssize_t *suboffsets, Py_ssize_t ndim, Py_ssize_t itemsize)
    613 {
    614     PyObject *lst, *x;
    615     Py_ssize_t i;
    616 
    617     assert(ndim >= 0);
    618     assert(shape != NULL);
    619     assert(strides != NULL);
    620 
    621     if (ndim == 0) {
    622         memcpy(item, ptr, itemsize);
    623         x = PyObject_CallFunctionObjArgs(unpack_from, mview, NULL);
    624         if (x == NULL)
    625             return NULL;
    626         if (PyTuple_GET_SIZE(x) == 1) {
    627             PyObject *tmp = PyTuple_GET_ITEM(x, 0);
    628             Py_INCREF(tmp);
    629             Py_DECREF(x);
    630             return tmp;
    631         }
    632         return x;
    633     }
    634 
    635     lst = PyList_New(shape[0]);
    636     if (lst == NULL)
    637         return NULL;
    638 
    639     for (i = 0; i < shape[0]; ptr+=strides[0], i++) {
    640         char *nextptr = ADJUST_PTR(ptr, suboffsets);
    641 
    642         x = unpack_rec(unpack_from, nextptr, mview, item,
    643                        shape+1, strides+1, suboffsets ? suboffsets+1 : NULL,
    644                        ndim-1, itemsize);
    645         if (x == NULL) {
    646             Py_DECREF(lst);
    647             return NULL;
    648         }
    649 
    650         PyList_SET_ITEM(lst, i, x);
    651     }
    652 
    653     return lst;
    654 }
    655 
    656 
    657 static PyObject *
    658 ndarray_as_list(NDArrayObject *nd)
    659 {
    660     PyObject *structobj = NULL, *unpack_from = NULL;
    661     PyObject *lst = NULL, *mview = NULL;
    662     Py_buffer *base = &nd->head->base;
    663     Py_ssize_t *shape = base->shape;
    664     Py_ssize_t *strides = base->strides;
    665     Py_ssize_t simple_shape[1];
    666     Py_ssize_t simple_strides[1];
    667     char *item = NULL;
    668     PyObject *format;
    669     char *fmt = base->format;
    670 
    671     base = &nd->head->base;
    672 
    673     if (fmt == NULL) {
    674         PyErr_SetString(PyExc_ValueError,
    675             "ndarray: tolist() does not support format=NULL, use "
    676             "tobytes()");
    677         return NULL;
    678     }
    679     if (shape == NULL) {
    680         assert(ND_C_CONTIGUOUS(nd->head->flags));
    681         assert(base->strides == NULL);
    682         assert(base->ndim <= 1);
    683         shape = simple_shape;
    684         shape[0] = base->len;
    685         strides = simple_strides;
    686         strides[0] = base->itemsize;
    687     }
    688     else if (strides == NULL) {
    689         assert(ND_C_CONTIGUOUS(nd->head->flags));
    690         strides = strides_from_shape(nd->head, 0);
    691         if (strides == NULL)
    692             return NULL;
    693     }
    694 
    695     format = PyUnicode_FromString(fmt);
    696     if (format == NULL)
    697         goto out;
    698 
    699     structobj = PyObject_CallFunctionObjArgs(Struct, format, NULL);
    700     Py_DECREF(format);
    701     if (structobj == NULL)
    702         goto out;
    703 
    704     unpack_from = PyObject_GetAttrString(structobj, "unpack_from");
    705     if (unpack_from == NULL)
    706         goto out;
    707 
    708     item = PyMem_Malloc(base->itemsize);
    709     if (item == NULL) {
    710         PyErr_NoMemory();
    711         goto out;
    712     }
    713 
    714     mview = PyMemoryView_FromMemory(item, base->itemsize, PyBUF_WRITE);
    715     if (mview == NULL)
    716         goto out;
    717 
    718     lst = unpack_rec(unpack_from, base->buf, mview, item,
    719                      shape, strides, base->suboffsets,
    720                      base->ndim, base->itemsize);
    721 
    722 out:
    723     Py_XDECREF(mview);
    724     PyMem_XFree(item);
    725     Py_XDECREF(unpack_from);
    726     Py_XDECREF(structobj);
    727     if (strides != base->strides && strides != simple_strides)
    728         PyMem_XFree(strides);
    729 
    730     return lst;
    731 }
    732 
    733 
    734 /****************************************************************************/
    735 /*                            Initialize ndbuf                              */
    736 /****************************************************************************/
    737 
    738 /*
    739    State of a new ndbuf during initialization. 'OK' means that initialization
    740    is complete. 'PTR' means that a pointer has been initialized, but the
    741    state of the memory is still undefined and ndbuf->offset is disregarded.
    742 
    743   +-----------------+-----------+-------------+----------------+
    744   |                 | ndbuf_new | init_simple | init_structure |
    745   +-----------------+-----------+-------------+----------------+
    746   | next            | OK (NULL) |     OK      |       OK       |
    747   +-----------------+-----------+-------------+----------------+
    748   | prev            | OK (NULL) |     OK      |       OK       |
    749   +-----------------+-----------+-------------+----------------+
    750   | len             |    OK     |     OK      |       OK       |
    751   +-----------------+-----------+-------------+----------------+
    752   | offset          |    OK     |     OK      |       OK       |
    753   +-----------------+-----------+-------------+----------------+
    754   | data            |    PTR    |     OK      |       OK       |
    755   +-----------------+-----------+-------------+----------------+
    756   | flags           |    user   |    user     |       OK       |
    757   +-----------------+-----------+-------------+----------------+
    758   | exports         |   OK (0)  |     OK      |       OK       |
    759   +-----------------+-----------+-------------+----------------+
    760   | base.obj        | OK (NULL) |     OK      |       OK       |
    761   +-----------------+-----------+-------------+----------------+
    762   | base.buf        |    PTR    |     PTR     |       OK       |
    763   +-----------------+-----------+-------------+----------------+
    764   | base.len        | len(data) |  len(data)  |       OK       |
    765   +-----------------+-----------+-------------+----------------+
    766   | base.itemsize   |     1     |     OK      |       OK       |
    767   +-----------------+-----------+-------------+----------------+
    768   | base.readonly   |     0     |     OK      |       OK       |
    769   +-----------------+-----------+-------------+----------------+
    770   | base.format     |    NULL   |     OK      |       OK       |
    771   +-----------------+-----------+-------------+----------------+
    772   | base.ndim       |     1     |      1      |       OK       |
    773   +-----------------+-----------+-------------+----------------+
    774   | base.shape      |    NULL   |    NULL     |       OK       |
    775   +-----------------+-----------+-------------+----------------+
    776   | base.strides    |    NULL   |    NULL     |       OK       |
    777   +-----------------+-----------+-------------+----------------+
    778   | base.suboffsets |    NULL   |    NULL     |       OK       |
    779   +-----------------+-----------+-------------+----------------+
    780   | base.internal   |    OK     |    OK       |       OK       |
    781   +-----------------+-----------+-------------+----------------+
    782 
    783 */
    784 
    785 static Py_ssize_t
    786 get_itemsize(PyObject *format)
    787 {
    788     PyObject *tmp;
    789     Py_ssize_t itemsize;
    790 
    791     tmp = PyObject_CallFunctionObjArgs(calcsize, format, NULL);
    792     if (tmp == NULL)
    793         return -1;
    794     itemsize = PyLong_AsSsize_t(tmp);
    795     Py_DECREF(tmp);
    796 
    797     return itemsize;
    798 }
    799 
    800 static char *
    801 get_format(PyObject *format)
    802 {
    803     PyObject *tmp;
    804     char *fmt;
    805 
    806     tmp = PyUnicode_AsASCIIString(format);
    807     if (tmp == NULL)
    808         return NULL;
    809     fmt = PyMem_Malloc(PyBytes_GET_SIZE(tmp)+1);
    810     if (fmt == NULL) {
    811         PyErr_NoMemory();
    812         Py_DECREF(tmp);
    813         return NULL;
    814     }
    815     strcpy(fmt, PyBytes_AS_STRING(tmp));
    816     Py_DECREF(tmp);
    817 
    818     return fmt;
    819 }
    820 
    821 static int
    822 init_simple(ndbuf_t *ndbuf, PyObject *items, PyObject *format,
    823             Py_ssize_t itemsize)
    824 {
    825     PyObject *mview;
    826     Py_buffer *base = &ndbuf->base;
    827     int ret;
    828 
    829     mview = PyMemoryView_FromBuffer(base);
    830     if (mview == NULL)
    831         return -1;
    832 
    833     ret = pack_from_list(mview, items, format, itemsize);
    834     Py_DECREF(mview);
    835     if (ret < 0)
    836         return -1;
    837 
    838     base->readonly = !(ndbuf->flags & ND_WRITABLE);
    839     base->itemsize = itemsize;
    840     base->format = get_format(format);
    841     if (base->format == NULL)
    842         return -1;
    843 
    844     return 0;
    845 }
    846 
    847 static Py_ssize_t *
    848 seq_as_ssize_array(PyObject *seq, Py_ssize_t len, int is_shape)
    849 {
    850     Py_ssize_t *dest;
    851     Py_ssize_t x, i;
    852 
    853     /* ndim = len <= ND_MAX_NDIM, so PyMem_New() is actually not needed. */
    854     dest = PyMem_New(Py_ssize_t, len);
    855     if (dest == NULL) {
    856         PyErr_NoMemory();
    857         return NULL;
    858     }
    859 
    860     for (i = 0; i < len; i++) {
    861         PyObject *tmp = PySequence_Fast_GET_ITEM(seq, i);
    862         if (!PyLong_Check(tmp)) {
    863             PyErr_Format(PyExc_ValueError,
    864                 "elements of %s must be integers",
    865                 is_shape ? "shape" : "strides");
    866             PyMem_Free(dest);
    867             return NULL;
    868         }
    869         x = PyLong_AsSsize_t(tmp);
    870         if (PyErr_Occurred()) {
    871             PyMem_Free(dest);
    872             return NULL;
    873         }
    874         if (is_shape && x < 0) {
    875             PyErr_Format(PyExc_ValueError,
    876                 "elements of shape must be integers >= 0");
    877             PyMem_Free(dest);
    878             return NULL;
    879         }
    880         dest[i] = x;
    881     }
    882 
    883     return dest;
    884 }
    885 
    886 static Py_ssize_t *
    887 strides_from_shape(const ndbuf_t *ndbuf, int flags)
    888 {
    889     const Py_buffer *base = &ndbuf->base;
    890     Py_ssize_t *s, i;
    891 
    892     s = PyMem_Malloc(base->ndim * (sizeof *s));
    893     if (s == NULL) {
    894         PyErr_NoMemory();
    895         return NULL;
    896     }
    897 
    898     if (flags & ND_FORTRAN) {
    899         s[0] = base->itemsize;
    900         for (i = 1; i < base->ndim; i++)
    901             s[i] = s[i-1] * base->shape[i-1];
    902     }
    903     else {
    904         s[base->ndim-1] = base->itemsize;
    905         for (i = base->ndim-2; i >= 0; i--)
    906             s[i] = s[i+1] * base->shape[i+1];
    907     }
    908 
    909     return s;
    910 }
    911 
    912 /* Bounds check:
    913 
    914      len := complete length of allocated memory
    915      offset := start of the array
    916 
    917      A single array element is indexed by:
    918 
    919        i = indices[0] * strides[0] + indices[1] * strides[1] + ...
    920 
    921      imin is reached when all indices[n] combined with positive strides are 0
    922      and all indices combined with negative strides are shape[n]-1, which is
    923      the maximum index for the nth dimension.
    924 
    925      imax is reached when all indices[n] combined with negative strides are 0
    926      and all indices combined with positive strides are shape[n]-1.
    927 */
    928 static int
    929 verify_structure(Py_ssize_t len, Py_ssize_t itemsize, Py_ssize_t offset,
    930                  const Py_ssize_t *shape, const Py_ssize_t *strides,
    931                  Py_ssize_t ndim)
    932 {
    933     Py_ssize_t imin, imax;
    934     Py_ssize_t n;
    935 
    936     assert(ndim >= 0);
    937 
    938     if (ndim == 0 && (offset < 0 || offset+itemsize > len))
    939         goto invalid_combination;
    940 
    941     for (n = 0; n < ndim; n++)
    942         if (strides[n] % itemsize) {
    943             PyErr_SetString(PyExc_ValueError,
    944             "strides must be a multiple of itemsize");
    945             return -1;
    946         }
    947 
    948     for (n = 0; n < ndim; n++)
    949         if (shape[n] == 0)
    950             return 0;
    951 
    952     imin = imax = 0;
    953     for (n = 0; n < ndim; n++)
    954         if (strides[n] <= 0)
    955             imin += (shape[n]-1) * strides[n];
    956         else
    957             imax += (shape[n]-1) * strides[n];
    958 
    959     if (imin + offset < 0 || imax + offset + itemsize > len)
    960         goto invalid_combination;
    961 
    962     return 0;
    963 
    964 
    965 invalid_combination:
    966     PyErr_SetString(PyExc_ValueError,
    967         "invalid combination of buffer, shape and strides");
    968     return -1;
    969 }
    970 
    971 /*
    972    Convert a NumPy-style array to an array using suboffsets to stride in
    973    the first dimension. Requirements: ndim > 0.
    974 
    975    Contiguous example
    976    ==================
    977 
    978      Input:
    979      ------
    980        shape      = {2, 2, 3};
    981        strides    = {6, 3, 1};
    982        suboffsets = NULL;
    983        data       = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
    984        buf        = &data[0]
    985 
    986      Output:
    987      -------
    988        shape      = {2, 2, 3};
    989        strides    = {sizeof(char *), 3, 1};
    990        suboffsets = {0, -1, -1};
    991        data       = {p1, p2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
    992                      |   |   ^                 ^
    993                      `---'---'                 |
    994                          |                     |
    995                          `---------------------'
    996        buf        = &data[0]
    997 
    998      So, in the example the input resembles the three-dimensional array
    999      char v[2][2][3], while the output resembles an array of two pointers
   1000      to two-dimensional arrays: char (*v[2])[2][3].
   1001 
   1002 
   1003    Non-contiguous example:
   1004    =======================
   1005 
   1006      Input (with offset and negative strides):
   1007      -----------------------------------------
   1008        shape      = {2, 2, 3};
   1009        strides    = {-6, 3, -1};
   1010        offset     = 8
   1011        suboffsets = NULL;
   1012        data       = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
   1013 
   1014      Output:
   1015      -------
   1016        shape      = {2, 2, 3};
   1017        strides    = {-sizeof(char *), 3, -1};
   1018        suboffsets = {2, -1, -1};
   1019        newdata    = {p1, p2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
   1020                      |   |   ^     ^           ^     ^
   1021                      `---'---'     |           |     `- p2+suboffsets[0]
   1022                          |         `-----------|--- p1+suboffsets[0]
   1023                          `---------------------'
   1024        buf        = &newdata[1]  # striding backwards over the pointers.
   1025 
   1026      suboffsets[0] is the same as the offset that one would specify if
   1027      the two {2, 3} subarrays were created directly, hence the name.
   1028 */
   1029 static int
   1030 init_suboffsets(ndbuf_t *ndbuf)
   1031 {
   1032     Py_buffer *base = &ndbuf->base;
   1033     Py_ssize_t start, step;
   1034     Py_ssize_t imin, suboffset0;
   1035     Py_ssize_t addsize;
   1036     Py_ssize_t n;
   1037     char *data;
   1038 
   1039     assert(base->ndim > 0);
   1040     assert(base->suboffsets == NULL);
   1041 
   1042     /* Allocate new data with additional space for shape[0] pointers. */
   1043     addsize = base->shape[0] * (sizeof (char *));
   1044 
   1045     /* Align array start to a multiple of 8. */
   1046     addsize = 8 * ((addsize + 7) / 8);
   1047 
   1048     data = PyMem_Malloc(ndbuf->len + addsize);
   1049     if (data == NULL) {
   1050         PyErr_NoMemory();
   1051         return -1;
   1052     }
   1053 
   1054     memcpy(data + addsize, ndbuf->data, ndbuf->len);
   1055 
   1056     PyMem_Free(ndbuf->data);
   1057     ndbuf->data = data;
   1058     ndbuf->len += addsize;
   1059     base->buf = ndbuf->data;
   1060 
   1061     /* imin: minimum index of the input array relative to ndbuf->offset.
   1062        suboffset0: offset for each sub-array of the output. This is the
   1063                    same as calculating -imin' for a sub-array of ndim-1. */
   1064     imin = suboffset0 = 0;
   1065     for (n = 0; n < base->ndim; n++) {
   1066         if (base->shape[n] == 0)
   1067             break;
   1068         if (base->strides[n] <= 0) {
   1069             Py_ssize_t x = (base->shape[n]-1) * base->strides[n];
   1070             imin += x;
   1071             suboffset0 += (n >= 1) ? -x : 0;
   1072         }
   1073     }
   1074 
   1075     /* Initialize the array of pointers to the sub-arrays. */
   1076     start = addsize + ndbuf->offset + imin;
   1077     step = base->strides[0] < 0 ? -base->strides[0] : base->strides[0];
   1078 
   1079     for (n = 0; n < base->shape[0]; n++)
   1080         ((char **)base->buf)[n] = (char *)base->buf + start + n*step;
   1081 
   1082     /* Initialize suboffsets. */
   1083     base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
   1084     if (base->suboffsets == NULL) {
   1085         PyErr_NoMemory();
   1086         return -1;
   1087     }
   1088     base->suboffsets[0] = suboffset0;
   1089     for (n = 1; n < base->ndim; n++)
   1090         base->suboffsets[n] = -1;
   1091 
   1092     /* Adjust strides for the first (zeroth) dimension. */
   1093     if (base->strides[0] >= 0) {
   1094         base->strides[0] = sizeof(char *);
   1095     }
   1096     else {
   1097         /* Striding backwards. */
   1098         base->strides[0] = -(Py_ssize_t)sizeof(char *);
   1099         if (base->shape[0] > 0)
   1100             base->buf = (char *)base->buf + (base->shape[0]-1) * sizeof(char *);
   1101     }
   1102 
   1103     ndbuf->flags &= ~(ND_C|ND_FORTRAN);
   1104     ndbuf->offset = 0;
   1105     return 0;
   1106 }
   1107 
   1108 static void
   1109 init_len(Py_buffer *base)
   1110 {
   1111     Py_ssize_t i;
   1112 
   1113     base->len = 1;
   1114     for (i = 0; i < base->ndim; i++)
   1115         base->len *= base->shape[i];
   1116     base->len *= base->itemsize;
   1117 }
   1118 
   1119 static int
   1120 init_structure(ndbuf_t *ndbuf, PyObject *shape, PyObject *strides,
   1121                Py_ssize_t ndim)
   1122 {
   1123     Py_buffer *base = &ndbuf->base;
   1124 
   1125     base->ndim = (int)ndim;
   1126     if (ndim == 0) {
   1127         if (ndbuf->flags & ND_PIL) {
   1128             PyErr_SetString(PyExc_TypeError,
   1129                 "ndim = 0 cannot be used in conjunction with ND_PIL");
   1130             return -1;
   1131         }
   1132         ndbuf->flags |= (ND_SCALAR|ND_C|ND_FORTRAN);
   1133         return 0;
   1134     }
   1135 
   1136     /* shape */
   1137     base->shape = seq_as_ssize_array(shape, ndim, 1);
   1138     if (base->shape == NULL)
   1139         return -1;
   1140 
   1141     /* strides */
   1142     if (strides) {
   1143         base->strides = seq_as_ssize_array(strides, ndim, 0);
   1144     }
   1145     else {
   1146         base->strides = strides_from_shape(ndbuf, ndbuf->flags);
   1147     }
   1148     if (base->strides == NULL)
   1149         return -1;
   1150     if (verify_structure(base->len, base->itemsize, ndbuf->offset,
   1151                          base->shape, base->strides, ndim) < 0)
   1152         return -1;
   1153 
   1154     /* buf */
   1155     base->buf = ndbuf->data + ndbuf->offset;
   1156 
   1157     /* len */
   1158     init_len(base);
   1159 
   1160     /* ndbuf->flags */
   1161     if (PyBuffer_IsContiguous(base, 'C'))
   1162         ndbuf->flags |= ND_C;
   1163     if (PyBuffer_IsContiguous(base, 'F'))
   1164         ndbuf->flags |= ND_FORTRAN;
   1165 
   1166 
   1167     /* convert numpy array to suboffset representation */
   1168     if (ndbuf->flags & ND_PIL) {
   1169         /* modifies base->buf, base->strides and base->suboffsets **/
   1170         return init_suboffsets(ndbuf);
   1171     }
   1172 
   1173     return 0;
   1174 }
   1175 
   1176 static ndbuf_t *
   1177 init_ndbuf(PyObject *items, PyObject *shape, PyObject *strides,
   1178            Py_ssize_t offset, PyObject *format, int flags)
   1179 {
   1180     ndbuf_t *ndbuf;
   1181     Py_ssize_t ndim;
   1182     Py_ssize_t nitems;
   1183     Py_ssize_t itemsize;
   1184 
   1185     /* ndim = len(shape) */
   1186     CHECK_LIST_OR_TUPLE(shape)
   1187     ndim = PySequence_Fast_GET_SIZE(shape);
   1188     if (ndim > ND_MAX_NDIM) {
   1189         PyErr_Format(PyExc_ValueError,
   1190             "ndim must not exceed %d", ND_MAX_NDIM);
   1191         return NULL;
   1192     }
   1193 
   1194     /* len(strides) = len(shape) */
   1195     if (strides) {
   1196         CHECK_LIST_OR_TUPLE(strides)
   1197         if (PySequence_Fast_GET_SIZE(strides) == 0)
   1198             strides = NULL;
   1199         else if (flags & ND_FORTRAN) {
   1200             PyErr_SetString(PyExc_TypeError,
   1201                 "ND_FORTRAN cannot be used together with strides");
   1202             return NULL;
   1203         }
   1204         else if (PySequence_Fast_GET_SIZE(strides) != ndim) {
   1205             PyErr_SetString(PyExc_ValueError,
   1206                 "len(shape) != len(strides)");
   1207             return NULL;
   1208         }
   1209     }
   1210 
   1211     /* itemsize */
   1212     itemsize = get_itemsize(format);
   1213     if (itemsize <= 0) {
   1214         if (itemsize == 0) {
   1215             PyErr_SetString(PyExc_ValueError,
   1216                 "itemsize must not be zero");
   1217         }
   1218         return NULL;
   1219     }
   1220 
   1221     /* convert scalar to list */
   1222     if (ndim == 0) {
   1223         items = Py_BuildValue("(O)", items);
   1224         if (items == NULL)
   1225             return NULL;
   1226     }
   1227     else {
   1228         CHECK_LIST_OR_TUPLE(items)
   1229         Py_INCREF(items);
   1230     }
   1231 
   1232     /* number of items */
   1233     nitems = PySequence_Fast_GET_SIZE(items);
   1234     if (nitems == 0) {
   1235         PyErr_SetString(PyExc_ValueError,
   1236             "initializer list or tuple must not be empty");
   1237         Py_DECREF(items);
   1238         return NULL;
   1239     }
   1240 
   1241     ndbuf = ndbuf_new(nitems, itemsize, offset, flags);
   1242     if (ndbuf == NULL) {
   1243         Py_DECREF(items);
   1244         return NULL;
   1245     }
   1246 
   1247 
   1248     if (init_simple(ndbuf, items, format, itemsize) < 0)
   1249         goto error;
   1250     if (init_structure(ndbuf, shape, strides, ndim) < 0)
   1251         goto error;
   1252 
   1253     Py_DECREF(items);
   1254     return ndbuf;
   1255 
   1256 error:
   1257     Py_DECREF(items);
   1258     ndbuf_free(ndbuf);
   1259     return NULL;
   1260 }
   1261 
   1262 /* initialize and push a new base onto the linked list */
   1263 static int
   1264 ndarray_push_base(NDArrayObject *nd, PyObject *items,
   1265                   PyObject *shape, PyObject *strides,
   1266                   Py_ssize_t offset, PyObject *format, int flags)
   1267 {
   1268     ndbuf_t *ndbuf;
   1269 
   1270     ndbuf = init_ndbuf(items, shape, strides, offset, format, flags);
   1271     if (ndbuf == NULL)
   1272         return -1;
   1273 
   1274     ndbuf_push(nd, ndbuf);
   1275     return 0;
   1276 }
   1277 
   1278 #define PyBUF_UNUSED 0x10000
   1279 static int
   1280 ndarray_init(PyObject *self, PyObject *args, PyObject *kwds)
   1281 {
   1282     NDArrayObject *nd = (NDArrayObject *)self;
   1283     static char *kwlist[] = {
   1284         "obj", "shape", "strides", "offset", "format", "flags", "getbuf", NULL
   1285     };
   1286     PyObject *v = NULL;  /* initializer: scalar, list, tuple or base object */
   1287     PyObject *shape = NULL;   /* size of each dimension */
   1288     PyObject *strides = NULL; /* number of bytes to the next elt in each dim */
   1289     Py_ssize_t offset = 0;            /* buffer offset */
   1290     PyObject *format = simple_format; /* struct module specifier: "B" */
   1291     int flags = ND_DEFAULT;           /* base buffer and ndarray flags */
   1292 
   1293     int getbuf = PyBUF_UNUSED; /* re-exporter: getbuffer request flags */
   1294 
   1295 
   1296     if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOnOii", kwlist,
   1297             &v, &shape, &strides, &offset, &format, &flags, &getbuf))
   1298         return -1;
   1299 
   1300     /* NDArrayObject is re-exporter */
   1301     if (PyObject_CheckBuffer(v) && shape == NULL) {
   1302         if (strides || offset || format != simple_format ||
   1303             !(flags == ND_DEFAULT || flags == ND_REDIRECT)) {
   1304             PyErr_SetString(PyExc_TypeError,
   1305                "construction from exporter object only takes 'obj', 'getbuf' "
   1306                "and 'flags' arguments");
   1307             return -1;
   1308         }
   1309 
   1310         getbuf = (getbuf == PyBUF_UNUSED) ? PyBUF_FULL_RO : getbuf;
   1311 
   1312         if (ndarray_init_staticbuf(v, nd, getbuf) < 0)
   1313             return -1;
   1314 
   1315         init_flags(nd->head);
   1316         nd->head->flags |= flags;
   1317 
   1318         return 0;
   1319     }
   1320 
   1321     /* NDArrayObject is the original base object. */
   1322     if (getbuf != PyBUF_UNUSED) {
   1323         PyErr_SetString(PyExc_TypeError,
   1324             "getbuf argument only valid for construction from exporter "
   1325             "object");
   1326         return -1;
   1327     }
   1328     if (shape == NULL) {
   1329         PyErr_SetString(PyExc_TypeError,
   1330             "shape is a required argument when constructing from "
   1331             "list, tuple or scalar");
   1332         return -1;
   1333     }
   1334 
   1335     if (flags & ND_VAREXPORT) {
   1336         nd->flags |= ND_VAREXPORT;
   1337         flags &= ~ND_VAREXPORT;
   1338     }
   1339 
   1340     /* Initialize and push the first base buffer onto the linked list. */
   1341     return ndarray_push_base(nd, v, shape, strides, offset, format, flags);
   1342 }
   1343 
   1344 /* Push an additional base onto the linked list. */
   1345 static PyObject *
   1346 ndarray_push(PyObject *self, PyObject *args, PyObject *kwds)
   1347 {
   1348     NDArrayObject *nd = (NDArrayObject *)self;
   1349     static char *kwlist[] = {
   1350         "items", "shape", "strides", "offset", "format", "flags", NULL
   1351     };
   1352     PyObject *items = NULL;   /* initializer: scalar, list or tuple */
   1353     PyObject *shape = NULL;   /* size of each dimension */
   1354     PyObject *strides = NULL; /* number of bytes to the next elt in each dim */
   1355     PyObject *format = simple_format;  /* struct module specifier: "B" */
   1356     Py_ssize_t offset = 0;             /* buffer offset */
   1357     int flags = ND_DEFAULT;            /* base buffer flags */
   1358 
   1359     if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OnOi", kwlist,
   1360             &items, &shape, &strides, &offset, &format, &flags))
   1361         return NULL;
   1362 
   1363     if (flags & ND_VAREXPORT) {
   1364         PyErr_SetString(PyExc_ValueError,
   1365             "ND_VAREXPORT flag can only be used during object creation");
   1366         return NULL;
   1367     }
   1368     if (ND_IS_CONSUMER(nd)) {
   1369         PyErr_SetString(PyExc_BufferError,
   1370             "structure of re-exporting object is immutable");
   1371         return NULL;
   1372     }
   1373     if (!(nd->flags&ND_VAREXPORT) && nd->head->exports > 0) {
   1374         PyErr_Format(PyExc_BufferError,
   1375             "cannot change structure: %zd exported buffer%s",
   1376             nd->head->exports, nd->head->exports==1 ? "" : "s");
   1377         return NULL;
   1378     }
   1379 
   1380     if (ndarray_push_base(nd, items, shape, strides,
   1381                           offset, format, flags) < 0)
   1382         return NULL;
   1383     Py_RETURN_NONE;
   1384 }
   1385 
   1386 /* Pop a base from the linked list (if possible). */
   1387 static PyObject *
   1388 ndarray_pop(PyObject *self, PyObject *dummy)
   1389 {
   1390     NDArrayObject *nd = (NDArrayObject *)self;
   1391     if (ND_IS_CONSUMER(nd)) {
   1392         PyErr_SetString(PyExc_BufferError,
   1393             "structure of re-exporting object is immutable");
   1394         return NULL;
   1395     }
   1396     if (nd->head->exports > 0) {
   1397         PyErr_Format(PyExc_BufferError,
   1398             "cannot change structure: %zd exported buffer%s",
   1399             nd->head->exports, nd->head->exports==1 ? "" : "s");
   1400         return NULL;
   1401     }
   1402     if (nd->head->next == NULL) {
   1403         PyErr_SetString(PyExc_BufferError,
   1404             "list only has a single base");
   1405         return NULL;
   1406     }
   1407 
   1408     ndbuf_pop(nd);
   1409     Py_RETURN_NONE;
   1410 }
   1411 
   1412 /**************************************************************************/
   1413 /*                               getbuffer                                */
   1414 /**************************************************************************/
   1415 
   1416 static int
   1417 ndarray_getbuf(NDArrayObject *self, Py_buffer *view, int flags)
   1418 {
   1419     ndbuf_t *ndbuf = self->head;
   1420     Py_buffer *base = &ndbuf->base;
   1421     int baseflags = ndbuf->flags;
   1422 
   1423     /* redirect mode */
   1424     if (base->obj != NULL && (baseflags&ND_REDIRECT)) {
   1425         return PyObject_GetBuffer(base->obj, view, flags);
   1426     }
   1427 
   1428     /* start with complete information */
   1429     *view = *base;
   1430     view->obj = NULL;
   1431 
   1432     /* reconstruct format */
   1433     if (view->format == NULL)
   1434         view->format = "B";
   1435 
   1436     if (base->ndim != 0 &&
   1437         ((REQ_SHAPE(flags) && base->shape == NULL) ||
   1438          (REQ_STRIDES(flags) && base->strides == NULL))) {
   1439         /* The ndarray is a re-exporter that has been created without full
   1440            information for testing purposes. In this particular case the
   1441            ndarray is not a PEP-3118 compliant buffer provider. */
   1442         PyErr_SetString(PyExc_BufferError,
   1443             "re-exporter does not provide format, shape or strides");
   1444         return -1;
   1445     }
   1446 
   1447     if (baseflags & ND_GETBUF_FAIL) {
   1448         PyErr_SetString(PyExc_BufferError,
   1449             "ND_GETBUF_FAIL: forced test exception");
   1450         if (baseflags & ND_GETBUF_UNDEFINED)
   1451             view->obj = (PyObject *)0x1; /* wrong but permitted in <= 3.2 */
   1452         return -1;
   1453     }
   1454 
   1455     if (REQ_WRITABLE(flags) && base->readonly) {
   1456         PyErr_SetString(PyExc_BufferError,
   1457             "ndarray is not writable");
   1458         return -1;
   1459     }
   1460     if (!REQ_FORMAT(flags)) {
   1461         /* NULL indicates that the buffer's data type has been cast to 'B'.
   1462            view->itemsize is the _previous_ itemsize. If shape is present,
   1463            the equality product(shape) * itemsize = len still holds at this
   1464            point. The equality calcsize(format) = itemsize does _not_ hold
   1465            from here on! */
   1466         view->format = NULL;
   1467     }
   1468 
   1469     if (REQ_C_CONTIGUOUS(flags) && !ND_C_CONTIGUOUS(baseflags)) {
   1470         PyErr_SetString(PyExc_BufferError,
   1471             "ndarray is not C-contiguous");
   1472         return -1;
   1473     }
   1474     if (REQ_F_CONTIGUOUS(flags) && !ND_FORTRAN_CONTIGUOUS(baseflags)) {
   1475         PyErr_SetString(PyExc_BufferError,
   1476             "ndarray is not Fortran contiguous");
   1477         return -1;
   1478     }
   1479     if (REQ_ANY_CONTIGUOUS(flags) && !ND_ANY_CONTIGUOUS(baseflags)) {
   1480         PyErr_SetString(PyExc_BufferError,
   1481             "ndarray is not contiguous");
   1482         return -1;
   1483     }
   1484     if (!REQ_INDIRECT(flags) && (baseflags & ND_PIL)) {
   1485         PyErr_SetString(PyExc_BufferError,
   1486             "ndarray cannot be represented without suboffsets");
   1487         return -1;
   1488     }
   1489     if (!REQ_STRIDES(flags)) {
   1490         if (!ND_C_CONTIGUOUS(baseflags)) {
   1491             PyErr_SetString(PyExc_BufferError,
   1492                 "ndarray is not C-contiguous");
   1493             return -1;
   1494         }
   1495         view->strides = NULL;
   1496     }
   1497     if (!REQ_SHAPE(flags)) {
   1498         /* PyBUF_SIMPLE or PyBUF_WRITABLE: at this point buf is C-contiguous,
   1499            so base->buf = ndbuf->data. */
   1500         if (view->format != NULL) {
   1501             /* PyBUF_SIMPLE|PyBUF_FORMAT and PyBUF_WRITABLE|PyBUF_FORMAT do
   1502                not make sense. */
   1503             PyErr_Format(PyExc_BufferError,
   1504                 "ndarray: cannot cast to unsigned bytes if the format flag "
   1505                 "is present");
   1506             return -1;
   1507         }
   1508         /* product(shape) * itemsize = len and calcsize(format) = itemsize
   1509            do _not_ hold from here on! */
   1510         view->ndim = 1;
   1511         view->shape = NULL;
   1512     }
   1513 
   1514     /* Ascertain that the new buffer has the same contiguity as the exporter */
   1515     if (ND_C_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'C') ||
   1516         /* skip cast to 1-d */
   1517         (view->format != NULL && view->shape != NULL &&
   1518          ND_FORTRAN_CONTIGUOUS(baseflags) != PyBuffer_IsContiguous(view, 'F')) ||
   1519         /* cast to 1-d */
   1520         (view->format == NULL && view->shape == NULL &&
   1521          !PyBuffer_IsContiguous(view, 'F'))) {
   1522         PyErr_SetString(PyExc_BufferError,
   1523             "ndarray: contiguity mismatch in getbuf()");
   1524             return -1;
   1525     }
   1526 
   1527     view->obj = (PyObject *)self;
   1528     Py_INCREF(view->obj);
   1529     self->head->exports++;
   1530 
   1531     return 0;
   1532 }
   1533 
   1534 static int
   1535 ndarray_releasebuf(NDArrayObject *self, Py_buffer *view)
   1536 {
   1537     if (!ND_IS_CONSUMER(self)) {
   1538         ndbuf_t *ndbuf = view->internal;
   1539         if (--ndbuf->exports == 0 && ndbuf != self->head)
   1540             ndbuf_delete(self, ndbuf);
   1541     }
   1542 
   1543     return 0;
   1544 }
   1545 
   1546 static PyBufferProcs ndarray_as_buffer = {
   1547     (getbufferproc)ndarray_getbuf,        /* bf_getbuffer */
   1548     (releasebufferproc)ndarray_releasebuf /* bf_releasebuffer */
   1549 };
   1550 
   1551 
   1552 /**************************************************************************/
   1553 /*                           indexing/slicing                             */
   1554 /**************************************************************************/
   1555 
   1556 static char *
   1557 ptr_from_index(Py_buffer *base, Py_ssize_t index)
   1558 {
   1559     char *ptr;
   1560     Py_ssize_t nitems; /* items in the first dimension */
   1561 
   1562     if (base->shape)
   1563         nitems = base->shape[0];
   1564     else {
   1565         assert(base->ndim == 1 && SIMPLE_FORMAT(base->format));
   1566         nitems = base->len;
   1567     }
   1568 
   1569     if (index < 0) {
   1570         index += nitems;
   1571     }
   1572     if (index < 0 || index >= nitems) {
   1573         PyErr_SetString(PyExc_IndexError, "index out of bounds");
   1574         return NULL;
   1575     }
   1576 
   1577     ptr = (char *)base->buf;
   1578 
   1579     if (base->strides == NULL)
   1580          ptr += base->itemsize * index;
   1581     else
   1582          ptr += base->strides[0] * index;
   1583 
   1584     ptr = ADJUST_PTR(ptr, base->suboffsets);
   1585 
   1586     return ptr;
   1587 }
   1588 
   1589 static PyObject *
   1590 ndarray_item(NDArrayObject *self, Py_ssize_t index)
   1591 {
   1592     ndbuf_t *ndbuf = self->head;
   1593     Py_buffer *base = &ndbuf->base;
   1594     char *ptr;
   1595 
   1596     if (base->ndim == 0) {
   1597         PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
   1598         return NULL;
   1599     }
   1600 
   1601     ptr = ptr_from_index(base, index);
   1602     if (ptr == NULL)
   1603         return NULL;
   1604 
   1605     if (base->ndim == 1) {
   1606         return unpack_single(ptr, base->format, base->itemsize);
   1607     }
   1608     else {
   1609         NDArrayObject *nd;
   1610         Py_buffer *subview;
   1611 
   1612         nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
   1613         if (nd == NULL)
   1614             return NULL;
   1615 
   1616         if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
   1617             Py_DECREF(nd);
   1618             return NULL;
   1619         }
   1620 
   1621         subview = &nd->staticbuf.base;
   1622 
   1623         subview->buf = ptr;
   1624         subview->len /= subview->shape[0];
   1625 
   1626         subview->ndim--;
   1627         subview->shape++;
   1628         if (subview->strides) subview->strides++;
   1629         if (subview->suboffsets) subview->suboffsets++;
   1630 
   1631         init_flags(&nd->staticbuf);
   1632 
   1633         return (PyObject *)nd;
   1634     }
   1635 }
   1636 
   1637 /*
   1638   For each dimension, we get valid (start, stop, step, slicelength) quadruples
   1639   from PySlice_GetIndicesEx().
   1640 
   1641   Slicing NumPy arrays
   1642   ====================
   1643 
   1644     A pointer to an element in a NumPy array is defined by:
   1645 
   1646       ptr = (char *)buf + indices[0] * strides[0] +
   1647                           ... +
   1648                           indices[ndim-1] * strides[ndim-1]
   1649 
   1650     Adjust buf:
   1651     -----------
   1652       Adding start[n] for each dimension effectively adds the constant:
   1653 
   1654         c = start[0] * strides[0] + ... + start[ndim-1] * strides[ndim-1]
   1655 
   1656       Therefore init_slice() adds all start[n] directly to buf.
   1657 
   1658     Adjust shape:
   1659     -------------
   1660       Obviously shape[n] = slicelength[n]
   1661 
   1662     Adjust strides:
   1663     ---------------
   1664       In the original array, the next element in a dimension is reached
   1665       by adding strides[n] to the pointer. In the sliced array, elements
   1666       may be skipped, so the next element is reached by adding:
   1667 
   1668         strides[n] * step[n]
   1669 
   1670   Slicing PIL arrays
   1671   ==================
   1672 
   1673     Layout:
   1674     -------
   1675       In the first (zeroth) dimension, PIL arrays have an array of pointers
   1676       to sub-arrays of ndim-1. Striding in the first dimension is done by
   1677       getting the index of the nth pointer, dereference it and then add a
   1678       suboffset to it. The arrays pointed to can best be seen a regular
   1679       NumPy arrays.
   1680 
   1681     Adjust buf:
   1682     -----------
   1683       In the original array, buf points to a location (usually the start)
   1684       in the array of pointers. For the sliced array, start[0] can be
   1685       added to buf in the same manner as for NumPy arrays.
   1686 
   1687     Adjust suboffsets:
   1688     ------------------
   1689       Due to the dereferencing step in the addressing scheme, it is not
   1690       possible to adjust buf for higher dimensions. Recall that the
   1691       sub-arrays pointed to are regular NumPy arrays, so for each of
   1692       those arrays adding start[n] effectively adds the constant:
   1693 
   1694         c = start[1] * strides[1] + ... + start[ndim-1] * strides[ndim-1]
   1695 
   1696       This constant is added to suboffsets[0]. suboffsets[0] in turn is
   1697       added to each pointer right after dereferencing.
   1698 
   1699     Adjust shape and strides:
   1700     -------------------------
   1701       Shape and strides are not influenced by the dereferencing step, so
   1702       they are adjusted in the same manner as for NumPy arrays.
   1703 
   1704   Multiple levels of suboffsets
   1705   =============================
   1706 
   1707       For a construct like an array of pointers to array of pointers to
   1708       sub-arrays of ndim-2:
   1709 
   1710         suboffsets[0] = start[1] * strides[1]
   1711         suboffsets[1] = start[2] * strides[2] + ...
   1712 */
   1713 static int
   1714 init_slice(Py_buffer *base, PyObject *key, int dim)
   1715 {
   1716     Py_ssize_t start, stop, step, slicelength;
   1717 
   1718     if (PySlice_GetIndicesEx(key, base->shape[dim],
   1719                              &start, &stop, &step, &slicelength) < 0) {
   1720         return -1;
   1721     }
   1722 
   1723 
   1724     if (base->suboffsets == NULL || dim == 0) {
   1725     adjust_buf:
   1726         base->buf = (char *)base->buf + base->strides[dim] * start;
   1727     }
   1728     else {
   1729         Py_ssize_t n = dim-1;
   1730         while (n >= 0 && base->suboffsets[n] < 0)
   1731             n--;
   1732         if (n < 0)
   1733             goto adjust_buf; /* all suboffsets are negative */
   1734         base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
   1735     }
   1736     base->shape[dim] = slicelength;
   1737     base->strides[dim] = base->strides[dim] * step;
   1738 
   1739     return 0;
   1740 }
   1741 
   1742 static int
   1743 copy_structure(Py_buffer *base)
   1744 {
   1745     Py_ssize_t *shape = NULL, *strides = NULL, *suboffsets = NULL;
   1746     Py_ssize_t i;
   1747 
   1748     shape = PyMem_Malloc(base->ndim * (sizeof *shape));
   1749     strides = PyMem_Malloc(base->ndim * (sizeof *strides));
   1750     if (shape == NULL || strides == NULL)
   1751         goto err_nomem;
   1752 
   1753     suboffsets = NULL;
   1754     if (base->suboffsets) {
   1755         suboffsets = PyMem_Malloc(base->ndim * (sizeof *suboffsets));
   1756         if (suboffsets == NULL)
   1757             goto err_nomem;
   1758     }
   1759 
   1760     for (i = 0; i < base->ndim; i++) {
   1761         shape[i] = base->shape[i];
   1762         strides[i] = base->strides[i];
   1763         if (suboffsets)
   1764             suboffsets[i] = base->suboffsets[i];
   1765     }
   1766 
   1767     base->shape = shape;
   1768     base->strides = strides;
   1769     base->suboffsets = suboffsets;
   1770 
   1771     return 0;
   1772 
   1773 err_nomem:
   1774     PyErr_NoMemory();
   1775     PyMem_XFree(shape);
   1776     PyMem_XFree(strides);
   1777     PyMem_XFree(suboffsets);
   1778     return -1;
   1779 }
   1780 
   1781 static PyObject *
   1782 ndarray_subscript(NDArrayObject *self, PyObject *key)
   1783 {
   1784     NDArrayObject *nd;
   1785     ndbuf_t *ndbuf;
   1786     Py_buffer *base = &self->head->base;
   1787 
   1788     if (base->ndim == 0) {
   1789         if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
   1790             return unpack_single(base->buf, base->format, base->itemsize);
   1791         }
   1792         else if (key == Py_Ellipsis) {
   1793             Py_INCREF(self);
   1794             return (PyObject *)self;
   1795         }
   1796         else {
   1797             PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
   1798             return NULL;
   1799         }
   1800     }
   1801     if (PyIndex_Check(key)) {
   1802         Py_ssize_t index = PyLong_AsSsize_t(key);
   1803         if (index == -1 && PyErr_Occurred())
   1804             return NULL;
   1805         return ndarray_item(self, index);
   1806     }
   1807 
   1808     nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
   1809     if (nd == NULL)
   1810         return NULL;
   1811 
   1812     /* new ndarray is a consumer */
   1813     if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
   1814         Py_DECREF(nd);
   1815         return NULL;
   1816     }
   1817 
   1818     /* copy shape, strides and suboffsets */
   1819     ndbuf = nd->head;
   1820     base = &ndbuf->base;
   1821     if (copy_structure(base) < 0) {
   1822         Py_DECREF(nd);
   1823         return NULL;
   1824     }
   1825     ndbuf->flags |= ND_OWN_ARRAYS;
   1826 
   1827     if (PySlice_Check(key)) {
   1828         /* one-dimensional slice */
   1829         if (init_slice(base, key, 0) < 0)
   1830             goto err_occurred;
   1831     }
   1832     else if (PyTuple_Check(key)) {
   1833         /* multi-dimensional slice */
   1834         PyObject *tuple = key;
   1835         Py_ssize_t i, n;
   1836 
   1837         n = PyTuple_GET_SIZE(tuple);
   1838         for (i = 0; i < n; i++) {
   1839             key = PyTuple_GET_ITEM(tuple, i);
   1840             if (!PySlice_Check(key))
   1841                 goto type_error;
   1842             if (init_slice(base, key, (int)i) < 0)
   1843                 goto err_occurred;
   1844         }
   1845     }
   1846     else {
   1847         goto type_error;
   1848     }
   1849 
   1850     init_len(base);
   1851     init_flags(ndbuf);
   1852 
   1853     return (PyObject *)nd;
   1854 
   1855 
   1856 type_error:
   1857     PyErr_Format(PyExc_TypeError,
   1858         "cannot index memory using \"%.200s\"",
   1859         key->ob_type->tp_name);
   1860 err_occurred:
   1861     Py_DECREF(nd);
   1862     return NULL;
   1863 }
   1864 
   1865 
   1866 static int
   1867 ndarray_ass_subscript(NDArrayObject *self, PyObject *key, PyObject *value)
   1868 {
   1869     NDArrayObject *nd;
   1870     Py_buffer *dest = &self->head->base;
   1871     Py_buffer src;
   1872     char *ptr;
   1873     Py_ssize_t index;
   1874     int ret = -1;
   1875 
   1876     if (dest->readonly) {
   1877         PyErr_SetString(PyExc_TypeError, "ndarray is not writable");
   1878         return -1;
   1879     }
   1880     if (value == NULL) {
   1881         PyErr_SetString(PyExc_TypeError, "ndarray data cannot be deleted");
   1882         return -1;
   1883     }
   1884     if (dest->ndim == 0) {
   1885         if (key == Py_Ellipsis ||
   1886             (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
   1887             ptr = (char *)dest->buf;
   1888             return pack_single(ptr, value, dest->format, dest->itemsize);
   1889         }
   1890         else {
   1891             PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
   1892             return -1;
   1893         }
   1894     }
   1895     if (dest->ndim == 1 && PyIndex_Check(key)) {
   1896         /* rvalue must be a single item */
   1897         index = PyLong_AsSsize_t(key);
   1898         if (index == -1 && PyErr_Occurred())
   1899             return -1;
   1900         else {
   1901             ptr = ptr_from_index(dest, index);
   1902             if (ptr == NULL)
   1903                 return -1;
   1904         }
   1905         return pack_single(ptr, value, dest->format, dest->itemsize);
   1906     }
   1907 
   1908     /* rvalue must be an exporter */
   1909     if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) == -1)
   1910         return -1;
   1911 
   1912     nd = (NDArrayObject *)ndarray_subscript(self, key);
   1913     if (nd != NULL) {
   1914         dest = &nd->head->base;
   1915         ret = copy_buffer(dest, &src);
   1916         Py_DECREF(nd);
   1917     }
   1918 
   1919     PyBuffer_Release(&src);
   1920     return ret;
   1921 }
   1922 
   1923 static PyObject *
   1924 slice_indices(PyObject *self, PyObject *args)
   1925 {
   1926     PyObject *ret, *key, *tmp;
   1927     Py_ssize_t s[4]; /* start, stop, step, slicelength */
   1928     Py_ssize_t i, len;
   1929 
   1930     if (!PyArg_ParseTuple(args, "On", &key, &len)) {
   1931         return NULL;
   1932     }
   1933     if (!PySlice_Check(key)) {
   1934         PyErr_SetString(PyExc_TypeError,
   1935             "first argument must be a slice object");
   1936         return NULL;
   1937     }
   1938     if (PySlice_GetIndicesEx(key, len, &s[0], &s[1], &s[2], &s[3]) < 0) {
   1939         return NULL;
   1940     }
   1941 
   1942     ret = PyTuple_New(4);
   1943     if (ret == NULL)
   1944         return NULL;
   1945 
   1946     for (i = 0; i < 4; i++) {
   1947         tmp = PyLong_FromSsize_t(s[i]);
   1948         if (tmp == NULL)
   1949             goto error;
   1950         PyTuple_SET_ITEM(ret, i, tmp);
   1951     }
   1952 
   1953     return ret;
   1954 
   1955 error:
   1956     Py_DECREF(ret);
   1957     return NULL;
   1958 }
   1959 
   1960 
   1961 static PyMappingMethods ndarray_as_mapping = {
   1962     NULL,                                 /* mp_length */
   1963     (binaryfunc)ndarray_subscript,        /* mp_subscript */
   1964     (objobjargproc)ndarray_ass_subscript  /* mp_ass_subscript */
   1965 };
   1966 
   1967 static PySequenceMethods ndarray_as_sequence = {
   1968         0,                                /* sq_length */
   1969         0,                                /* sq_concat */
   1970         0,                                /* sq_repeat */
   1971         (ssizeargfunc)ndarray_item,       /* sq_item */
   1972 };
   1973 
   1974 
   1975 /**************************************************************************/
   1976 /*                                 getters                                */
   1977 /**************************************************************************/
   1978 
   1979 static PyObject *
   1980 ssize_array_as_tuple(Py_ssize_t *array, Py_ssize_t len)
   1981 {
   1982     PyObject *tuple, *x;
   1983     Py_ssize_t i;
   1984 
   1985     if (array == NULL)
   1986         return PyTuple_New(0);
   1987 
   1988     tuple = PyTuple_New(len);
   1989     if (tuple == NULL)
   1990         return NULL;
   1991 
   1992     for (i = 0; i < len; i++) {
   1993         x = PyLong_FromSsize_t(array[i]);
   1994         if (x == NULL) {
   1995             Py_DECREF(tuple);
   1996             return NULL;
   1997         }
   1998         PyTuple_SET_ITEM(tuple, i, x);
   1999     }
   2000 
   2001     return tuple;
   2002 }
   2003 
   2004 static PyObject *
   2005 ndarray_get_flags(NDArrayObject *self, void *closure)
   2006 {
   2007     return PyLong_FromLong(self->head->flags);
   2008 }
   2009 
   2010 static PyObject *
   2011 ndarray_get_offset(NDArrayObject *self, void *closure)
   2012 {
   2013     ndbuf_t *ndbuf = self->head;
   2014     return PyLong_FromSsize_t(ndbuf->offset);
   2015 }
   2016 
   2017 static PyObject *
   2018 ndarray_get_obj(NDArrayObject *self, void *closure)
   2019 {
   2020     Py_buffer *base = &self->head->base;
   2021 
   2022     if (base->obj == NULL) {
   2023         Py_RETURN_NONE;
   2024     }
   2025     Py_INCREF(base->obj);
   2026     return base->obj;
   2027 }
   2028 
   2029 static PyObject *
   2030 ndarray_get_nbytes(NDArrayObject *self, void *closure)
   2031 {
   2032     Py_buffer *base = &self->head->base;
   2033     return PyLong_FromSsize_t(base->len);
   2034 }
   2035 
   2036 static PyObject *
   2037 ndarray_get_readonly(NDArrayObject *self, void *closure)
   2038 {
   2039     Py_buffer *base = &self->head->base;
   2040     return PyLong_FromLong(base->readonly);
   2041 }
   2042 
   2043 static PyObject *
   2044 ndarray_get_itemsize(NDArrayObject *self, void *closure)
   2045 {
   2046     Py_buffer *base = &self->head->base;
   2047     return PyLong_FromSsize_t(base->itemsize);
   2048 }
   2049 
   2050 static PyObject *
   2051 ndarray_get_format(NDArrayObject *self, void *closure)
   2052 {
   2053     Py_buffer *base = &self->head->base;
   2054     char *fmt = base->format ? base->format : "";
   2055     return PyUnicode_FromString(fmt);
   2056 }
   2057 
   2058 static PyObject *
   2059 ndarray_get_ndim(NDArrayObject *self, void *closure)
   2060 {
   2061     Py_buffer *base = &self->head->base;
   2062     return PyLong_FromSsize_t(base->ndim);
   2063 }
   2064 
   2065 static PyObject *
   2066 ndarray_get_shape(NDArrayObject *self, void *closure)
   2067 {
   2068     Py_buffer *base = &self->head->base;
   2069     return ssize_array_as_tuple(base->shape, base->ndim);
   2070 }
   2071 
   2072 static PyObject *
   2073 ndarray_get_strides(NDArrayObject *self, void *closure)
   2074 {
   2075     Py_buffer *base = &self->head->base;
   2076     return ssize_array_as_tuple(base->strides, base->ndim);
   2077 }
   2078 
   2079 static PyObject *
   2080 ndarray_get_suboffsets(NDArrayObject *self, void *closure)
   2081 {
   2082     Py_buffer *base = &self->head->base;
   2083     return ssize_array_as_tuple(base->suboffsets, base->ndim);
   2084 }
   2085 
   2086 static PyObject *
   2087 ndarray_c_contig(PyObject *self, PyObject *dummy)
   2088 {
   2089     NDArrayObject *nd = (NDArrayObject *)self;
   2090     int ret = PyBuffer_IsContiguous(&nd->head->base, 'C');
   2091 
   2092     if (ret != ND_C_CONTIGUOUS(nd->head->flags)) {
   2093         PyErr_SetString(PyExc_RuntimeError,
   2094             "results from PyBuffer_IsContiguous() and flags differ");
   2095         return NULL;
   2096     }
   2097     return PyBool_FromLong(ret);
   2098 }
   2099 
   2100 static PyObject *
   2101 ndarray_fortran_contig(PyObject *self, PyObject *dummy)
   2102 {
   2103     NDArrayObject *nd = (NDArrayObject *)self;
   2104     int ret = PyBuffer_IsContiguous(&nd->head->base, 'F');
   2105 
   2106     if (ret != ND_FORTRAN_CONTIGUOUS(nd->head->flags)) {
   2107         PyErr_SetString(PyExc_RuntimeError,
   2108             "results from PyBuffer_IsContiguous() and flags differ");
   2109         return NULL;
   2110     }
   2111     return PyBool_FromLong(ret);
   2112 }
   2113 
   2114 static PyObject *
   2115 ndarray_contig(PyObject *self, PyObject *dummy)
   2116 {
   2117     NDArrayObject *nd = (NDArrayObject *)self;
   2118     int ret = PyBuffer_IsContiguous(&nd->head->base, 'A');
   2119 
   2120     if (ret != ND_ANY_CONTIGUOUS(nd->head->flags)) {
   2121         PyErr_SetString(PyExc_RuntimeError,
   2122             "results from PyBuffer_IsContiguous() and flags differ");
   2123         return NULL;
   2124     }
   2125     return PyBool_FromLong(ret);
   2126 }
   2127 
   2128 
   2129 static PyGetSetDef ndarray_getset [] =
   2130 {
   2131   /* ndbuf */
   2132   { "flags",        (getter)ndarray_get_flags,      NULL, NULL, NULL},
   2133   { "offset",       (getter)ndarray_get_offset,     NULL, NULL, NULL},
   2134   /* ndbuf.base */
   2135   { "obj",          (getter)ndarray_get_obj,        NULL, NULL, NULL},
   2136   { "nbytes",       (getter)ndarray_get_nbytes,     NULL, NULL, NULL},
   2137   { "readonly",     (getter)ndarray_get_readonly,   NULL, NULL, NULL},
   2138   { "itemsize",     (getter)ndarray_get_itemsize,   NULL, NULL, NULL},
   2139   { "format",       (getter)ndarray_get_format,     NULL, NULL, NULL},
   2140   { "ndim",         (getter)ndarray_get_ndim,       NULL, NULL, NULL},
   2141   { "shape",        (getter)ndarray_get_shape,      NULL, NULL, NULL},
   2142   { "strides",      (getter)ndarray_get_strides,    NULL, NULL, NULL},
   2143   { "suboffsets",   (getter)ndarray_get_suboffsets, NULL, NULL, NULL},
   2144   { "c_contiguous", (getter)ndarray_c_contig,       NULL, NULL, NULL},
   2145   { "f_contiguous", (getter)ndarray_fortran_contig, NULL, NULL, NULL},
   2146   { "contiguous",   (getter)ndarray_contig,         NULL, NULL, NULL},
   2147   {NULL}
   2148 };
   2149 
   2150 static PyObject *
   2151 ndarray_tolist(PyObject *self, PyObject *dummy)
   2152 {
   2153     return ndarray_as_list((NDArrayObject *)self);
   2154 }
   2155 
   2156 static PyObject *
   2157 ndarray_tobytes(PyObject *self, PyObject *dummy)
   2158 {
   2159     ndbuf_t *ndbuf = ((NDArrayObject *)self)->head;
   2160     Py_buffer *src = &ndbuf->base;
   2161     Py_buffer dest;
   2162     PyObject *ret = NULL;
   2163     char *mem;
   2164 
   2165     if (ND_C_CONTIGUOUS(ndbuf->flags))
   2166         return PyBytes_FromStringAndSize(src->buf, src->len);
   2167 
   2168     assert(src->shape != NULL);
   2169     assert(src->strides != NULL);
   2170     assert(src->ndim > 0);
   2171 
   2172     mem = PyMem_Malloc(src->len);
   2173     if (mem == NULL) {
   2174         PyErr_NoMemory();
   2175         return NULL;
   2176     }
   2177 
   2178     dest = *src;
   2179     dest.buf = mem;
   2180     dest.suboffsets = NULL;
   2181     dest.strides = strides_from_shape(ndbuf, 0);
   2182     if (dest.strides == NULL)
   2183         goto out;
   2184     if (copy_buffer(&dest, src) < 0)
   2185         goto out;
   2186 
   2187     ret = PyBytes_FromStringAndSize(mem, src->len);
   2188 
   2189 out:
   2190     PyMem_XFree(dest.strides);
   2191     PyMem_Free(mem);
   2192     return ret;
   2193 }
   2194 
   2195 /* add redundant (negative) suboffsets for testing */
   2196 static PyObject *
   2197 ndarray_add_suboffsets(PyObject *self, PyObject *dummy)
   2198 {
   2199     NDArrayObject *nd = (NDArrayObject *)self;
   2200     Py_buffer *base = &nd->head->base;
   2201     Py_ssize_t i;
   2202 
   2203     if (base->suboffsets != NULL) {
   2204         PyErr_SetString(PyExc_TypeError,
   2205             "cannot add suboffsets to PIL-style array");
   2206             return NULL;
   2207     }
   2208     if (base->strides == NULL) {
   2209         PyErr_SetString(PyExc_TypeError,
   2210             "cannot add suboffsets to array without strides");
   2211             return NULL;
   2212     }
   2213 
   2214     base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
   2215     if (base->suboffsets == NULL) {
   2216         PyErr_NoMemory();
   2217         return NULL;
   2218     }
   2219 
   2220     for (i = 0; i < base->ndim; i++)
   2221         base->suboffsets[i] = -1;
   2222 
   2223     nd->head->flags &= ~(ND_C|ND_FORTRAN);
   2224 
   2225     Py_RETURN_NONE;
   2226 }
   2227 
   2228 /* Test PyMemoryView_FromBuffer(): return a memoryview from a static buffer.
   2229    Obviously this is fragile and only one such view may be active at any
   2230    time. Never use anything like this in real code! */
   2231 static char *infobuf = NULL;
   2232 static PyObject *
   2233 ndarray_memoryview_from_buffer(PyObject *self, PyObject *dummy)
   2234 {
   2235     const NDArrayObject *nd = (NDArrayObject *)self;
   2236     const Py_buffer *view = &nd->head->base;
   2237     const ndbuf_t *ndbuf;
   2238     static char format[ND_MAX_NDIM+1];
   2239     static Py_ssize_t shape[ND_MAX_NDIM];
   2240     static Py_ssize_t strides[ND_MAX_NDIM];
   2241     static Py_ssize_t suboffsets[ND_MAX_NDIM];
   2242     static Py_buffer info;
   2243     char *p;
   2244 
   2245     if (!ND_IS_CONSUMER(nd))
   2246         ndbuf = nd->head; /* self is ndarray/original exporter */
   2247     else if (NDArray_Check(view->obj) && !ND_IS_CONSUMER(view->obj))
   2248         /* self is ndarray and consumer from ndarray/original exporter */
   2249         ndbuf = ((NDArrayObject *)view->obj)->head;
   2250     else {
   2251         PyErr_SetString(PyExc_TypeError,
   2252         "memoryview_from_buffer(): ndarray must be original exporter or "
   2253         "consumer from ndarray/original exporter");
   2254          return NULL;
   2255     }
   2256 
   2257     info = *view;
   2258     p = PyMem_Realloc(infobuf, ndbuf->len);
   2259     if (p == NULL) {
   2260         PyMem_Free(infobuf);
   2261         PyErr_NoMemory();
   2262         infobuf = NULL;
   2263         return NULL;
   2264     }
   2265     else {
   2266         infobuf = p;
   2267     }
   2268     /* copy the complete raw data */
   2269     memcpy(infobuf, ndbuf->data, ndbuf->len);
   2270     info.buf = infobuf + ((char *)view->buf - ndbuf->data);
   2271 
   2272     if (view->format) {
   2273         if (strlen(view->format) > ND_MAX_NDIM) {
   2274             PyErr_Format(PyExc_TypeError,
   2275                 "memoryview_from_buffer: format is limited to %d characters",
   2276                 ND_MAX_NDIM);
   2277                 return NULL;
   2278         }
   2279         strcpy(format, view->format);
   2280         info.format = format;
   2281     }
   2282     if (view->ndim > ND_MAX_NDIM) {
   2283         PyErr_Format(PyExc_TypeError,
   2284             "memoryview_from_buffer: ndim is limited to %d", ND_MAX_NDIM);
   2285             return NULL;
   2286     }
   2287     if (view->shape) {
   2288         memcpy(shape, view->shape, view->ndim * sizeof(Py_ssize_t));
   2289         info.shape = shape;
   2290     }
   2291     if (view->strides) {
   2292         memcpy(strides, view->strides, view->ndim * sizeof(Py_ssize_t));
   2293         info.strides = strides;
   2294     }
   2295     if (view->suboffsets) {
   2296         memcpy(suboffsets, view->suboffsets, view->ndim * sizeof(Py_ssize_t));
   2297         info.suboffsets = suboffsets;
   2298     }
   2299 
   2300     return PyMemoryView_FromBuffer(&info);
   2301 }
   2302 
   2303 /* Get a single item from bufobj at the location specified by seq.
   2304    seq is a list or tuple of indices. The purpose of this function
   2305    is to check other functions against PyBuffer_GetPointer(). */
   2306 static PyObject *
   2307 get_pointer(PyObject *self, PyObject *args)
   2308 {
   2309     PyObject *ret = NULL, *bufobj, *seq;
   2310     Py_buffer view;
   2311     Py_ssize_t indices[ND_MAX_NDIM];
   2312     Py_ssize_t i;
   2313     void *ptr;
   2314 
   2315     if (!PyArg_ParseTuple(args, "OO", &bufobj, &seq)) {
   2316         return NULL;
   2317     }
   2318 
   2319     CHECK_LIST_OR_TUPLE(seq);
   2320     if (PyObject_GetBuffer(bufobj, &view, PyBUF_FULL_RO) < 0)
   2321         return NULL;
   2322 
   2323     if (view.ndim > ND_MAX_NDIM) {
   2324         PyErr_Format(PyExc_ValueError,
   2325             "get_pointer(): ndim > %d", ND_MAX_NDIM);
   2326         goto out;
   2327     }
   2328     if (PySequence_Fast_GET_SIZE(seq) != view.ndim) {
   2329         PyErr_SetString(PyExc_ValueError,
   2330             "get_pointer(): len(indices) != ndim");
   2331         goto out;
   2332     }
   2333 
   2334     for (i = 0; i < view.ndim; i++) {
   2335         PyObject *x = PySequence_Fast_GET_ITEM(seq, i);
   2336         indices[i] = PyLong_AsSsize_t(x);
   2337         if (PyErr_Occurred())
   2338             goto out;
   2339         if (indices[i] < 0 || indices[i] >= view.shape[i]) {
   2340             PyErr_Format(PyExc_ValueError,
   2341                 "get_pointer(): invalid index %zd at position %zd",
   2342                 indices[i], i);
   2343             goto out;
   2344         }
   2345     }
   2346 
   2347     ptr = PyBuffer_GetPointer(&view, indices);
   2348     ret = unpack_single(ptr, view.format, view.itemsize);
   2349 
   2350 out:
   2351     PyBuffer_Release(&view);
   2352     return ret;
   2353 }
   2354 
   2355 static PyObject *
   2356 get_sizeof_void_p(PyObject *self)
   2357 {
   2358     return PyLong_FromSize_t(sizeof(void *));
   2359 }
   2360 
   2361 static char
   2362 get_ascii_order(PyObject *order)
   2363 {
   2364     PyObject *ascii_order;
   2365     char ord;
   2366 
   2367     if (!PyUnicode_Check(order)) {
   2368         PyErr_SetString(PyExc_TypeError,
   2369             "order must be a string");
   2370         return CHAR_MAX;
   2371     }
   2372 
   2373     ascii_order = PyUnicode_AsASCIIString(order);
   2374     if (ascii_order == NULL) {
   2375         return CHAR_MAX;
   2376     }
   2377 
   2378     ord = PyBytes_AS_STRING(ascii_order)[0];
   2379     Py_DECREF(ascii_order);
   2380 
   2381     if (ord != 'C' && ord != 'F' && ord != 'A') {
   2382         PyErr_SetString(PyExc_ValueError,
   2383             "invalid order, must be C, F or A");
   2384         return CHAR_MAX;
   2385     }
   2386 
   2387     return ord;
   2388 }
   2389 
   2390 /* Get a contiguous memoryview. */
   2391 static PyObject *
   2392 get_contiguous(PyObject *self, PyObject *args)
   2393 {
   2394     PyObject *obj;
   2395     PyObject *buffertype;
   2396     PyObject *order;
   2397     long type;
   2398     char ord;
   2399 
   2400     if (!PyArg_ParseTuple(args, "OOO", &obj, &buffertype, &order)) {
   2401         return NULL;
   2402     }
   2403 
   2404     if (!PyLong_Check(buffertype)) {
   2405         PyErr_SetString(PyExc_TypeError,
   2406             "buffertype must be PyBUF_READ or PyBUF_WRITE");
   2407         return NULL;
   2408     }
   2409 
   2410     type = PyLong_AsLong(buffertype);
   2411     if (type == -1 && PyErr_Occurred()) {
   2412         return NULL;
   2413     }
   2414     if (type != PyBUF_READ && type != PyBUF_WRITE) {
   2415         PyErr_SetString(PyExc_ValueError,
   2416             "invalid buffer type");
   2417         return NULL;
   2418     }
   2419 
   2420     ord = get_ascii_order(order);
   2421     if (ord == CHAR_MAX)
   2422         return NULL;
   2423 
   2424     return PyMemoryView_GetContiguous(obj, (int)type, ord);
   2425 }
   2426 
   2427 /* PyBuffer_ToContiguous() */
   2428 static PyObject *
   2429 py_buffer_to_contiguous(PyObject *self, PyObject *args)
   2430 {
   2431     PyObject *obj;
   2432     PyObject *order;
   2433     PyObject *ret = NULL;
   2434     int flags;
   2435     char ord;
   2436     Py_buffer view;
   2437     char *buf = NULL;
   2438 
   2439     if (!PyArg_ParseTuple(args, "OOi", &obj, &order, &flags)) {
   2440         return NULL;
   2441     }
   2442 
   2443     if (PyObject_GetBuffer(obj, &view, flags) < 0) {
   2444         return NULL;
   2445     }
   2446 
   2447     ord = get_ascii_order(order);
   2448     if (ord == CHAR_MAX) {
   2449         goto out;
   2450     }
   2451 
   2452     buf = PyMem_Malloc(view.len);
   2453     if (buf == NULL) {
   2454         PyErr_NoMemory();
   2455         goto out;
   2456     }
   2457 
   2458     if (PyBuffer_ToContiguous(buf, &view, view.len, ord) < 0) {
   2459         goto out;
   2460     }
   2461 
   2462     ret = PyBytes_FromStringAndSize(buf, view.len);
   2463 
   2464 out:
   2465     PyBuffer_Release(&view);
   2466     PyMem_XFree(buf);
   2467     return ret;
   2468 }
   2469 
   2470 static int
   2471 fmtcmp(const char *fmt1, const char *fmt2)
   2472 {
   2473     if (fmt1 == NULL) {
   2474         return fmt2 == NULL || strcmp(fmt2, "B") == 0;
   2475     }
   2476     if (fmt2 == NULL) {
   2477         return fmt1 == NULL || strcmp(fmt1, "B") == 0;
   2478     }
   2479     return strcmp(fmt1, fmt2) == 0;
   2480 }
   2481 
   2482 static int
   2483 arraycmp(const Py_ssize_t *a1, const Py_ssize_t *a2, const Py_ssize_t *shape,
   2484          Py_ssize_t ndim)
   2485 {
   2486     Py_ssize_t i;
   2487 
   2488 
   2489     for (i = 0; i < ndim; i++) {
   2490         if (shape && shape[i] <= 1) {
   2491             /* strides can differ if the dimension is less than 2 */
   2492             continue;
   2493         }
   2494         if (a1[i] != a2[i]) {
   2495             return 0;
   2496         }
   2497     }
   2498 
   2499     return 1;
   2500 }
   2501 
   2502 /* Compare two contiguous buffers for physical equality. */
   2503 static PyObject *
   2504 cmp_contig(PyObject *self, PyObject *args)
   2505 {
   2506     PyObject *b1, *b2; /* buffer objects */
   2507     Py_buffer v1, v2;
   2508     PyObject *ret;
   2509     int equal = 0;
   2510 
   2511     if (!PyArg_ParseTuple(args, "OO", &b1, &b2)) {
   2512         return NULL;
   2513     }
   2514 
   2515     if (PyObject_GetBuffer(b1, &v1, PyBUF_FULL_RO) < 0) {
   2516         PyErr_SetString(PyExc_TypeError,
   2517             "cmp_contig: first argument does not implement the buffer "
   2518             "protocol");
   2519         return NULL;
   2520     }
   2521     if (PyObject_GetBuffer(b2, &v2, PyBUF_FULL_RO) < 0) {
   2522         PyErr_SetString(PyExc_TypeError,
   2523             "cmp_contig: second argument does not implement the buffer "
   2524             "protocol");
   2525         PyBuffer_Release(&v1);
   2526         return NULL;
   2527     }
   2528 
   2529     if (!(PyBuffer_IsContiguous(&v1, 'C')&&PyBuffer_IsContiguous(&v2, 'C')) &&
   2530         !(PyBuffer_IsContiguous(&v1, 'F')&&PyBuffer_IsContiguous(&v2, 'F'))) {
   2531         goto result;
   2532     }
   2533 
   2534     /* readonly may differ if created from non-contiguous */
   2535     if (v1.len != v2.len ||
   2536         v1.itemsize != v2.itemsize ||
   2537         v1.ndim != v2.ndim ||
   2538         !fmtcmp(v1.format, v2.format) ||
   2539         !!v1.shape != !!v2.shape ||
   2540         !!v1.strides != !!v2.strides ||
   2541         !!v1.suboffsets != !!v2.suboffsets) {
   2542         goto result;
   2543     }
   2544 
   2545     if ((v1.shape && !arraycmp(v1.shape, v2.shape, NULL, v1.ndim)) ||
   2546         (v1.strides && !arraycmp(v1.strides, v2.strides, v1.shape, v1.ndim)) ||
   2547         (v1.suboffsets && !arraycmp(v1.suboffsets, v2.suboffsets, NULL,
   2548                                     v1.ndim))) {
   2549         goto result;
   2550     }
   2551 
   2552     if (memcmp((char *)v1.buf, (char *)v2.buf, v1.len) != 0) {
   2553         goto result;
   2554     }
   2555 
   2556     equal = 1;
   2557 
   2558 result:
   2559     PyBuffer_Release(&v1);
   2560     PyBuffer_Release(&v2);
   2561 
   2562     ret = equal ? Py_True : Py_False;
   2563     Py_INCREF(ret);
   2564     return ret;
   2565 }
   2566 
   2567 static PyObject *
   2568 is_contiguous(PyObject *self, PyObject *args)
   2569 {
   2570     PyObject *obj;
   2571     PyObject *order;
   2572     PyObject *ret = NULL;
   2573     Py_buffer view, *base;
   2574     char ord;
   2575 
   2576     if (!PyArg_ParseTuple(args, "OO", &obj, &order)) {
   2577         return NULL;
   2578     }
   2579 
   2580     ord = get_ascii_order(order);
   2581     if (ord == CHAR_MAX) {
   2582         return NULL;
   2583     }
   2584 
   2585     if (NDArray_Check(obj)) {
   2586         /* Skip the buffer protocol to check simple etc. buffers directly. */
   2587         base = &((NDArrayObject *)obj)->head->base;
   2588         ret = PyBuffer_IsContiguous(base, ord) ? Py_True : Py_False;
   2589     }
   2590     else {
   2591         if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) {
   2592             PyErr_SetString(PyExc_TypeError,
   2593                 "is_contiguous: object does not implement the buffer "
   2594                 "protocol");
   2595             return NULL;
   2596         }
   2597         ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
   2598         PyBuffer_Release(&view);
   2599     }
   2600 
   2601     Py_INCREF(ret);
   2602     return ret;
   2603 }
   2604 
   2605 static Py_hash_t
   2606 ndarray_hash(PyObject *self)
   2607 {
   2608     const NDArrayObject *nd = (NDArrayObject *)self;
   2609     const Py_buffer *view = &nd->head->base;
   2610     PyObject *bytes;
   2611     Py_hash_t hash;
   2612 
   2613     if (!view->readonly) {
   2614          PyErr_SetString(PyExc_ValueError,
   2615              "cannot hash writable ndarray object");
   2616          return -1;
   2617     }
   2618     if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
   2619          return -1;
   2620     }
   2621 
   2622     bytes = ndarray_tobytes(self, NULL);
   2623     if (bytes == NULL) {
   2624         return -1;
   2625     }
   2626 
   2627     hash = PyObject_Hash(bytes);
   2628     Py_DECREF(bytes);
   2629     return hash;
   2630 }
   2631 
   2632 
   2633 static PyMethodDef ndarray_methods [] =
   2634 {
   2635     { "tolist", ndarray_tolist, METH_NOARGS, NULL },
   2636     { "tobytes", ndarray_tobytes, METH_NOARGS, NULL },
   2637     { "push", (PyCFunction)ndarray_push, METH_VARARGS|METH_KEYWORDS, NULL },
   2638     { "pop", ndarray_pop, METH_NOARGS, NULL },
   2639     { "add_suboffsets", ndarray_add_suboffsets, METH_NOARGS, NULL },
   2640     { "memoryview_from_buffer", ndarray_memoryview_from_buffer, METH_NOARGS, NULL },
   2641     {NULL}
   2642 };
   2643 
   2644 static PyTypeObject NDArray_Type = {
   2645     PyVarObject_HEAD_INIT(NULL, 0)
   2646     "ndarray",                   /* Name of this type */
   2647     sizeof(NDArrayObject),       /* Basic object size */
   2648     0,                           /* Item size for varobject */
   2649     (destructor)ndarray_dealloc, /* tp_dealloc */
   2650     0,                           /* tp_print */
   2651     0,                           /* tp_getattr */
   2652     0,                           /* tp_setattr */
   2653     0,                           /* tp_compare */
   2654     0,                           /* tp_repr */
   2655     0,                           /* tp_as_number */
   2656     &ndarray_as_sequence,        /* tp_as_sequence */
   2657     &ndarray_as_mapping,         /* tp_as_mapping */
   2658     (hashfunc)ndarray_hash,      /* tp_hash */
   2659     0,                           /* tp_call */
   2660     0,                           /* tp_str */
   2661     PyObject_GenericGetAttr,     /* tp_getattro */
   2662     0,                           /* tp_setattro */
   2663     &ndarray_as_buffer,          /* tp_as_buffer */
   2664     Py_TPFLAGS_DEFAULT,          /* tp_flags */
   2665     0,                           /* tp_doc */
   2666     0,                           /* tp_traverse */
   2667     0,                           /* tp_clear */
   2668     0,                           /* tp_richcompare */
   2669     0,                           /* tp_weaklistoffset */
   2670     0,                           /* tp_iter */
   2671     0,                           /* tp_iternext */
   2672     ndarray_methods,             /* tp_methods */
   2673     0,                           /* tp_members */
   2674     ndarray_getset,              /* tp_getset */
   2675     0,                           /* tp_base */
   2676     0,                           /* tp_dict */
   2677     0,                           /* tp_descr_get */
   2678     0,                           /* tp_descr_set */
   2679     0,                           /* tp_dictoffset */
   2680     ndarray_init,                /* tp_init */
   2681     0,                           /* tp_alloc */
   2682     ndarray_new,                 /* tp_new */
   2683 };
   2684 
   2685 /**************************************************************************/
   2686 /*                          StaticArray Object                            */
   2687 /**************************************************************************/
   2688 
   2689 static PyTypeObject StaticArray_Type;
   2690 
   2691 typedef struct {
   2692     PyObject_HEAD
   2693     int legacy_mode; /* if true, use the view.obj==NULL hack */
   2694 } StaticArrayObject;
   2695 
   2696 static char static_mem[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
   2697 static Py_ssize_t static_shape[1] = {12};
   2698 static Py_ssize_t static_strides[1] = {1};
   2699 static Py_buffer static_buffer = {
   2700     static_mem,     /* buf */
   2701     NULL,           /* obj */
   2702     12,             /* len */
   2703     1,              /* itemsize */
   2704     1,              /* readonly */
   2705     1,              /* ndim */
   2706     "B",            /* format */
   2707     static_shape,   /* shape */
   2708     static_strides, /* strides */
   2709     NULL,           /* suboffsets */
   2710     NULL            /* internal */
   2711 };
   2712 
   2713 static PyObject *
   2714 staticarray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
   2715 {
   2716     return (PyObject *)PyObject_New(StaticArrayObject, &StaticArray_Type);
   2717 }
   2718 
   2719 static int
   2720 staticarray_init(PyObject *self, PyObject *args, PyObject *kwds)
   2721 {
   2722     StaticArrayObject *a = (StaticArrayObject *)self;
   2723     static char *kwlist[] = {
   2724         "legacy_mode", NULL
   2725     };
   2726     PyObject *legacy_mode = Py_False;
   2727 
   2728     if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &legacy_mode))
   2729         return -1;
   2730 
   2731     a->legacy_mode = (legacy_mode != Py_False);
   2732     return 0;
   2733 }
   2734 
   2735 static void
   2736 staticarray_dealloc(StaticArrayObject *self)
   2737 {
   2738     PyObject_Del(self);
   2739 }
   2740 
   2741 /* Return a buffer for a PyBUF_FULL_RO request. Flags are not checked,
   2742    which makes this object a non-compliant exporter! */
   2743 static int
   2744 staticarray_getbuf(StaticArrayObject *self, Py_buffer *view, int flags)
   2745 {
   2746     *view = static_buffer;
   2747 
   2748     if (self->legacy_mode) {
   2749         view->obj = NULL; /* Don't use this in new code. */
   2750     }
   2751     else {
   2752         view->obj = (PyObject *)self;
   2753         Py_INCREF(view->obj);
   2754     }
   2755 
   2756     return 0;
   2757 }
   2758 
   2759 static PyBufferProcs staticarray_as_buffer = {
   2760     (getbufferproc)staticarray_getbuf, /* bf_getbuffer */
   2761     NULL,                              /* bf_releasebuffer */
   2762 };
   2763 
   2764 static PyTypeObject StaticArray_Type = {
   2765     PyVarObject_HEAD_INIT(NULL, 0)
   2766     "staticarray",                   /* Name of this type */
   2767     sizeof(StaticArrayObject),       /* Basic object size */
   2768     0,                               /* Item size for varobject */
   2769     (destructor)staticarray_dealloc, /* tp_dealloc */
   2770     0,                               /* tp_print */
   2771     0,                               /* tp_getattr */
   2772     0,                               /* tp_setattr */
   2773     0,                               /* tp_compare */
   2774     0,                               /* tp_repr */
   2775     0,                               /* tp_as_number */
   2776     0,                               /* tp_as_sequence */
   2777     0,                               /* tp_as_mapping */
   2778     0,                               /* tp_hash */
   2779     0,                               /* tp_call */
   2780     0,                               /* tp_str */
   2781     0,                               /* tp_getattro */
   2782     0,                               /* tp_setattro */
   2783     &staticarray_as_buffer,          /* tp_as_buffer */
   2784     Py_TPFLAGS_DEFAULT,              /* tp_flags */
   2785     0,                               /* tp_doc */
   2786     0,                               /* tp_traverse */
   2787     0,                               /* tp_clear */
   2788     0,                               /* tp_richcompare */
   2789     0,                               /* tp_weaklistoffset */
   2790     0,                               /* tp_iter */
   2791     0,                               /* tp_iternext */
   2792     0,                               /* tp_methods */
   2793     0,                               /* tp_members */
   2794     0,                               /* tp_getset */
   2795     0,                               /* tp_base */
   2796     0,                               /* tp_dict */
   2797     0,                               /* tp_descr_get */
   2798     0,                               /* tp_descr_set */
   2799     0,                               /* tp_dictoffset */
   2800     staticarray_init,                /* tp_init */
   2801     0,                               /* tp_alloc */
   2802     staticarray_new,                 /* tp_new */
   2803 };
   2804 
   2805 
   2806 static struct PyMethodDef _testbuffer_functions[] = {
   2807     {"slice_indices", slice_indices, METH_VARARGS, NULL},
   2808     {"get_pointer", get_pointer, METH_VARARGS, NULL},
   2809     {"get_sizeof_void_p", (PyCFunction)get_sizeof_void_p, METH_NOARGS, NULL},
   2810     {"get_contiguous", get_contiguous, METH_VARARGS, NULL},
   2811     {"py_buffer_to_contiguous", py_buffer_to_contiguous, METH_VARARGS, NULL},
   2812     {"is_contiguous", is_contiguous, METH_VARARGS, NULL},
   2813     {"cmp_contig", cmp_contig, METH_VARARGS, NULL},
   2814     {NULL, NULL}
   2815 };
   2816 
   2817 static struct PyModuleDef _testbuffermodule = {
   2818     PyModuleDef_HEAD_INIT,
   2819     "_testbuffer",
   2820     NULL,
   2821     -1,
   2822     _testbuffer_functions,
   2823     NULL,
   2824     NULL,
   2825     NULL,
   2826     NULL
   2827 };
   2828 
   2829 
   2830 PyMODINIT_FUNC
   2831 PyInit__testbuffer(void)
   2832 {
   2833     PyObject *m;
   2834 
   2835     m = PyModule_Create(&_testbuffermodule);
   2836     if (m == NULL)
   2837         return NULL;
   2838 
   2839     Py_TYPE(&NDArray_Type) = &PyType_Type;
   2840     Py_INCREF(&NDArray_Type);
   2841     PyModule_AddObject(m, "ndarray", (PyObject *)&NDArray_Type);
   2842 
   2843     Py_TYPE(&StaticArray_Type) = &PyType_Type;
   2844     Py_INCREF(&StaticArray_Type);
   2845     PyModule_AddObject(m, "staticarray", (PyObject *)&StaticArray_Type);
   2846 
   2847     structmodule = PyImport_ImportModule("struct");
   2848     if (structmodule == NULL)
   2849         return NULL;
   2850 
   2851     Struct = PyObject_GetAttrString(structmodule, "Struct");
   2852     calcsize = PyObject_GetAttrString(structmodule, "calcsize");
   2853     if (Struct == NULL || calcsize == NULL)
   2854         return NULL;
   2855 
   2856     simple_format = PyUnicode_FromString(simple_fmt);
   2857     if (simple_format == NULL)
   2858         return NULL;
   2859 
   2860     PyModule_AddIntMacro(m, ND_MAX_NDIM);
   2861     PyModule_AddIntMacro(m, ND_VAREXPORT);
   2862     PyModule_AddIntMacro(m, ND_WRITABLE);
   2863     PyModule_AddIntMacro(m, ND_FORTRAN);
   2864     PyModule_AddIntMacro(m, ND_SCALAR);
   2865     PyModule_AddIntMacro(m, ND_PIL);
   2866     PyModule_AddIntMacro(m, ND_GETBUF_FAIL);
   2867     PyModule_AddIntMacro(m, ND_GETBUF_UNDEFINED);
   2868     PyModule_AddIntMacro(m, ND_REDIRECT);
   2869 
   2870     PyModule_AddIntMacro(m, PyBUF_SIMPLE);
   2871     PyModule_AddIntMacro(m, PyBUF_WRITABLE);
   2872     PyModule_AddIntMacro(m, PyBUF_FORMAT);
   2873     PyModule_AddIntMacro(m, PyBUF_ND);
   2874     PyModule_AddIntMacro(m, PyBUF_STRIDES);
   2875     PyModule_AddIntMacro(m, PyBUF_INDIRECT);
   2876     PyModule_AddIntMacro(m, PyBUF_C_CONTIGUOUS);
   2877     PyModule_AddIntMacro(m, PyBUF_F_CONTIGUOUS);
   2878     PyModule_AddIntMacro(m, PyBUF_ANY_CONTIGUOUS);
   2879     PyModule_AddIntMacro(m, PyBUF_FULL);
   2880     PyModule_AddIntMacro(m, PyBUF_FULL_RO);
   2881     PyModule_AddIntMacro(m, PyBUF_RECORDS);
   2882     PyModule_AddIntMacro(m, PyBUF_RECORDS_RO);
   2883     PyModule_AddIntMacro(m, PyBUF_STRIDED);
   2884     PyModule_AddIntMacro(m, PyBUF_STRIDED_RO);
   2885     PyModule_AddIntMacro(m, PyBUF_CONTIG);
   2886     PyModule_AddIntMacro(m, PyBUF_CONTIG_RO);
   2887 
   2888     PyModule_AddIntMacro(m, PyBUF_READ);
   2889     PyModule_AddIntMacro(m, PyBUF_WRITE);
   2890 
   2891     return m;
   2892 }
   2893 
   2894 
   2895 
   2896