blob: a4e1643a0dd8654a4e25e3b9fbaeb1b9300a278c [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)
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;
193
194 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 +-----------------+-----------+-------------+----------------+
770 | base.format | NULL | OK | OK |
771 +-----------------+-----------+-------------+----------------+
772 | base.ndim | 1 | 1 | OK |
773 +-----------------+-----------+-------------+----------------+
774 | base.shape | NULL | NULL | OK |
775 +-----------------+-----------+-------------+----------------+
776 | base.strides | NULL | NULL | OK |
777 +-----------------+-----------+-------------+----------------+
778 | base.suboffsets | NULL | NULL | OK |
779 +-----------------+-----------+-------------+----------------+
780 | base.internal | OK | OK | OK |
781 +-----------------+-----------+-------------+----------------+
782
783*/
784
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
853 dest = PyMem_Malloc(len * (sizeof *dest));
854 if (dest == NULL) {
855 PyErr_NoMemory();
856 return NULL;
857 }
858
859 for (i = 0; i < len; i++) {
860 PyObject *tmp = PySequence_Fast_GET_ITEM(seq, i);
861 if (!PyLong_Check(tmp)) {
862 PyErr_Format(PyExc_ValueError,
863 "elements of %s must be integers",
864 is_shape ? "shape" : "strides");
865 PyMem_Free(dest);
866 return NULL;
867 }
868 x = PyLong_AsSsize_t(tmp);
869 if (PyErr_Occurred()) {
870 PyMem_Free(dest);
871 return NULL;
872 }
873 if (is_shape && x < 0) {
874 PyErr_Format(PyExc_ValueError,
875 "elements of shape must be integers >= 0");
876 PyMem_Free(dest);
877 return NULL;
878 }
879 dest[i] = x;
880 }
881
882 return dest;
883}
884
885static Py_ssize_t *
886strides_from_shape(const ndbuf_t *ndbuf, int flags)
887{
888 const Py_buffer *base = &ndbuf->base;
889 Py_ssize_t *s, i;
890
891 s = PyMem_Malloc(base->ndim * (sizeof *s));
892 if (s == NULL) {
893 PyErr_NoMemory();
894 return NULL;
895 }
896
897 if (flags & ND_FORTRAN) {
898 s[0] = base->itemsize;
899 for (i = 1; i < base->ndim; i++)
900 s[i] = s[i-1] * base->shape[i-1];
901 }
902 else {
903 s[base->ndim-1] = base->itemsize;
904 for (i = base->ndim-2; i >= 0; i--)
905 s[i] = s[i+1] * base->shape[i+1];
906 }
907
908 return s;
909}
910
911/* Bounds check:
912
913 len := complete length of allocated memory
914 offset := start of the array
915
916 A single array element is indexed by:
917
918 i = indices[0] * strides[0] + indices[1] * strides[1] + ...
919
920 imin is reached when all indices[n] combined with positive strides are 0
921 and all indices combined with negative strides are shape[n]-1, which is
922 the maximum index for the nth dimension.
923
924 imax is reached when all indices[n] combined with negative strides are 0
925 and all indices combined with positive strides are shape[n]-1.
926*/
927static int
928verify_structure(Py_ssize_t len, Py_ssize_t itemsize, Py_ssize_t offset,
929 const Py_ssize_t *shape, const Py_ssize_t *strides,
930 Py_ssize_t ndim)
931{
932 Py_ssize_t imin, imax;
933 Py_ssize_t n;
934
935 assert(ndim >= 0);
936
937 if (ndim == 0 && (offset < 0 || offset+itemsize > len))
938 goto invalid_combination;
939
940 for (n = 0; n < ndim; n++)
941 if (strides[n] % itemsize) {
942 PyErr_SetString(PyExc_ValueError,
943 "strides must be a multiple of itemsize");
944 return -1;
945 }
946
947 for (n = 0; n < ndim; n++)
948 if (shape[n] == 0)
949 return 0;
950
951 imin = imax = 0;
952 for (n = 0; n < ndim; n++)
953 if (strides[n] <= 0)
954 imin += (shape[n]-1) * strides[n];
955 else
956 imax += (shape[n]-1) * strides[n];
957
958 if (imin + offset < 0 || imax + offset + itemsize > len)
959 goto invalid_combination;
960
961 return 0;
962
963
964invalid_combination:
965 PyErr_SetString(PyExc_ValueError,
966 "invalid combination of buffer, shape and strides");
967 return -1;
968}
969
970/*
971 Convert a NumPy-style array to an array using suboffsets to stride in
972 the first dimension. Requirements: ndim > 0.
973
974 Contiguous example
975 ==================
976
977 Input:
978 ------
979 shape = {2, 2, 3};
980 strides = {6, 3, 1};
981 suboffsets = NULL;
982 data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
983 buf = &data[0]
984
985 Output:
986 -------
987 shape = {2, 2, 3};
988 strides = {sizeof(char *), 3, 1};
989 suboffsets = {0, -1, -1};
990 data = {p1, p2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
991 | | ^ ^
992 `---'---' |
993 | |
994 `---------------------'
995 buf = &data[0]
996
997 So, in the example the input resembles the three-dimensional array
998 char v[2][2][3], while the output resembles an array of two pointers
999 to two-dimensional arrays: char (*v[2])[2][3].
1000
1001
1002 Non-contiguous example:
1003 =======================
1004
1005 Input (with offset and negative strides):
1006 -----------------------------------------
1007 shape = {2, 2, 3};
1008 strides = {-6, 3, -1};
1009 offset = 8
1010 suboffsets = NULL;
1011 data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
1012
1013 Output:
1014 -------
1015 shape = {2, 2, 3};
1016 strides = {-sizeof(char *), 3, -1};
1017 suboffsets = {2, -1, -1};
1018 newdata = {p1, p2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
1019 | | ^ ^ ^ ^
1020 `---'---' | | `- p2+suboffsets[0]
1021 | `-----------|--- p1+suboffsets[0]
1022 `---------------------'
1023 buf = &newdata[1] # striding backwards over the pointers.
1024
1025 suboffsets[0] is the same as the offset that one would specify if
1026 the two {2, 3} subarrays were created directly, hence the name.
1027*/
1028static int
1029init_suboffsets(ndbuf_t *ndbuf)
1030{
1031 Py_buffer *base = &ndbuf->base;
1032 Py_ssize_t start, step;
1033 Py_ssize_t imin, suboffset0;
1034 Py_ssize_t addsize;
1035 Py_ssize_t n;
1036 char *data;
1037
1038 assert(base->ndim > 0);
1039 assert(base->suboffsets == NULL);
1040
1041 /* Allocate new data with additional space for shape[0] pointers. */
1042 addsize = base->shape[0] * (sizeof (char *));
1043
1044 /* Align array start to a multiple of 8. */
1045 addsize = 8 * ((addsize + 7) / 8);
1046
1047 data = PyMem_Malloc(ndbuf->len + addsize);
1048 if (data == NULL) {
1049 PyErr_NoMemory();
1050 return -1;
1051 }
1052
1053 memcpy(data + addsize, ndbuf->data, ndbuf->len);
1054
1055 PyMem_Free(ndbuf->data);
1056 ndbuf->data = data;
1057 ndbuf->len += addsize;
1058 base->buf = ndbuf->data;
1059
1060 /* imin: minimum index of the input array relative to ndbuf->offset.
1061 suboffset0: offset for each sub-array of the output. This is the
1062 same as calculating -imin' for a sub-array of ndim-1. */
1063 imin = suboffset0 = 0;
1064 for (n = 0; n < base->ndim; n++) {
1065 if (base->shape[n] == 0)
1066 break;
1067 if (base->strides[n] <= 0) {
1068 Py_ssize_t x = (base->shape[n]-1) * base->strides[n];
1069 imin += x;
1070 suboffset0 += (n >= 1) ? -x : 0;
1071 }
1072 }
1073
1074 /* Initialize the array of pointers to the sub-arrays. */
1075 start = addsize + ndbuf->offset + imin;
1076 step = base->strides[0] < 0 ? -base->strides[0] : base->strides[0];
1077
1078 for (n = 0; n < base->shape[0]; n++)
1079 ((char **)base->buf)[n] = (char *)base->buf + start + n*step;
1080
1081 /* Initialize suboffsets. */
1082 base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
1083 if (base->suboffsets == NULL) {
1084 PyErr_NoMemory();
1085 return -1;
1086 }
1087 base->suboffsets[0] = suboffset0;
1088 for (n = 1; n < base->ndim; n++)
1089 base->suboffsets[n] = -1;
1090
1091 /* Adjust strides for the first (zeroth) dimension. */
1092 if (base->strides[0] >= 0) {
1093 base->strides[0] = sizeof(char *);
1094 }
1095 else {
1096 /* Striding backwards. */
1097 base->strides[0] = -(Py_ssize_t)sizeof(char *);
1098 if (base->shape[0] > 0)
1099 base->buf = (char *)base->buf + (base->shape[0]-1) * sizeof(char *);
1100 }
1101
1102 ndbuf->flags &= ~(ND_C|ND_FORTRAN);
1103 ndbuf->offset = 0;
1104 return 0;
1105}
1106
1107static void
1108init_len(Py_buffer *base)
1109{
1110 Py_ssize_t i;
1111
1112 base->len = 1;
1113 for (i = 0; i < base->ndim; i++)
1114 base->len *= base->shape[i];
1115 base->len *= base->itemsize;
1116}
1117
1118static int
1119init_structure(ndbuf_t *ndbuf, PyObject *shape, PyObject *strides,
1120 Py_ssize_t ndim)
1121{
1122 Py_buffer *base = &ndbuf->base;
1123
1124 base->ndim = (int)ndim;
1125 if (ndim == 0) {
1126 if (ndbuf->flags & ND_PIL) {
1127 PyErr_SetString(PyExc_TypeError,
1128 "ndim = 0 cannot be used in conjunction with ND_PIL");
1129 return -1;
1130 }
1131 ndbuf->flags |= (ND_SCALAR|ND_C|ND_FORTRAN);
1132 return 0;
1133 }
1134
1135 /* shape */
1136 base->shape = seq_as_ssize_array(shape, ndim, 1);
1137 if (base->shape == NULL)
1138 return -1;
1139
1140 /* strides */
1141 if (strides) {
1142 base->strides = seq_as_ssize_array(strides, ndim, 0);
1143 }
1144 else {
1145 base->strides = strides_from_shape(ndbuf, ndbuf->flags);
1146 }
1147 if (base->strides == NULL)
1148 return -1;
1149 if (verify_structure(base->len, base->itemsize, ndbuf->offset,
1150 base->shape, base->strides, ndim) < 0)
1151 return -1;
1152
1153 /* buf */
1154 base->buf = ndbuf->data + ndbuf->offset;
1155
1156 /* len */
1157 init_len(base);
1158
1159 /* ndbuf->flags */
1160 if (PyBuffer_IsContiguous(base, 'C'))
1161 ndbuf->flags |= ND_C;
1162 if (PyBuffer_IsContiguous(base, 'F'))
1163 ndbuf->flags |= ND_FORTRAN;
1164
1165
1166 /* convert numpy array to suboffset representation */
1167 if (ndbuf->flags & ND_PIL) {
1168 /* modifies base->buf, base->strides and base->suboffsets **/
1169 return init_suboffsets(ndbuf);
1170 }
1171
1172 return 0;
1173}
1174
1175static ndbuf_t *
1176init_ndbuf(PyObject *items, PyObject *shape, PyObject *strides,
1177 Py_ssize_t offset, PyObject *format, int flags)
1178{
1179 ndbuf_t *ndbuf;
1180 Py_ssize_t ndim;
1181 Py_ssize_t nitems;
1182 Py_ssize_t itemsize;
1183
1184 /* ndim = len(shape) */
1185 CHECK_LIST_OR_TUPLE(shape)
1186 ndim = PySequence_Fast_GET_SIZE(shape);
1187 if (ndim > ND_MAX_NDIM) {
1188 PyErr_Format(PyExc_ValueError,
1189 "ndim must not exceed %d", ND_MAX_NDIM);
1190 return NULL;
1191 }
1192
1193 /* len(strides) = len(shape) */
1194 if (strides) {
1195 CHECK_LIST_OR_TUPLE(strides)
1196 if (PySequence_Fast_GET_SIZE(strides) == 0)
1197 strides = NULL;
1198 else if (flags & ND_FORTRAN) {
1199 PyErr_SetString(PyExc_TypeError,
1200 "ND_FORTRAN cannot be used together with strides");
1201 return NULL;
1202 }
1203 else if (PySequence_Fast_GET_SIZE(strides) != ndim) {
1204 PyErr_SetString(PyExc_ValueError,
1205 "len(shape) != len(strides)");
1206 return NULL;
1207 }
1208 }
1209
1210 /* itemsize */
1211 itemsize = get_itemsize(format);
1212 if (itemsize <= 0) {
1213 if (itemsize == 0) {
1214 PyErr_SetString(PyExc_ValueError,
1215 "itemsize must not be zero");
1216 }
1217 return NULL;
1218 }
1219
1220 /* convert scalar to list */
1221 if (ndim == 0) {
1222 items = Py_BuildValue("(O)", items);
1223 if (items == NULL)
1224 return NULL;
1225 }
1226 else {
1227 CHECK_LIST_OR_TUPLE(items)
1228 Py_INCREF(items);
1229 }
1230
1231 /* number of items */
1232 nitems = PySequence_Fast_GET_SIZE(items);
1233 if (nitems == 0) {
1234 PyErr_SetString(PyExc_ValueError,
1235 "initializer list or tuple must not be empty");
1236 Py_DECREF(items);
1237 return NULL;
1238 }
1239
1240 ndbuf = ndbuf_new(nitems, itemsize, offset, flags);
1241 if (ndbuf == NULL) {
1242 Py_DECREF(items);
1243 return NULL;
1244 }
1245
1246
1247 if (init_simple(ndbuf, items, format, itemsize) < 0)
1248 goto error;
1249 if (init_structure(ndbuf, shape, strides, ndim) < 0)
1250 goto error;
1251
1252 Py_DECREF(items);
1253 return ndbuf;
1254
1255error:
1256 Py_DECREF(items);
1257 ndbuf_free(ndbuf);
1258 return NULL;
1259}
1260
1261/* initialize and push a new base onto the linked list */
1262static int
1263ndarray_push_base(NDArrayObject *nd, PyObject *items,
1264 PyObject *shape, PyObject *strides,
1265 Py_ssize_t offset, PyObject *format, int flags)
1266{
1267 ndbuf_t *ndbuf;
1268
1269 ndbuf = init_ndbuf(items, shape, strides, offset, format, flags);
1270 if (ndbuf == NULL)
1271 return -1;
1272
1273 ndbuf_push(nd, ndbuf);
1274 return 0;
1275}
1276
1277#define PyBUF_UNUSED 0x10000
1278static int
1279ndarray_init(PyObject *self, PyObject *args, PyObject *kwds)
1280{
1281 NDArrayObject *nd = (NDArrayObject *)self;
1282 static char *kwlist[] = {
1283 "obj", "shape", "strides", "offset", "format", "flags", "getbuf", NULL
1284 };
1285 PyObject *v = NULL; /* initializer: scalar, list, tuple or base object */
1286 PyObject *shape = NULL; /* size of each dimension */
1287 PyObject *strides = NULL; /* number of bytes to the next elt in each dim */
1288 Py_ssize_t offset = 0; /* buffer offset */
1289 PyObject *format = simple_format; /* struct module specifier: "B" */
Stefan Krah4e99a312012-03-05 09:30:47 +01001290 int flags = ND_DEFAULT; /* base buffer and ndarray flags */
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001291
1292 int getbuf = PyBUF_UNUSED; /* re-exporter: getbuffer request flags */
1293
1294
1295 if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|OOnOii", kwlist,
1296 &v, &shape, &strides, &offset, &format, &flags, &getbuf))
1297 return -1;
1298
1299 /* NDArrayObject is re-exporter */
1300 if (PyObject_CheckBuffer(v) && shape == NULL) {
1301 if (strides || offset || format != simple_format ||
Stefan Krah4e99a312012-03-05 09:30:47 +01001302 !(flags == ND_DEFAULT || flags == ND_REDIRECT)) {
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001303 PyErr_SetString(PyExc_TypeError,
Stefan Krah4e99a312012-03-05 09:30:47 +01001304 "construction from exporter object only takes 'obj', 'getbuf' "
1305 "and 'flags' arguments");
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001306 return -1;
1307 }
1308
1309 getbuf = (getbuf == PyBUF_UNUSED) ? PyBUF_FULL_RO : getbuf;
1310
1311 if (ndarray_init_staticbuf(v, nd, getbuf) < 0)
1312 return -1;
1313
1314 init_flags(nd->head);
Stefan Krah4e99a312012-03-05 09:30:47 +01001315 nd->head->flags |= flags;
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001316
1317 return 0;
1318 }
1319
1320 /* NDArrayObject is the original base object. */
1321 if (getbuf != PyBUF_UNUSED) {
1322 PyErr_SetString(PyExc_TypeError,
1323 "getbuf argument only valid for construction from exporter "
1324 "object");
1325 return -1;
1326 }
1327 if (shape == NULL) {
1328 PyErr_SetString(PyExc_TypeError,
1329 "shape is a required argument when constructing from "
1330 "list, tuple or scalar");
1331 return -1;
1332 }
1333
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001334 if (flags & ND_VAREXPORT) {
1335 nd->flags |= ND_VAREXPORT;
1336 flags &= ~ND_VAREXPORT;
1337 }
1338
1339 /* Initialize and push the first base buffer onto the linked list. */
1340 return ndarray_push_base(nd, v, shape, strides, offset, format, flags);
1341}
1342
1343/* Push an additional base onto the linked list. */
1344static PyObject *
1345ndarray_push(PyObject *self, PyObject *args, PyObject *kwds)
1346{
1347 NDArrayObject *nd = (NDArrayObject *)self;
1348 static char *kwlist[] = {
1349 "items", "shape", "strides", "offset", "format", "flags", NULL
1350 };
1351 PyObject *items = NULL; /* initializer: scalar, list or tuple */
1352 PyObject *shape = NULL; /* size of each dimension */
1353 PyObject *strides = NULL; /* number of bytes to the next elt in each dim */
1354 PyObject *format = simple_format; /* struct module specifier: "B" */
1355 Py_ssize_t offset = 0; /* buffer offset */
Stefan Krah4e99a312012-03-05 09:30:47 +01001356 int flags = ND_DEFAULT; /* base buffer flags */
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001357
1358 if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO|OnOi", kwlist,
1359 &items, &shape, &strides, &offset, &format, &flags))
1360 return NULL;
1361
1362 if (flags & ND_VAREXPORT) {
1363 PyErr_SetString(PyExc_ValueError,
1364 "ND_VAREXPORT flag can only be used during object creation");
1365 return NULL;
1366 }
1367 if (ND_IS_CONSUMER(nd)) {
1368 PyErr_SetString(PyExc_BufferError,
1369 "structure of re-exporting object is immutable");
1370 return NULL;
1371 }
1372 if (!(nd->flags&ND_VAREXPORT) && nd->head->exports > 0) {
1373 PyErr_Format(PyExc_BufferError,
1374 "cannot change structure: %zd exported buffer%s",
1375 nd->head->exports, nd->head->exports==1 ? "" : "s");
1376 return NULL;
1377 }
1378
1379 if (ndarray_push_base(nd, items, shape, strides,
1380 offset, format, flags) < 0)
1381 return NULL;
1382 Py_RETURN_NONE;
1383}
1384
1385/* Pop a base from the linked list (if possible). */
1386static PyObject *
1387ndarray_pop(PyObject *self, PyObject *dummy)
1388{
1389 NDArrayObject *nd = (NDArrayObject *)self;
1390 if (ND_IS_CONSUMER(nd)) {
1391 PyErr_SetString(PyExc_BufferError,
1392 "structure of re-exporting object is immutable");
1393 return NULL;
1394 }
1395 if (nd->head->exports > 0) {
1396 PyErr_Format(PyExc_BufferError,
1397 "cannot change structure: %zd exported buffer%s",
1398 nd->head->exports, nd->head->exports==1 ? "" : "s");
1399 return NULL;
1400 }
1401 if (nd->head->next == NULL) {
1402 PyErr_SetString(PyExc_BufferError,
1403 "list only has a single base");
1404 return NULL;
1405 }
1406
1407 ndbuf_pop(nd);
1408 Py_RETURN_NONE;
1409}
1410
1411/**************************************************************************/
1412/* getbuffer */
1413/**************************************************************************/
1414
1415static int
1416ndarray_getbuf(NDArrayObject *self, Py_buffer *view, int flags)
1417{
1418 ndbuf_t *ndbuf = self->head;
1419 Py_buffer *base = &ndbuf->base;
1420 int baseflags = ndbuf->flags;
1421
Stefan Krah4e99a312012-03-05 09:30:47 +01001422 /* redirect mode */
1423 if (base->obj != NULL && (baseflags&ND_REDIRECT)) {
1424 return PyObject_GetBuffer(base->obj, view, flags);
1425 }
1426
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001427 /* start with complete information */
1428 *view = *base;
1429 view->obj = NULL;
1430
1431 /* reconstruct format */
1432 if (view->format == NULL)
1433 view->format = "B";
1434
1435 if (base->ndim != 0 &&
1436 ((REQ_SHAPE(flags) && base->shape == NULL) ||
1437 (REQ_STRIDES(flags) && base->strides == NULL))) {
1438 /* The ndarray is a re-exporter that has been created without full
1439 information for testing purposes. In this particular case the
1440 ndarray is not a PEP-3118 compliant buffer provider. */
1441 PyErr_SetString(PyExc_BufferError,
1442 "re-exporter does not provide format, shape or strides");
1443 return -1;
1444 }
1445
1446 if (baseflags & ND_GETBUF_FAIL) {
1447 PyErr_SetString(PyExc_BufferError,
1448 "ND_GETBUF_FAIL: forced test exception");
Stefan Krah1649c1b2012-03-05 17:45:17 +01001449 if (baseflags & ND_GETBUF_UNDEFINED)
1450 view->obj = (PyObject *)0x1; /* wrong but permitted in <= 3.2 */
Stefan Krah9a2d99e2012-02-25 12:24:21 +01001451 return -1;
1452 }
1453
1454 if (REQ_WRITABLE(flags) && base->readonly) {
1455 PyErr_SetString(PyExc_BufferError,
1456 "ndarray is not writable");
1457 return -1;
1458 }
1459 if (!REQ_FORMAT(flags)) {
1460 /* NULL indicates that the buffer's data type has been cast to 'B'.
1461 view->itemsize is the _previous_ itemsize. If shape is present,
1462 the equality product(shape) * itemsize = len still holds at this
1463 point. The equality calcsize(format) = itemsize does _not_ hold
1464 from here on! */
1465 view->format = NULL;
1466 }
1467
1468 if (REQ_C_CONTIGUOUS(flags) && !ND_C_CONTIGUOUS(baseflags)) {
1469 PyErr_SetString(PyExc_BufferError,
1470 "ndarray is not C-contiguous");
1471 return -1;
1472 }
1473 if (REQ_F_CONTIGUOUS(flags) && !ND_FORTRAN_CONTIGUOUS(baseflags)) {
1474 PyErr_SetString(PyExc_BufferError,
1475 "ndarray is not Fortran contiguous");
1476 return -1;
1477 }
1478 if (REQ_ANY_CONTIGUOUS(flags) && !ND_ANY_CONTIGUOUS(baseflags)) {
1479 PyErr_SetString(PyExc_BufferError,
1480 "ndarray is not contiguous");
1481 return -1;
1482 }
1483 if (!REQ_INDIRECT(flags) && (baseflags & ND_PIL)) {
1484 PyErr_SetString(PyExc_BufferError,
1485 "ndarray cannot be represented without suboffsets");
1486 return -1;
1487 }
1488 if (!REQ_STRIDES(flags)) {
1489 if (!ND_C_CONTIGUOUS(baseflags)) {
1490 PyErr_SetString(PyExc_BufferError,
1491 "ndarray is not C-contiguous");
1492 return -1;
1493 }
1494 view->strides = NULL;
1495 }
1496 if (!REQ_SHAPE(flags)) {
1497 /* PyBUF_SIMPLE or PyBUF_WRITABLE: at this point buf is C-contiguous,
1498 so base->buf = ndbuf->data. */
1499 if (view->format != NULL) {
1500 /* PyBUF_SIMPLE|PyBUF_FORMAT and PyBUF_WRITABLE|PyBUF_FORMAT do
1501 not make sense. */
1502 PyErr_Format(PyExc_BufferError,
1503 "ndarray: cannot cast to unsigned bytes if the format flag "
1504 "is present");
1505 return -1;
1506 }
1507 /* product(shape) * itemsize = len and calcsize(format) = itemsize
1508 do _not_ hold from here on! */
1509 view->ndim = 1;
1510 view->shape = NULL;
1511 }
1512
1513 view->obj = (PyObject *)self;
1514 Py_INCREF(view->obj);
1515 self->head->exports++;
1516
1517 return 0;
1518}
1519
1520static int
1521ndarray_releasebuf(NDArrayObject *self, Py_buffer *view)
1522{
1523 if (!ND_IS_CONSUMER(self)) {
1524 ndbuf_t *ndbuf = view->internal;
1525 if (--ndbuf->exports == 0 && ndbuf != self->head)
1526 ndbuf_delete(self, ndbuf);
1527 }
1528
1529 return 0;
1530}
1531
1532static PyBufferProcs ndarray_as_buffer = {
1533 (getbufferproc)ndarray_getbuf, /* bf_getbuffer */
1534 (releasebufferproc)ndarray_releasebuf /* bf_releasebuffer */
1535};
1536
1537
1538/**************************************************************************/
1539/* indexing/slicing */
1540/**************************************************************************/
1541
1542static char *
1543ptr_from_index(Py_buffer *base, Py_ssize_t index)
1544{
1545 char *ptr;
1546 Py_ssize_t nitems; /* items in the first dimension */
1547
1548 if (base->shape)
1549 nitems = base->shape[0];
1550 else {
1551 assert(base->ndim == 1 && SIMPLE_FORMAT(base->format));
1552 nitems = base->len;
1553 }
1554
1555 if (index < 0) {
1556 index += nitems;
1557 }
1558 if (index < 0 || index >= nitems) {
1559 PyErr_SetString(PyExc_IndexError, "index out of bounds");
1560 return NULL;
1561 }
1562
1563 ptr = (char *)base->buf;
1564
1565 if (base->strides == NULL)
1566 ptr += base->itemsize * index;
1567 else
1568 ptr += base->strides[0] * index;
1569
1570 ptr = ADJUST_PTR(ptr, base->suboffsets);
1571
1572 return ptr;
1573}
1574
1575static PyObject *
1576ndarray_item(NDArrayObject *self, Py_ssize_t index)
1577{
1578 ndbuf_t *ndbuf = self->head;
1579 Py_buffer *base = &ndbuf->base;
1580 char *ptr;
1581
1582 if (base->ndim == 0) {
1583 PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
1584 return NULL;
1585 }
1586
1587 ptr = ptr_from_index(base, index);
1588 if (ptr == NULL)
1589 return NULL;
1590
1591 if (base->ndim == 1) {
1592 return unpack_single(ptr, base->format, base->itemsize);
1593 }
1594 else {
1595 NDArrayObject *nd;
1596 Py_buffer *subview;
1597
1598 nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
1599 if (nd == NULL)
1600 return NULL;
1601
1602 if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
1603 Py_DECREF(nd);
1604 return NULL;
1605 }
1606
1607 subview = &nd->staticbuf.base;
1608
1609 subview->buf = ptr;
1610 subview->len /= subview->shape[0];
1611
1612 subview->ndim--;
1613 subview->shape++;
1614 if (subview->strides) subview->strides++;
1615 if (subview->suboffsets) subview->suboffsets++;
1616
1617 init_flags(&nd->staticbuf);
1618
1619 return (PyObject *)nd;
1620 }
1621}
1622
1623/*
1624 For each dimension, we get valid (start, stop, step, slicelength) quadruples
1625 from PySlice_GetIndicesEx().
1626
1627 Slicing NumPy arrays
1628 ====================
1629
1630 A pointer to an element in a NumPy array is defined by:
1631
1632 ptr = (char *)buf + indices[0] * strides[0] +
1633 ... +
1634 indices[ndim-1] * strides[ndim-1]
1635
1636 Adjust buf:
1637 -----------
1638 Adding start[n] for each dimension effectively adds the constant:
1639
1640 c = start[0] * strides[0] + ... + start[ndim-1] * strides[ndim-1]
1641
1642 Therefore init_slice() adds all start[n] directly to buf.
1643
1644 Adjust shape:
1645 -------------
1646 Obviously shape[n] = slicelength[n]
1647
1648 Adjust strides:
1649 ---------------
1650 In the original array, the next element in a dimension is reached
1651 by adding strides[n] to the pointer. In the sliced array, elements
1652 may be skipped, so the next element is reached by adding:
1653
1654 strides[n] * step[n]
1655
1656 Slicing PIL arrays
1657 ==================
1658
1659 Layout:
1660 -------
1661 In the first (zeroth) dimension, PIL arrays have an array of pointers
1662 to sub-arrays of ndim-1. Striding in the first dimension is done by
1663 getting the index of the nth pointer, dereference it and then add a
1664 suboffset to it. The arrays pointed to can best be seen a regular
1665 NumPy arrays.
1666
1667 Adjust buf:
1668 -----------
1669 In the original array, buf points to a location (usually the start)
1670 in the array of pointers. For the sliced array, start[0] can be
1671 added to buf in the same manner as for NumPy arrays.
1672
1673 Adjust suboffsets:
1674 ------------------
1675 Due to the dereferencing step in the addressing scheme, it is not
1676 possible to adjust buf for higher dimensions. Recall that the
1677 sub-arrays pointed to are regular NumPy arrays, so for each of
1678 those arrays adding start[n] effectively adds the constant:
1679
1680 c = start[1] * strides[1] + ... + start[ndim-1] * strides[ndim-1]
1681
1682 This constant is added to suboffsets[0]. suboffsets[0] in turn is
1683 added to each pointer right after dereferencing.
1684
1685 Adjust shape and strides:
1686 -------------------------
1687 Shape and strides are not influenced by the dereferencing step, so
1688 they are adjusted in the same manner as for NumPy arrays.
1689
1690 Multiple levels of suboffsets
1691 =============================
1692
1693 For a construct like an array of pointers to array of pointers to
1694 sub-arrays of ndim-2:
1695
1696 suboffsets[0] = start[1] * strides[1]
1697 suboffsets[1] = start[2] * strides[2] + ...
1698*/
1699static int
1700init_slice(Py_buffer *base, PyObject *key, int dim)
1701{
1702 Py_ssize_t start, stop, step, slicelength;
1703
1704 if (PySlice_GetIndicesEx(key, base->shape[dim],
1705 &start, &stop, &step, &slicelength) < 0) {
1706 return -1;
1707 }
1708
1709
1710 if (base->suboffsets == NULL || dim == 0) {
1711 adjust_buf:
1712 base->buf = (char *)base->buf + base->strides[dim] * start;
1713 }
1714 else {
1715 Py_ssize_t n = dim-1;
1716 while (n >= 0 && base->suboffsets[n] < 0)
1717 n--;
1718 if (n < 0)
1719 goto adjust_buf; /* all suboffsets are negative */
1720 base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
1721 }
1722 base->shape[dim] = slicelength;
1723 base->strides[dim] = base->strides[dim] * step;
1724
1725 return 0;
1726}
1727
1728static int
1729copy_structure(Py_buffer *base)
1730{
1731 Py_ssize_t *shape = NULL, *strides = NULL, *suboffsets = NULL;
1732 Py_ssize_t i;
1733
1734 shape = PyMem_Malloc(base->ndim * (sizeof *shape));
1735 strides = PyMem_Malloc(base->ndim * (sizeof *strides));
1736 if (shape == NULL || strides == NULL)
1737 goto err_nomem;
1738
1739 suboffsets = NULL;
1740 if (base->suboffsets) {
1741 suboffsets = PyMem_Malloc(base->ndim * (sizeof *suboffsets));
1742 if (suboffsets == NULL)
1743 goto err_nomem;
1744 }
1745
1746 for (i = 0; i < base->ndim; i++) {
1747 shape[i] = base->shape[i];
1748 strides[i] = base->strides[i];
1749 if (suboffsets)
1750 suboffsets[i] = base->suboffsets[i];
1751 }
1752
1753 base->shape = shape;
1754 base->strides = strides;
1755 base->suboffsets = suboffsets;
1756
1757 return 0;
1758
1759err_nomem:
1760 PyErr_NoMemory();
1761 PyMem_XFree(shape);
1762 PyMem_XFree(strides);
1763 PyMem_XFree(suboffsets);
1764 return -1;
1765}
1766
1767static PyObject *
1768ndarray_subscript(NDArrayObject *self, PyObject *key)
1769{
1770 NDArrayObject *nd;
1771 ndbuf_t *ndbuf;
1772 Py_buffer *base = &self->head->base;
1773
1774 if (base->ndim == 0) {
1775 if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
1776 return unpack_single(base->buf, base->format, base->itemsize);
1777 }
1778 else if (key == Py_Ellipsis) {
1779 Py_INCREF(self);
1780 return (PyObject *)self;
1781 }
1782 else {
1783 PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
1784 return NULL;
1785 }
1786 }
1787 if (PyIndex_Check(key)) {
1788 Py_ssize_t index = PyLong_AsSsize_t(key);
1789 if (index == -1 && PyErr_Occurred())
1790 return NULL;
1791 return ndarray_item(self, index);
1792 }
1793
1794 nd = (NDArrayObject *)ndarray_new(&NDArray_Type, NULL, NULL);
1795 if (nd == NULL)
1796 return NULL;
1797
1798 /* new ndarray is a consumer */
1799 if (ndarray_init_staticbuf((PyObject *)self, nd, PyBUF_FULL_RO) < 0) {
1800 Py_DECREF(nd);
1801 return NULL;
1802 }
1803
1804 /* copy shape, strides and suboffsets */
1805 ndbuf = nd->head;
1806 base = &ndbuf->base;
1807 if (copy_structure(base) < 0) {
1808 Py_DECREF(nd);
1809 return NULL;
1810 }
1811 ndbuf->flags |= ND_OWN_ARRAYS;
1812
1813 if (PySlice_Check(key)) {
1814 /* one-dimensional slice */
1815 if (init_slice(base, key, 0) < 0)
1816 goto err_occurred;
1817 }
1818 else if PyTuple_Check(key) {
1819 /* multi-dimensional slice */
1820 PyObject *tuple = key;
1821 Py_ssize_t i, n;
1822
1823 n = PyTuple_GET_SIZE(tuple);
1824 for (i = 0; i < n; i++) {
1825 key = PyTuple_GET_ITEM(tuple, i);
1826 if (!PySlice_Check(key))
1827 goto type_error;
1828 if (init_slice(base, key, (int)i) < 0)
1829 goto err_occurred;
1830 }
1831 }
1832 else {
1833 goto type_error;
1834 }
1835
1836 init_len(base);
1837 init_flags(ndbuf);
1838
1839 return (PyObject *)nd;
1840
1841
1842type_error:
1843 PyErr_Format(PyExc_TypeError,
1844 "cannot index memory using \"%.200s\"",
1845 key->ob_type->tp_name);
1846err_occurred:
1847 Py_DECREF(nd);
1848 return NULL;
1849}
1850
1851
1852static int
1853ndarray_ass_subscript(NDArrayObject *self, PyObject *key, PyObject *value)
1854{
1855 NDArrayObject *nd;
1856 Py_buffer *dest = &self->head->base;
1857 Py_buffer src;
1858 char *ptr;
1859 Py_ssize_t index;
1860 int ret = -1;
1861
1862 if (dest->readonly) {
1863 PyErr_SetString(PyExc_TypeError, "ndarray is not writable");
1864 return -1;
1865 }
1866 if (value == NULL) {
1867 PyErr_SetString(PyExc_TypeError, "ndarray data cannot be deleted");
1868 return -1;
1869 }
1870 if (dest->ndim == 0) {
1871 if (key == Py_Ellipsis ||
1872 (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
1873 ptr = (char *)dest->buf;
1874 return pack_single(ptr, value, dest->format, dest->itemsize);
1875 }
1876 else {
1877 PyErr_SetString(PyExc_TypeError, "invalid indexing of scalar");
1878 return -1;
1879 }
1880 }
1881 if (dest->ndim == 1 && PyIndex_Check(key)) {
1882 /* rvalue must be a single item */
1883 index = PyLong_AsSsize_t(key);
1884 if (index == -1 && PyErr_Occurred())
1885 return -1;
1886 else {
1887 ptr = ptr_from_index(dest, index);
1888 if (ptr == NULL)
1889 return -1;
1890 }
1891 return pack_single(ptr, value, dest->format, dest->itemsize);
1892 }
1893
1894 /* rvalue must be an exporter */
1895 if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) == -1)
1896 return -1;
1897
1898 nd = (NDArrayObject *)ndarray_subscript(self, key);
1899 if (nd != NULL) {
1900 dest = &nd->head->base;
1901 ret = copy_buffer(dest, &src);
1902 Py_DECREF(nd);
1903 }
1904
1905 PyBuffer_Release(&src);
1906 return ret;
1907}
1908
1909static PyObject *
1910slice_indices(PyObject *self, PyObject *args)
1911{
1912 PyObject *ret, *key, *tmp;
1913 Py_ssize_t s[4]; /* start, stop, step, slicelength */
1914 Py_ssize_t i, len;
1915
1916 if (!PyArg_ParseTuple(args, "On", &key, &len)) {
1917 return NULL;
1918 }
1919 if (!PySlice_Check(key)) {
1920 PyErr_SetString(PyExc_TypeError,
1921 "first argument must be a slice object");
1922 return NULL;
1923 }
1924 if (PySlice_GetIndicesEx(key, len, &s[0], &s[1], &s[2], &s[3]) < 0) {
1925 return NULL;
1926 }
1927
1928 ret = PyTuple_New(4);
1929 if (ret == NULL)
1930 return NULL;
1931
1932 for (i = 0; i < 4; i++) {
1933 tmp = PyLong_FromSsize_t(s[i]);
1934 if (tmp == NULL)
1935 goto error;
1936 PyTuple_SET_ITEM(ret, i, tmp);
1937 }
1938
1939 return ret;
1940
1941error:
1942 Py_DECREF(ret);
1943 return NULL;
1944}
1945
1946
1947static PyMappingMethods ndarray_as_mapping = {
1948 NULL, /* mp_length */
1949 (binaryfunc)ndarray_subscript, /* mp_subscript */
1950 (objobjargproc)ndarray_ass_subscript /* mp_ass_subscript */
1951};
1952
1953static PySequenceMethods ndarray_as_sequence = {
1954 0, /* sq_length */
1955 0, /* sq_concat */
1956 0, /* sq_repeat */
1957 (ssizeargfunc)ndarray_item, /* sq_item */
1958};
1959
1960
1961/**************************************************************************/
1962/* getters */
1963/**************************************************************************/
1964
1965static PyObject *
1966ssize_array_as_tuple(Py_ssize_t *array, Py_ssize_t len)
1967{
1968 PyObject *tuple, *x;
1969 Py_ssize_t i;
1970
1971 if (array == NULL)
1972 return PyTuple_New(0);
1973
1974 tuple = PyTuple_New(len);
1975 if (tuple == NULL)
1976 return NULL;
1977
1978 for (i = 0; i < len; i++) {
1979 x = PyLong_FromSsize_t(array[i]);
1980 if (x == NULL) {
1981 Py_DECREF(tuple);
1982 return NULL;
1983 }
1984 PyTuple_SET_ITEM(tuple, i, x);
1985 }
1986
1987 return tuple;
1988}
1989
1990static PyObject *
1991ndarray_get_flags(NDArrayObject *self, void *closure)
1992{
1993 return PyLong_FromLong(self->head->flags);
1994}
1995
1996static PyObject *
1997ndarray_get_offset(NDArrayObject *self, void *closure)
1998{
1999 ndbuf_t *ndbuf = self->head;
2000 return PyLong_FromSsize_t(ndbuf->offset);
2001}
2002
2003static PyObject *
2004ndarray_get_obj(NDArrayObject *self, void *closure)
2005{
2006 Py_buffer *base = &self->head->base;
2007
2008 if (base->obj == NULL) {
2009 Py_RETURN_NONE;
2010 }
2011 Py_INCREF(base->obj);
2012 return base->obj;
2013}
2014
2015static PyObject *
2016ndarray_get_nbytes(NDArrayObject *self, void *closure)
2017{
2018 Py_buffer *base = &self->head->base;
2019 return PyLong_FromSsize_t(base->len);
2020}
2021
2022static PyObject *
2023ndarray_get_readonly(NDArrayObject *self, void *closure)
2024{
2025 Py_buffer *base = &self->head->base;
2026 return PyLong_FromLong(base->readonly);
2027}
2028
2029static PyObject *
2030ndarray_get_itemsize(NDArrayObject *self, void *closure)
2031{
2032 Py_buffer *base = &self->head->base;
2033 return PyLong_FromSsize_t(base->itemsize);
2034}
2035
2036static PyObject *
2037ndarray_get_format(NDArrayObject *self, void *closure)
2038{
2039 Py_buffer *base = &self->head->base;
2040 char *fmt = base->format ? base->format : "";
2041 return PyUnicode_FromString(fmt);
2042}
2043
2044static PyObject *
2045ndarray_get_ndim(NDArrayObject *self, void *closure)
2046{
2047 Py_buffer *base = &self->head->base;
2048 return PyLong_FromSsize_t(base->ndim);
2049}
2050
2051static PyObject *
2052ndarray_get_shape(NDArrayObject *self, void *closure)
2053{
2054 Py_buffer *base = &self->head->base;
2055 return ssize_array_as_tuple(base->shape, base->ndim);
2056}
2057
2058static PyObject *
2059ndarray_get_strides(NDArrayObject *self, void *closure)
2060{
2061 Py_buffer *base = &self->head->base;
2062 return ssize_array_as_tuple(base->strides, base->ndim);
2063}
2064
2065static PyObject *
2066ndarray_get_suboffsets(NDArrayObject *self, void *closure)
2067{
2068 Py_buffer *base = &self->head->base;
2069 return ssize_array_as_tuple(base->suboffsets, base->ndim);
2070}
2071
2072static PyObject *
2073ndarray_c_contig(PyObject *self, PyObject *dummy)
2074{
2075 NDArrayObject *nd = (NDArrayObject *)self;
2076 int ret = PyBuffer_IsContiguous(&nd->head->base, 'C');
2077
2078 if (ret != ND_C_CONTIGUOUS(nd->head->flags)) {
2079 PyErr_SetString(PyExc_RuntimeError,
2080 "results from PyBuffer_IsContiguous() and flags differ");
2081 return NULL;
2082 }
2083 return PyBool_FromLong(ret);
2084}
2085
2086static PyObject *
2087ndarray_fortran_contig(PyObject *self, PyObject *dummy)
2088{
2089 NDArrayObject *nd = (NDArrayObject *)self;
2090 int ret = PyBuffer_IsContiguous(&nd->head->base, 'F');
2091
2092 if (ret != ND_FORTRAN_CONTIGUOUS(nd->head->flags)) {
2093 PyErr_SetString(PyExc_RuntimeError,
2094 "results from PyBuffer_IsContiguous() and flags differ");
2095 return NULL;
2096 }
2097 return PyBool_FromLong(ret);
2098}
2099
2100static PyObject *
2101ndarray_contig(PyObject *self, PyObject *dummy)
2102{
2103 NDArrayObject *nd = (NDArrayObject *)self;
2104 int ret = PyBuffer_IsContiguous(&nd->head->base, 'A');
2105
2106 if (ret != ND_ANY_CONTIGUOUS(nd->head->flags)) {
2107 PyErr_SetString(PyExc_RuntimeError,
2108 "results from PyBuffer_IsContiguous() and flags differ");
2109 return NULL;
2110 }
2111 return PyBool_FromLong(ret);
2112}
2113
2114
2115static PyGetSetDef ndarray_getset [] =
2116{
2117 /* ndbuf */
2118 { "flags", (getter)ndarray_get_flags, NULL, NULL, NULL},
2119 { "offset", (getter)ndarray_get_offset, NULL, NULL, NULL},
2120 /* ndbuf.base */
2121 { "obj", (getter)ndarray_get_obj, NULL, NULL, NULL},
2122 { "nbytes", (getter)ndarray_get_nbytes, NULL, NULL, NULL},
2123 { "readonly", (getter)ndarray_get_readonly, NULL, NULL, NULL},
2124 { "itemsize", (getter)ndarray_get_itemsize, NULL, NULL, NULL},
2125 { "format", (getter)ndarray_get_format, NULL, NULL, NULL},
2126 { "ndim", (getter)ndarray_get_ndim, NULL, NULL, NULL},
2127 { "shape", (getter)ndarray_get_shape, NULL, NULL, NULL},
2128 { "strides", (getter)ndarray_get_strides, NULL, NULL, NULL},
2129 { "suboffsets", (getter)ndarray_get_suboffsets, NULL, NULL, NULL},
2130 { "c_contiguous", (getter)ndarray_c_contig, NULL, NULL, NULL},
2131 { "f_contiguous", (getter)ndarray_fortran_contig, NULL, NULL, NULL},
2132 { "contiguous", (getter)ndarray_contig, NULL, NULL, NULL},
2133 {NULL}
2134};
2135
2136static PyObject *
2137ndarray_tolist(PyObject *self, PyObject *dummy)
2138{
2139 return ndarray_as_list((NDArrayObject *)self);
2140}
2141
2142static PyObject *
2143ndarray_tobytes(PyObject *self, PyObject *dummy)
2144{
2145 ndbuf_t *ndbuf = ((NDArrayObject *)self)->head;
2146 Py_buffer *src = &ndbuf->base;
2147 Py_buffer dest;
2148 PyObject *ret = NULL;
2149 char *mem;
2150
2151 if (ND_C_CONTIGUOUS(ndbuf->flags))
2152 return PyBytes_FromStringAndSize(src->buf, src->len);
2153
2154 assert(src->shape != NULL);
2155 assert(src->strides != NULL);
2156 assert(src->ndim > 0);
2157
2158 mem = PyMem_Malloc(src->len);
2159 if (mem == NULL) {
2160 PyErr_NoMemory();
2161 return NULL;
2162 }
2163
2164 dest = *src;
2165 dest.buf = mem;
2166 dest.suboffsets = NULL;
2167 dest.strides = strides_from_shape(ndbuf, 0);
2168 if (dest.strides == NULL)
2169 goto out;
2170 if (copy_buffer(&dest, src) < 0)
2171 goto out;
2172
2173 ret = PyBytes_FromStringAndSize(mem, src->len);
2174
2175out:
2176 PyMem_XFree(dest.strides);
2177 PyMem_Free(mem);
2178 return ret;
2179}
2180
2181/* add redundant (negative) suboffsets for testing */
2182static PyObject *
2183ndarray_add_suboffsets(PyObject *self, PyObject *dummy)
2184{
2185 NDArrayObject *nd = (NDArrayObject *)self;
2186 Py_buffer *base = &nd->head->base;
2187 Py_ssize_t i;
2188
2189 if (base->suboffsets != NULL) {
2190 PyErr_SetString(PyExc_TypeError,
2191 "cannot add suboffsets to PIL-style array");
2192 return NULL;
2193 }
2194 if (base->strides == NULL) {
2195 PyErr_SetString(PyExc_TypeError,
2196 "cannot add suboffsets to array without strides");
2197 return NULL;
2198 }
2199
2200 base->suboffsets = PyMem_Malloc(base->ndim * (sizeof *base->suboffsets));
2201 if (base->suboffsets == NULL) {
2202 PyErr_NoMemory();
2203 return NULL;
2204 }
2205
2206 for (i = 0; i < base->ndim; i++)
2207 base->suboffsets[i] = -1;
2208
2209 Py_RETURN_NONE;
2210}
2211
2212/* Test PyMemoryView_FromBuffer(): return a memoryview from a static buffer.
2213 Obviously this is fragile and only one such view may be active at any
2214 time. Never use anything like this in real code! */
2215static char *infobuf = NULL;
2216static PyObject *
2217ndarray_memoryview_from_buffer(PyObject *self, PyObject *dummy)
2218{
2219 const NDArrayObject *nd = (NDArrayObject *)self;
2220 const Py_buffer *view = &nd->head->base;
2221 const ndbuf_t *ndbuf;
2222 static char format[ND_MAX_NDIM+1];
2223 static Py_ssize_t shape[ND_MAX_NDIM];
2224 static Py_ssize_t strides[ND_MAX_NDIM];
2225 static Py_ssize_t suboffsets[ND_MAX_NDIM];
2226 static Py_buffer info;
2227 char *p;
2228
2229 if (!ND_IS_CONSUMER(nd))
2230 ndbuf = nd->head; /* self is ndarray/original exporter */
2231 else if (NDArray_Check(view->obj) && !ND_IS_CONSUMER(view->obj))
2232 /* self is ndarray and consumer from ndarray/original exporter */
2233 ndbuf = ((NDArrayObject *)view->obj)->head;
2234 else {
2235 PyErr_SetString(PyExc_TypeError,
2236 "memoryview_from_buffer(): ndarray must be original exporter or "
2237 "consumer from ndarray/original exporter");
2238 return NULL;
2239 }
2240
2241 info = *view;
2242 p = PyMem_Realloc(infobuf, ndbuf->len);
2243 if (p == NULL) {
2244 PyMem_Free(infobuf);
2245 PyErr_NoMemory();
2246 infobuf = NULL;
2247 return NULL;
2248 }
2249 else {
2250 infobuf = p;
2251 }
2252 /* copy the complete raw data */
2253 memcpy(infobuf, ndbuf->data, ndbuf->len);
2254 info.buf = infobuf + ((char *)view->buf - ndbuf->data);
2255
2256 if (view->format) {
2257 if (strlen(view->format) > ND_MAX_NDIM) {
2258 PyErr_Format(PyExc_TypeError,
2259 "memoryview_from_buffer: format is limited to %d characters",
2260 ND_MAX_NDIM);
2261 return NULL;
2262 }
2263 strcpy(format, view->format);
2264 info.format = format;
2265 }
2266 if (view->ndim > ND_MAX_NDIM) {
2267 PyErr_Format(PyExc_TypeError,
2268 "memoryview_from_buffer: ndim is limited to %d", ND_MAX_NDIM);
2269 return NULL;
2270 }
2271 if (view->shape) {
2272 memcpy(shape, view->shape, view->ndim * sizeof(Py_ssize_t));
2273 info.shape = shape;
2274 }
2275 if (view->strides) {
2276 memcpy(strides, view->strides, view->ndim * sizeof(Py_ssize_t));
2277 info.strides = strides;
2278 }
2279 if (view->suboffsets) {
2280 memcpy(suboffsets, view->suboffsets, view->ndim * sizeof(Py_ssize_t));
2281 info.suboffsets = suboffsets;
2282 }
2283
2284 return PyMemoryView_FromBuffer(&info);
2285}
2286
2287/* Get a single item from bufobj at the location specified by seq.
2288 seq is a list or tuple of indices. The purpose of this function
2289 is to check other functions against PyBuffer_GetPointer(). */
2290static PyObject *
2291get_pointer(PyObject *self, PyObject *args)
2292{
2293 PyObject *ret = NULL, *bufobj, *seq;
2294 Py_buffer view;
2295 Py_ssize_t indices[ND_MAX_NDIM];
2296 Py_ssize_t i;
2297 void *ptr;
2298
2299 if (!PyArg_ParseTuple(args, "OO", &bufobj, &seq)) {
2300 return NULL;
2301 }
2302
2303 CHECK_LIST_OR_TUPLE(seq);
2304 if (PyObject_GetBuffer(bufobj, &view, PyBUF_FULL_RO) < 0)
2305 return NULL;
2306
2307 if (view.ndim > ND_MAX_NDIM) {
2308 PyErr_Format(PyExc_ValueError,
2309 "get_pointer(): ndim > %d", ND_MAX_NDIM);
2310 goto out;
2311 }
2312 if (PySequence_Fast_GET_SIZE(seq) != view.ndim) {
2313 PyErr_SetString(PyExc_ValueError,
2314 "get_pointer(): len(indices) != ndim");
2315 goto out;
2316 }
2317
2318 for (i = 0; i < view.ndim; i++) {
2319 PyObject *x = PySequence_Fast_GET_ITEM(seq, i);
2320 indices[i] = PyLong_AsSsize_t(x);
2321 if (PyErr_Occurred())
2322 goto out;
2323 if (indices[i] < 0 || indices[i] >= view.shape[i]) {
2324 PyErr_Format(PyExc_ValueError,
2325 "get_pointer(): invalid index %zd at position %zd",
2326 indices[i], i);
2327 goto out;
2328 }
2329 }
2330
2331 ptr = PyBuffer_GetPointer(&view, indices);
2332 ret = unpack_single(ptr, view.format, view.itemsize);
2333
2334out:
2335 PyBuffer_Release(&view);
2336 return ret;
2337}
2338
Stefan Krah5d953182012-05-16 20:41:56 +02002339static PyObject *
2340get_sizeof_void_p(PyObject *self)
2341{
2342 return PyLong_FromSize_t(sizeof(void *));
2343}
2344
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002345static char
2346get_ascii_order(PyObject *order)
2347{
2348 PyObject *ascii_order;
2349 char ord;
2350
2351 if (!PyUnicode_Check(order)) {
2352 PyErr_SetString(PyExc_TypeError,
2353 "order must be a string");
2354 return CHAR_MAX;
2355 }
2356
2357 ascii_order = PyUnicode_AsASCIIString(order);
2358 if (ascii_order == NULL) {
2359 return CHAR_MAX;
2360 }
2361
2362 ord = PyBytes_AS_STRING(ascii_order)[0];
2363 Py_DECREF(ascii_order);
Stefan Krah66e63172012-08-23 15:53:45 +02002364
2365 if (ord != 'C' && ord != 'F' && ord != 'A') {
2366 PyErr_SetString(PyExc_ValueError,
2367 "invalid order, must be C, F or A");
2368 return CHAR_MAX;
2369 }
2370
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002371 return ord;
2372}
2373
2374/* Get a contiguous memoryview. */
2375static PyObject *
2376get_contiguous(PyObject *self, PyObject *args)
2377{
2378 PyObject *obj;
2379 PyObject *buffertype;
2380 PyObject *order;
2381 long type;
2382 char ord;
2383
2384 if (!PyArg_ParseTuple(args, "OOO", &obj, &buffertype, &order)) {
2385 return NULL;
2386 }
2387
2388 if (!PyLong_Check(buffertype)) {
2389 PyErr_SetString(PyExc_TypeError,
2390 "buffertype must be PyBUF_READ or PyBUF_WRITE");
2391 return NULL;
2392 }
Stefan Krah66e63172012-08-23 15:53:45 +02002393
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002394 type = PyLong_AsLong(buffertype);
2395 if (type == -1 && PyErr_Occurred()) {
2396 return NULL;
2397 }
Stefan Krah66e63172012-08-23 15:53:45 +02002398 if (type != PyBUF_READ && type != PyBUF_WRITE) {
2399 PyErr_SetString(PyExc_ValueError,
2400 "invalid buffer type");
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002401 return NULL;
2402 }
2403
Stefan Krah66e63172012-08-23 15:53:45 +02002404 ord = get_ascii_order(order);
2405 if (ord == CHAR_MAX)
2406 return NULL;
2407
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002408 return PyMemoryView_GetContiguous(obj, (int)type, ord);
2409}
2410
Stefan Krah7d12d9d2012-07-28 12:25:55 +02002411/* PyBuffer_ToContiguous() */
2412static PyObject *
2413py_buffer_to_contiguous(PyObject *self, PyObject *args)
2414{
2415 PyObject *obj;
2416 PyObject *order;
2417 PyObject *ret = NULL;
2418 int flags;
2419 char ord;
2420 Py_buffer view;
2421 char *buf = NULL;
2422
2423 if (!PyArg_ParseTuple(args, "OOi", &obj, &order, &flags)) {
2424 return NULL;
2425 }
2426
2427 if (PyObject_GetBuffer(obj, &view, flags) < 0) {
2428 return NULL;
2429 }
2430
2431 ord = get_ascii_order(order);
2432 if (ord == CHAR_MAX) {
2433 goto out;
2434 }
2435
2436 buf = PyMem_Malloc(view.len);
2437 if (buf == NULL) {
2438 PyErr_NoMemory();
2439 goto out;
2440 }
2441
2442 if (PyBuffer_ToContiguous(buf, &view, view.len, ord) < 0) {
2443 goto out;
2444 }
2445
2446 ret = PyBytes_FromStringAndSize(buf, view.len);
2447
2448out:
2449 PyBuffer_Release(&view);
2450 PyMem_XFree(buf);
2451 return ret;
2452}
2453
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002454static int
2455fmtcmp(const char *fmt1, const char *fmt2)
2456{
2457 if (fmt1 == NULL) {
2458 return fmt2 == NULL || strcmp(fmt2, "B") == 0;
2459 }
2460 if (fmt2 == NULL) {
2461 return fmt1 == NULL || strcmp(fmt1, "B") == 0;
2462 }
2463 return strcmp(fmt1, fmt2) == 0;
2464}
2465
2466static int
2467arraycmp(const Py_ssize_t *a1, const Py_ssize_t *a2, const Py_ssize_t *shape,
2468 Py_ssize_t ndim)
2469{
2470 Py_ssize_t i;
2471
2472 if (ndim == 1 && shape && shape[0] == 1) {
2473 /* This is for comparing strides: For example, the array
2474 [175], shape=[1], strides=[-5] is considered contiguous. */
2475 return 1;
2476 }
2477
2478 for (i = 0; i < ndim; i++) {
2479 if (a1[i] != a2[i]) {
2480 return 0;
2481 }
2482 }
2483
2484 return 1;
2485}
2486
2487/* Compare two contiguous buffers for physical equality. */
2488static PyObject *
2489cmp_contig(PyObject *self, PyObject *args)
2490{
2491 PyObject *b1, *b2; /* buffer objects */
2492 Py_buffer v1, v2;
2493 PyObject *ret;
2494 int equal = 0;
2495
2496 if (!PyArg_ParseTuple(args, "OO", &b1, &b2)) {
2497 return NULL;
2498 }
2499
2500 if (PyObject_GetBuffer(b1, &v1, PyBUF_FULL_RO) < 0) {
2501 PyErr_SetString(PyExc_TypeError,
2502 "cmp_contig: first argument does not implement the buffer "
2503 "protocol");
2504 return NULL;
2505 }
2506 if (PyObject_GetBuffer(b2, &v2, PyBUF_FULL_RO) < 0) {
2507 PyErr_SetString(PyExc_TypeError,
2508 "cmp_contig: second argument does not implement the buffer "
2509 "protocol");
2510 PyBuffer_Release(&v1);
2511 return NULL;
2512 }
2513
2514 if (!(PyBuffer_IsContiguous(&v1, 'C')&&PyBuffer_IsContiguous(&v2, 'C')) &&
2515 !(PyBuffer_IsContiguous(&v1, 'F')&&PyBuffer_IsContiguous(&v2, 'F'))) {
2516 goto result;
2517 }
2518
2519 /* readonly may differ if created from non-contiguous */
2520 if (v1.len != v2.len ||
2521 v1.itemsize != v2.itemsize ||
2522 v1.ndim != v2.ndim ||
2523 !fmtcmp(v1.format, v2.format) ||
2524 !!v1.shape != !!v2.shape ||
2525 !!v1.strides != !!v2.strides ||
2526 !!v1.suboffsets != !!v2.suboffsets) {
2527 goto result;
2528 }
2529
2530 if ((v1.shape && !arraycmp(v1.shape, v2.shape, NULL, v1.ndim)) ||
2531 (v1.strides && !arraycmp(v1.strides, v2.strides, v1.shape, v1.ndim)) ||
2532 (v1.suboffsets && !arraycmp(v1.suboffsets, v2.suboffsets, NULL,
2533 v1.ndim))) {
2534 goto result;
2535 }
2536
2537 if (memcmp((char *)v1.buf, (char *)v2.buf, v1.len) != 0) {
2538 goto result;
2539 }
2540
2541 equal = 1;
2542
2543result:
2544 PyBuffer_Release(&v1);
2545 PyBuffer_Release(&v2);
2546
2547 ret = equal ? Py_True : Py_False;
2548 Py_INCREF(ret);
2549 return ret;
2550}
2551
2552static PyObject *
2553is_contiguous(PyObject *self, PyObject *args)
2554{
2555 PyObject *obj;
2556 PyObject *order;
2557 PyObject *ret = NULL;
2558 Py_buffer view;
2559 char ord;
2560
2561 if (!PyArg_ParseTuple(args, "OO", &obj, &order)) {
2562 return NULL;
2563 }
2564
2565 if (PyObject_GetBuffer(obj, &view, PyBUF_FULL_RO) < 0) {
2566 PyErr_SetString(PyExc_TypeError,
2567 "is_contiguous: object does not implement the buffer "
2568 "protocol");
2569 return NULL;
2570 }
2571
2572 ord = get_ascii_order(order);
2573 if (ord == CHAR_MAX) {
2574 goto release;
2575 }
2576
2577 ret = PyBuffer_IsContiguous(&view, ord) ? Py_True : Py_False;
2578 Py_INCREF(ret);
2579
2580release:
2581 PyBuffer_Release(&view);
2582 return ret;
2583}
2584
2585static Py_hash_t
2586ndarray_hash(PyObject *self)
2587{
2588 const NDArrayObject *nd = (NDArrayObject *)self;
2589 const Py_buffer *view = &nd->head->base;
2590 PyObject *bytes;
2591 Py_hash_t hash;
2592
2593 if (!view->readonly) {
2594 PyErr_SetString(PyExc_ValueError,
2595 "cannot hash writable ndarray object");
2596 return -1;
2597 }
2598 if (view->obj != NULL && PyObject_Hash(view->obj) == -1) {
2599 return -1;
2600 }
2601
2602 bytes = ndarray_tobytes(self, NULL);
2603 if (bytes == NULL) {
2604 return -1;
2605 }
2606
2607 hash = PyObject_Hash(bytes);
2608 Py_DECREF(bytes);
2609 return hash;
2610}
2611
2612
2613static PyMethodDef ndarray_methods [] =
2614{
2615 { "tolist", ndarray_tolist, METH_NOARGS, NULL },
2616 { "tobytes", ndarray_tobytes, METH_NOARGS, NULL },
2617 { "push", (PyCFunction)ndarray_push, METH_VARARGS|METH_KEYWORDS, NULL },
2618 { "pop", ndarray_pop, METH_NOARGS, NULL },
2619 { "add_suboffsets", ndarray_add_suboffsets, METH_NOARGS, NULL },
2620 { "memoryview_from_buffer", ndarray_memoryview_from_buffer, METH_NOARGS, NULL },
2621 {NULL}
2622};
2623
2624static PyTypeObject NDArray_Type = {
2625 PyVarObject_HEAD_INIT(NULL, 0)
2626 "ndarray", /* Name of this type */
2627 sizeof(NDArrayObject), /* Basic object size */
2628 0, /* Item size for varobject */
2629 (destructor)ndarray_dealloc, /* tp_dealloc */
2630 0, /* tp_print */
2631 0, /* tp_getattr */
2632 0, /* tp_setattr */
2633 0, /* tp_compare */
2634 0, /* tp_repr */
2635 0, /* tp_as_number */
2636 &ndarray_as_sequence, /* tp_as_sequence */
2637 &ndarray_as_mapping, /* tp_as_mapping */
2638 (hashfunc)ndarray_hash, /* tp_hash */
2639 0, /* tp_call */
2640 0, /* tp_str */
2641 PyObject_GenericGetAttr, /* tp_getattro */
2642 0, /* tp_setattro */
2643 &ndarray_as_buffer, /* tp_as_buffer */
2644 Py_TPFLAGS_DEFAULT, /* tp_flags */
2645 0, /* tp_doc */
2646 0, /* tp_traverse */
2647 0, /* tp_clear */
2648 0, /* tp_richcompare */
2649 0, /* tp_weaklistoffset */
2650 0, /* tp_iter */
2651 0, /* tp_iternext */
2652 ndarray_methods, /* tp_methods */
2653 0, /* tp_members */
2654 ndarray_getset, /* tp_getset */
2655 0, /* tp_base */
2656 0, /* tp_dict */
2657 0, /* tp_descr_get */
2658 0, /* tp_descr_set */
2659 0, /* tp_dictoffset */
2660 ndarray_init, /* tp_init */
2661 0, /* tp_alloc */
2662 ndarray_new, /* tp_new */
2663};
2664
Stefan Krahbf6c7ec2012-03-05 14:37:34 +01002665/**************************************************************************/
2666/* StaticArray Object */
2667/**************************************************************************/
2668
2669static PyTypeObject StaticArray_Type;
2670
2671typedef struct {
2672 PyObject_HEAD
2673 int legacy_mode; /* if true, use the view.obj==NULL hack */
2674} StaticArrayObject;
2675
2676static char static_mem[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
2677static Py_ssize_t static_shape[1] = {12};
2678static Py_ssize_t static_strides[1] = {1};
2679static Py_buffer static_buffer = {
2680 static_mem, /* buf */
2681 NULL, /* obj */
2682 12, /* len */
2683 1, /* itemsize */
2684 1, /* readonly */
2685 1, /* ndim */
2686 "B", /* format */
2687 static_shape, /* shape */
2688 static_strides, /* strides */
2689 NULL, /* suboffsets */
2690 NULL /* internal */
2691};
2692
2693static PyObject *
2694staticarray_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
2695{
2696 return (PyObject *)PyObject_New(StaticArrayObject, &StaticArray_Type);
2697}
2698
2699static int
2700staticarray_init(PyObject *self, PyObject *args, PyObject *kwds)
2701{
2702 StaticArrayObject *a = (StaticArrayObject *)self;
2703 static char *kwlist[] = {
2704 "legacy_mode", NULL
2705 };
2706 PyObject *legacy_mode = Py_False;
2707
2708 if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O", kwlist, &legacy_mode))
2709 return -1;
2710
2711 a->legacy_mode = (legacy_mode != Py_False);
2712 return 0;
2713}
2714
2715static void
2716staticarray_dealloc(StaticArrayObject *self)
2717{
2718 PyObject_Del(self);
2719}
2720
2721/* Return a buffer for a PyBUF_FULL_RO request. Flags are not checked,
2722 which makes this object a non-compliant exporter! */
2723static int
2724staticarray_getbuf(StaticArrayObject *self, Py_buffer *view, int flags)
2725{
2726 *view = static_buffer;
2727
2728 if (self->legacy_mode) {
2729 view->obj = NULL; /* Don't use this in new code. */
2730 }
2731 else {
2732 view->obj = (PyObject *)self;
2733 Py_INCREF(view->obj);
2734 }
2735
2736 return 0;
2737}
2738
2739static PyBufferProcs staticarray_as_buffer = {
2740 (getbufferproc)staticarray_getbuf, /* bf_getbuffer */
2741 NULL, /* bf_releasebuffer */
2742};
2743
2744static PyTypeObject StaticArray_Type = {
2745 PyVarObject_HEAD_INIT(NULL, 0)
2746 "staticarray", /* Name of this type */
2747 sizeof(StaticArrayObject), /* Basic object size */
2748 0, /* Item size for varobject */
2749 (destructor)staticarray_dealloc, /* tp_dealloc */
2750 0, /* tp_print */
2751 0, /* tp_getattr */
2752 0, /* tp_setattr */
2753 0, /* tp_compare */
2754 0, /* tp_repr */
2755 0, /* tp_as_number */
2756 0, /* tp_as_sequence */
2757 0, /* tp_as_mapping */
2758 0, /* tp_hash */
2759 0, /* tp_call */
2760 0, /* tp_str */
2761 0, /* tp_getattro */
2762 0, /* tp_setattro */
2763 &staticarray_as_buffer, /* tp_as_buffer */
2764 Py_TPFLAGS_DEFAULT, /* tp_flags */
2765 0, /* tp_doc */
2766 0, /* tp_traverse */
2767 0, /* tp_clear */
2768 0, /* tp_richcompare */
2769 0, /* tp_weaklistoffset */
2770 0, /* tp_iter */
2771 0, /* tp_iternext */
2772 0, /* tp_methods */
2773 0, /* tp_members */
2774 0, /* tp_getset */
2775 0, /* tp_base */
2776 0, /* tp_dict */
2777 0, /* tp_descr_get */
2778 0, /* tp_descr_set */
2779 0, /* tp_dictoffset */
2780 staticarray_init, /* tp_init */
2781 0, /* tp_alloc */
2782 staticarray_new, /* tp_new */
2783};
2784
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002785
2786static struct PyMethodDef _testbuffer_functions[] = {
2787 {"slice_indices", slice_indices, METH_VARARGS, NULL},
2788 {"get_pointer", get_pointer, METH_VARARGS, NULL},
Stefan Krah5d953182012-05-16 20:41:56 +02002789 {"get_sizeof_void_p", (PyCFunction)get_sizeof_void_p, METH_NOARGS, NULL},
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002790 {"get_contiguous", get_contiguous, METH_VARARGS, NULL},
Stefan Krah7d12d9d2012-07-28 12:25:55 +02002791 {"py_buffer_to_contiguous", py_buffer_to_contiguous, METH_VARARGS, NULL},
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002792 {"is_contiguous", is_contiguous, METH_VARARGS, NULL},
2793 {"cmp_contig", cmp_contig, METH_VARARGS, NULL},
2794 {NULL, NULL}
2795};
2796
2797static struct PyModuleDef _testbuffermodule = {
2798 PyModuleDef_HEAD_INIT,
2799 "_testbuffer",
2800 NULL,
2801 -1,
2802 _testbuffer_functions,
2803 NULL,
2804 NULL,
2805 NULL,
2806 NULL
2807};
2808
2809
2810PyMODINIT_FUNC
2811PyInit__testbuffer(void)
2812{
2813 PyObject *m;
2814
2815 m = PyModule_Create(&_testbuffermodule);
2816 if (m == NULL)
2817 return NULL;
2818
Stefan Krahbf6c7ec2012-03-05 14:37:34 +01002819 Py_TYPE(&NDArray_Type) = &PyType_Type;
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002820 Py_INCREF(&NDArray_Type);
2821 PyModule_AddObject(m, "ndarray", (PyObject *)&NDArray_Type);
2822
Stefan Krahbf6c7ec2012-03-05 14:37:34 +01002823 Py_TYPE(&StaticArray_Type) = &PyType_Type;
2824 Py_INCREF(&StaticArray_Type);
2825 PyModule_AddObject(m, "staticarray", (PyObject *)&StaticArray_Type);
2826
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002827 structmodule = PyImport_ImportModule("struct");
2828 if (structmodule == NULL)
2829 return NULL;
2830
2831 Struct = PyObject_GetAttrString(structmodule, "Struct");
2832 calcsize = PyObject_GetAttrString(structmodule, "calcsize");
2833 if (Struct == NULL || calcsize == NULL)
2834 return NULL;
2835
2836 simple_format = PyUnicode_FromString(simple_fmt);
2837 if (simple_format == NULL)
2838 return NULL;
2839
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02002840 PyModule_AddIntMacro(m, ND_MAX_NDIM);
2841 PyModule_AddIntMacro(m, ND_VAREXPORT);
2842 PyModule_AddIntMacro(m, ND_WRITABLE);
2843 PyModule_AddIntMacro(m, ND_FORTRAN);
2844 PyModule_AddIntMacro(m, ND_SCALAR);
2845 PyModule_AddIntMacro(m, ND_PIL);
2846 PyModule_AddIntMacro(m, ND_GETBUF_FAIL);
2847 PyModule_AddIntMacro(m, ND_GETBUF_UNDEFINED);
2848 PyModule_AddIntMacro(m, ND_REDIRECT);
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002849
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02002850 PyModule_AddIntMacro(m, PyBUF_SIMPLE);
2851 PyModule_AddIntMacro(m, PyBUF_WRITABLE);
2852 PyModule_AddIntMacro(m, PyBUF_FORMAT);
2853 PyModule_AddIntMacro(m, PyBUF_ND);
2854 PyModule_AddIntMacro(m, PyBUF_STRIDES);
2855 PyModule_AddIntMacro(m, PyBUF_INDIRECT);
2856 PyModule_AddIntMacro(m, PyBUF_C_CONTIGUOUS);
2857 PyModule_AddIntMacro(m, PyBUF_F_CONTIGUOUS);
2858 PyModule_AddIntMacro(m, PyBUF_ANY_CONTIGUOUS);
2859 PyModule_AddIntMacro(m, PyBUF_FULL);
2860 PyModule_AddIntMacro(m, PyBUF_FULL_RO);
2861 PyModule_AddIntMacro(m, PyBUF_RECORDS);
2862 PyModule_AddIntMacro(m, PyBUF_RECORDS_RO);
2863 PyModule_AddIntMacro(m, PyBUF_STRIDED);
2864 PyModule_AddIntMacro(m, PyBUF_STRIDED_RO);
2865 PyModule_AddIntMacro(m, PyBUF_CONTIG);
2866 PyModule_AddIntMacro(m, PyBUF_CONTIG_RO);
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002867
Charles-Francois Natali74ca8862013-05-20 19:13:19 +02002868 PyModule_AddIntMacro(m, PyBUF_READ);
2869 PyModule_AddIntMacro(m, PyBUF_WRITE);
Stefan Krah9a2d99e2012-02-25 12:24:21 +01002870
2871 return m;
2872}
2873
2874
2875