blob: d1bf2585fb1dd7c22ed7be1b3abad2dd1e0b7b17 [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 */
Stefan Krah4e99a312012-03-05 09:30:47 +010056#define ND_REDIRECT 0x040 /* redirect buffer requests */
Stefan Krah9a2d99e2012-02-25 12:24:21 +010057
58/* Default: NumPy style (strides), read-only, no var-export, C-style layout */
59#define ND_DEFAULT 0x0
60
61/* Internal flags for the base buffer */
Stefan Krah4e99a312012-03-05 09:30:47 +010062#define ND_C 0x080 /* C contiguous layout (default) */
63#define ND_OWN_ARRAYS 0x100 /* consumer owns arrays */
Stefan Krah9a2d99e2012-02-25 12:24:21 +010064
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" */
Stefan Krah4e99a312012-03-05 09:30:47 +01001293 int flags = ND_DEFAULT; /* base buffer and ndarray flags */
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001294
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 ||
Stefan Krah4e99a312012-03-05 09:30:47 +01001305 !(flags == ND_DEFAULT || flags == ND_REDIRECT)) {
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001306 PyErr_SetString(PyExc_TypeError,
Stefan Krah4e99a312012-03-05 09:30:47 +01001307 "construction from exporter object only takes 'obj', 'getbuf' "
1308 "and 'flags' arguments");
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001309 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);
Stefan Krah4e99a312012-03-05 09:30:47 +01001318 nd->head->flags |= flags;
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001319
1320 return 0;
1321 }
1322
1323 /* NDArrayObject is the original base object. */
1324 if (getbuf != PyBUF_UNUSED) {
1325 PyErr_SetString(PyExc_TypeError,
1326 "getbuf argument only valid for construction from exporter "
1327 "object");
1328 return -1;
1329 }
1330 if (shape == NULL) {
1331 PyErr_SetString(PyExc_TypeError,
1332 "shape is a required argument when constructing from "
1333 "list, tuple or scalar");
1334 return -1;
1335 }
1336
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001337 if (flags & ND_VAREXPORT) {
1338 nd->flags |= ND_VAREXPORT;
1339 flags &= ~ND_VAREXPORT;
1340 }
1341
1342 /* Initialize and push the first base buffer onto the linked list. */
1343 return ndarray_push_base(nd, v, shape, strides, offset, format, flags);
1344}
1345
1346/* Push an additional base onto the linked list. */
1347static PyObject *
1348ndarray_push(PyObject *self, PyObject *args, PyObject *kwds)
1349{
1350 NDArrayObject *nd = (NDArrayObject *)self;
1351 static char *kwlist[] = {
1352 "items", "shape", "strides", "offset", "format", "flags", NULL
1353 };
1354 PyObject *items = NULL; /* initializer: scalar, list or tuple */
1355 PyObject *shape = NULL; /* size of each dimension */
1356 PyObject *strides = NULL; /* number of bytes to the next elt in each dim */
1357 PyObject *format = simple_format; /* struct module specifier: "B" */
1358 Py_ssize_t offset = 0; /* buffer offset */
Stefan Krah4e99a312012-03-05 09:30:47 +01001359 int flags = ND_DEFAULT; /* base buffer flags */
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001360
1361 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OnOi", kwlist,
1362 &items, &shape, &strides, &offset, &format, &flags))
1363 return NULL;
1364
1365 if (flags & ND_VAREXPORT) {
1366 PyErr_SetString(PyExc_ValueError,
1367 "ND_VAREXPORT flag can only be used during object creation");
1368 return NULL;
1369 }
1370 if (ND_IS_CONSUMER(nd)) {
1371 PyErr_SetString(PyExc_BufferError,
1372 "structure of re-exporting object is immutable");
1373 return NULL;
1374 }
1375 if (!(nd->flags&ND_VAREXPORT) && nd->head->exports > 0) {
1376 PyErr_Format(PyExc_BufferError,
1377 "cannot change structure: %zd exported buffer%s",
1378 nd->head->exports, nd->head->exports==1 ? "" : "s");
1379 return NULL;
1380 }
1381
1382 if (ndarray_push_base(nd, items, shape, strides,
1383 offset, format, flags) < 0)
1384 return NULL;
1385 Py_RETURN_NONE;
1386}
1387
1388/* Pop a base from the linked list (if possible). */
1389static PyObject *
1390ndarray_pop(PyObject *self, PyObject *dummy)
1391{
1392 NDArrayObject *nd = (NDArrayObject *)self;
1393 if (ND_IS_CONSUMER(nd)) {
1394 PyErr_SetString(PyExc_BufferError,
1395 "structure of re-exporting object is immutable");
1396 return NULL;
1397 }
1398 if (nd->head->exports > 0) {
1399 PyErr_Format(PyExc_BufferError,
1400 "cannot change structure: %zd exported buffer%s",
1401 nd->head->exports, nd->head->exports==1 ? "" : "s");
1402 return NULL;
1403 }
1404 if (nd->head->next == NULL) {
1405 PyErr_SetString(PyExc_BufferError,
1406 "list only has a single base");
1407 return NULL;
1408 }
1409
1410 ndbuf_pop(nd);
1411 Py_RETURN_NONE;
1412}
1413
1414/**************************************************************************/
1415/* getbuffer */
1416/**************************************************************************/
1417
1418static int
1419ndarray_getbuf(NDArrayObject *self, Py_buffer *view, int flags)
1420{
1421 ndbuf_t *ndbuf = self->head;
1422 Py_buffer *base = &ndbuf->base;
1423 int baseflags = ndbuf->flags;
1424
Stefan Krah4e99a312012-03-05 09:30:47 +01001425 /* redirect mode */
1426 if (base->obj != NULL && (baseflags&ND_REDIRECT)) {
1427 return PyObject_GetBuffer(base->obj, view, flags);
1428 }
1429
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001430 /* start with complete information */
1431 *view = *base;
1432 view->obj = NULL;
1433
1434 /* reconstruct format */
1435 if (view->format == NULL)
1436 view->format = "B";
1437
1438 if (base->ndim != 0 &&
1439 ((REQ_SHAPE(flags) && base->shape == NULL) ||
1440 (REQ_STRIDES(flags) && base->strides == NULL))) {
1441 /* The ndarray is a re-exporter that has been created without full
1442 information for testing purposes. In this particular case the
1443 ndarray is not a PEP-3118 compliant buffer provider. */
1444 PyErr_SetString(PyExc_BufferError,
1445 "re-exporter does not provide format, shape or strides");
1446 return -1;
1447 }
1448
1449 if (baseflags & ND_GETBUF_FAIL) {
1450 PyErr_SetString(PyExc_BufferError,
1451 "ND_GETBUF_FAIL: forced test exception");
1452 return -1;
1453 }
1454
1455 if (REQ_WRITABLE(flags) && base->readonly) {
1456 PyErr_SetString(PyExc_BufferError,
1457 "ndarray is not writable");
1458 return -1;
1459 }
1460 if (!REQ_FORMAT(flags)) {
1461 /* NULL indicates that the buffer's data type has been cast to 'B'.
1462 view->itemsize is the _previous_ itemsize. If shape is present,
1463 the equality product(shape) * itemsize = len still holds at this
1464 point. The equality calcsize(format) = itemsize does _not_ hold
1465 from here on! */
1466 view->format = NULL;
1467 }
1468
1469 if (REQ_C_CONTIGUOUS(flags) && !ND_C_CONTIGUOUS(baseflags)) {
1470 PyErr_SetString(PyExc_BufferError,
1471 "ndarray is not C-contiguous");
1472 return -1;
1473 }
1474 if (REQ_F_CONTIGUOUS(flags) && !ND_FORTRAN_CONTIGUOUS(baseflags)) {
1475 PyErr_SetString(PyExc_BufferError,
1476 "ndarray is not Fortran contiguous");
1477 return -1;
1478 }
1479 if (REQ_ANY_CONTIGUOUS(flags) && !ND_ANY_CONTIGUOUS(baseflags)) {
1480 PyErr_SetString(PyExc_BufferError,
1481 "ndarray is not contiguous");
1482 return -1;
1483 }
1484 if (!REQ_INDIRECT(flags) && (baseflags & ND_PIL)) {
1485 PyErr_SetString(PyExc_BufferError,
1486 "ndarray cannot be represented without suboffsets");
1487 return -1;
1488 }
1489 if (!REQ_STRIDES(flags)) {
1490 if (!ND_C_CONTIGUOUS(baseflags)) {
1491 PyErr_SetString(PyExc_BufferError,
1492 "ndarray is not C-contiguous");
1493 return -1;
1494 }
1495 view->strides = NULL;
1496 }
1497 if (!REQ_SHAPE(flags)) {
1498 /* PyBUF_SIMPLE or PyBUF_WRITABLE: at this point buf is C-contiguous,
1499 so base->buf = ndbuf->data. */
1500 if (view->format != NULL) {
1501 /* PyBUF_SIMPLE|PyBUF_FORMAT and PyBUF_WRITABLE|PyBUF_FORMAT do
1502 not make sense. */
1503 PyErr_Format(PyExc_BufferError,
1504 "ndarray: cannot cast to unsigned bytes if the format flag "
1505 "is present");
1506 return -1;
1507 }
1508 /* product(shape) * itemsize = len and calcsize(format) = itemsize
1509 do _not_ hold from here on! */
1510 view->ndim = 1;
1511 view->shape = NULL;
1512 }
1513
1514 view->obj = (PyObject *)self;
1515 Py_INCREF(view->obj);
1516 self->head->exports++;
1517
1518 return 0;
1519}
1520
1521static int
1522ndarray_releasebuf(NDArrayObject *self, Py_buffer *view)
1523{
1524 if (!ND_IS_CONSUMER(self)) {
1525 ndbuf_t *ndbuf = view->internal;
1526 if (--ndbuf->exports == 0 && ndbuf != self->head)
1527 ndbuf_delete(self, ndbuf);
1528 }
1529
1530 return 0;
1531}
1532
1533static PyBufferProcs ndarray_as_buffer = {
1534 (getbufferproc)ndarray_getbuf, /* bf_getbuffer */
1535 (releasebufferproc)ndarray_releasebuf /* bf_releasebuffer */
1536};
1537
1538
1539/**************************************************************************/
1540/* indexing/slicing */
1541/**************************************************************************/
1542
1543static char *
1544ptr_from_index(Py_buffer *base, Py_ssize_t index)
1545{
1546 char *ptr;
1547 Py_ssize_t nitems; /* items in the first dimension */
1548
1549 if (base->shape)
1550 nitems = base->shape[0];
1551 else {
1552 assert(base->ndim == 1 && SIMPLE_FORMAT(base->format));
1553 nitems = base->len;
1554 }
1555
1556 if (index < 0) {
1557 index += nitems;
1558 }
1559 if (index < 0 || index >= nitems) {
1560 PyErr_SetString(PyExc_IndexError, "index out of bounds");
1561 return NULL;
1562 }
1563
1564 ptr = (char *)base->buf;
1565
1566 if (base->strides == NULL)
1567 ptr += base->itemsize * index;
1568 else
1569 ptr += base->strides[0] * index;
1570
1571 ptr = ADJUST_PTR(ptr, base->suboffsets);
1572
1573 return ptr;
1574}
1575
1576static PyObject *
1577ndarray_item(NDArrayObject *self, Py_ssize_t index)
1578{
1579 ndbuf_t *ndbuf = self->head;
1580 Py_buffer *base = &ndbuf->base;
1581 char *ptr;
1582
1583 if (base->ndim == 0) {
1584 PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
1585 return NULL;
1586 }
1587
1588 ptr = ptr_from_index(base, index);
1589 if (ptr == NULL)
1590 return NULL;
1591
1592 if (base->ndim == 1) {
1593 return unpack_single(ptr, base->format, base->itemsize);
1594 }
1595 else {
1596 NDArrayObject *nd;
1597 Py_buffer *subview;
1598
1599 nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
1600 if (nd == NULL)
1601 return NULL;
1602
1603 if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
1604 Py_DECREF(nd);
1605 return NULL;
1606 }
1607
1608 subview = &nd->staticbuf.base;
1609
1610 subview->buf = ptr;
1611 subview->len /= subview->shape[0];
1612
1613 subview->ndim--;
1614 subview->shape++;
1615 if (subview->strides) subview->strides++;
1616 if (subview->suboffsets) subview->suboffsets++;
1617
1618 init_flags(&nd->staticbuf);
1619
1620 return (PyObject *)nd;
1621 }
1622}
1623
1624/*
1625 For each dimension, we get valid (start, stop, step, slicelength) quadruples
1626 from PySlice_GetIndicesEx().
1627
1628 Slicing NumPy arrays
1629 ====================
1630
1631 A pointer to an element in a NumPy array is defined by:
1632
1633 ptr = (char *)buf + indices[0] * strides[0] +
1634 ... +
1635 indices[ndim-1] * strides[ndim-1]
1636
1637 Adjust buf:
1638 -----------
1639 Adding start[n] for each dimension effectively adds the constant:
1640
1641 c = start[0] * strides[0] + ... + start[ndim-1] * strides[ndim-1]
1642
1643 Therefore init_slice() adds all start[n] directly to buf.
1644
1645 Adjust shape:
1646 -------------
1647 Obviously shape[n] = slicelength[n]
1648
1649 Adjust strides:
1650 ---------------
1651 In the original array, the next element in a dimension is reached
1652 by adding strides[n] to the pointer. In the sliced array, elements
1653 may be skipped, so the next element is reached by adding:
1654
1655 strides[n] * step[n]
1656
1657 Slicing PIL arrays
1658 ==================
1659
1660 Layout:
1661 -------
1662 In the first (zeroth) dimension, PIL arrays have an array of pointers
1663 to sub-arrays of ndim-1. Striding in the first dimension is done by
1664 getting the index of the nth pointer, dereference it and then add a
1665 suboffset to it. The arrays pointed to can best be seen a regular
1666 NumPy arrays.
1667
1668 Adjust buf:
1669 -----------
1670 In the original array, buf points to a location (usually the start)
1671 in the array of pointers. For the sliced array, start[0] can be
1672 added to buf in the same manner as for NumPy arrays.
1673
1674 Adjust suboffsets:
1675 ------------------
1676 Due to the dereferencing step in the addressing scheme, it is not
1677 possible to adjust buf for higher dimensions. Recall that the
1678 sub-arrays pointed to are regular NumPy arrays, so for each of
1679 those arrays adding start[n] effectively adds the constant:
1680
1681 c = start[1] * strides[1] + ... + start[ndim-1] * strides[ndim-1]
1682
1683 This constant is added to suboffsets[0]. suboffsets[0] in turn is
1684 added to each pointer right after dereferencing.
1685
1686 Adjust shape and strides:
1687 -------------------------
1688 Shape and strides are not influenced by the dereferencing step, so
1689 they are adjusted in the same manner as for NumPy arrays.
1690
1691 Multiple levels of suboffsets
1692 =============================
1693
1694 For a construct like an array of pointers to array of pointers to
1695 sub-arrays of ndim-2:
1696
1697 suboffsets[0] = start[1] * strides[1]
1698 suboffsets[1] = start[2] * strides[2] + ...
1699*/
1700static int
1701init_slice(Py_buffer *base, PyObject *key, int dim)
1702{
1703 Py_ssize_t start, stop, step, slicelength;
1704
1705 if (PySlice_GetIndicesEx(key, base->shape[dim],
1706 &start, &stop, &step, &slicelength) < 0) {
1707 return -1;
1708 }
1709
1710
1711 if (base->suboffsets == NULL || dim == 0) {
1712 adjust_buf:
1713 base->buf = (char *)base->buf + base->strides[dim] * start;
1714 }
1715 else {
1716 Py_ssize_t n = dim-1;
1717 while (n >= 0 && base->suboffsets[n] < 0)
1718 n--;
1719 if (n < 0)
1720 goto adjust_buf; /* all suboffsets are negative */
1721 base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
1722 }
1723 base->shape[dim] = slicelength;
1724 base->strides[dim] = base->strides[dim] * step;
1725
1726 return 0;
1727}
1728
1729static int
1730copy_structure(Py_buffer *base)
1731{
1732 Py_ssize_t *shape = NULL, *strides = NULL, *suboffsets = NULL;
1733 Py_ssize_t i;
1734
1735 shape = PyMem_Malloc(base->ndim * (sizeof *shape));
1736 strides = PyMem_Malloc(base->ndim * (sizeof *strides));
1737 if (shape == NULL || strides == NULL)
1738 goto err_nomem;
1739
1740 suboffsets = NULL;
1741 if (base->suboffsets) {
1742 suboffsets = PyMem_Malloc(base->ndim * (sizeof *suboffsets));
1743 if (suboffsets == NULL)
1744 goto err_nomem;
1745 }
1746
1747 for (i = 0; i < base->ndim; i++) {
1748 shape[i] = base->shape[i];
1749 strides[i] = base->strides[i];
1750 if (suboffsets)
1751 suboffsets[i] = base->suboffsets[i];
1752 }
1753
1754 base->shape = shape;
1755 base->strides = strides;
1756 base->suboffsets = suboffsets;
1757
1758 return 0;
1759
1760err_nomem:
1761 PyErr_NoMemory();
1762 PyMem_XFree(shape);
1763 PyMem_XFree(strides);
1764 PyMem_XFree(suboffsets);
1765 return -1;
1766}
1767
1768static PyObject *
1769ndarray_subscript(NDArrayObject *self, PyObject *key)
1770{
1771 NDArrayObject *nd;
1772 ndbuf_t *ndbuf;
1773 Py_buffer *base = &self->head->base;
1774
1775 if (base->ndim == 0) {
1776 if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
1777 return unpack_single(base->buf, base->format, base->itemsize);
1778 }
1779 else if (key == Py_Ellipsis) {
1780 Py_INCREF(self);
1781 return (PyObject *)self;
1782 }
1783 else {
1784 PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
1785 return NULL;
1786 }
1787 }
1788 if (PyIndex_Check(key)) {
1789 Py_ssize_t index = PyLong_AsSsize_t(key);
1790 if (index == -1 && PyErr_Occurred())
1791 return NULL;
1792 return ndarray_item(self, index);
1793 }
1794
1795 nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
1796 if (nd == NULL)
1797 return NULL;
1798
1799 /* new ndarray is a consumer */
1800 if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
1801 Py_DECREF(nd);
1802 return NULL;
1803 }
1804
1805 /* copy shape, strides and suboffsets */
1806 ndbuf = nd->head;
1807 base = &ndbuf->base;
1808 if (copy_structure(base) < 0) {
1809 Py_DECREF(nd);
1810 return NULL;
1811 }
1812 ndbuf->flags |= ND_OWN_ARRAYS;
1813
1814 if (PySlice_Check(key)) {
1815 /* one-dimensional slice */
1816 if (init_slice(base, key, 0) < 0)
1817 goto err_occurred;
1818 }
1819 else if PyTuple_Check(key) {
1820 /* multi-dimensional slice */
1821 PyObject *tuple = key;
1822 Py_ssize_t i, n;
1823
1824 n = PyTuple_GET_SIZE(tuple);
1825 for (i = 0; i < n; i++) {
1826 key = PyTuple_GET_ITEM(tuple, i);
1827 if (!PySlice_Check(key))
1828 goto type_error;
1829 if (init_slice(base, key, (int)i) < 0)
1830 goto err_occurred;
1831 }
1832 }
1833 else {
1834 goto type_error;
1835 }
1836
1837 init_len(base);
1838 init_flags(ndbuf);
1839
1840 return (PyObject *)nd;
1841
1842
1843type_error:
1844 PyErr_Format(PyExc_TypeError,
1845 "cannot index memory using \"%.200s\"",
1846 key->ob_type->tp_name);
1847err_occurred:
1848 Py_DECREF(nd);
1849 return NULL;
1850}
1851
1852
1853static int
1854ndarray_ass_subscript(NDArrayObject *self, PyObject *key, PyObject *value)
1855{
1856 NDArrayObject *nd;
1857 Py_buffer *dest = &self->head->base;
1858 Py_buffer src;
1859 char *ptr;
1860 Py_ssize_t index;
1861 int ret = -1;
1862
1863 if (dest->readonly) {
1864 PyErr_SetString(PyExc_TypeError, "ndarray is not writable");
1865 return -1;
1866 }
1867 if (value == NULL) {
1868 PyErr_SetString(PyExc_TypeError, "ndarray data cannot be deleted");
1869 return -1;
1870 }
1871 if (dest->ndim == 0) {
1872 if (key == Py_Ellipsis ||
1873 (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
1874 ptr = (char *)dest->buf;
1875 return pack_single(ptr, value, dest->format, dest->itemsize);
1876 }
1877 else {
1878 PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
1879 return -1;
1880 }
1881 }
1882 if (dest->ndim == 1 && PyIndex_Check(key)) {
1883 /* rvalue must be a single item */
1884 index = PyLong_AsSsize_t(key);
1885 if (index == -1 && PyErr_Occurred())
1886 return -1;
1887 else {
1888 ptr = ptr_from_index(dest, index);
1889 if (ptr == NULL)
1890 return -1;
1891 }
1892 return pack_single(ptr, value, dest->format, dest->itemsize);
1893 }
1894
1895 /* rvalue must be an exporter */
1896 if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) == -1)
1897 return -1;
1898
1899 nd = (NDArrayObject *)ndarray_subscript(self, key);
1900 if (nd != NULL) {
1901 dest = &nd->head->base;
1902 ret = copy_buffer(dest, &src);
1903 Py_DECREF(nd);
1904 }
1905
1906 PyBuffer_Release(&src);
1907 return ret;
1908}
1909
1910static PyObject *
1911slice_indices(PyObject *self, PyObject *args)
1912{
1913 PyObject *ret, *key, *tmp;
1914 Py_ssize_t s[4]; /* start, stop, step, slicelength */
1915 Py_ssize_t i, len;
1916
1917 if (!PyArg_ParseTuple(args, "On", &key, &len)) {
1918 return NULL;
1919 }
1920 if (!PySlice_Check(key)) {
1921 PyErr_SetString(PyExc_TypeError,
1922 "first argument must be a slice object");
1923 return NULL;
1924 }
1925 if (PySlice_GetIndicesEx(key, len, &s[0], &s[1], &s[2], &s[3]) < 0) {
1926 return NULL;
1927 }
1928
1929 ret = PyTuple_New(4);
1930 if (ret == NULL)
1931 return NULL;
1932
1933 for (i = 0; i < 4; i++) {
1934 tmp = PyLong_FromSsize_t(s[i]);
1935 if (tmp == NULL)
1936 goto error;
1937 PyTuple_SET_ITEM(ret, i, tmp);
1938 }
1939
1940 return ret;
1941
1942error:
1943 Py_DECREF(ret);
1944 return NULL;
1945}
1946
1947
1948static PyMappingMethods ndarray_as_mapping = {
1949 NULL, /* mp_length */
1950 (binaryfunc)ndarray_subscript, /* mp_subscript */
1951 (objobjargproc)ndarray_ass_subscript /* mp_ass_subscript */
1952};
1953
1954static PySequenceMethods ndarray_as_sequence = {
1955 0, /* sq_length */
1956 0, /* sq_concat */
1957 0, /* sq_repeat */
1958 (ssizeargfunc)ndarray_item, /* sq_item */
1959};
1960
1961
1962/**************************************************************************/
1963/* getters */
1964/**************************************************************************/
1965
1966static PyObject *
1967ssize_array_as_tuple(Py_ssize_t *array, Py_ssize_t len)
1968{
1969 PyObject *tuple, *x;
1970 Py_ssize_t i;
1971
1972 if (array == NULL)
1973 return PyTuple_New(0);
1974
1975 tuple = PyTuple_New(len);
1976 if (tuple == NULL)
1977 return NULL;
1978
1979 for (i = 0; i < len; i++) {
1980 x = PyLong_FromSsize_t(array[i]);
1981 if (x == NULL) {
1982 Py_DECREF(tuple);
1983 return NULL;
1984 }
1985 PyTuple_SET_ITEM(tuple, i, x);
1986 }
1987
1988 return tuple;
1989}
1990
1991static PyObject *
1992ndarray_get_flags(NDArrayObject *self, void *closure)
1993{
1994 return PyLong_FromLong(self->head->flags);
1995}
1996
1997static PyObject *
1998ndarray_get_offset(NDArrayObject *self, void *closure)
1999{
2000 ndbuf_t *ndbuf = self->head;
2001 return PyLong_FromSsize_t(ndbuf->offset);
2002}
2003
2004static PyObject *
2005ndarray_get_obj(NDArrayObject *self, void *closure)
2006{
2007 Py_buffer *base = &self->head->base;
2008
2009 if (base->obj == NULL) {
2010 Py_RETURN_NONE;
2011 }
2012 Py_INCREF(base->obj);
2013 return base->obj;
2014}
2015
2016static PyObject *
2017ndarray_get_nbytes(NDArrayObject *self, void *closure)
2018{
2019 Py_buffer *base = &self->head->base;
2020 return PyLong_FromSsize_t(base->len);
2021}
2022
2023static PyObject *
2024ndarray_get_readonly(NDArrayObject *self, void *closure)
2025{
2026 Py_buffer *base = &self->head->base;
2027 return PyLong_FromLong(base->readonly);
2028}
2029
2030static PyObject *
2031ndarray_get_itemsize(NDArrayObject *self, void *closure)
2032{
2033 Py_buffer *base = &self->head->base;
2034 return PyLong_FromSsize_t(base->itemsize);
2035}
2036
2037static PyObject *
2038ndarray_get_format(NDArrayObject *self, void *closure)
2039{
2040 Py_buffer *base = &self->head->base;
2041 char *fmt = base->format ? base->format : "";
2042 return PyUnicode_FromString(fmt);
2043}
2044
2045static PyObject *
2046ndarray_get_ndim(NDArrayObject *self, void *closure)
2047{
2048 Py_buffer *base = &self->head->base;
2049 return PyLong_FromSsize_t(base->ndim);
2050}
2051
2052static PyObject *
2053ndarray_get_shape(NDArrayObject *self, void *closure)
2054{
2055 Py_buffer *base = &self->head->base;
2056 return ssize_array_as_tuple(base->shape, base->ndim);
2057}
2058
2059static PyObject *
2060ndarray_get_strides(NDArrayObject *self, void *closure)
2061{
2062 Py_buffer *base = &self->head->base;
2063 return ssize_array_as_tuple(base->strides, base->ndim);
2064}
2065
2066static PyObject *
2067ndarray_get_suboffsets(NDArrayObject *self, void *closure)
2068{
2069 Py_buffer *base = &self->head->base;
2070 return ssize_array_as_tuple(base->suboffsets, base->ndim);
2071}
2072
2073static PyObject *
2074ndarray_c_contig(PyObject *self, PyObject *dummy)
2075{
2076 NDArrayObject *nd = (NDArrayObject *)self;
2077 int ret = PyBuffer_IsContiguous(&nd->head->base, 'C');
2078
2079 if (ret != ND_C_CONTIGUOUS(nd->head->flags)) {
2080 PyErr_SetString(PyExc_RuntimeError,
2081 "results from PyBuffer_IsContiguous() and flags differ");
2082 return NULL;
2083 }
2084 return PyBool_FromLong(ret);
2085}
2086
2087static PyObject *
2088ndarray_fortran_contig(PyObject *self, PyObject *dummy)
2089{
2090 NDArrayObject *nd = (NDArrayObject *)self;
2091 int ret = PyBuffer_IsContiguous(&nd->head->base, 'F');
2092
2093 if (ret != ND_FORTRAN_CONTIGUOUS(nd->head->flags)) {
2094 PyErr_SetString(PyExc_RuntimeError,
2095 "results from PyBuffer_IsContiguous() and flags differ");
2096 return NULL;
2097 }
2098 return PyBool_FromLong(ret);
2099}
2100
2101static PyObject *
2102ndarray_contig(PyObject *self, PyObject *dummy)
2103{
2104 NDArrayObject *nd = (NDArrayObject *)self;
2105 int ret = PyBuffer_IsContiguous(&nd->head->base, 'A');
2106
2107 if (ret != ND_ANY_CONTIGUOUS(nd->head->flags)) {
2108 PyErr_SetString(PyExc_RuntimeError,
2109 "results from PyBuffer_IsContiguous() and flags differ");
2110 return NULL;
2111 }
2112 return PyBool_FromLong(ret);
2113}
2114
2115
2116static PyGetSetDef ndarray_getset [] =
2117{
2118 /* ndbuf */
2119 { "flags", (getter)ndarray_get_flags, NULL, NULL, NULL},
2120 { "offset", (getter)ndarray_get_offset, NULL, NULL, NULL},
2121 /* ndbuf.base */
2122 { "obj", (getter)ndarray_get_obj, NULL, NULL, NULL},
2123 { "nbytes", (getter)ndarray_get_nbytes, NULL, NULL, NULL},
2124 { "readonly", (getter)ndarray_get_readonly, NULL, NULL, NULL},
2125 { "itemsize", (getter)ndarray_get_itemsize, NULL, NULL, NULL},
2126 { "format", (getter)ndarray_get_format, NULL, NULL, NULL},
2127 { "ndim", (getter)ndarray_get_ndim, NULL, NULL, NULL},
2128 { "shape", (getter)ndarray_get_shape, NULL, NULL, NULL},
2129 { "strides", (getter)ndarray_get_strides, NULL, NULL, NULL},
2130 { "suboffsets", (getter)ndarray_get_suboffsets, NULL, NULL, NULL},
2131 { "c_contiguous", (getter)ndarray_c_contig, NULL, NULL, NULL},
2132 { "f_contiguous", (getter)ndarray_fortran_contig, NULL, NULL, NULL},
2133 { "contiguous", (getter)ndarray_contig, NULL, NULL, NULL},
2134 {NULL}
2135};
2136
2137static PyObject *
2138ndarray_tolist(PyObject *self, PyObject *dummy)
2139{
2140 return ndarray_as_list((NDArrayObject *)self);
2141}
2142
2143static PyObject *
2144ndarray_tobytes(PyObject *self, PyObject *dummy)
2145{
2146 ndbuf_t *ndbuf = ((NDArrayObject *)self)->head;
2147 Py_buffer *src = &ndbuf->base;
2148 Py_buffer dest;
2149 PyObject *ret = NULL;
2150 char *mem;
2151
2152 if (ND_C_CONTIGUOUS(ndbuf->flags))
2153 return PyBytes_FromStringAndSize(src->buf, src->len);
2154
2155 assert(src->shape != NULL);
2156 assert(src->strides != NULL);
2157 assert(src->ndim > 0);
2158
2159 mem = PyMem_Malloc(src->len);
2160 if (mem == NULL) {
2161 PyErr_NoMemory();
2162 return NULL;
2163 }
2164
2165 dest = *src;
2166 dest.buf = mem;
2167 dest.suboffsets = NULL;
2168 dest.strides = strides_from_shape(ndbuf, 0);
2169 if (dest.strides == NULL)
2170 goto out;
2171 if (copy_buffer(&dest, src) < 0)
2172 goto out;
2173
2174 ret = PyBytes_FromStringAndSize(mem, src->len);
2175
2176out:
2177 PyMem_XFree(dest.strides);
2178 PyMem_Free(mem);
2179 return ret;
2180}
2181
2182/* add redundant (negative) suboffsets for testing */
2183static PyObject *
2184ndarray_add_suboffsets(PyObject *self, PyObject *dummy)
2185{
2186 NDArrayObject *nd = (NDArrayObject *)self;
2187 Py_buffer *base = &nd->head->base;
2188 Py_ssize_t i;
2189
2190 if (base->suboffsets != NULL) {
2191 PyErr_SetString(PyExc_TypeError,
2192 "cannot add suboffsets to PIL-style array");
2193 return NULL;
2194 }
2195 if (base->strides == NULL) {
2196 PyErr_SetString(PyExc_TypeError,
2197 "cannot add suboffsets to array without strides");
2198 return NULL;
2199 }
2200
2201 base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
2202 if (base->suboffsets == NULL) {
2203 PyErr_NoMemory();
2204 return NULL;
2205 }
2206
2207 for (i = 0; i < base->ndim; i++)
2208 base->suboffsets[i] = -1;
2209
2210 Py_RETURN_NONE;
2211}
2212
2213/* Test PyMemoryView_FromBuffer(): return a memoryview from a static buffer.
2214 Obviously this is fragile and only one such view may be active at any
2215 time. Never use anything like this in real code! */
2216static char *infobuf = NULL;
2217static PyObject *
2218ndarray_memoryview_from_buffer(PyObject *self, PyObject *dummy)
2219{
2220 const NDArrayObject *nd = (NDArrayObject *)self;
2221 const Py_buffer *view = &nd->head->base;
2222 const ndbuf_t *ndbuf;
2223 static char format[ND_MAX_NDIM+1];
2224 static Py_ssize_t shape[ND_MAX_NDIM];
2225 static Py_ssize_t strides[ND_MAX_NDIM];
2226 static Py_ssize_t suboffsets[ND_MAX_NDIM];
2227 static Py_buffer info;
2228 char *p;
2229
2230 if (!ND_IS_CONSUMER(nd))
2231 ndbuf = nd->head; /* self is ndarray/original exporter */
2232 else if (NDArray_Check(view->obj) && !ND_IS_CONSUMER(view->obj))
2233 /* self is ndarray and consumer from ndarray/original exporter */
2234 ndbuf = ((NDArrayObject *)view->obj)->head;
2235 else {
2236 PyErr_SetString(PyExc_TypeError,
2237 "memoryview_from_buffer(): ndarray must be original exporter or "
2238 "consumer from ndarray/original exporter");
2239 return NULL;
2240 }
2241
2242 info = *view;
2243 p = PyMem_Realloc(infobuf, ndbuf->len);
2244 if (p == NULL) {
2245 PyMem_Free(infobuf);
2246 PyErr_NoMemory();
2247 infobuf = NULL;
2248 return NULL;
2249 }
2250 else {
2251 infobuf = p;
2252 }
2253 /* copy the complete raw data */
2254 memcpy(infobuf, ndbuf->data, ndbuf->len);
2255 info.buf = infobuf + ((char *)view->buf - ndbuf->data);
2256
2257 if (view->format) {
2258 if (strlen(view->format) > ND_MAX_NDIM) {
2259 PyErr_Format(PyExc_TypeError,
2260 "memoryview_from_buffer: format is limited to %d characters",
2261 ND_MAX_NDIM);
2262 return NULL;
2263 }
2264 strcpy(format, view->format);
2265 info.format = format;
2266 }
2267 if (view->ndim > ND_MAX_NDIM) {
2268 PyErr_Format(PyExc_TypeError,
2269 "memoryview_from_buffer: ndim is limited to %d", ND_MAX_NDIM);
2270 return NULL;
2271 }
2272 if (view->shape) {
2273 memcpy(shape, view->shape, view->ndim * sizeof(Py_ssize_t));
2274 info.shape = shape;
2275 }
2276 if (view->strides) {
2277 memcpy(strides, view->strides, view->ndim * sizeof(Py_ssize_t));
2278 info.strides = strides;
2279 }
2280 if (view->suboffsets) {
2281 memcpy(suboffsets, view->suboffsets, view->ndim * sizeof(Py_ssize_t));
2282 info.suboffsets = suboffsets;
2283 }
2284
2285 return PyMemoryView_FromBuffer(&info);
2286}
2287
2288/* Get a single item from bufobj at the location specified by seq.
2289 seq is a list or tuple of indices. The purpose of this function
2290 is to check other functions against PyBuffer_GetPointer(). */
2291static PyObject *
2292get_pointer(PyObject *self, PyObject *args)
2293{
2294 PyObject *ret = NULL, *bufobj, *seq;
2295 Py_buffer view;
2296 Py_ssize_t indices[ND_MAX_NDIM];
2297 Py_ssize_t i;
2298 void *ptr;
2299
2300 if (!PyArg_ParseTuple(args, "OO", &bufobj, &seq)) {
2301 return NULL;
2302 }
2303
2304 CHECK_LIST_OR_TUPLE(seq);
2305 if (PyObject_GetBuffer(bufobj, &view, PyBUF_FULL_RO) < 0)
2306 return NULL;
2307
2308 if (view.ndim > ND_MAX_NDIM) {
2309 PyErr_Format(PyExc_ValueError,
2310 "get_pointer(): ndim > %d", ND_MAX_NDIM);
2311 goto out;
2312 }
2313 if (PySequence_Fast_GET_SIZE(seq) != view.ndim) {
2314 PyErr_SetString(PyExc_ValueError,
2315 "get_pointer(): len(indices) != ndim");
2316 goto out;
2317 }
2318
2319 for (i = 0; i < view.ndim; i++) {
2320 PyObject *x = PySequence_Fast_GET_ITEM(seq, i);
2321 indices[i] = PyLong_AsSsize_t(x);
2322 if (PyErr_Occurred())
2323 goto out;
2324 if (indices[i] < 0 || indices[i] >= view.shape[i]) {
2325 PyErr_Format(PyExc_ValueError,
2326 "get_pointer(): invalid index %zd at position %zd",
2327 indices[i], i);
2328 goto out;
2329 }
2330 }
2331
2332 ptr = PyBuffer_GetPointer(&view, indices);
2333 ret = unpack_single(ptr, view.format, view.itemsize);
2334
2335out:
2336 PyBuffer_Release(&view);
2337 return ret;
2338}
2339
2340static char
2341get_ascii_order(PyObject *order)
2342{
2343 PyObject *ascii_order;
2344 char ord;
2345
2346 if (!PyUnicode_Check(order)) {
2347 PyErr_SetString(PyExc_TypeError,
2348 "order must be a string");
2349 return CHAR_MAX;
2350 }
2351
2352 ascii_order = PyUnicode_AsASCIIString(order);
2353 if (ascii_order == NULL) {
2354 return CHAR_MAX;
2355 }
2356
2357 ord = PyBytes_AS_STRING(ascii_order)[0];
2358 Py_DECREF(ascii_order);
2359 return ord;
2360}
2361
2362/* Get a contiguous memoryview. */
2363static PyObject *
2364get_contiguous(PyObject *self, PyObject *args)
2365{
2366 PyObject *obj;
2367 PyObject *buffertype;
2368 PyObject *order;
2369 long type;
2370 char ord;
2371
2372 if (!PyArg_ParseTuple(args, "OOO", &obj, &buffertype, &order)) {
2373 return NULL;
2374 }
2375
2376 if (!PyLong_Check(buffertype)) {
2377 PyErr_SetString(PyExc_TypeError,
2378 "buffertype must be PyBUF_READ or PyBUF_WRITE");
2379 return NULL;
2380 }
2381 type = PyLong_AsLong(buffertype);
2382 if (type == -1 && PyErr_Occurred()) {
2383 return NULL;
2384 }
2385
2386 ord = get_ascii_order(order);
2387 if (ord == CHAR_MAX) {
2388 return NULL;
2389 }
2390
2391 return PyMemoryView_GetContiguous(obj, (int)type, ord);
2392}
2393
2394static int
2395fmtcmp(const char *fmt1, const char *fmt2)
2396{
2397 if (fmt1 == NULL) {
2398 return fmt2 == NULL || strcmp(fmt2, "B") == 0;
2399 }
2400 if (fmt2 == NULL) {
2401 return fmt1 == NULL || strcmp(fmt1, "B") == 0;
2402 }
2403 return strcmp(fmt1, fmt2) == 0;
2404}
2405
2406static int
2407arraycmp(const Py_ssize_t *a1, const Py_ssize_t *a2, const Py_ssize_t *shape,
2408 Py_ssize_t ndim)
2409{
2410 Py_ssize_t i;
2411
2412 if (ndim == 1 && shape && shape[0] == 1) {
2413 /* This is for comparing strides: For example, the array
2414 [175], shape=[1], strides=[-5] is considered contiguous. */
2415 return 1;
2416 }
2417
2418 for (i = 0; i < ndim; i++) {
2419 if (a1[i] != a2[i]) {
2420 return 0;
2421 }
2422 }
2423
2424 return 1;
2425}
2426
2427/* Compare two contiguous buffers for physical equality. */
2428static PyObject *
2429cmp_contig(PyObject *self, PyObject *args)
2430{
2431 PyObject *b1, *b2; /* buffer objects */
2432 Py_buffer v1, v2;
2433 PyObject *ret;
2434 int equal = 0;
2435
2436 if (!PyArg_ParseTuple(args, "OO", &b1, &b2)) {
2437 return NULL;
2438 }
2439
2440 if (PyObject_GetBuffer(b1, &v1, PyBUF_FULL_RO) < 0) {
2441 PyErr_SetString(PyExc_TypeError,
2442 "cmp_contig: first argument does not implement the buffer "
2443 "protocol");
2444 return NULL;
2445 }
2446 if (PyObject_GetBuffer(b2, &v2, PyBUF_FULL_RO) < 0) {
2447 PyErr_SetString(PyExc_TypeError,
2448 "cmp_contig: second argument does not implement the buffer "
2449 "protocol");
2450 PyBuffer_Release(&v1);
2451 return NULL;
2452 }
2453
2454 if (!(PyBuffer_IsContiguous(&v1, 'C')&&PyBuffer_IsContiguous(&v2, 'C')) &&
2455 !(PyBuffer_IsContiguous(&v1, 'F')&&PyBuffer_IsContiguous(&v2, 'F'))) {
2456 goto result;
2457 }
2458
2459 /* readonly may differ if created from non-contiguous */
2460 if (v1.len != v2.len ||
2461 v1.itemsize != v2.itemsize ||
2462 v1.ndim != v2.ndim ||
2463 !fmtcmp(v1.format, v2.format) ||
2464 !!v1.shape != !!v2.shape ||
2465 !!v1.strides != !!v2.strides ||
2466 !!v1.suboffsets != !!v2.suboffsets) {
2467 goto result;
2468 }
2469
2470 if ((v1.shape && !arraycmp(v1.shape, v2.shape, NULL, v1.ndim)) ||
2471 (v1.strides && !arraycmp(v1.strides, v2.strides, v1.shape, v1.ndim)) ||
2472 (v1.suboffsets && !arraycmp(v1.suboffsets, v2.suboffsets, NULL,
2473 v1.ndim))) {
2474 goto result;
2475 }
2476
2477 if (memcmp((char *)v1.buf, (char *)v2.buf, v1.len) != 0) {
2478 goto result;
2479 }
2480
2481 equal = 1;
2482
2483result:
2484 PyBuffer_Release(&v1);
2485 PyBuffer_Release(&v2);
2486
2487 ret = equal ? Py_True : Py_False;
2488 Py_INCREF(ret);
2489 return ret;
2490}
2491
2492static PyObject *
2493is_contiguous(PyObject *self, PyObject *args)
2494{
2495 PyObject *obj;
2496 PyObject *order;
2497 PyObject *ret = NULL;
2498 Py_buffer view;
2499 char ord;
2500
2501 if (!PyArg_ParseTuple(args, "OO", &obj, &order)) {
2502 return NULL;
2503 }
2504
2505 if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) {
2506 PyErr_SetString(PyExc_TypeError,
2507 "is_contiguous: object does not implement the buffer "
2508 "protocol");
2509 return NULL;
2510 }
2511
2512 ord = get_ascii_order(order);
2513 if (ord == CHAR_MAX) {
2514 goto release;
2515 }
2516
2517 ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
2518 Py_INCREF(ret);
2519
2520release:
2521 PyBuffer_Release(&view);
2522 return ret;
2523}
2524
2525static Py_hash_t
2526ndarray_hash(PyObject *self)
2527{
2528 const NDArrayObject *nd = (NDArrayObject *)self;
2529 const Py_buffer *view = &nd->head->base;
2530 PyObject *bytes;
2531 Py_hash_t hash;
2532
2533 if (!view->readonly) {
2534 PyErr_SetString(PyExc_ValueError,
2535 "cannot hash writable ndarray object");
2536 return -1;
2537 }
2538 if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
2539 return -1;
2540 }
2541
2542 bytes = ndarray_tobytes(self, NULL);
2543 if (bytes == NULL) {
2544 return -1;
2545 }
2546
2547 hash = PyObject_Hash(bytes);
2548 Py_DECREF(bytes);
2549 return hash;
2550}
2551
2552
2553static PyMethodDef ndarray_methods [] =
2554{
2555 { "tolist", ndarray_tolist, METH_NOARGS, NULL },
2556 { "tobytes", ndarray_tobytes, METH_NOARGS, NULL },
2557 { "push", (PyCFunction)ndarray_push, METH_VARARGS|METH_KEYWORDS, NULL },
2558 { "pop", ndarray_pop, METH_NOARGS, NULL },
2559 { "add_suboffsets", ndarray_add_suboffsets, METH_NOARGS, NULL },
2560 { "memoryview_from_buffer", ndarray_memoryview_from_buffer, METH_NOARGS, NULL },
2561 {NULL}
2562};
2563
2564static PyTypeObject NDArray_Type = {
2565 PyVarObject_HEAD_INIT(NULL, 0)
2566 "ndarray", /* Name of this type */
2567 sizeof(NDArrayObject), /* Basic object size */
2568 0, /* Item size for varobject */
2569 (destructor)ndarray_dealloc, /* tp_dealloc */
2570 0, /* tp_print */
2571 0, /* tp_getattr */
2572 0, /* tp_setattr */
2573 0, /* tp_compare */
2574 0, /* tp_repr */
2575 0, /* tp_as_number */
2576 &ndarray_as_sequence, /* tp_as_sequence */
2577 &ndarray_as_mapping, /* tp_as_mapping */
2578 (hashfunc)ndarray_hash, /* tp_hash */
2579 0, /* tp_call */
2580 0, /* tp_str */
2581 PyObject_GenericGetAttr, /* tp_getattro */
2582 0, /* tp_setattro */
2583 &ndarray_as_buffer, /* tp_as_buffer */
2584 Py_TPFLAGS_DEFAULT, /* tp_flags */
2585 0, /* tp_doc */
2586 0, /* tp_traverse */
2587 0, /* tp_clear */
2588 0, /* tp_richcompare */
2589 0, /* tp_weaklistoffset */
2590 0, /* tp_iter */
2591 0, /* tp_iternext */
2592 ndarray_methods, /* tp_methods */
2593 0, /* tp_members */
2594 ndarray_getset, /* tp_getset */
2595 0, /* tp_base */
2596 0, /* tp_dict */
2597 0, /* tp_descr_get */
2598 0, /* tp_descr_set */
2599 0, /* tp_dictoffset */
2600 ndarray_init, /* tp_init */
2601 0, /* tp_alloc */
2602 ndarray_new, /* tp_new */
2603};
2604
2605
2606static struct PyMethodDef _testbuffer_functions[] = {
2607 {"slice_indices", slice_indices, METH_VARARGS, NULL},
2608 {"get_pointer", get_pointer, METH_VARARGS, NULL},
2609 {"get_contiguous", get_contiguous, METH_VARARGS, NULL},
2610 {"is_contiguous", is_contiguous, METH_VARARGS, NULL},
2611 {"cmp_contig", cmp_contig, METH_VARARGS, NULL},
2612 {NULL, NULL}
2613};
2614
2615static struct PyModuleDef _testbuffermodule = {
2616 PyModuleDef_HEAD_INIT,
2617 "_testbuffer",
2618 NULL,
2619 -1,
2620 _testbuffer_functions,
2621 NULL,
2622 NULL,
2623 NULL,
2624 NULL
2625};
2626
2627
2628PyMODINIT_FUNC
2629PyInit__testbuffer(void)
2630{
2631 PyObject *m;
2632
2633 m = PyModule_Create(&_testbuffermodule);
2634 if (m == NULL)
2635 return NULL;
2636
2637 Py_TYPE(&NDArray_Type)=&PyType_Type;
2638 Py_INCREF(&NDArray_Type);
2639 PyModule_AddObject(m, "ndarray", (PyObject *)&NDArray_Type);
2640
2641 structmodule = PyImport_ImportModule("struct");
2642 if (structmodule == NULL)
2643 return NULL;
2644
2645 Struct = PyObject_GetAttrString(structmodule, "Struct");
2646 calcsize = PyObject_GetAttrString(structmodule, "calcsize");
2647 if (Struct == NULL || calcsize == NULL)
2648 return NULL;
2649
2650 simple_format = PyUnicode_FromString(simple_fmt);
2651 if (simple_format == NULL)
2652 return NULL;
2653
2654 PyModule_AddIntConstant(m, "ND_MAX_NDIM", ND_MAX_NDIM);
2655 PyModule_AddIntConstant(m, "ND_VAREXPORT", ND_VAREXPORT);
2656 PyModule_AddIntConstant(m, "ND_WRITABLE", ND_WRITABLE);
2657 PyModule_AddIntConstant(m, "ND_FORTRAN", ND_FORTRAN);
2658 PyModule_AddIntConstant(m, "ND_SCALAR", ND_SCALAR);
2659 PyModule_AddIntConstant(m, "ND_PIL", ND_PIL);
2660 PyModule_AddIntConstant(m, "ND_GETBUF_FAIL", ND_GETBUF_FAIL);
Stefan Krah4e99a312012-03-05 09:30:47 +01002661 PyModule_AddIntConstant(m, "ND_REDIRECT", ND_REDIRECT);
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002662
2663 PyModule_AddIntConstant(m, "PyBUF_SIMPLE", PyBUF_SIMPLE);
2664 PyModule_AddIntConstant(m, "PyBUF_WRITABLE", PyBUF_WRITABLE);
2665 PyModule_AddIntConstant(m, "PyBUF_FORMAT", PyBUF_FORMAT);
2666 PyModule_AddIntConstant(m, "PyBUF_ND", PyBUF_ND);
2667 PyModule_AddIntConstant(m, "PyBUF_STRIDES", PyBUF_STRIDES);
2668 PyModule_AddIntConstant(m, "PyBUF_INDIRECT", PyBUF_INDIRECT);
2669 PyModule_AddIntConstant(m, "PyBUF_C_CONTIGUOUS", PyBUF_C_CONTIGUOUS);
2670 PyModule_AddIntConstant(m, "PyBUF_F_CONTIGUOUS", PyBUF_F_CONTIGUOUS);
2671 PyModule_AddIntConstant(m, "PyBUF_ANY_CONTIGUOUS", PyBUF_ANY_CONTIGUOUS);
2672 PyModule_AddIntConstant(m, "PyBUF_FULL", PyBUF_FULL);
2673 PyModule_AddIntConstant(m, "PyBUF_FULL_RO", PyBUF_FULL_RO);
2674 PyModule_AddIntConstant(m, "PyBUF_RECORDS", PyBUF_RECORDS);
2675 PyModule_AddIntConstant(m, "PyBUF_RECORDS_RO", PyBUF_RECORDS_RO);
2676 PyModule_AddIntConstant(m, "PyBUF_STRIDED", PyBUF_STRIDED);
2677 PyModule_AddIntConstant(m, "PyBUF_STRIDED_RO", PyBUF_STRIDED_RO);
2678 PyModule_AddIntConstant(m, "PyBUF_CONTIG", PyBUF_CONTIG);
2679 PyModule_AddIntConstant(m, "PyBUF_CONTIG_RO", PyBUF_CONTIG_RO);
2680
2681 PyModule_AddIntConstant(m, "PyBUF_READ", PyBUF_READ);
2682 PyModule_AddIntConstant(m, "PyBUF_WRITE", PyBUF_WRITE);
2683
2684 return m;
2685}
2686
2687
2688