ยปCore Development>Code coverage>Modules/_testbuffer.c

Python code coverage for Modules/_testbuffer.c

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