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