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