(root)/
Python-3.12.0/
Modules/
_testbuffer.c
       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  static PyObject *structmodule = NULL;
      12  static PyObject *Struct = NULL;
      13  static PyObject *calcsize = NULL;
      14  
      15  /* cache simple format string */
      16  static const char *simple_fmt = "B";
      17  static 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_IS_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_Free(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 = Py_NewRef(self);
    1528      self->head->exports++;
    1529  
    1530      return 0;
    1531  }
    1532  
    1533  static void
    1534  ndarray_releasebuf(NDArrayObject *self, Py_buffer *view)
    1535  {
    1536      if (!ND_IS_CONSUMER(self)) {
    1537          ndbuf_t *ndbuf = view->internal;
    1538          if (--ndbuf->exports == 0 && ndbuf != self->head)
    1539              ndbuf_delete(self, ndbuf);
    1540      }
    1541  }
    1542  
    1543  static PyBufferProcs ndarray_as_buffer = {
    1544      (getbufferproc)ndarray_getbuf,        /* bf_getbuffer */
    1545      (releasebufferproc)ndarray_releasebuf /* bf_releasebuffer */
    1546  };
    1547  
    1548  
    1549  /**************************************************************************/
    1550  /*                           indexing/slicing                             */
    1551  /**************************************************************************/
    1552  
    1553  static char *
    1554  ptr_from_index(Py_buffer *base, Py_ssize_t index)
    1555  {
    1556      char *ptr;
    1557      Py_ssize_t nitems; /* items in the first dimension */
    1558  
    1559      if (base->shape)
    1560          nitems = base->shape[0];
    1561      else {
    1562          assert(base->ndim == 1 && SIMPLE_FORMAT(base->format));
    1563          nitems = base->len;
    1564      }
    1565  
    1566      if (index < 0) {
    1567          index += nitems;
    1568      }
    1569      if (index < 0 || index >= nitems) {
    1570          PyErr_SetString(PyExc_IndexError, "index out of bounds");
    1571          return NULL;
    1572      }
    1573  
    1574      ptr = (char *)base->buf;
    1575  
    1576      if (base->strides == NULL)
    1577           ptr += base->itemsize * index;
    1578      else
    1579           ptr += base->strides[0] * index;
    1580  
    1581      ptr = ADJUST_PTR(ptr, base->suboffsets);
    1582  
    1583      return ptr;
    1584  }
    1585  
    1586  static PyObject *
    1587  ndarray_item(NDArrayObject *self, Py_ssize_t index)
    1588  {
    1589      ndbuf_t *ndbuf = self->head;
    1590      Py_buffer *base = &ndbuf->base;
    1591      char *ptr;
    1592  
    1593      if (base->ndim == 0) {
    1594          PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
    1595          return NULL;
    1596      }
    1597  
    1598      ptr = ptr_from_index(base, index);
    1599      if (ptr == NULL)
    1600          return NULL;
    1601  
    1602      if (base->ndim == 1) {
    1603          return unpack_single(ptr, base->format, base->itemsize);
    1604      }
    1605      else {
    1606          NDArrayObject *nd;
    1607          Py_buffer *subview;
    1608  
    1609          nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
    1610          if (nd == NULL)
    1611              return NULL;
    1612  
    1613          if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
    1614              Py_DECREF(nd);
    1615              return NULL;
    1616          }
    1617  
    1618          subview = &nd->staticbuf.base;
    1619  
    1620          subview->buf = ptr;
    1621          subview->len /= subview->shape[0];
    1622  
    1623          subview->ndim--;
    1624          subview->shape++;
    1625          if (subview->strides) subview->strides++;
    1626          if (subview->suboffsets) subview->suboffsets++;
    1627  
    1628          init_flags(&nd->staticbuf);
    1629  
    1630          return (PyObject *)nd;
    1631      }
    1632  }
    1633  
    1634  /*
    1635    For each dimension, we get valid (start, stop, step, slicelength) quadruples
    1636    from PySlice_GetIndicesEx().
    1637  
    1638    Slicing NumPy arrays
    1639    ====================
    1640  
    1641      A pointer to an element in a NumPy array is defined by:
    1642  
    1643        ptr = (char *)buf + indices[0] * strides[0] +
    1644                            ... +
    1645                            indices[ndim-1] * strides[ndim-1]
    1646  
    1647      Adjust buf:
    1648      -----------
    1649        Adding start[n] for each dimension effectively adds the constant:
    1650  
    1651          c = start[0] * strides[0] + ... + start[ndim-1] * strides[ndim-1]
    1652  
    1653        Therefore init_slice() adds all start[n] directly to buf.
    1654  
    1655      Adjust shape:
    1656      -------------
    1657        Obviously shape[n] = slicelength[n]
    1658  
    1659      Adjust strides:
    1660      ---------------
    1661        In the original array, the next element in a dimension is reached
    1662        by adding strides[n] to the pointer. In the sliced array, elements
    1663        may be skipped, so the next element is reached by adding:
    1664  
    1665          strides[n] * step[n]
    1666  
    1667    Slicing PIL arrays
    1668    ==================
    1669  
    1670      Layout:
    1671      -------
    1672        In the first (zeroth) dimension, PIL arrays have an array of pointers
    1673        to sub-arrays of ndim-1. Striding in the first dimension is done by
    1674        getting the index of the nth pointer, dereference it and then add a
    1675        suboffset to it. The arrays pointed to can best be seen a regular
    1676        NumPy arrays.
    1677  
    1678      Adjust buf:
    1679      -----------
    1680        In the original array, buf points to a location (usually the start)
    1681        in the array of pointers. For the sliced array, start[0] can be
    1682        added to buf in the same manner as for NumPy arrays.
    1683  
    1684      Adjust suboffsets:
    1685      ------------------
    1686        Due to the dereferencing step in the addressing scheme, it is not
    1687        possible to adjust buf for higher dimensions. Recall that the
    1688        sub-arrays pointed to are regular NumPy arrays, so for each of
    1689        those arrays adding start[n] effectively adds the constant:
    1690  
    1691          c = start[1] * strides[1] + ... + start[ndim-1] * strides[ndim-1]
    1692  
    1693        This constant is added to suboffsets[0]. suboffsets[0] in turn is
    1694        added to each pointer right after dereferencing.
    1695  
    1696      Adjust shape and strides:
    1697      -------------------------
    1698        Shape and strides are not influenced by the dereferencing step, so
    1699        they are adjusted in the same manner as for NumPy arrays.
    1700  
    1701    Multiple levels of suboffsets
    1702    =============================
    1703  
    1704        For a construct like an array of pointers to array of pointers to
    1705        sub-arrays of ndim-2:
    1706  
    1707          suboffsets[0] = start[1] * strides[1]
    1708          suboffsets[1] = start[2] * strides[2] + ...
    1709  */
    1710  static int
    1711  init_slice(Py_buffer *base, PyObject *key, int dim)
    1712  {
    1713      Py_ssize_t start, stop, step, slicelength;
    1714  
    1715      if (PySlice_Unpack(key, &start, &stop, &step) < 0) {
    1716          return -1;
    1717      }
    1718      slicelength = PySlice_AdjustIndices(base->shape[dim], &start, &stop, step);
    1719  
    1720  
    1721      if (base->suboffsets == NULL || dim == 0) {
    1722      adjust_buf:
    1723          base->buf = (char *)base->buf + base->strides[dim] * start;
    1724      }
    1725      else {
    1726          Py_ssize_t n = dim-1;
    1727          while (n >= 0 && base->suboffsets[n] < 0)
    1728              n--;
    1729          if (n < 0)
    1730              goto adjust_buf; /* all suboffsets are negative */
    1731          base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
    1732      }
    1733      base->shape[dim] = slicelength;
    1734      base->strides[dim] = base->strides[dim] * step;
    1735  
    1736      return 0;
    1737  }
    1738  
    1739  static int
    1740  copy_structure(Py_buffer *base)
    1741  {
    1742      Py_ssize_t *shape = NULL, *strides = NULL, *suboffsets = NULL;
    1743      Py_ssize_t i;
    1744  
    1745      shape = PyMem_Malloc(base->ndim * (sizeof *shape));
    1746      strides = PyMem_Malloc(base->ndim * (sizeof *strides));
    1747      if (shape == NULL || strides == NULL)
    1748          goto err_nomem;
    1749  
    1750      suboffsets = NULL;
    1751      if (base->suboffsets) {
    1752          suboffsets = PyMem_Malloc(base->ndim * (sizeof *suboffsets));
    1753          if (suboffsets == NULL)
    1754              goto err_nomem;
    1755      }
    1756  
    1757      for (i = 0; i < base->ndim; i++) {
    1758          shape[i] = base->shape[i];
    1759          strides[i] = base->strides[i];
    1760          if (suboffsets)
    1761              suboffsets[i] = base->suboffsets[i];
    1762      }
    1763  
    1764      base->shape = shape;
    1765      base->strides = strides;
    1766      base->suboffsets = suboffsets;
    1767  
    1768      return 0;
    1769  
    1770  err_nomem:
    1771      PyErr_NoMemory();
    1772      PyMem_XFree(shape);
    1773      PyMem_XFree(strides);
    1774      PyMem_XFree(suboffsets);
    1775      return -1;
    1776  }
    1777  
    1778  static PyObject *
    1779  ndarray_subscript(NDArrayObject *self, PyObject *key)
    1780  {
    1781      NDArrayObject *nd;
    1782      ndbuf_t *ndbuf;
    1783      Py_buffer *base = &self->head->base;
    1784  
    1785      if (base->ndim == 0) {
    1786          if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
    1787              return unpack_single(base->buf, base->format, base->itemsize);
    1788          }
    1789          else if (key == Py_Ellipsis) {
    1790              return Py_NewRef(self);
    1791          }
    1792          else {
    1793              PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
    1794              return NULL;
    1795          }
    1796      }
    1797      if (PyIndex_Check(key)) {
    1798          Py_ssize_t index = PyLong_AsSsize_t(key);
    1799          if (index == -1 && PyErr_Occurred())
    1800              return NULL;
    1801          return ndarray_item(self, index);
    1802      }
    1803  
    1804      nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
    1805      if (nd == NULL)
    1806          return NULL;
    1807  
    1808      /* new ndarray is a consumer */
    1809      if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
    1810          Py_DECREF(nd);
    1811          return NULL;
    1812      }
    1813  
    1814      /* copy shape, strides and suboffsets */
    1815      ndbuf = nd->head;
    1816      base = &ndbuf->base;
    1817      if (copy_structure(base) < 0) {
    1818          Py_DECREF(nd);
    1819          return NULL;
    1820      }
    1821      ndbuf->flags |= ND_OWN_ARRAYS;
    1822  
    1823      if (PySlice_Check(key)) {
    1824          /* one-dimensional slice */
    1825          if (init_slice(base, key, 0) < 0)
    1826              goto err_occurred;
    1827      }
    1828      else if (PyTuple_Check(key)) {
    1829          /* multi-dimensional slice */
    1830          PyObject *tuple = key;
    1831          Py_ssize_t i, n;
    1832  
    1833          n = PyTuple_GET_SIZE(tuple);
    1834          for (i = 0; i < n; i++) {
    1835              key = PyTuple_GET_ITEM(tuple, i);
    1836              if (!PySlice_Check(key))
    1837                  goto type_error;
    1838              if (init_slice(base, key, (int)i) < 0)
    1839                  goto err_occurred;
    1840          }
    1841      }
    1842      else {
    1843          goto type_error;
    1844      }
    1845  
    1846      init_len(base);
    1847      init_flags(ndbuf);
    1848  
    1849      return (PyObject *)nd;
    1850  
    1851  
    1852  type_error:
    1853      PyErr_Format(PyExc_TypeError,
    1854          "cannot index memory using \"%.200s\"",
    1855          Py_TYPE(key)->tp_name);
    1856  err_occurred:
    1857      Py_DECREF(nd);
    1858      return NULL;
    1859  }
    1860  
    1861  
    1862  static int
    1863  ndarray_ass_subscript(NDArrayObject *self, PyObject *key, PyObject *value)
    1864  {
    1865      NDArrayObject *nd;
    1866      Py_buffer *dest = &self->head->base;
    1867      Py_buffer src;
    1868      char *ptr;
    1869      Py_ssize_t index;
    1870      int ret = -1;
    1871  
    1872      if (dest->readonly) {
    1873          PyErr_SetString(PyExc_TypeError, "ndarray is not writable");
    1874          return -1;
    1875      }
    1876      if (value == NULL) {
    1877          PyErr_SetString(PyExc_TypeError, "ndarray data cannot be deleted");
    1878          return -1;
    1879      }
    1880      if (dest->ndim == 0) {
    1881          if (key == Py_Ellipsis ||
    1882              (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
    1883              ptr = (char *)dest->buf;
    1884              return pack_single(ptr, value, dest->format, dest->itemsize);
    1885          }
    1886          else {
    1887              PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
    1888              return -1;
    1889          }
    1890      }
    1891      if (dest->ndim == 1 && PyIndex_Check(key)) {
    1892          /* rvalue must be a single item */
    1893          index = PyLong_AsSsize_t(key);
    1894          if (index == -1 && PyErr_Occurred())
    1895              return -1;
    1896          else {
    1897              ptr = ptr_from_index(dest, index);
    1898              if (ptr == NULL)
    1899                  return -1;
    1900          }
    1901          return pack_single(ptr, value, dest->format, dest->itemsize);
    1902      }
    1903  
    1904      /* rvalue must be an exporter */
    1905      if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) == -1)
    1906          return -1;
    1907  
    1908      nd = (NDArrayObject *)ndarray_subscript(self, key);
    1909      if (nd != NULL) {
    1910          dest = &nd->head->base;
    1911          ret = copy_buffer(dest, &src);
    1912          Py_DECREF(nd);
    1913      }
    1914  
    1915      PyBuffer_Release(&src);
    1916      return ret;
    1917  }
    1918  
    1919  static PyObject *
    1920  slice_indices(PyObject *self, PyObject *args)
    1921  {
    1922      PyObject *ret, *key, *tmp;
    1923      Py_ssize_t s[4]; /* start, stop, step, slicelength */
    1924      Py_ssize_t i, len;
    1925  
    1926      if (!PyArg_ParseTuple(args, "On", &key, &len)) {
    1927          return NULL;
    1928      }
    1929      if (!PySlice_Check(key)) {
    1930          PyErr_SetString(PyExc_TypeError,
    1931              "first argument must be a slice object");
    1932          return NULL;
    1933      }
    1934      if (PySlice_Unpack(key, &s[0], &s[1], &s[2]) < 0) {
    1935          return NULL;
    1936      }
    1937      s[3] = PySlice_AdjustIndices(len, &s[0], &s[1], s[2]);
    1938  
    1939      ret = PyTuple_New(4);
    1940      if (ret == NULL)
    1941          return NULL;
    1942  
    1943      for (i = 0; i < 4; i++) {
    1944          tmp = PyLong_FromSsize_t(s[i]);
    1945          if (tmp == NULL)
    1946              goto error;
    1947          PyTuple_SET_ITEM(ret, i, tmp);
    1948      }
    1949  
    1950      return ret;
    1951  
    1952  error:
    1953      Py_DECREF(ret);
    1954      return NULL;
    1955  }
    1956  
    1957  
    1958  static PyMappingMethods ndarray_as_mapping = {
    1959      NULL,                                 /* mp_length */
    1960      (binaryfunc)ndarray_subscript,        /* mp_subscript */
    1961      (objobjargproc)ndarray_ass_subscript  /* mp_ass_subscript */
    1962  };
    1963  
    1964  static PySequenceMethods ndarray_as_sequence = {
    1965          0,                                /* sq_length */
    1966          0,                                /* sq_concat */
    1967          0,                                /* sq_repeat */
    1968          (ssizeargfunc)ndarray_item,       /* sq_item */
    1969  };
    1970  
    1971  
    1972  /**************************************************************************/
    1973  /*                                 getters                                */
    1974  /**************************************************************************/
    1975  
    1976  static PyObject *
    1977  ssize_array_as_tuple(Py_ssize_t *array, Py_ssize_t len)
    1978  {
    1979      PyObject *tuple, *x;
    1980      Py_ssize_t i;
    1981  
    1982      if (array == NULL)
    1983          return PyTuple_New(0);
    1984  
    1985      tuple = PyTuple_New(len);
    1986      if (tuple == NULL)
    1987          return NULL;
    1988  
    1989      for (i = 0; i < len; i++) {
    1990          x = PyLong_FromSsize_t(array[i]);
    1991          if (x == NULL) {
    1992              Py_DECREF(tuple);
    1993              return NULL;
    1994          }
    1995          PyTuple_SET_ITEM(tuple, i, x);
    1996      }
    1997  
    1998      return tuple;
    1999  }
    2000  
    2001  static PyObject *
    2002  ndarray_get_flags(NDArrayObject *self, void *closure)
    2003  {
    2004      return PyLong_FromLong(self->head->flags);
    2005  }
    2006  
    2007  static PyObject *
    2008  ndarray_get_offset(NDArrayObject *self, void *closure)
    2009  {
    2010      ndbuf_t *ndbuf = self->head;
    2011      return PyLong_FromSsize_t(ndbuf->offset);
    2012  }
    2013  
    2014  static PyObject *
    2015  ndarray_get_obj(NDArrayObject *self, void *closure)
    2016  {
    2017      Py_buffer *base = &self->head->base;
    2018  
    2019      if (base->obj == NULL) {
    2020          Py_RETURN_NONE;
    2021      }
    2022      return Py_NewRef(base->obj);
    2023  }
    2024  
    2025  static PyObject *
    2026  ndarray_get_nbytes(NDArrayObject *self, void *closure)
    2027  {
    2028      Py_buffer *base = &self->head->base;
    2029      return PyLong_FromSsize_t(base->len);
    2030  }
    2031  
    2032  static PyObject *
    2033  ndarray_get_readonly(NDArrayObject *self, void *closure)
    2034  {
    2035      Py_buffer *base = &self->head->base;
    2036      return PyBool_FromLong(base->readonly);
    2037  }
    2038  
    2039  static PyObject *
    2040  ndarray_get_itemsize(NDArrayObject *self, void *closure)
    2041  {
    2042      Py_buffer *base = &self->head->base;
    2043      return PyLong_FromSsize_t(base->itemsize);
    2044  }
    2045  
    2046  static PyObject *
    2047  ndarray_get_format(NDArrayObject *self, void *closure)
    2048  {
    2049      Py_buffer *base = &self->head->base;
    2050      const char *fmt = base->format ? base->format : "";
    2051      return PyUnicode_FromString(fmt);
    2052  }
    2053  
    2054  static PyObject *
    2055  ndarray_get_ndim(NDArrayObject *self, void *closure)
    2056  {
    2057      Py_buffer *base = &self->head->base;
    2058      return PyLong_FromSsize_t(base->ndim);
    2059  }
    2060  
    2061  static PyObject *
    2062  ndarray_get_shape(NDArrayObject *self, void *closure)
    2063  {
    2064      Py_buffer *base = &self->head->base;
    2065      return ssize_array_as_tuple(base->shape, base->ndim);
    2066  }
    2067  
    2068  static PyObject *
    2069  ndarray_get_strides(NDArrayObject *self, void *closure)
    2070  {
    2071      Py_buffer *base = &self->head->base;
    2072      return ssize_array_as_tuple(base->strides, base->ndim);
    2073  }
    2074  
    2075  static PyObject *
    2076  ndarray_get_suboffsets(NDArrayObject *self, void *closure)
    2077  {
    2078      Py_buffer *base = &self->head->base;
    2079      return ssize_array_as_tuple(base->suboffsets, base->ndim);
    2080  }
    2081  
    2082  static PyObject *
    2083  ndarray_c_contig(PyObject *self, PyObject *dummy)
    2084  {
    2085      NDArrayObject *nd = (NDArrayObject *)self;
    2086      int ret = PyBuffer_IsContiguous(&nd->head->base, 'C');
    2087  
    2088      if (ret != ND_C_CONTIGUOUS(nd->head->flags)) {
    2089          PyErr_SetString(PyExc_RuntimeError,
    2090              "results from PyBuffer_IsContiguous() and flags differ");
    2091          return NULL;
    2092      }
    2093      return PyBool_FromLong(ret);
    2094  }
    2095  
    2096  static PyObject *
    2097  ndarray_fortran_contig(PyObject *self, PyObject *dummy)
    2098  {
    2099      NDArrayObject *nd = (NDArrayObject *)self;
    2100      int ret = PyBuffer_IsContiguous(&nd->head->base, 'F');
    2101  
    2102      if (ret != ND_FORTRAN_CONTIGUOUS(nd->head->flags)) {
    2103          PyErr_SetString(PyExc_RuntimeError,
    2104              "results from PyBuffer_IsContiguous() and flags differ");
    2105          return NULL;
    2106      }
    2107      return PyBool_FromLong(ret);
    2108  }
    2109  
    2110  static PyObject *
    2111  ndarray_contig(PyObject *self, PyObject *dummy)
    2112  {
    2113      NDArrayObject *nd = (NDArrayObject *)self;
    2114      int ret = PyBuffer_IsContiguous(&nd->head->base, 'A');
    2115  
    2116      if (ret != ND_ANY_CONTIGUOUS(nd->head->flags)) {
    2117          PyErr_SetString(PyExc_RuntimeError,
    2118              "results from PyBuffer_IsContiguous() and flags differ");
    2119          return NULL;
    2120      }
    2121      return PyBool_FromLong(ret);
    2122  }
    2123  
    2124  
    2125  static PyGetSetDef ndarray_getset [] =
    2126  {
    2127    /* ndbuf */
    2128    { "flags",        (getter)ndarray_get_flags,      NULL, NULL, NULL},
    2129    { "offset",       (getter)ndarray_get_offset,     NULL, NULL, NULL},
    2130    /* ndbuf.base */
    2131    { "obj",          (getter)ndarray_get_obj,        NULL, NULL, NULL},
    2132    { "nbytes",       (getter)ndarray_get_nbytes,     NULL, NULL, NULL},
    2133    { "readonly",     (getter)ndarray_get_readonly,   NULL, NULL, NULL},
    2134    { "itemsize",     (getter)ndarray_get_itemsize,   NULL, NULL, NULL},
    2135    { "format",       (getter)ndarray_get_format,     NULL, NULL, NULL},
    2136    { "ndim",         (getter)ndarray_get_ndim,       NULL, NULL, NULL},
    2137    { "shape",        (getter)ndarray_get_shape,      NULL, NULL, NULL},
    2138    { "strides",      (getter)ndarray_get_strides,    NULL, NULL, NULL},
    2139    { "suboffsets",   (getter)ndarray_get_suboffsets, NULL, NULL, NULL},
    2140    { "c_contiguous", (getter)ndarray_c_contig,       NULL, NULL, NULL},
    2141    { "f_contiguous", (getter)ndarray_fortran_contig, NULL, NULL, NULL},
    2142    { "contiguous",   (getter)ndarray_contig,         NULL, NULL, NULL},
    2143    {NULL}
    2144  };
    2145  
    2146  static PyObject *
    2147  ndarray_tolist(PyObject *self, PyObject *dummy)
    2148  {
    2149      return ndarray_as_list((NDArrayObject *)self);
    2150  }
    2151  
    2152  static PyObject *
    2153  ndarray_tobytes(PyObject *self, PyObject *dummy)
    2154  {
    2155      ndbuf_t *ndbuf = ((NDArrayObject *)self)->head;
    2156      Py_buffer *src = &ndbuf->base;
    2157      Py_buffer dest;
    2158      PyObject *ret = NULL;
    2159      char *mem;
    2160  
    2161      if (ND_C_CONTIGUOUS(ndbuf->flags))
    2162          return PyBytes_FromStringAndSize(src->buf, src->len);
    2163  
    2164      assert(src->shape != NULL);
    2165      assert(src->strides != NULL);
    2166      assert(src->ndim > 0);
    2167  
    2168      mem = PyMem_Malloc(src->len);
    2169      if (mem == NULL) {
    2170          PyErr_NoMemory();
    2171          return NULL;
    2172      }
    2173  
    2174      dest = *src;
    2175      dest.buf = mem;
    2176      dest.suboffsets = NULL;
    2177      dest.strides = strides_from_shape(ndbuf, 0);
    2178      if (dest.strides == NULL)
    2179          goto out;
    2180      if (copy_buffer(&dest, src) < 0)
    2181          goto out;
    2182  
    2183      ret = PyBytes_FromStringAndSize(mem, src->len);
    2184  
    2185  out:
    2186      PyMem_XFree(dest.strides);
    2187      PyMem_Free(mem);
    2188      return ret;
    2189  }
    2190  
    2191  /* add redundant (negative) suboffsets for testing */
    2192  static PyObject *
    2193  ndarray_add_suboffsets(PyObject *self, PyObject *dummy)
    2194  {
    2195      NDArrayObject *nd = (NDArrayObject *)self;
    2196      Py_buffer *base = &nd->head->base;
    2197      Py_ssize_t i;
    2198  
    2199      if (base->suboffsets != NULL) {
    2200          PyErr_SetString(PyExc_TypeError,
    2201              "cannot add suboffsets to PIL-style array");
    2202              return NULL;
    2203      }
    2204      if (base->strides == NULL) {
    2205          PyErr_SetString(PyExc_TypeError,
    2206              "cannot add suboffsets to array without strides");
    2207              return NULL;
    2208      }
    2209  
    2210      base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
    2211      if (base->suboffsets == NULL) {
    2212          PyErr_NoMemory();
    2213          return NULL;
    2214      }
    2215  
    2216      for (i = 0; i < base->ndim; i++)
    2217          base->suboffsets[i] = -1;
    2218  
    2219      nd->head->flags &= ~(ND_C|ND_FORTRAN);
    2220  
    2221      Py_RETURN_NONE;
    2222  }
    2223  
    2224  /* Test PyMemoryView_FromBuffer(): return a memoryview from a static buffer.
    2225     Obviously this is fragile and only one such view may be active at any
    2226     time. Never use anything like this in real code! */
    2227  static char *infobuf = NULL;
    2228  static PyObject *
    2229  ndarray_memoryview_from_buffer(PyObject *self, PyObject *dummy)
    2230  {
    2231      const NDArrayObject *nd = (NDArrayObject *)self;
    2232      const Py_buffer *view = &nd->head->base;
    2233      const ndbuf_t *ndbuf;
    2234      static char format[ND_MAX_NDIM+1];
    2235      static Py_ssize_t shape[ND_MAX_NDIM];
    2236      static Py_ssize_t strides[ND_MAX_NDIM];
    2237      static Py_ssize_t suboffsets[ND_MAX_NDIM];
    2238      static Py_buffer info;
    2239      char *p;
    2240  
    2241      if (!ND_IS_CONSUMER(nd))
    2242          ndbuf = nd->head; /* self is ndarray/original exporter */
    2243      else if (NDArray_Check(view->obj) && !ND_IS_CONSUMER(view->obj))
    2244          /* self is ndarray and consumer from ndarray/original exporter */
    2245          ndbuf = ((NDArrayObject *)view->obj)->head;
    2246      else {
    2247          PyErr_SetString(PyExc_TypeError,
    2248          "memoryview_from_buffer(): ndarray must be original exporter or "
    2249          "consumer from ndarray/original exporter");
    2250           return NULL;
    2251      }
    2252  
    2253      info = *view;
    2254      p = PyMem_Realloc(infobuf, ndbuf->len);
    2255      if (p == NULL) {
    2256          PyMem_Free(infobuf);
    2257          PyErr_NoMemory();
    2258          infobuf = NULL;
    2259          return NULL;
    2260      }
    2261      else {
    2262          infobuf = p;
    2263      }
    2264      /* copy the complete raw data */
    2265      memcpy(infobuf, ndbuf->data, ndbuf->len);
    2266      info.buf = infobuf + ((char *)view->buf - ndbuf->data);
    2267  
    2268      if (view->format) {
    2269          if (strlen(view->format) > ND_MAX_NDIM) {
    2270              PyErr_Format(PyExc_TypeError,
    2271                  "memoryview_from_buffer: format is limited to %d characters",
    2272                  ND_MAX_NDIM);
    2273                  return NULL;
    2274          }
    2275          strcpy(format, view->format);
    2276          info.format = format;
    2277      }
    2278      if (view->ndim > ND_MAX_NDIM) {
    2279          PyErr_Format(PyExc_TypeError,
    2280              "memoryview_from_buffer: ndim is limited to %d", ND_MAX_NDIM);
    2281              return NULL;
    2282      }
    2283      if (view->shape) {
    2284          memcpy(shape, view->shape, view->ndim * sizeof(Py_ssize_t));
    2285          info.shape = shape;
    2286      }
    2287      if (view->strides) {
    2288          memcpy(strides, view->strides, view->ndim * sizeof(Py_ssize_t));
    2289          info.strides = strides;
    2290      }
    2291      if (view->suboffsets) {
    2292          memcpy(suboffsets, view->suboffsets, view->ndim * sizeof(Py_ssize_t));
    2293          info.suboffsets = suboffsets;
    2294      }
    2295  
    2296      return PyMemoryView_FromBuffer(&info);
    2297  }
    2298  
    2299  /* Get a single item from bufobj at the location specified by seq.
    2300     seq is a list or tuple of indices. The purpose of this function
    2301     is to check other functions against PyBuffer_GetPointer(). */
    2302  static PyObject *
    2303  get_pointer(PyObject *self, PyObject *args)
    2304  {
    2305      PyObject *ret = NULL, *bufobj, *seq;
    2306      Py_buffer view;
    2307      Py_ssize_t indices[ND_MAX_NDIM];
    2308      Py_ssize_t i;
    2309      void *ptr;
    2310  
    2311      if (!PyArg_ParseTuple(args, "OO", &bufobj, &seq)) {
    2312          return NULL;
    2313      }
    2314  
    2315      CHECK_LIST_OR_TUPLE(seq);
    2316      if (PyObject_GetBuffer(bufobj, &view, PyBUF_FULL_RO) < 0)
    2317          return NULL;
    2318  
    2319      if (view.ndim > ND_MAX_NDIM) {
    2320          PyErr_Format(PyExc_ValueError,
    2321              "get_pointer(): ndim > %d", ND_MAX_NDIM);
    2322          goto out;
    2323      }
    2324      if (PySequence_Fast_GET_SIZE(seq) != view.ndim) {
    2325          PyErr_SetString(PyExc_ValueError,
    2326              "get_pointer(): len(indices) != ndim");
    2327          goto out;
    2328      }
    2329  
    2330      for (i = 0; i < view.ndim; i++) {
    2331          PyObject *x = PySequence_Fast_GET_ITEM(seq, i);
    2332          indices[i] = PyLong_AsSsize_t(x);
    2333          if (PyErr_Occurred())
    2334              goto out;
    2335          if (indices[i] < 0 || indices[i] >= view.shape[i]) {
    2336              PyErr_Format(PyExc_ValueError,
    2337                  "get_pointer(): invalid index %zd at position %zd",
    2338                  indices[i], i);
    2339              goto out;
    2340          }
    2341      }
    2342  
    2343      ptr = PyBuffer_GetPointer(&view, indices);
    2344      ret = unpack_single(ptr, view.format, view.itemsize);
    2345  
    2346  out:
    2347      PyBuffer_Release(&view);
    2348      return ret;
    2349  }
    2350  
    2351  static PyObject *
    2352  get_sizeof_void_p(PyObject *self, PyObject *Py_UNUSED(ignored))
    2353  {
    2354      return PyLong_FromSize_t(sizeof(void *));
    2355  }
    2356  
    2357  static char
    2358  get_ascii_order(PyObject *order)
    2359  {
    2360      PyObject *ascii_order;
    2361      char ord;
    2362  
    2363      if (!PyUnicode_Check(order)) {
    2364          PyErr_SetString(PyExc_TypeError,
    2365              "order must be a string");
    2366          return CHAR_MAX;
    2367      }
    2368  
    2369      ascii_order = PyUnicode_AsASCIIString(order);
    2370      if (ascii_order == NULL) {
    2371          return CHAR_MAX;
    2372      }
    2373  
    2374      ord = PyBytes_AS_STRING(ascii_order)[0];
    2375      Py_DECREF(ascii_order);
    2376  
    2377      if (ord != 'C' && ord != 'F' && ord != 'A') {
    2378          PyErr_SetString(PyExc_ValueError,
    2379              "invalid order, must be C, F or A");
    2380          return CHAR_MAX;
    2381      }
    2382  
    2383      return ord;
    2384  }
    2385  
    2386  /* Get a contiguous memoryview. */
    2387  static PyObject *
    2388  get_contiguous(PyObject *self, PyObject *args)
    2389  {
    2390      PyObject *obj;
    2391      PyObject *buffertype;
    2392      PyObject *order;
    2393      long type;
    2394      char ord;
    2395  
    2396      if (!PyArg_ParseTuple(args, "OOO", &obj, &buffertype, &order)) {
    2397          return NULL;
    2398      }
    2399  
    2400      if (!PyLong_Check(buffertype)) {
    2401          PyErr_SetString(PyExc_TypeError,
    2402              "buffertype must be PyBUF_READ or PyBUF_WRITE");
    2403          return NULL;
    2404      }
    2405  
    2406      type = PyLong_AsLong(buffertype);
    2407      if (type == -1 && PyErr_Occurred()) {
    2408          return NULL;
    2409      }
    2410      if (type != PyBUF_READ && type != PyBUF_WRITE) {
    2411          PyErr_SetString(PyExc_ValueError,
    2412              "invalid buffer type");
    2413          return NULL;
    2414      }
    2415  
    2416      ord = get_ascii_order(order);
    2417      if (ord == CHAR_MAX)
    2418          return NULL;
    2419  
    2420      return PyMemoryView_GetContiguous(obj, (int)type, ord);
    2421  }
    2422  
    2423  /* PyBuffer_ToContiguous() */
    2424  static PyObject *
    2425  py_buffer_to_contiguous(PyObject *self, PyObject *args)
    2426  {
    2427      PyObject *obj;
    2428      PyObject *order;
    2429      PyObject *ret = NULL;
    2430      int flags;
    2431      char ord;
    2432      Py_buffer view;
    2433      char *buf = NULL;
    2434  
    2435      if (!PyArg_ParseTuple(args, "OOi", &obj, &order, &flags)) {
    2436          return NULL;
    2437      }
    2438  
    2439      if (PyObject_GetBuffer(obj, &view, flags) < 0) {
    2440          return NULL;
    2441      }
    2442  
    2443      ord = get_ascii_order(order);
    2444      if (ord == CHAR_MAX) {
    2445          goto out;
    2446      }
    2447  
    2448      buf = PyMem_Malloc(view.len);
    2449      if (buf == NULL) {
    2450          PyErr_NoMemory();
    2451          goto out;
    2452      }
    2453  
    2454      if (PyBuffer_ToContiguous(buf, &view, view.len, ord) < 0) {
    2455          goto out;
    2456      }
    2457  
    2458      ret = PyBytes_FromStringAndSize(buf, view.len);
    2459  
    2460  out:
    2461      PyBuffer_Release(&view);
    2462      PyMem_XFree(buf);
    2463      return ret;
    2464  }
    2465  
    2466  static int
    2467  fmtcmp(const char *fmt1, const char *fmt2)
    2468  {
    2469      if (fmt1 == NULL) {
    2470          return fmt2 == NULL || strcmp(fmt2, "B") == 0;
    2471      }
    2472      if (fmt2 == NULL) {
    2473          return fmt1 == NULL || strcmp(fmt1, "B") == 0;
    2474      }
    2475      return strcmp(fmt1, fmt2) == 0;
    2476  }
    2477  
    2478  static int
    2479  arraycmp(const Py_ssize_t *a1, const Py_ssize_t *a2, const Py_ssize_t *shape,
    2480           Py_ssize_t ndim)
    2481  {
    2482      Py_ssize_t i;
    2483  
    2484  
    2485      for (i = 0; i < ndim; i++) {
    2486          if (shape && shape[i] <= 1) {
    2487              /* strides can differ if the dimension is less than 2 */
    2488              continue;
    2489          }
    2490          if (a1[i] != a2[i]) {
    2491              return 0;
    2492          }
    2493      }
    2494  
    2495      return 1;
    2496  }
    2497  
    2498  /* Compare two contiguous buffers for physical equality. */
    2499  static PyObject *
    2500  cmp_contig(PyObject *self, PyObject *args)
    2501  {
    2502      PyObject *b1, *b2; /* buffer objects */
    2503      Py_buffer v1, v2;
    2504      PyObject *ret;
    2505      int equal = 0;
    2506  
    2507      if (!PyArg_ParseTuple(args, "OO", &b1, &b2)) {
    2508          return NULL;
    2509      }
    2510  
    2511      if (PyObject_GetBuffer(b1, &v1, PyBUF_FULL_RO) < 0) {
    2512          PyErr_SetString(PyExc_TypeError,
    2513              "cmp_contig: first argument does not implement the buffer "
    2514              "protocol");
    2515          return NULL;
    2516      }
    2517      if (PyObject_GetBuffer(b2, &v2, PyBUF_FULL_RO) < 0) {
    2518          PyErr_SetString(PyExc_TypeError,
    2519              "cmp_contig: second argument does not implement the buffer "
    2520              "protocol");
    2521          PyBuffer_Release(&v1);
    2522          return NULL;
    2523      }
    2524  
    2525      if (!(PyBuffer_IsContiguous(&v1, 'C')&&PyBuffer_IsContiguous(&v2, 'C')) &&
    2526          !(PyBuffer_IsContiguous(&v1, 'F')&&PyBuffer_IsContiguous(&v2, 'F'))) {
    2527          goto result;
    2528      }
    2529  
    2530      /* readonly may differ if created from non-contiguous */
    2531      if (v1.len != v2.len ||
    2532          v1.itemsize != v2.itemsize ||
    2533          v1.ndim != v2.ndim ||
    2534          !fmtcmp(v1.format, v2.format) ||
    2535          !!v1.shape != !!v2.shape ||
    2536          !!v1.strides != !!v2.strides ||
    2537          !!v1.suboffsets != !!v2.suboffsets) {
    2538          goto result;
    2539      }
    2540  
    2541      if ((v1.shape && !arraycmp(v1.shape, v2.shape, NULL, v1.ndim)) ||
    2542          (v1.strides && !arraycmp(v1.strides, v2.strides, v1.shape, v1.ndim)) ||
    2543          (v1.suboffsets && !arraycmp(v1.suboffsets, v2.suboffsets, NULL,
    2544                                      v1.ndim))) {
    2545          goto result;
    2546      }
    2547  
    2548      if (memcmp((char *)v1.buf, (char *)v2.buf, v1.len) != 0) {
    2549          goto result;
    2550      }
    2551  
    2552      equal = 1;
    2553  
    2554  result:
    2555      PyBuffer_Release(&v1);
    2556      PyBuffer_Release(&v2);
    2557  
    2558      ret = equal ? Py_True : Py_False;
    2559      return Py_NewRef(ret);
    2560  }
    2561  
    2562  static PyObject *
    2563  is_contiguous(PyObject *self, PyObject *args)
    2564  {
    2565      PyObject *obj;
    2566      PyObject *order;
    2567      PyObject *ret = NULL;
    2568      Py_buffer view, *base;
    2569      char ord;
    2570  
    2571      if (!PyArg_ParseTuple(args, "OO", &obj, &order)) {
    2572          return NULL;
    2573      }
    2574  
    2575      ord = get_ascii_order(order);
    2576      if (ord == CHAR_MAX) {
    2577          return NULL;
    2578      }
    2579  
    2580      if (NDArray_Check(obj)) {
    2581          /* Skip the buffer protocol to check simple etc. buffers directly. */
    2582          base = &((NDArrayObject *)obj)->head->base;
    2583          ret = PyBuffer_IsContiguous(base, ord) ? Py_True : Py_False;
    2584      }
    2585      else {
    2586          if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) {
    2587              PyErr_SetString(PyExc_TypeError,
    2588                  "is_contiguous: object does not implement the buffer "
    2589                  "protocol");
    2590              return NULL;
    2591          }
    2592          ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
    2593          PyBuffer_Release(&view);
    2594      }
    2595  
    2596      return Py_NewRef(ret);
    2597  }
    2598  
    2599  static Py_hash_t
    2600  ndarray_hash(PyObject *self)
    2601  {
    2602      const NDArrayObject *nd = (NDArrayObject *)self;
    2603      const Py_buffer *view = &nd->head->base;
    2604      PyObject *bytes;
    2605      Py_hash_t hash;
    2606  
    2607      if (!view->readonly) {
    2608           PyErr_SetString(PyExc_ValueError,
    2609               "cannot hash writable ndarray object");
    2610           return -1;
    2611      }
    2612      if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
    2613           return -1;
    2614      }
    2615  
    2616      bytes = ndarray_tobytes(self, NULL);
    2617      if (bytes == NULL) {
    2618          return -1;
    2619      }
    2620  
    2621      hash = PyObject_Hash(bytes);
    2622      Py_DECREF(bytes);
    2623      return hash;
    2624  }
    2625  
    2626  
    2627  static PyMethodDef ndarray_methods [] =
    2628  {
    2629      { "tolist", ndarray_tolist, METH_NOARGS, NULL },
    2630      { "tobytes", ndarray_tobytes, METH_NOARGS, NULL },
    2631      { "push", _PyCFunction_CAST(ndarray_push), METH_VARARGS|METH_KEYWORDS, NULL },
    2632      { "pop", ndarray_pop, METH_NOARGS, NULL },
    2633      { "add_suboffsets", ndarray_add_suboffsets, METH_NOARGS, NULL },
    2634      { "memoryview_from_buffer", ndarray_memoryview_from_buffer, METH_NOARGS, NULL },
    2635      {NULL}
    2636  };
    2637  
    2638  static PyTypeObject NDArray_Type = {
    2639      PyVarObject_HEAD_INIT(NULL, 0)
    2640      "ndarray",                   /* Name of this type */
    2641      sizeof(NDArrayObject),       /* Basic object size */
    2642      0,                           /* Item size for varobject */
    2643      (destructor)ndarray_dealloc, /* tp_dealloc */
    2644      0,                           /* tp_vectorcall_offset */
    2645      0,                           /* tp_getattr */
    2646      0,                           /* tp_setattr */
    2647      0,                           /* tp_as_async */
    2648      0,                           /* tp_repr */
    2649      0,                           /* tp_as_number */
    2650      &ndarray_as_sequence,        /* tp_as_sequence */
    2651      &ndarray_as_mapping,         /* tp_as_mapping */
    2652      (hashfunc)ndarray_hash,      /* tp_hash */
    2653      0,                           /* tp_call */
    2654      0,                           /* tp_str */
    2655      PyObject_GenericGetAttr,     /* tp_getattro */
    2656      0,                           /* tp_setattro */
    2657      &ndarray_as_buffer,          /* tp_as_buffer */
    2658      Py_TPFLAGS_DEFAULT,          /* tp_flags */
    2659      0,                           /* tp_doc */
    2660      0,                           /* tp_traverse */
    2661      0,                           /* tp_clear */
    2662      0,                           /* tp_richcompare */
    2663      0,                           /* tp_weaklistoffset */
    2664      0,                           /* tp_iter */
    2665      0,                           /* tp_iternext */
    2666      ndarray_methods,             /* tp_methods */
    2667      0,                           /* tp_members */
    2668      ndarray_getset,              /* tp_getset */
    2669      0,                           /* tp_base */
    2670      0,                           /* tp_dict */
    2671      0,                           /* tp_descr_get */
    2672      0,                           /* tp_descr_set */
    2673      0,                           /* tp_dictoffset */
    2674      ndarray_init,                /* tp_init */
    2675      0,                           /* tp_alloc */
    2676      ndarray_new,                 /* tp_new */
    2677  };
    2678  
    2679  /**************************************************************************/
    2680  /*                          StaticArray Object                            */
    2681  /**************************************************************************/
    2682  
    2683  static PyTypeObject StaticArray_Type;
    2684  
    2685  typedef struct {
    2686      PyObject_HEAD
    2687      int legacy_mode; /* if true, use the view.obj==NULL hack */
    2688  } StaticArrayObject;
    2689  
    2690  static char static_mem[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
    2691  static Py_ssize_t static_shape[1] = {12};
    2692  static Py_ssize_t static_strides[1] = {1};
    2693  static Py_buffer static_buffer = {
    2694      static_mem,     /* buf */
    2695      NULL,           /* obj */
    2696      12,             /* len */
    2697      1,              /* itemsize */
    2698      1,              /* readonly */
    2699      1,              /* ndim */
    2700      "B",            /* format */
    2701      static_shape,   /* shape */
    2702      static_strides, /* strides */
    2703      NULL,           /* suboffsets */
    2704      NULL            /* internal */
    2705  };
    2706  
    2707  static PyObject *
    2708  staticarray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
    2709  {
    2710      return (PyObject *)PyObject_New(StaticArrayObject, &StaticArray_Type);
    2711  }
    2712  
    2713  static int
    2714  staticarray_init(PyObject *self, PyObject *args, PyObject *kwds)
    2715  {
    2716      StaticArrayObject *a = (StaticArrayObject *)self;
    2717      static char *kwlist[] = {
    2718          "legacy_mode", NULL
    2719      };
    2720      PyObject *legacy_mode = Py_False;
    2721  
    2722      if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &legacy_mode))
    2723          return -1;
    2724  
    2725      a->legacy_mode = (legacy_mode != Py_False);
    2726      return 0;
    2727  }
    2728  
    2729  static void
    2730  staticarray_dealloc(StaticArrayObject *self)
    2731  {
    2732      PyObject_Free(self);
    2733  }
    2734  
    2735  /* Return a buffer for a PyBUF_FULL_RO request. Flags are not checked,
    2736     which makes this object a non-compliant exporter! */
    2737  static int
    2738  staticarray_getbuf(StaticArrayObject *self, Py_buffer *view, int flags)
    2739  {
    2740      *view = static_buffer;
    2741  
    2742      if (self->legacy_mode) {
    2743          view->obj = NULL; /* Don't use this in new code. */
    2744      }
    2745      else {
    2746          view->obj = Py_NewRef(self);
    2747      }
    2748  
    2749      return 0;
    2750  }
    2751  
    2752  static PyBufferProcs staticarray_as_buffer = {
    2753      (getbufferproc)staticarray_getbuf, /* bf_getbuffer */
    2754      NULL,                              /* bf_releasebuffer */
    2755  };
    2756  
    2757  static PyTypeObject StaticArray_Type = {
    2758      PyVarObject_HEAD_INIT(NULL, 0)
    2759      "staticarray",                   /* Name of this type */
    2760      sizeof(StaticArrayObject),       /* Basic object size */
    2761      0,                               /* Item size for varobject */
    2762      (destructor)staticarray_dealloc, /* tp_dealloc */
    2763      0,                               /* tp_vectorcall_offset */
    2764      0,                               /* tp_getattr */
    2765      0,                               /* tp_setattr */
    2766      0,                               /* tp_as_async */
    2767      0,                               /* tp_repr */
    2768      0,                               /* tp_as_number */
    2769      0,                               /* tp_as_sequence */
    2770      0,                               /* tp_as_mapping */
    2771      0,                               /* tp_hash */
    2772      0,                               /* tp_call */
    2773      0,                               /* tp_str */
    2774      0,                               /* tp_getattro */
    2775      0,                               /* tp_setattro */
    2776      &staticarray_as_buffer,          /* tp_as_buffer */
    2777      Py_TPFLAGS_DEFAULT,              /* tp_flags */
    2778      0,                               /* tp_doc */
    2779      0,                               /* tp_traverse */
    2780      0,                               /* tp_clear */
    2781      0,                               /* tp_richcompare */
    2782      0,                               /* tp_weaklistoffset */
    2783      0,                               /* tp_iter */
    2784      0,                               /* tp_iternext */
    2785      0,                               /* tp_methods */
    2786      0,                               /* tp_members */
    2787      0,                               /* tp_getset */
    2788      0,                               /* tp_base */
    2789      0,                               /* tp_dict */
    2790      0,                               /* tp_descr_get */
    2791      0,                               /* tp_descr_set */
    2792      0,                               /* tp_dictoffset */
    2793      staticarray_init,                /* tp_init */
    2794      0,                               /* tp_alloc */
    2795      staticarray_new,                 /* tp_new */
    2796  };
    2797  
    2798  
    2799  static struct PyMethodDef _testbuffer_functions[] = {
    2800      {"slice_indices", slice_indices, METH_VARARGS, NULL},
    2801      {"get_pointer", get_pointer, METH_VARARGS, NULL},
    2802      {"get_sizeof_void_p", get_sizeof_void_p, METH_NOARGS, NULL},
    2803      {"get_contiguous", get_contiguous, METH_VARARGS, NULL},
    2804      {"py_buffer_to_contiguous", py_buffer_to_contiguous, METH_VARARGS, NULL},
    2805      {"is_contiguous", is_contiguous, METH_VARARGS, NULL},
    2806      {"cmp_contig", cmp_contig, METH_VARARGS, NULL},
    2807      {NULL, NULL}
    2808  };
    2809  
    2810  static struct PyModuleDef _testbuffermodule = {
    2811      PyModuleDef_HEAD_INIT,
    2812      "_testbuffer",
    2813      NULL,
    2814      -1,
    2815      _testbuffer_functions,
    2816      NULL,
    2817      NULL,
    2818      NULL,
    2819      NULL
    2820  };
    2821  
    2822  
    2823  PyMODINIT_FUNC
    2824  PyInit__testbuffer(void)
    2825  {
    2826      PyObject *m;
    2827  
    2828      m = PyModule_Create(&_testbuffermodule);
    2829      if (m == NULL)
    2830          return NULL;
    2831  
    2832      Py_SET_TYPE(&NDArray_Type, &PyType_Type);
    2833      Py_INCREF(&NDArray_Type);
    2834      PyModule_AddObject(m, "ndarray", (PyObject *)&NDArray_Type);
    2835  
    2836      Py_SET_TYPE(&StaticArray_Type, &PyType_Type);
    2837      Py_INCREF(&StaticArray_Type);
    2838      PyModule_AddObject(m, "staticarray", (PyObject *)&StaticArray_Type);
    2839  
    2840      structmodule = PyImport_ImportModule("struct");
    2841      if (structmodule == NULL)
    2842          return NULL;
    2843  
    2844      Struct = PyObject_GetAttrString(structmodule, "Struct");
    2845      calcsize = PyObject_GetAttrString(structmodule, "calcsize");
    2846      if (Struct == NULL || calcsize == NULL)
    2847          return NULL;
    2848  
    2849      simple_format = PyUnicode_FromString(simple_fmt);
    2850      if (simple_format == NULL)
    2851          return NULL;
    2852  
    2853      PyModule_AddIntMacro(m, ND_MAX_NDIM);
    2854      PyModule_AddIntMacro(m, ND_VAREXPORT);
    2855      PyModule_AddIntMacro(m, ND_WRITABLE);
    2856      PyModule_AddIntMacro(m, ND_FORTRAN);
    2857      PyModule_AddIntMacro(m, ND_SCALAR);
    2858      PyModule_AddIntMacro(m, ND_PIL);
    2859      PyModule_AddIntMacro(m, ND_GETBUF_FAIL);
    2860      PyModule_AddIntMacro(m, ND_GETBUF_UNDEFINED);
    2861      PyModule_AddIntMacro(m, ND_REDIRECT);
    2862  
    2863      PyModule_AddIntMacro(m, PyBUF_SIMPLE);
    2864      PyModule_AddIntMacro(m, PyBUF_WRITABLE);
    2865      PyModule_AddIntMacro(m, PyBUF_FORMAT);
    2866      PyModule_AddIntMacro(m, PyBUF_ND);
    2867      PyModule_AddIntMacro(m, PyBUF_STRIDES);
    2868      PyModule_AddIntMacro(m, PyBUF_INDIRECT);
    2869      PyModule_AddIntMacro(m, PyBUF_C_CONTIGUOUS);
    2870      PyModule_AddIntMacro(m, PyBUF_F_CONTIGUOUS);
    2871      PyModule_AddIntMacro(m, PyBUF_ANY_CONTIGUOUS);
    2872      PyModule_AddIntMacro(m, PyBUF_FULL);
    2873      PyModule_AddIntMacro(m, PyBUF_FULL_RO);
    2874      PyModule_AddIntMacro(m, PyBUF_RECORDS);
    2875      PyModule_AddIntMacro(m, PyBUF_RECORDS_RO);
    2876      PyModule_AddIntMacro(m, PyBUF_STRIDED);
    2877      PyModule_AddIntMacro(m, PyBUF_STRIDED_RO);
    2878      PyModule_AddIntMacro(m, PyBUF_CONTIG);
    2879      PyModule_AddIntMacro(m, PyBUF_CONTIG_RO);
    2880  
    2881      PyModule_AddIntMacro(m, PyBUF_READ);
    2882      PyModule_AddIntMacro(m, PyBUF_WRITE);
    2883  
    2884      return m;
    2885  }
    2886  
    2887  
    2888