Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
LowLevelViews.cxx
Go to the documentation of this file.
1// Bindings
2#include "cpyrt.h"
3
4using namespace cppjit;
5#include "Converters.h"
6#include "CustomPyTypes.h"
7#include "LowLevelViews.h"
8#include "PyStrings.h"
9
10// Standard
11#include <assert.h>
12#include <limits.h>
13#include <map>
14#include <string.h>
15
16//= memoryview-like object ===================================================
17// This is largely setup just like Python builtin memory view objects, with
18// the exceptions that there is no need of a "base" object (it views on C++
19// memory, not a Python object with a buffer interface), it uses the cpyrt
20// converters, and typed results and assignments are supported. Code reused
21// under PSF License Version 2.
22
23//- helpers ------------------------------------------------------------------
24static inline void set_strides(Py_buffer& view, size_t itemsize, bool isfix) {
25 if (isfix) {
26 Py_ssize_t stride = itemsize;
27 for (Py_ssize_t idim = view.ndim - 1; 0 <= idim; --idim) {
28 view.strides[idim] = stride;
29 stride *= view.shape[idim];
30 }
31 } else {
32 view.strides[view.ndim - 1] = itemsize;
33 for (Py_ssize_t idim = 0; idim < view.ndim - 1; ++idim)
34 view.strides[idim] = view.itemsize;
35 }
36}
37
38// The creators mark an outermost dimension of unknown extent with this cap
39// rather than with UNKNOWN_SIZE, keeping byte lengths and loops non-negative.
40static inline Py_ssize_t fake_max(size_t elemsize) {
41 return INT_MAX / (Py_ssize_t)elemsize;
42}
43
44//= cpyrt low level view construction/destruction =========================
45static cpyrt::LowLevelView* ll_new(PyTypeObject* subtype, PyObject*,
46 PyObject*) {
47 // Create a new low level ptr type
48 cpyrt::LowLevelView* pyobj =
49 (cpyrt::LowLevelView*)subtype->tp_alloc(subtype, 0);
50 if (!pyobj)
51 return nullptr;
52
53 memset(&pyobj->fBufInfo, 0, sizeof(Py_buffer));
54 (intptr_t&)pyobj->fBufInfo.internal |= cpyrt::LowLevelView::kIsCppArray;
55 pyobj->fBuf = nullptr;
56 pyobj->fConverter = nullptr;
57 pyobj->fElemCnv = nullptr;
58
59 return pyobj;
60}
61
62//----------------------------------------------------------------------------
63static void ll_dealloc(cpyrt::LowLevelView* pyobj) {
64 // Destruction requires the deletion of the converter (if any)
65 PyMem_Free(pyobj->fBufInfo.shape);
66 PyMem_Free(pyobj->fBufInfo.strides);
67 if ((intptr_t)pyobj->fBufInfo.internal & cpyrt::LowLevelView::kIsOwner) {
68 if ((intptr_t)pyobj->fBufInfo.internal & cpyrt::LowLevelView::kIsCppArray)
69 delete[] pyobj->fBuf;
70 else
71 free(pyobj->fBuf);
72 }
73
74 if (pyobj->fElemCnv != pyobj->fConverter && pyobj->fElemCnv &&
75 pyobj->fElemCnv->HasState())
76 delete pyobj->fElemCnv;
77
78 if (pyobj->fConverter && pyobj->fConverter->HasState())
79 delete pyobj->fConverter;
80
81 Py_TYPE(pyobj)->tp_free((PyObject*)pyobj);
82}
83
84//----------------------------------------------------------------------------
85#define CPYRT_LL_FLAG_GETSET(name, flag, doc) \
86 static PyObject* ll_get##name(cpyrt::LowLevelView* pyobj) { \
87 return PyBool_FromLong((long)((intptr_t)pyobj->fBufInfo.internal & flag)); \
88 } \
89 \
90 static int ll_set##name(cpyrt::LowLevelView* pyobj, PyObject* value, \
91 void*) { \
92 long settrue = PyLong_AsLong(value); \
93 if (settrue == -1 && PyErr_Occurred()) { \
94 PyErr_SetString(PyExc_ValueError, \
95 #doc " should be either True or False"); \
96 return -1; \
97 } \
98 \
99 if ((bool)settrue) \
100 (intptr_t&)pyobj->fBufInfo.internal |= flag; \
101 else \
102 (intptr_t&)pyobj->fBufInfo.internal &= ~flag; \
103 \
104 return 0; \
105 }
106
107// clang-format off
109CPYRT_LL_FLAG_GETSET(cpparray, cpyrt::LowLevelView::kIsCppArray, __cpp_array__)
110// clang-format on
111
112//---------------------------------------------------------------------------
113static PyObject* ll_typecode(cpyrt::LowLevelView* self, void*) {
114 return PyUnicode_FromString((char*)self->fBufInfo.format);
115}
116
117//- Copy memoryview buffers =================================================
118
119// The functions in this section take a source and a destination buffer
120// with the same logical structure: format, itemsize, ndim and shape
121// are identical, with ndim > 0.
122
123// Check for the presence of suboffsets in the first dimension.
124#define HAVE_PTR(suboffsets, dim) (suboffsets && suboffsets[dim] >= 0)
125// Adjust ptr if suboffsets are present.
126#define ADJUST_PTR(ptr, suboffsets, dim) \
127 (HAVE_PTR(suboffsets, dim) ? *((char**)ptr) + suboffsets[dim] : ptr)
128
129// Assumptions: ndim >= 1. The macro tests for a corner case that should
130// perhaps be explicitly forbidden in the PEP.
131#define HAVE_SUBOFFSETS_IN_LAST_DIM(view) \
132 (view->suboffsets && view->suboffsets[dest->ndim - 1] >= 0)
133
134//---------------------------------------------------------------------------
135static inline int last_dim_is_contiguous(const Py_buffer* dest,
136 const Py_buffer* src) {
137 assert(dest->ndim > 0 && src->ndim > 0);
140 dest->strides[dest->ndim - 1] == dest->itemsize &&
141 src->strides[src->ndim - 1] == src->itemsize);
142}
143
144//---------------------------------------------------------------------------
145static inline bool equiv_shape(const Py_buffer* dest, const Py_buffer* src) {
146 // Two shapes are equivalent if they are either equal or identical up
147 // to a zero element at the same position. For example, in NumPy arrays
148 // the shapes [1, 0, 5] and [1, 0, 7] are equivalent.
149 if (dest->ndim != src->ndim)
150 return false;
151
152 for (int i = 0; i < dest->ndim; i++) {
153 if (dest->shape[i] != src->shape[i])
154 return 0;
155 if (dest->shape[i] == 0)
156 break;
157 }
158
159 return true;
160}
161
162//---------------------------------------------------------------------------
163static bool equiv_structure(const Py_buffer* dest, const Py_buffer* src) {
164 // Check that the logical structure of the destination and source buffers
165 // is identical.
166 if (strcmp(dest->format, src->format) != 0 ||
167 dest->itemsize != src->itemsize || !equiv_shape(dest, src)) {
168 PyErr_SetString(PyExc_ValueError, "low level pointer assignment: lvalue "
169 "and rvalue have different structures");
170 return false;
171 }
172
173 return true;
174}
175
176//---------------------------------------------------------------------------
177static void copy_base(const Py_ssize_t* shape, Py_ssize_t itemsize, char* dptr,
178 const Py_ssize_t* dstrides, const Py_ssize_t* dsuboffsets,
179 char* sptr, const Py_ssize_t* sstrides,
180 const Py_ssize_t* ssuboffsets, char* mem) {
181 // Base case for recursive multi-dimensional copying. Contiguous arrays are
182 // copied with very little overhead. Assumptions: ndim == 1, mem == nullptr or
183 // sizeof(mem) == shape[0] * itemsize.
184 if (!mem) { // contiguous
185 Py_ssize_t size = shape[0] * itemsize;
186 if (dptr + size < sptr || sptr + size < dptr)
187 memcpy(dptr, sptr, size); // no overlapping
188 else
189 memmove(dptr, sptr, size);
190 } else {
191 char* p;
192 Py_ssize_t i;
193 for (i = 0, p = mem; i < shape[0];
194 p += itemsize, sptr += sstrides[0], i++) {
195 char* xsptr = ADJUST_PTR(sptr, ssuboffsets, 0);
196 memcpy(p, xsptr, itemsize);
197 }
198 for (i = 0, p = mem; i < shape[0];
199 p += itemsize, dptr += dstrides[0], i++) {
200 char* xdptr = ADJUST_PTR(dptr, dsuboffsets, 0);
201 memcpy(xdptr, p, itemsize);
202 }
203 }
204}
205
206//---------------------------------------------------------------------------
207static int copy_single(Py_buffer* dest, Py_buffer* src) {
208 // Faster copying of one-dimensional arrays.
209 char* mem = nullptr;
210
211 assert(dest->ndim == 1);
212
213 if (!equiv_structure(dest, src))
214 return -1;
215
217 mem = (char*)PyMem_Malloc(dest->shape[0] * dest->itemsize);
218 if (!mem) {
219 PyErr_NoMemory();
220 return -1;
221 }
222 }
223
224 copy_base(dest->shape, dest->itemsize, (char*)dest->buf, dest->strides,
225 dest->suboffsets, (char*)src->buf, src->strides, src->suboffsets,
226 mem);
227
228 if (mem)
229 PyMem_Free(mem);
230
231 return 0;
232}
233
234//- Indexing and slicing ----------------------------------------------------
235static char* lookup_dimension(Py_buffer& view, char* ptr, int dim,
236 Py_ssize_t index) {
237 Py_ssize_t nitems; // items in the given dimension
238
239 assert(view.shape);
240 assert(view.strides);
241
242 nitems = view.shape[dim];
243 if (index < 0) {
245 index += nitems;
246 else {
247 PyErr_Format(
248 PyExc_IndexError,
249 "negative index not supported on dimension %d with unknown size",
250 dim + 1);
251 return nullptr;
252 }
253 }
254
255 if (view.strides[dim] == cpyrt::UNKNOWN_SIZE) {
256 PyErr_Format(
257 PyExc_IndexError,
258 "multi index not supported on dimension %d with unknown stride",
259 dim + 1);
260 return nullptr;
261 }
262
263 if (nitems != cpyrt::UNKNOWN_SIZE && (index < 0 || index >= nitems)) {
264 PyErr_Format(PyExc_IndexError, "index out of bounds on dimension %d",
265 dim + 1);
266 return nullptr;
267 }
268
269 ptr += view.strides[dim] * index;
270 ptr = ADJUST_PTR(ptr, view.suboffsets, dim);
271
272 return ptr;
273}
274
275// Get the pointer to the item at index.
276//---------------------------------------------------------------------------
277static inline void* ptr_from_index(cpyrt::LowLevelView* llview,
278 Py_ssize_t index) {
279 Py_buffer& view = llview->fBufInfo;
280 return lookup_dimension(view, (char*)llview->get_buf(), 0, index);
281}
282
283// Get the pointer to the item at tuple.
284//---------------------------------------------------------------------------
285static void* ptr_from_tuple(cpyrt::LowLevelView* llview, PyObject* tup) {
286 Py_buffer& view = llview->fBufInfo;
287
288 Py_ssize_t nindices = PyTuple_GET_SIZE(tup);
289 if (nindices > view.ndim) {
290 PyErr_Format(PyExc_TypeError,
291 "cannot index %d-dimension view with %zd-element tuple",
292 view.ndim, nindices);
293 return nullptr;
294 }
295
296 char* ptr = (char*)llview->get_buf();
297 for (Py_ssize_t dim = 0; dim < nindices; dim++) {
298 Py_ssize_t index;
299 index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(tup, dim), PyExc_IndexError);
300 if (index == -1 && PyErr_Occurred())
301 return nullptr;
302
303 ptr = lookup_dimension(view, ptr, (int)dim, index);
304 if (!ptr)
305 return nullptr;
306
307 if (!((intptr_t)view.internal & cpyrt::LowLevelView::kIsFixed) &&
308 dim != view.ndim - 1)
309 ptr = *(char**)ptr;
310 }
311 return ptr;
312}
313
314//= mapping methods =========================================================
315static Py_ssize_t ll_length(cpyrt::LowLevelView* self) {
316 if (!self->get_buf())
317 return 0;
318 return self->fBufInfo.ndim == 0 ? 1 : self->fBufInfo.shape[0];
319}
320
321//---------------------------------------------------------------------------
322static inline int init_slice(Py_buffer* base, PyObject* _key, int dim) {
323 Py_ssize_t start, stop, step, slicelength;
324
325 PyObject* key = _key;
326
327 if (PySlice_GetIndicesEx(key, base->shape[dim], &start, &stop, &step,
328 &slicelength) < 0)
329 return -1;
330
331 if (!base->suboffsets || dim == 0) {
332 adjust_buf:
333 base->buf = (char*)base->buf + base->strides[dim] * start;
334 } else {
335 Py_ssize_t n = dim - 1;
336 while (n >= 0 && base->suboffsets[n] < 0)
337 n--;
338 if (n < 0)
339 goto adjust_buf; // all suboffsets are negative
340 base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
341 }
342 base->shape[dim] = slicelength;
343 base->strides[dim] = base->strides[dim] * step;
344
345 return 0;
346}
347
348//---------------------------------------------------------------------------
349static bool is_multislice(PyObject* key) {
350 if (!PyTuple_Check(key))
351 return false;
352
353 Py_ssize_t size = PyTuple_GET_SIZE(key);
354 if (size == 0)
355 return false;
356
357 for (Py_ssize_t i = 0; i < size; i++) {
358 PyObject* x = PyTuple_GET_ITEM(key, i);
359 if (!PySlice_Check(x))
360 return false;
361 }
362 return true;
363}
364
365//---------------------------------------------------------------------------
366static Py_ssize_t is_multiindex(PyObject* key) {
367 if (!PyTuple_Check(key))
368 return 0;
369
370 Py_ssize_t size = PyTuple_GET_SIZE(key);
371 for (Py_ssize_t i = 0; i < size; i++) {
372 PyObject* x = PyTuple_GET_ITEM(key, i);
373 if (!PyIndex_Check(x))
374 return 0;
375 }
376 return 1;
377}
378
379// Return the item at index. In a one-dimensional view, this is an object
380// with the type specified by view->format. Otherwise, the item is a sub-view.
381// The function is used in ll_subscript() and ll_as_sequence.
382//---------------------------------------------------------------------------
383static PyObject* ll_item(cpyrt::LowLevelView* self, Py_ssize_t index) {
384 Py_buffer& view = self->fBufInfo;
385
386 if (!self->get_buf()) {
387 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
388 return nullptr;
389 }
390
391 if (view.ndim == 0) {
392 PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
393 return nullptr;
394 }
395
396 void* ptr = ptr_from_index(self, index);
397 if (ptr) {
398 bool isfix = (intptr_t)view.internal & cpyrt::LowLevelView::kIsFixed;
399 if (self->fBufInfo.ndim == 1 || !isfix)
400 return self->fConverter->FromMemory(ptr);
401 return self->fConverter->FromMemory((void*)&ptr);
402 }
403
404 return nullptr; // error already set by lookup_dimension
405}
406
407// Return the item at position *key* (a tuple of indices).
408//---------------------------------------------------------------------------
410 Py_buffer& view = self->fBufInfo;
411 Py_ssize_t nindices = PyTuple_GET_SIZE(tup);
412
413 if (nindices < view.ndim) {
414 // TODO: implement
415 PyErr_SetString(PyExc_NotImplementedError, "sub-views are not implemented");
416 return nullptr;
417 }
418
419 void* ptr = ptr_from_tuple(self, tup);
420
421 // if there's an error, it was already set by lookup_dimension
422 return ptr ? self->fElemCnv->FromMemory(ptr) : nullptr;
423}
424
425// llp[obj] returns an object holding the data for one element if obj
426// fully indexes the lowlevelptr or another lowlevelptr object if it
427// does not.
428//
429// 0-d lowlevelptr objects can be referenced using llp[...] or llp[()]
430// but not with anything else.
431//---------------------------------------------------------------------------
433 Py_buffer& view = self->fBufInfo;
434
435 if (view.ndim == 0) {
436 if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
437 return self->fConverter->FromMemory(self->get_buf());
438 } else if (key == Py_Ellipsis) {
439 Py_INCREF(self);
440 return (PyObject*)self;
441 } else {
442 PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
443 return nullptr;
444 }
445 }
446
447 if (PyIndex_Check(key)) {
448 Py_ssize_t index = PyNumber_AsSsize_t(key, PyExc_IndexError);
449 if (index == -1 && PyErr_Occurred())
450 return nullptr;
451 return ll_item(self, index);
452 } else if (PySlice_Check(key)) {
453 if (view.ndim == 1) {
454 Py_ssize_t start, stop, step, slicelen;
455 if (PySlice_Unpack(key, &start, &stop, &step) < 0)
456 return nullptr;
457
458 slicelen = PySlice_AdjustIndices(view.shape[0], &start, &stop, step);
459 if (slicelen <= 0)
460 slicelen = view.shape[0];
461
462 char* buf = (char*)self->get_buf();
463 char* slice_buf = new char[slicelen * view.itemsize];
464 size_t isize = view.itemsize;
465 for (size_t i = 0, cur = 0; i < (size_t)slicelen; cur += step, ++i) {
466 for (size_t j = 0; j < isize; ++j)
467 slice_buf[i * isize + j] = buf[(start + cur) * isize + j];
468 }
469
470 cpyrt::LowLevelView* ll = self->fCreator(slice_buf, {1, slicelen});
471 if (!ll)
472 delete[] slice_buf;
473 else
474 (intptr_t&)ll->fBufInfo.internal |= cpyrt::LowLevelView::kIsOwner;
475
476 return (PyObject*)ll;
477
478 } else {
479 // TODO: handle slicing. This should be simpler than the memoryview
480 // case as there is no Python object holding the buffer.
481 PyErr_SetString(PyExc_NotImplementedError,
482 "multi-dimensional slicing is not implemented");
483 return nullptr;
484 }
485 } else if (is_multiindex(key)) {
486 return ll_item_multi(self, key);
487 } else if (is_multislice(key)) {
488 PyErr_SetString(PyExc_NotImplementedError,
489 "multi-dimensional slicing is not implemented");
490 return nullptr;
491 }
492
493 PyErr_SetString(PyExc_TypeError, "invalid slice key");
494 return nullptr;
495}
496
497//---------------------------------------------------------------------------
499 PyObject* value) {
500 Py_buffer& view = self->fBufInfo;
501 Py_buffer src;
502
503 if (view.readonly) {
504 PyErr_SetString(PyExc_TypeError, "cannot modify read-only memory");
505 return -1;
506 }
507
508 if (value == nullptr) {
509 PyErr_SetString(PyExc_TypeError, "cannot delete memory");
510 return -1;
511 }
512
513 if (view.ndim == 0) {
514 if (key == Py_Ellipsis ||
515 (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
516 return self->fConverter->ToMemory(value, self->get_buf()) ? 0 : -1;
517 } else {
518 PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
519 return -1;
520 }
521 }
522
523 if (PyIndex_Check(key)) {
524 Py_ssize_t index;
525 if (1 < view.ndim) {
526 PyErr_SetString(PyExc_NotImplementedError,
527 "sub-views are not implemented");
528 return -1;
529 }
530 index = PyNumber_AsSsize_t(key, PyExc_IndexError);
531 if (index == -1 && PyErr_Occurred())
532 return -1;
533 void* ptr = ptr_from_index(self, index);
534 if (ptr == nullptr)
535 return -1;
536 return self->fConverter->ToMemory(value, ptr) ? 0 : -1;
537 }
538
539 // one-dimensional: fast path
540 if (PySlice_Check(key) && view.ndim == 1) {
541 Py_buffer dest; // sliced view
542 Py_ssize_t arrays[3];
543 int ret = -1;
544
545 // rvalue must be an exporter
546 if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) < 0) {
547 if (src.obj)
548 PyBuffer_Release(&src);
549 return ret;
550 }
551
552 dest = view;
553 dest.shape = &arrays[0];
554 dest.shape[0] = view.shape[0];
555 dest.strides = &arrays[1];
556 dest.strides[0] = view.strides[0];
557 if (view.suboffsets) {
558 dest.suboffsets = &arrays[2];
559 dest.suboffsets[0] = view.suboffsets[0];
560 }
561
562 if (init_slice(&dest, key, 0) < 0)
563 return -1;
564 dest.len = dest.shape[0] * dest.itemsize;
565
566 ret = copy_single(&dest, &src);
567 PyBuffer_Release(&src);
568 return ret;
569 }
570
571 if (is_multiindex(key)) {
572 // TODO: implement
573 if (PyTuple_GET_SIZE(key) < view.ndim) {
574 PyErr_SetString(PyExc_NotImplementedError,
575 "sub-views are not implemented");
576 return -1;
577 }
578 void* ptr = ptr_from_tuple(self, key);
579 if (ptr == nullptr)
580 return -1;
581 return self->fElemCnv->ToMemory(value, ptr) ? 0 : -1;
582 }
583
584 if (PySlice_Check(key) || is_multislice(key)) {
585 // TODO: implement
586 PyErr_SetString(PyExc_NotImplementedError,
587 "LowLevelView slice assignments are currently restricted "
588 "to ndim = 1");
589 return -1;
590 }
591
592 PyErr_SetString(PyExc_TypeError, "invalid slice key");
593 return -1;
594}
595
596//---------------------------------------------------------------------------
597static int ll_getbuf(cpyrt::LowLevelView* self, Py_buffer* view, int flags) {
598 // Simplified from memoryobject, as we're always dealing with C arrays.
599
600 // start with full copy
601 *view = self->fBufInfo;
602
603 if (!(flags & PyBUF_FORMAT)) {
604 /* NULL indicates that the buffer's data type has been cast to 'B'.
605 view->itemsize is the _previous_ itemsize. If shape is present,
606 the equality product(shape) * itemsize = len still holds at this
607 point. The equality calcsize(format) = itemsize does _not_ hold
608 from here on! */
609 view->format = NULL;
610 }
611
612 if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS) {
613 PyErr_SetString(PyExc_BufferError,
614 "underlying buffer is not Fortran contiguous");
615 return -1;
616 }
617
618 if (!(flags & PyBUF_FORMAT)) {
619 /* PyBUF_SIMPLE or PyBUF_WRITABLE: at this point buf is C-contiguous,
620 so base->buf = ndbuf->data. */
621 if (view->format != NULL) {
622 /* PyBUF_SIMPLE|PyBUF_FORMAT and PyBUF_WRITABLE|PyBUF_FORMAT do
623 not make sense. */
624 PyErr_Format(
625 PyExc_BufferError,
626 "cannot cast to unsigned bytes if the format flag is present");
627 return -1;
628 }
629 /* product(shape) * itemsize = len and calcsize(format) = itemsize
630 do _not_ hold from here on! */
631 view->ndim = 1;
632 view->shape = NULL;
633 }
634
635 view->obj = (PyObject*)self;
636 Py_INCREF(view->obj);
637
638 return 0;
639}
640
641//= iterator protocol =======================================================
642static PyObject* ll_iter(PyObject* self) {
643 // The index iterator indexes through getitem, just like python would do by
644 // default, except that it checks the size externally to raise StopIteration,
645 // rather than geitem failing.
646
647 using namespace cppjit::cpyrt;
648
649 indexiterobject* ii = PyObject_GC_New(indexiterobject, &IndexIter_Type);
650 if (!ii)
651 return nullptr;
652
653 Py_INCREF(self);
654 ii->ii_container = self;
655 ii->ii_pos = 0;
656 ii->ii_len = ll_length((LowLevelView*)self);
657
658 PyObject_GC_Track(ii);
659 return (PyObject*)ii;
660}
661
662//- mapping methods ---------------------------------------------------------
663static PyMappingMethods ll_as_mapping = {
664 (lenfunc)ll_length, // mp_length
665 (binaryfunc)ll_subscript, // mp_subscript
666 (objobjargproc)ll_ass_sub, // mp_ass_subscript
667};
668
669//- sequence methods --------------------------------------------------------
670static PySequenceMethods ll_as_sequence = {
671 (lenfunc)ll_length, // sq_length
672 0, // sq_concat
673 0, // sq_repeat
674 (ssizeargfunc)ll_item, // sq_item
675 0, // sq_slice
676 0, // sq_ass_item
677 0, // sq_ass_slice
678 0, // sq_contains
679 0, // sq_inplace_concat
680 0, // sq_inplace_repeat
681};
682
683//- buffer methods ----------------------------------------------------------
684static PyBufferProcs ll_as_buffer = {
685 (getbufferproc)ll_getbuf, // bf_getbuffer
686 0, // bf_releasebuffer
687};
688
689//---------------------------------------------------------------------------
691 Py_buffer& view = self->fBufInfo;
692
693 PyObject* shape = PyTuple_New(view.ndim);
694 if (!shape)
695 return nullptr;
696 for (Py_ssize_t idim = 0; idim < view.ndim; ++idim) {
697 PyObject* pydim = PyLong_FromSsize_t(view.shape[idim]);
698 if (!pydim) {
699 Py_DECREF(shape);
700 return nullptr;
701 }
702 PyTuple_SET_ITEM(shape, idim, pydim);
703 }
704
705 return shape;
706}
707
708//---------------------------------------------------------------------------
710 const char* why) {
711 PyObject* current = ll_shape(self);
712 if (!current)
713 return nullptr;
714 PyErr_Format(PyExc_ValueError,
715 "cannot reshape array of shape %S into shape %S: %s", current,
716 shape, why);
717 Py_DECREF(current);
718 return nullptr;
719}
720
721//---------------------------------------------------------------------------
723 // Fill in the dimensions of the buffer that are not known from its type.
724 //
725 // A view is created with the rank and layout of its C++ type: a flat block
726 // for a rank-1 or fixed-size array, an array of row pointers otherwise. The
727 // strides and the converter projecting sub-views are derived from that and
728 // cannot be re-derived from a shape alone, so the rank of a view never
729 // changes and a dimension, once known, stays what it is. What reshaping can
730 // do is provide the extent of a dimension that the type leaves open, such as
731 // the size of an array behind a pointer.
732 if (!PyTuple_Check(shape)) {
733 if (shape) {
734 PyObject* pystr = PyObject_Str(shape);
735 if (pystr) {
736 PyErr_Format(PyExc_TypeError, "tuple object expected, received %s",
737 PyUnicode_AsUTF8(pystr));
738 Py_DECREF(pystr);
739 return nullptr;
740 }
741 }
742 PyErr_SetString(PyExc_TypeError, "tuple object expected");
743 return nullptr;
744 }
745
746 Py_buffer& view = self->fBufInfo;
747 if (view.ndim < 1 || !view.shape || !view.strides) {
748 PyErr_SetString(PyExc_TypeError,
749 "this low level view has no dimensions to set");
750 return nullptr;
751 }
752
753 // An unknown outermost dimension holds the fake max for the element size
754 // (the innermost stride), any other unknown dimension UNKNOWN_SIZE. An
755 // empty dimension of a non-fixed view may also be filled in: it came from
756 // a pointer, with nothing behind it yet that a size could contradict.
757 bool isfix = (intptr_t)view.internal & cpyrt::LowLevelView::kIsFixed;
758 Py_ssize_t elemsize = view.strides[view.ndim - 1];
759 Py_ssize_t fakemax = fake_max(elemsize);
760 Py_ssize_t unit0 = view.ndim == 1 ? elemsize : view.itemsize;
761
762 Py_ssize_t ndim = PyTuple_GET_SIZE(shape);
763 cpyrt::dims_t dims(ndim);
764 for (Py_ssize_t idim = 0; idim < ndim; ++idim) {
765 Py_ssize_t nlen = PyLong_AsSsize_t(PyTuple_GET_ITEM(shape, idim));
766 if (nlen == -1 && PyErr_Occurred())
767 return nullptr;
768 if (nlen < cpyrt::UNKNOWN_SIZE) {
769 PyErr_SetString(PyExc_ValueError, "negative dimensions are not allowed");
770 return nullptr;
771 }
772 if (nlen == cpyrt::UNKNOWN_SIZE) // store as the creators would
773 nlen = idim == 0 ? fakemax : cpyrt::UNKNOWN_SIZE;
774 else if (PY_SSIZE_T_MAX / (idim == 0 ? unit0 : elemsize) < nlen)
775 return ll_reshape_error(self, shape, "the shape is too large");
776 dims[idim] = nlen;
777 }
778
779 if (ndim != view.ndim)
780 return ll_reshape_error(
781 self, shape,
782 "the number of dimensions of a low level view is fixed by its type");
783
784 for (Py_ssize_t idim = 0; idim < ndim; ++idim) {
785 Py_ssize_t cur = view.shape[idim];
786 bool unknown = cur == cpyrt::UNKNOWN_SIZE ||
787 (idim == 0 && cur == fakemax) || (cur == 0 && !isfix);
788 if (!unknown && dims[idim] != cur)
789 return ll_reshape_error(self, shape,
790 "only dimensions of unknown size can be set");
791 }
792
793 for (Py_ssize_t idim = 0; idim < ndim; ++idim)
794 view.shape[idim] = dims[idim];
795
796 // the byte length counts the outermost dimension as the creators set it;
797 // the strides depend only on the layout and stay as they were laid down
798 view.len = dims[0] * unit0;
799
801}
802
803//---------------------------------------------------------------------------
804static int ll_setshape(cpyrt::LowLevelView* self, PyObject* value, void*) {
805 if (!value) {
806 PyErr_SetString(PyExc_TypeError, "cannot delete the shape of a view");
807 return -1;
808 }
809
811 if (!result)
812 return -1;
813 Py_DECREF(result);
814 return 0;
815}
816
817//---------------------------------------------------------------------------
819 PyObject* kwds) {
820 // Construct a numpy array from the lowlevelview (w/o copy if possible); this
821 // uses the Python methods to avoid depending on numpy directly
822
823 // Expect as most a dtype from the arguments;
824 static PyObject* npmod = PyImport_ImportModule("numpy"); // ref-count kept
825 if (!npmod)
826 return nullptr;
827
828 bool docopy = false;
829 if (kwds) {
830 PyObject* pycp = PyObject_GetItem(kwds, cpyrt::PyStrings::gCopy);
831 if (!pycp) {
832 PyErr_SetString(PyExc_TypeError,
833 "__array__ only supports the \"copy\" keyword");
834 return nullptr;
835 }
836
837 docopy = PyObject_IsTrue(pycp);
838 Py_DECREF(pycp);
839 }
840
841 if (!docopy) { // view requested
842 // expect possible dtype from the arguments, otherwise take it from the type
843 // code
844 PyObject* dtype;
845 if (!args || PyTuple_GET_SIZE(args) != 1) {
846 PyObject* npdtype = PyObject_GetAttr(npmod, cpyrt::PyStrings::gDType);
847 PyObject* typecode = ll_typecode(self, nullptr);
848 dtype = PyObject_CallFunctionObjArgs(npdtype, typecode, nullptr);
849 Py_DECREF(typecode);
850 Py_DECREF(npdtype);
851 } else {
852 dtype = PyTuple_GET_ITEM(args, 0);
853 Py_INCREF(dtype);
854 }
855
856 if (!dtype)
857 return nullptr;
858
859 PyObject* npfrombuf =
860 PyObject_GetAttr(npmod, cpyrt::PyStrings::gFromBuffer);
861 PyObject* view = PyObject_CallFunctionObjArgs(npfrombuf, (PyObject*)self,
862 dtype, nullptr);
863 Py_DECREF(dtype);
864 Py_DECREF(npfrombuf);
865
866 return view;
867
868 } else { // copy requested
869 PyObject* npcopy = PyObject_GetAttr(npmod, cpyrt::PyStrings::gCopy);
870 PyObject* newarr =
871 PyObject_CallFunctionObjArgs(npcopy, (PyObject*)self, nullptr);
872 Py_DECREF(npcopy);
873
874 return newarr;
875 }
876
877 // never get here
878 return nullptr;
879}
880
881//---------------------------------------------------------------------------
883 // Interpret memory as a null-terminated char string.
884 Py_buffer& view = self->fBufInfo;
885
886 if (strcmp(view.format, "b") != 0 || view.ndim != 1) {
887 PyErr_Format(
888 PyExc_TypeError,
889 "as_string only supported for 1-dim char strings (format: %s, dim: %d)",
890 view.format, (int)view.ndim);
891 return nullptr;
892 }
893
894 char* buf = (char*)self->get_buf();
895 size_t sz = strnlen(buf, (size_t)view.shape[0]);
896 return PyUnicode_FromStringAndSize(buf, sz);
897}
898
899//---------------------------------------------------------------------------
900static PyMethodDef ll_methods[] = {
901 {(char*)"reshape", (PyCFunction)ll_reshape, METH_O,
902 (char*)"change the shape (not layout) of the low level view"},
903 {(char*)"as_string", (PyCFunction)ll_as_string, METH_NOARGS,
904 (char*)"interpret memory as a null-terminated char string and return "
905 "Python str"},
906 {(char*)"__array__", (PyCFunction)ll_array, METH_VARARGS | METH_KEYWORDS,
907 (char*)"return a numpy array from the low level view"},
908 {(char*)nullptr, nullptr, 0, nullptr}};
909
910//---------------------------------------------------------------------------
911static PyGetSetDef ll_getset[] = {
912 {(char*)"__python_owns__", (getter)ll_getownership, (setter)ll_setownership,
913 (char*)"If true, python manages the life time of this buffer", nullptr},
914 {(char*)"__cpp_array__", (getter)ll_getcpparray, (setter)ll_setcpparray,
915 (char*)"If true, this array was allocated with C++\'s new[]", nullptr},
916 {(char*)"format", (getter)ll_typecode, nullptr, nullptr, nullptr},
917 {(char*)"typecode", (getter)ll_typecode, nullptr, nullptr, nullptr},
918 {(char*)"shape", (getter)ll_shape, (setter)ll_setshape, nullptr, nullptr},
919 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr}};
920
921namespace cppjit::cpyrt {
922
923//= cpyrt low level view type ============================================
924PyTypeObject LowLevelView_Type = {
925 PyVarObject_HEAD_INIT(&PyType_Type,
926 0)(char*) "cppjit.LowLevelView", // tp_name
927 sizeof(cpyrt::LowLevelView), // tp_basicsize
928 0, // tp_itemsize
929 (destructor)ll_dealloc, // tp_dealloc
930 0, // tp_vectorcall_offset / tp_print
931 0, // tp_getattr
932 0, // tp_setattr
933 0, // itp_as_async / tp_compare
934 0, // tp_repr
935 0, // tp_as_number
936 &ll_as_sequence, // tp_as_sequence
937 &ll_as_mapping, // tp_as_mapping
938 0, // tp_hash
939 0, // tp_call
940 0, // tp_str
941 0, // tp_getattro
942 0, // tp_setattro
943 &ll_as_buffer, // tp_as_buffer
944 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, // tp_flags
945 (char*)"memory view on C++ pointer", // tp_doc
946 0, // tp_traverse
947 0, // tp_clear
948 0, // tp_richcompare
949 0, // tp_weaklistoffset
950 (getiterfunc)ll_iter, // tp_iter
951 0, // tp_iternext
952 ll_methods, // tp_methods
953 0, // tp_members
954 ll_getset, // tp_getset
955 0, // tp_base
956 0, // tp_dict
957 0, // tp_descr_get
958 0, // tp_descr_set
959 0, // tp_dictoffset
960 0, // tp_init
961 0, // tp_alloc
962 (newfunc)ll_new, // tp_new
963 0, // tp_free
964 0, // tp_is_gc
965 0, // tp_bases
966 0, // tp_mro
967 0, // tp_cache
968 0, // tp_subclasses
969 0, // tp_weaklist
970 0, // tp_del
971 0, // tp_version_tag
972 0, // tp_finalize
973 0 // tp_vectorcall
975
976} // namespace cppjit::cpyrt
977
978namespace {
979
980template <typename T> struct typecode_traits {};
981template <> struct typecode_traits<bool> {
982 static constexpr const char* format = "?";
983 static constexpr const char* name = "bool";
984};
985template <> struct typecode_traits<char> {
986 static constexpr const char* format = "b";
987 static constexpr const char* name = "char";
988};
989template <> struct typecode_traits<signed char> {
990 static constexpr const char* format = "b";
991 static constexpr const char* name = "SCharAsInt";
992};
993template <> struct typecode_traits<unsigned char> {
994 static constexpr const char* format = "B";
995 static constexpr const char* name = "UCharAsInt";
996};
997template <> struct typecode_traits<std::byte> {
998 static constexpr const char* format = "B";
999 static constexpr const char* name = "UCharAsInt";
1000};
1001template <> struct typecode_traits<char*> {
1002 static constexpr const char* format = "b";
1003 static constexpr const char* name = "char*";
1004};
1005template <> struct typecode_traits<const char*> {
1006 static constexpr const char* format = "b";
1007 static constexpr const char* name = "const char*";
1008};
1009template <> struct typecode_traits<short> {
1010 static constexpr const char* format = "h";
1011 static constexpr const char* name = "short";
1012};
1013template <> struct typecode_traits<unsigned short> {
1014 static constexpr const char* format = "H";
1015 static constexpr const char* name = "unsigned short";
1016};
1017template <> struct typecode_traits<int> {
1018 static constexpr const char* format = "i";
1019 static constexpr const char* name = "int";
1020};
1021template <> struct typecode_traits<unsigned int> {
1022 static constexpr const char* format = "I";
1023 static constexpr const char* name = "unsigned int";
1024};
1025template <> struct typecode_traits<long> {
1026 static constexpr const char* format = "l";
1027 static constexpr const char* name = "long";
1028};
1029template <> struct typecode_traits<unsigned long> {
1030 static constexpr const char* format = "L";
1031 static constexpr const char* name = "unsigned long";
1032};
1033template <> struct typecode_traits<long long> {
1034 static constexpr const char* format = "q";
1035 static constexpr const char* name = "long long";
1036};
1037template <> struct typecode_traits<unsigned long long> {
1038 static constexpr const char* format = "Q";
1039 static constexpr const char* name = "unsigned long long";
1040};
1041template <> struct typecode_traits<float> {
1042 static constexpr const char* format = "f";
1043 static constexpr const char* name = "float";
1044};
1045template <> struct typecode_traits<double> {
1046 static constexpr const char* format = "d";
1047 static constexpr const char* name = "double";
1048};
1049template <> struct typecode_traits<long double> {
1050 static constexpr const char* format = "D";
1051 static constexpr const char* name = "long double";
1052};
1053template <> struct typecode_traits<std::complex<float>> {
1054 static constexpr const char* format = "Zf";
1055 static constexpr const char* name = "std::complex<float>";
1056};
1057template <> struct typecode_traits<std::complex<double>> {
1058 static constexpr const char* format = "Zd";
1059 static constexpr const char* name = "std::complex<double>";
1060};
1061template <> struct typecode_traits<std::complex<int>> {
1062 static constexpr const char* format = "Zi";
1063 static constexpr const char* name = "std::complex<int>";
1064};
1065template <> struct typecode_traits<std::complex<long>> {
1066 static constexpr const char* format = "Zl";
1067 static constexpr const char* name = "std::complex<long>";
1068};
1069
1070} // unnamed namespace
1071
1072//---------------------------------------------------------------------------
1074 Py_buffer& bi = this->fBufInfo;
1075 if (bi.ndim == 1 && bi.shape) {
1076 bi.len = sz * bi.itemsize;
1077 bi.shape[0] = sz;
1078 return true;
1079 }
1080
1081 return false;
1082}
1083
1084//---------------------------------------------------------------------------
1085template <typename T>
1086static inline cpyrt::LowLevelView*
1088 const char* format = nullptr, const char* name = nullptr,
1089 Py_ssize_t itemsize = -1) {
1090 using namespace cppjit::cpyrt;
1091 Py_ssize_t nx =
1092 (shape.ndim() != UNKNOWN_SIZE) ? shape[0] : fake_max(sizeof(T));
1093 if (nx == UNKNOWN_SIZE)
1094 nx = fake_max(sizeof(T));
1095 PyObject* args = PyTuple_New(0);
1097 &LowLevelView_Type, args, nullptr);
1098 Py_DECREF(args);
1099
1100 Py_buffer& view = llp->fBufInfo;
1101 view.buf = address;
1102 view.obj = nullptr;
1103 view.readonly = 0;
1104 view.format = (char*)(format ? format : typecode_traits<T>::format);
1105 view.ndim = int(shape.ndim() != UNKNOWN_SIZE ? shape.ndim() : 1);
1106 view.shape = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
1107 view.shape[0] = nx; // view.len / view.itemsize
1108 view.strides = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
1109 view.suboffsets = nullptr;
1110 (intptr_t&)view.internal = cpyrt::LowLevelView::kIsCppArray; // assumed
1111 bool isfix = shape.ndim() != UNKNOWN_SIZE;
1112 if (isfix) {
1113 for (int i = 0; i < shape.ndim(); ++i)
1114 isfix = isfix && (shape[i] != UNKNOWN_SIZE);
1115 if (isfix)
1116 (intptr_t&)view.internal |= cpyrt::LowLevelView::kIsFixed;
1117 }
1118
1119 llp->fElemCnv = CreateConverter(name ? name : typecode_traits<T>::name);
1120 if (view.ndim == 1) {
1121 // simple 1-dim array of the declared type
1122 view.len = nx * sizeof(T);
1123 view.itemsize = (itemsize > 0 ? (size_t)itemsize : sizeof(T));
1124 llp->fConverter = llp->fElemCnv;
1125 } else {
1126 // multi-dim array; sub-views are projected by using more LLViews
1127 const size_t elemsize = isfix ? sizeof(T) : sizeof(void*);
1128 view.len = nx * elemsize;
1129 view.itemsize = elemsize;
1130 for (Py_ssize_t idim = 1; idim < view.ndim; ++idim)
1131 view.shape[idim] = shape[idim];
1132
1133 // peel off one dimension and create a new LLView converter
1134 std::string tname{name ? name : typecode_traits<T>::name};
1135 tname.append("[]"); // make sure to ask for another array
1136 // TODO: although this will work, it means that "naive" loops are expensive
1137 llp->fConverter = CreateConverter(tname, shape.sub());
1138 }
1139
1140 set_strides(view, sizeof(T), isfix);
1141
1142 return llp;
1143}
1144
1145//---------------------------------------------------------------------------
1146template <typename T>
1147static inline cpyrt::LowLevelView*
1149 const char* format = nullptr, const char* name = nullptr) {
1150 using namespace cppjit::cpyrt;
1151 LowLevelView* llp =
1152 (LowLevelView*)CreateLowLevelViewT((T*)address, shape, format, name);
1153 llp->set_buf((void**)address);
1154 return llp;
1155}
1156
1157//---------------------------------------------------------------------------
1158#define CPPJIT_RET_W_CREATOR(type, fname) \
1159 PyObject* (*c)(type, cdims_t) = &fname; \
1160 ll->fCreator = (LowLevelView::Creator_t)c; \
1161 return (PyObject*)ll
1162
1163#define CPPJIT_IMPL_VIEW_CREATOR(type) \
1164 PyObject* cpyrt::CreateLowLevelView(type* address, cdims_t shape) { \
1165 LowLevelView* ll = CreateLowLevelViewT<type>(address, shape); \
1166 CPPJIT_RET_W_CREATOR(type*, CreateLowLevelView); \
1167 } \
1168 PyObject* cpyrt::CreateLowLevelView(type** address, cdims_t shape) { \
1169 LowLevelView* ll = CreateLowLevelViewT<type>(address, shape); \
1170 CPPJIT_RET_W_CREATOR(type**, CreateLowLevelView); \
1171 }
1172
1184CPPJIT_IMPL_VIEW_CREATOR(unsigned long long);
1188CPPJIT_IMPL_VIEW_CREATOR(std::complex<float>);
1189CPPJIT_IMPL_VIEW_CREATOR(std::complex<double>);
1190CPPJIT_IMPL_VIEW_CREATOR(std::complex<int>);
1191CPPJIT_IMPL_VIEW_CREATOR(std::complex<long>);
1192
1194 LowLevelView* ll = CreateLowLevelViewT<char>(address, shape);
1196}
1197
1199 LowLevelView* ll = CreateLowLevelViewT<char>(address, shape);
1201}
1202
1204 LowLevelView* ll = CreateLowLevelViewT<char*>(address, shape, nullptr,
1205 nullptr, sizeof(char));
1207}
1208
1209PyObject* cpyrt::CreateLowLevelViewString(const char** address, cdims_t shape) {
1210 LowLevelView* ll = CreateLowLevelViewT<const char*>(address, shape, nullptr,
1211 nullptr, sizeof(char));
1213}
1214
1216 LowLevelView* ll = CreateLowLevelViewT<int8_t>(address, shape, "b", "int8_t");
1218}
1219
1221 LowLevelView* ll = CreateLowLevelViewT<int8_t>(address, shape, "b", "int8_t");
1223}
1224
1226 LowLevelView* ll =
1227 CreateLowLevelViewT<uint8_t>(address, shape, "B", "uint8_t");
1229}
1230
1232 LowLevelView* ll =
1233 CreateLowLevelViewT<uint8_t>(address, shape, "B", "uint8_t");
1235}
1236
1238 LowLevelView* ll =
1239 CreateLowLevelViewT<int16_t>(address, shape, "h", "int16_t");
1241}
1242
1244 LowLevelView* ll =
1245 CreateLowLevelViewT<int16_t>(address, shape, "h", "int16_t");
1247}
1248
1250 LowLevelView* ll =
1251 CreateLowLevelViewT<uint16_t>(address, shape, "H", "uint16_t");
1253}
1254
1256 LowLevelView* ll =
1257 CreateLowLevelViewT<uint16_t>(address, shape, "H", "uint16_t");
1259}
1260
1262 LowLevelView* ll =
1263 CreateLowLevelViewT<int32_t>(address, shape, "i", "int32_t");
1265}
1266
1268 LowLevelView* ll =
1269 CreateLowLevelViewT<int32_t>(address, shape, "i", "int32_t");
1271}
1272
1274 LowLevelView* ll =
1275 CreateLowLevelViewT<uint32_t>(address, shape, "I", "uint32_t");
1277}
1278
1280 LowLevelView* ll =
1281 CreateLowLevelViewT<uint32_t>(address, shape, "I", "uint32_t");
1283}
free(fBuffer)
static PyObject * ll_shape(cpyrt::LowLevelView *self)
static void * ptr_from_tuple(cpyrt::LowLevelView *llview, PyObject *tup)
#define ADJUST_PTR(ptr, suboffsets, dim)
static int ll_setcpparray(cpyrt::LowLevelView *pyobj, PyObject *value, void *)
static PyObject * ll_getcpparray(cpyrt::LowLevelView *pyobj)
static PyObject * ll_reshape(cpyrt::LowLevelView *self, PyObject *shape)
static PyObject * ll_reshape_error(cpyrt::LowLevelView *self, PyObject *shape, const char *why)
static Py_ssize_t is_multiindex(PyObject *key)
#define CPYRT_LL_FLAG_GETSET(name, flag, doc)
static PySequenceMethods ll_as_sequence
static int ll_getbuf(cpyrt::LowLevelView *self, Py_buffer *view, int flags)
static bool equiv_shape(const Py_buffer *dest, const Py_buffer *src)
static cpyrt::LowLevelView * CreateLowLevelViewT(T *address, cpyrt::cdims_t shape, const char *format=nullptr, const char *name=nullptr, Py_ssize_t itemsize=-1)
static void ll_dealloc(cpyrt::LowLevelView *pyobj)
static PyObject * ll_array(cpyrt::LowLevelView *self, PyObject *args, PyObject *kwds)
static int ll_setshape(cpyrt::LowLevelView *self, PyObject *value, void *)
static Py_ssize_t ll_length(cpyrt::LowLevelView *self)
static cpyrt::LowLevelView * ll_new(PyTypeObject *subtype, PyObject *, PyObject *)
static PyObject * ll_subscript(cpyrt::LowLevelView *self, PyObject *key)
#define CPPJIT_RET_W_CREATOR(type, fname)
static PyObject * ll_as_string(cpyrt::LowLevelView *self)
static int last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
static int ll_setownership(cpyrt::LowLevelView *pyobj, PyObject *value, void *)
static Py_ssize_t fake_max(size_t elemsize)
static PyObject * ll_item(cpyrt::LowLevelView *self, Py_ssize_t index)
static PyBufferProcs ll_as_buffer
static PyMethodDef ll_methods[]
static bool equiv_structure(const Py_buffer *dest, const Py_buffer *src)
static void copy_base(const Py_ssize_t *shape, Py_ssize_t itemsize, char *dptr, const Py_ssize_t *dstrides, const Py_ssize_t *dsuboffsets, char *sptr, const Py_ssize_t *sstrides, const Py_ssize_t *ssuboffsets, char *mem)
static PyObject * ll_item_multi(cpyrt::LowLevelView *self, PyObject *tup)
static int ll_ass_sub(cpyrt::LowLevelView *self, PyObject *key, PyObject *value)
#define CPPJIT_IMPL_VIEW_CREATOR(type)
static PyMappingMethods ll_as_mapping
static PyGetSetDef ll_getset[]
static int copy_single(Py_buffer *dest, Py_buffer *src)
static void * ptr_from_index(cpyrt::LowLevelView *llview, Py_ssize_t index)
static PyObject * ll_iter(PyObject *self)
static int init_slice(Py_buffer *base, PyObject *_key, int dim)
static void set_strides(Py_buffer &view, size_t itemsize, bool isfix)
static PyObject * ll_getownership(cpyrt::LowLevelView *pyobj)
static PyObject * ll_typecode(cpyrt::LowLevelView *self, void *)
static bool is_multislice(PyObject *key)
static char * lookup_dimension(Py_buffer &view, char *ptr, int dim, Py_ssize_t index)
#define HAVE_SUBOFFSETS_IN_LAST_DIM(view)
_object PyObject
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
winID h TVirtualViewer3D TVirtualGLPainter p
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t dest
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t nitems
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t format
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
char name[80]
Definition TGX11.cxx:142
virtual PyObject * FromMemory(void *address)
virtual bool HasState()
Definition API.h:133
virtual bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr)
Dimensions sub() const
Definition Dimensions.h:80
PyObject_HEAD Py_buffer fBufInfo
#define CPYRT_PYTYPE_TAIL
Definition cpyrt.h:159
#define Py_RETURN_NONE
Definition cpyrt.h:69
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
PyObject * CreateLowLevelViewString(char **, cdims_t shape)
CPYRT_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyObject * CreateLowLevelView(bool *, cdims_t shape)
PyTypeObject LowLevelView_Type
PyObject * CreateLowLevelView_i8(int8_t *, cdims_t shape)
static const dim_t UNKNOWN_SIZE
Definition Dimensions.h:16
PyObject * CreateLowLevelView_i32(int32_t *, cdims_t shape)
PyObject * CreateLowLevelView_i16(int16_t *, cdims_t shape)
Definition API.cxx:34
PyObject_HEAD PyObject * ii_container