Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
LowLevelViews.cxx
Go to the documentation of this file.
1// Bindings
2#include "CPyCppyy.h"
3#include "LowLevelViews.h"
4#include "Converters.h"
5#include "CustomPyTypes.h"
6#include "PyStrings.h"
7
8// Standard
9#include <map>
10#include <assert.h>
11#include <string.h>
12#include <limits.h>
13
14
15//= memoryview-like object ===================================================
16// This is largely setup just like Python builtin memory view objects, with
17// the exceptions that there is no need of a "base" object (it views on C++
18// memory, not a Python object with a buffer interface), it uses the CPyCppyy
19// converters, and typed results and assignments are supported. Code reused
20// under PSF License Version 2.
21
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
39//= CPyCppyy low level view construction/destruction =========================
40static CPyCppyy::LowLevelView* ll_new(PyTypeObject* subtype, PyObject*, PyObject*)
41{
42// Create a new low level ptr type
43 CPyCppyy::LowLevelView* pyobj = (CPyCppyy::LowLevelView*)subtype->tp_alloc(subtype, 0);
44 if (!pyobj)
45 return nullptr;
46
47 memset(&pyobj->fBufInfo, 0, sizeof(Py_buffer));
48 (intptr_t&)pyobj->fBufInfo.internal |= CPyCppyy::LowLevelView::kIsCppArray;
49 pyobj->fBuf = nullptr;
50 pyobj->fConverter = nullptr;
51 pyobj->fElemCnv = nullptr;
52
53 return pyobj;
54}
55
56//----------------------------------------------------------------------------
58{
59// Destruction requires the deletion of the converter (if any)
60 PyMem_Free(pyobj->fBufInfo.shape);
61 PyMem_Free(pyobj->fBufInfo.strides);
62 if ((intptr_t)pyobj->fBufInfo.internal & CPyCppyy::LowLevelView::kIsOwner) {
63 if ((intptr_t)pyobj->fBufInfo.internal & CPyCppyy::LowLevelView::kIsCppArray)
64 delete [] pyobj->fBuf;
65 else
66 free(pyobj->fBuf);
67 }
68
69 if (pyobj->fElemCnv != pyobj->fConverter &&\
70 pyobj->fElemCnv && pyobj->fElemCnv->HasState())
71 delete pyobj->fElemCnv;
72
73 if (pyobj->fConverter && pyobj->fConverter->HasState())
74 delete pyobj->fConverter;
75
76 Py_TYPE(pyobj)->tp_free((PyObject*)pyobj);
77}
78
79
80//----------------------------------------------------------------------------
81#define CPYCPPYY_LL_FLAG_GETSET(name, flag, doc) \
82static PyObject* ll_get##name(CPyCppyy::LowLevelView* pyobj) \
83{ \
84 return PyBool_FromLong((long)((intptr_t)pyobj->fBufInfo.internal & flag));\
85} \
86 \
87static int ll_set##name(CPyCppyy::LowLevelView* pyobj, PyObject* value, void*)\
88{ \
89 long settrue = PyLong_AsLong(value); \
90 if (settrue == -1 && PyErr_Occurred()) { \
91 PyErr_SetString(PyExc_ValueError, #doc" should be either True or False");\
92 return -1; \
93 } \
94 \
95 if ((bool)settrue) \
96 (intptr_t&)pyobj->fBufInfo.internal |= flag; \
97 else \
98 (intptr_t&)pyobj->fBufInfo.internal &= ~flag; \
99 \
100 return 0; \
101}
102
104CPYCPPYY_LL_FLAG_GETSET(cpparray, CPyCppyy::LowLevelView::kIsCppArray, __cpp_array__)
105
106//---------------------------------------------------------------------------
107static PyObject* ll_typecode(CPyCppyy::LowLevelView* self, void*)
108{
109 return CPyCppyy_PyText_FromString((char*)self->fBufInfo.format);
110}
111
112
113//- Copy memoryview buffers =================================================
114
115// The functions in this section take a source and a destination buffer
116// with the same logical structure: format, itemsize, ndim and shape
117// are identical, with ndim > 0.
118
119// Check for the presence of suboffsets in the first dimension.
120#define HAVE_PTR(suboffsets, dim) (suboffsets && suboffsets[dim] >= 0)
121// Adjust ptr if suboffsets are present.
122#define ADJUST_PTR(ptr, suboffsets, dim) \
123 (HAVE_PTR(suboffsets, dim) ? *((char**)ptr) + suboffsets[dim] : ptr)
124
125// Assumptions: ndim >= 1. The macro tests for a corner case that should
126// perhaps be explicitly forbidden in the PEP.
127#define HAVE_SUBOFFSETS_IN_LAST_DIM(view) \
128 (view->suboffsets && view->suboffsets[dest->ndim-1] >= 0)
129
130//---------------------------------------------------------------------------
131static inline int last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
132{
133 assert(dest->ndim > 0 && src->ndim > 0);
136 dest->strides[dest->ndim-1] == dest->itemsize &&
137 src->strides[src->ndim-1] == src->itemsize);
138}
139
140//---------------------------------------------------------------------------
141static inline bool equiv_shape(const Py_buffer* dest, const Py_buffer* src)
142{
143// Two shapes are equivalent if they are either equal or identical up
144// to a zero element at the same position. For example, in NumPy arrays
145// the shapes [1, 0, 5] and [1, 0, 7] are equivalent.
146 if (dest->ndim != src->ndim)
147 return false;
148
149 for (int i = 0; i < dest->ndim; i++) {
150 if (dest->shape[i] != src->shape[i])
151 return 0;
152 if (dest->shape[i] == 0)
153 break;
154 }
155
156 return true;
157}
158
159//---------------------------------------------------------------------------
160static bool equiv_structure(const Py_buffer* dest, const Py_buffer* src)
161{
162// Check that the logical structure of the destination and source buffers
163// is identical.
164 if (strcmp(dest->format, src->format) != 0 || dest->itemsize != src->itemsize ||
165 !equiv_shape(dest, src)) {
166 PyErr_SetString(PyExc_ValueError,
167 "low level pointer assignment: lvalue and rvalue have different structures");
168 return false;
169 }
170
171 return true;
172}
173
174//---------------------------------------------------------------------------
175static void copy_base(const Py_ssize_t* shape, Py_ssize_t itemsize,
176 char* dptr, const Py_ssize_t* dstrides, const Py_ssize_t* dsuboffsets,
177 char* sptr, const Py_ssize_t* sstrides, const Py_ssize_t* ssuboffsets,
178 char* mem)
179{
180// Base case for recursive multi-dimensional copying. Contiguous arrays are
181// copied with very little overhead. Assumptions: ndim == 1, mem == nullptr or
182// sizeof(mem) == shape[0] * itemsize.
183 if (!mem) { // contiguous
184 Py_ssize_t size = shape[0] * itemsize;
185 if (dptr + size < sptr || sptr + size < dptr)
186 memcpy(dptr, sptr, size); // no overlapping
187 else
188 memmove(dptr, sptr, size);
189 }
190 else {
191 char *p;
192 Py_ssize_t i;
193 for (i=0, p=mem; i < shape[0]; p+=itemsize, sptr+=sstrides[0], i++) {
194 char *xsptr = ADJUST_PTR(sptr, ssuboffsets, 0);
195 memcpy(p, xsptr, itemsize);
196 }
197 for (i=0, p=mem; i < shape[0]; p+=itemsize, dptr+=dstrides[0], i++) {
198 char *xdptr = ADJUST_PTR(dptr, dsuboffsets, 0);
199 memcpy(xdptr, p, itemsize);
200 }
201 }
202
203}
204
205//---------------------------------------------------------------------------
206static int copy_single(Py_buffer* dest, Py_buffer* src)
207{
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,
225 (char*)dest->buf, dest->strides, dest->suboffsets,
226 (char*)src->buf, src->strides, src->suboffsets,
227 mem);
228
229 if (mem)
230 PyMem_Free(mem);
231
232 return 0;
233}
234
235
236//- Indexing and slicing ----------------------------------------------------
237static char* lookup_dimension(Py_buffer& view, char* ptr, int dim, Py_ssize_t index)
238{
239 Py_ssize_t nitems; // items in the given dimension
240
241 assert(view.shape);
242 assert(view.strides);
243
244 nitems = view.shape[dim];
245 if (index < 0) {
247 index += nitems;
248 else {
249 PyErr_Format(PyExc_IndexError,
250 "negative index not supported on dimension %d with unknown size", dim + 1);
251 return nullptr;
252 }
253 }
254
255 if (view.strides[dim] == CPyCppyy::UNKNOWN_SIZE) {
256 PyErr_Format(PyExc_IndexError,
257 "multi index not supported on dimension %d with unknown stride", dim + 1);
258 return nullptr;
259 }
260
261 if (nitems != CPyCppyy::UNKNOWN_SIZE && (index < 0 || index >= nitems)) {
262 PyErr_Format(PyExc_IndexError,
263 "index out of bounds on dimension %d", dim + 1);
264 return nullptr;
265 }
266
267 ptr += view.strides[dim] * index;
268 ptr = ADJUST_PTR(ptr, view.suboffsets, dim);
269
270 return ptr;
271}
272
273// Get the pointer to the item at index.
274//---------------------------------------------------------------------------
276{
277 Py_buffer& view = llview->fBufInfo;
278 return lookup_dimension(view, (char*)llview->get_buf(), 0, index);
279}
280
281// Get the pointer to the item at tuple.
282//---------------------------------------------------------------------------
284{
285 Py_buffer& view = llview->fBufInfo;
286
287 Py_ssize_t nindices = PyTuple_GET_SIZE(tup);
288 if (nindices > view.ndim) {
289 PyErr_Format(PyExc_TypeError,
290 "cannot index %d-dimension view with %zd-element tuple", view.ndim, nindices);
291 return nullptr;
292 }
293
294 char* ptr = (char*)llview->get_buf();
295 for (Py_ssize_t dim = 0; dim < nindices; dim++) {
297 index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(tup, dim),
298 PyExc_IndexError);
299 if (index == -1 && PyErr_Occurred())
300 return nullptr;
301
302 ptr = lookup_dimension(view, ptr, (int)dim, index);
303 if (!ptr)
304 return nullptr;
305
306 if (!((intptr_t)view.internal & CPyCppyy::LowLevelView::kIsFixed) && dim != view.ndim-1)
307 ptr = *(char**)ptr;
308 }
309 return ptr;
310}
311
312
313//= mapping methods =========================================================
315{
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{
324 Py_ssize_t start, stop, step, slicelength;
325
326#if PY_VERSION_HEX < 0x03000000
327 PySliceObject* key = (PySliceObject*)_key;
328#else
329 PyObject* key = _key;
330#endif
331
332 if (PySlice_GetIndicesEx(key, base->shape[dim], &start, &stop, &step, &slicelength) < 0)
333 return -1;
334
335 if (!base->suboffsets || dim == 0) {
336 adjust_buf:
337 base->buf = (char *)base->buf + base->strides[dim] * start;
338 }
339 else {
340 Py_ssize_t n = dim-1;
341 while (n >= 0 && base->suboffsets[n] < 0)
342 n--;
343 if (n < 0)
344 goto adjust_buf; // all suboffsets are negative
345 base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
346 }
347 base->shape[dim] = slicelength;
348 base->strides[dim] = base->strides[dim] * step;
349
350 return 0;
351}
352
353//---------------------------------------------------------------------------
354static bool is_multislice(PyObject* key)
355{
356 if (!PyTuple_Check(key))
357 return false;
358
359 Py_ssize_t size = PyTuple_GET_SIZE(key);
360 if (size == 0)
361 return false;
362
363 for (Py_ssize_t i = 0; i < size; i++) {
364 PyObject *x = PyTuple_GET_ITEM(key, i);
365 if (!PySlice_Check(x))
366 return false;
367 }
368 return true;
369}
370
371//---------------------------------------------------------------------------
373{
374 if (!PyTuple_Check(key))
375 return 0;
376
377 Py_ssize_t size = PyTuple_GET_SIZE(key);
378 for (Py_ssize_t i = 0; i < size; i++) {
379 PyObject *x = PyTuple_GET_ITEM(key, i);
380 if (!PyIndex_Check(x))
381 return 0;
382 }
383 return 1;
384}
385
386
387// Return the item at index. In a one-dimensional view, this is an object
388// with the type specified by view->format. Otherwise, the item is a sub-view.
389// The function is used in ll_subscript() and ll_as_sequence.
390//---------------------------------------------------------------------------
392{
393 Py_buffer& view = self->fBufInfo;
394
395 if (!self->get_buf()) {
396 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
397 return nullptr;
398 }
399
400 if (view.ndim == 0) {
401 PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
402 return nullptr;
403 }
404
405 void* ptr = ptr_from_index(self, index);
406 if (ptr) {
407 bool isfix = (intptr_t)view.internal & CPyCppyy::LowLevelView::kIsFixed;
408 if (self->fBufInfo.ndim == 1 || !isfix)
409 return self->fConverter->FromMemory(ptr);
410 return self->fConverter->FromMemory((void*)&ptr);
411 }
412
413 return nullptr; // error already set by lookup_dimension
414}
415
416// Return the item at position *key* (a tuple of indices).
417//---------------------------------------------------------------------------
419{
420 Py_buffer& view = self->fBufInfo;
421 Py_ssize_t nindices = PyTuple_GET_SIZE(tup);
422
423 if (nindices < view.ndim) {
424 // TODO: implement
425 PyErr_SetString(PyExc_NotImplementedError,
426 "sub-views are not implemented");
427 return nullptr;
428 }
429
430 void* ptr = ptr_from_tuple(self, tup);
431
432// if there's an error, it was already set by lookup_dimension
433 return ptr ? self->fElemCnv->FromMemory(ptr) : nullptr;
434}
435
436
437// llp[obj] returns an object holding the data for one element if obj
438// fully indexes the lowlevelptr or another lowlevelptr object if it
439// does not.
440//
441// 0-d lowlevelptr objects can be referenced using llp[...] or llp[()]
442// but not with anything else.
443//---------------------------------------------------------------------------
445{
446 Py_buffer& view = self->fBufInfo;
447
448 if (view.ndim == 0) {
449 if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
450 return self->fConverter->FromMemory(self->get_buf());
451 }
452 else if (key == Py_Ellipsis) {
453 Py_INCREF(self);
454 return (PyObject*)self;
455 }
456 else {
457 PyErr_SetString(PyExc_TypeError,
458 "invalid indexing of 0-dim memory");
459 return nullptr;
460 }
461 }
462
463 if (PyIndex_Check(key)) {
464 Py_ssize_t index = PyNumber_AsSsize_t(key, PyExc_IndexError);
465 if (index == -1 && PyErr_Occurred())
466 return nullptr;
467 return ll_item(self, index);
468 }
469 else if (PySlice_Check(key)) {
470 if (view.ndim == 1) {
471 Py_ssize_t start, stop, step, slicelen;
472 if (PySlice_Unpack(key, &start, &stop, &step) < 0)
473 return nullptr;
474
475 slicelen = PySlice_AdjustIndices(view.shape[0], &start, &stop, step);
476 if (slicelen <= 0)
477 slicelen = view.shape[0];
478
479 char* buf = (char*)self->get_buf();
480 char* slice_buf = new char[slicelen*view.itemsize];
481 size_t isize = view.itemsize;
482 for (size_t i=0, cur=0; i < (size_t)slicelen; cur += step, ++i) {
483 for (size_t j=0; j < isize; ++j)
484 slice_buf[i*isize+j] = buf[(start+cur)*isize + j];
485 }
486
487 CPyCppyy::LowLevelView* ll = self->fCreator(slice_buf, {1, slicelen});
488 if (!ll)
489 delete [] slice_buf;
490 else
491 (intptr_t&)ll->fBufInfo.internal |= CPyCppyy::LowLevelView::kIsOwner;
492
493 return (PyObject*)ll;
494
495 } else {
496 // TODO: handle slicing. This should be simpler than the memoryview
497 // case as there is no Python object holding the buffer.
498 PyErr_SetString(PyExc_NotImplementedError,
499 "multi-dimensional slicing is not implemented");
500 return nullptr;
501 }
502 }
503 else if (is_multiindex(key)) {
504 return ll_item_multi(self, key);
505 }
506 else if (is_multislice(key)) {
507 PyErr_SetString(PyExc_NotImplementedError,
508 "multi-dimensional slicing is not implemented");
509 return nullptr;
510 }
511
512 PyErr_SetString(PyExc_TypeError, "invalid slice key");
513 return nullptr;
514}
515
516//---------------------------------------------------------------------------
518{
519 Py_buffer& view = self->fBufInfo;
520 Py_buffer src;
521
522 if (view.readonly) {
523 PyErr_SetString(PyExc_TypeError, "cannot modify read-only memory");
524 return -1;
525 }
526
527 if (value == nullptr) {
528 PyErr_SetString(PyExc_TypeError, "cannot delete memory");
529 return -1;
530 }
531
532 if (view.ndim == 0) {
533 if (key == Py_Ellipsis ||
534 (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
535 return self->fConverter->ToMemory(value, self->get_buf()) ? 0 : -1;
536 }
537 else {
538 PyErr_SetString(PyExc_TypeError,
539 "invalid indexing of 0-dim memory");
540 return -1;
541 }
542 }
543
544 if (PyIndex_Check(key)) {
546 if (1 < view.ndim) {
547 PyErr_SetString(PyExc_NotImplementedError,
548 "sub-views are not implemented");
549 return -1;
550 }
551 index = PyNumber_AsSsize_t(key, PyExc_IndexError);
552 if (index == -1 && PyErr_Occurred())
553 return -1;
554 void* ptr = ptr_from_index(self, index);
555 if (ptr == nullptr)
556 return -1;
557 return self->fConverter->ToMemory(value, ptr) ? 0 : -1;
558 }
559
560 // one-dimensional: fast path
561 if (PySlice_Check(key) && view.ndim == 1) {
562 Py_buffer dest; // sliced view
563 Py_ssize_t arrays[3];
564 int ret = -1;
565
566 // rvalue must be an exporter
567 if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) < 0) {
569 return ret;
570 }
571
572 dest = view;
573 dest.shape = &arrays[0]; dest.shape[0] = view.shape[0];
574 dest.strides = &arrays[1]; dest.strides[0] = view.strides[0];
575 if (view.suboffsets) {
576 dest.suboffsets = &arrays[2]; dest.suboffsets[0] = view.suboffsets[0];
577 }
578
579 if (init_slice(&dest, key, 0) < 0)
580 return -1;
581 dest.len = dest.shape[0] * dest.itemsize;
582
583 ret = copy_single(&dest, &src);
585 return ret;
586 }
587
588 if (is_multiindex(key)) {
589 // TODO: implement
590 if (PyTuple_GET_SIZE(key) < view.ndim) {
591 PyErr_SetString(PyExc_NotImplementedError,
592 "sub-views are not implemented");
593 return -1;
594 }
595 void* ptr = ptr_from_tuple(self, key);
596 if (ptr == nullptr)
597 return -1;
598 return self->fElemCnv->ToMemory(value, ptr) ? 0 : -1;
599 }
600
601 if (PySlice_Check(key) || is_multislice(key)) {
602 // TODO: implement
603 PyErr_SetString(PyExc_NotImplementedError,
604 "LowLevelView slice assignments are currently restricted "
605 "to ndim = 1");
606 return -1;
607 }
608
609 PyErr_SetString(PyExc_TypeError, "invalid slice key");
610 return -1;
611}
612
613#if PY_VERSION_HEX < 0x03000000
614//---------------------------------------------------------------------------
616{
617 if (seg != 0) {
618 PyErr_SetString(PyExc_TypeError, "accessing non-existent segment");
619 return -1;
620 }
621
622 *pptr = self->get_buf();
623 return self->fBufInfo.len;
624}
625
626//---------------------------------------------------------------------------
628{
629 if (lenp) *lenp = 1;
630 return 1;
631}
632#endif
633
634//---------------------------------------------------------------------------
635static int ll_getbuf(CPyCppyy::LowLevelView* self, Py_buffer* view, int flags)
636{
637// Simplified from memoryobject, as we're always dealing with C arrays.
638
639// start with full copy
640 *view = self->fBufInfo;
641
642 if (!(flags & PyBUF_FORMAT)) {
643 /* NULL indicates that the buffer's data type has been cast to 'B'.
644 view->itemsize is the _previous_ itemsize. If shape is present,
645 the equality product(shape) * itemsize = len still holds at this
646 point. The equality calcsize(format) = itemsize does _not_ hold
647 from here on! */
648 view->format = NULL;
649 }
650
651 if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS) {
652 PyErr_SetString(PyExc_BufferError,
653 "underlying buffer is not Fortran contiguous");
654 return -1;
655 }
656
657 if (!(flags & PyBUF_FORMAT)) {
658 /* PyBUF_SIMPLE or PyBUF_WRITABLE: at this point buf is C-contiguous,
659 so base->buf = ndbuf->data. */
660 if (view->format != NULL) {
661 /* PyBUF_SIMPLE|PyBUF_FORMAT and PyBUF_WRITABLE|PyBUF_FORMAT do
662 not make sense. */
663 PyErr_Format(PyExc_BufferError,
664 "cannot cast to unsigned bytes if the format flag is present");
665 return -1;
666 }
667 /* product(shape) * itemsize = len and calcsize(format) = itemsize
668 do _not_ hold from here on! */
669 view->ndim = 1;
670 view->shape = NULL;
671 }
672
673 view->obj = (PyObject*)self;
674 Py_INCREF(view->obj);
675
676 return 0;
677}
678
679
680//= iterator protocol =======================================================
681static PyObject* ll_iter(PyObject* self) {
682// The index iterator indexes through getitem, just like python would do by
683// default, except that it checks the size externally to raise StopIteration,
684// rather than geitem failing.
685
686 using namespace CPyCppyy;
687
688 indexiterobject* ii = PyObject_GC_New(indexiterobject, &IndexIter_Type);
689 if (!ii) return nullptr;
690
691 Py_INCREF(self);
692 ii->ii_container = self;
693 ii->ii_pos = 0;
694 ii->ii_len = ll_length((LowLevelView*)self);
695
696 PyObject_GC_Track(ii);
697 return (PyObject*)ii;
698}
699
700
701//- mapping methods ---------------------------------------------------------
702static PyMappingMethods ll_as_mapping = {
703 (lenfunc) ll_length, // mp_length
704 (binaryfunc) ll_subscript, // mp_subscript
705 (objobjargproc)ll_ass_sub, // mp_ass_subscript
706};
707
708//- sequence methods --------------------------------------------------------
709static PySequenceMethods ll_as_sequence = {
710 (lenfunc)ll_length, // sq_length
711 0, // sq_concat
712 0, // sq_repeat
713 (ssizeargfunc)ll_item, // sq_item
714 0, // sq_slice
715 0, // sq_ass_item
716 0, // sq_ass_slice
717 0, // sq_contains
718 0, // sq_inplace_concat
719 0, // sq_inplace_repeat
720};
721
722//- buffer methods ----------------------------------------------------------
723static PyBufferProcs ll_as_buffer = {
724#if PY_VERSION_HEX < 0x03000000
725 (readbufferproc)ll_oldgetbuf, // bf_getreadbuffer
726 (writebufferproc)ll_oldgetbuf, // bf_getwritebuffer
727 (segcountproc)ll_getsegcount, // bf_getsegcount
728 0, // bf_getcharbuffer
729#endif
730 (getbufferproc)ll_getbuf, // bf_getbuffer
731 0, // bf_releasebuffer
732};
733
734
735
736//---------------------------------------------------------------------------
738{
739 Py_buffer& view = self->fBufInfo;
740
741 PyObject* shape = PyTuple_New(view.ndim);
742 for (Py_ssize_t idim = 0; idim < view.ndim; ++idim)
743 PyTuple_SET_ITEM(shape, idim, PyInt_FromSsize_t(view.shape[idim]));
744
745 return shape;
746}
747
748//---------------------------------------------------------------------------
750{
751// Allow the user to fix up the actual (type-strided) size of the buffer.
752 if (!PyTuple_Check(shape)) {
753 if (shape) {
754 PyObject* pystr = PyObject_Str(shape);
755 if (pystr) {
756 PyErr_Format(PyExc_TypeError, "tuple object expected, received %s",
758 Py_DECREF(pystr);
759 return nullptr;
760 }
761 }
762 PyErr_SetString(PyExc_TypeError, "tuple object expected");
763 return nullptr;
764 }
765
766 Py_buffer& view = self->fBufInfo;
767
768// verify size match
769 Py_ssize_t oldsz = 0;
770 for (Py_ssize_t idim = 0; idim < view.ndim; ++idim) {
771 Py_ssize_t nlen = view.shape[idim];
772 if (nlen == CPyCppyy::UNKNOWN_SIZE || nlen == INT_MAX/view.itemsize /* fake 'max' */) {
773 oldsz = -1; // meaning, unable to check size match
774 break;
775 }
776 oldsz += view.shape[idim];
777 }
778
779 if (0 < oldsz) {
780 Py_ssize_t newsz = 0;
781 for (Py_ssize_t idim = 0; idim < PyTuple_GET_SIZE(shape); ++idim)
782 newsz += PyInt_AsSsize_t(PyTuple_GET_ITEM(shape, idim));
783 if (oldsz != newsz) {
784 PyObject* tas = PyObject_Str(shape);
785 PyErr_Format(PyExc_ValueError,
786 "cannot reshape array of size %ld into shape %s", (long)oldsz, CPyCppyy_PyText_AsString(tas));
787 Py_DECREF(tas);
788 return nullptr;
789 }
790 }
791
792// reshape
793 size_t itemsize = view.strides[view.ndim-1];
794 if (view.ndim != PyTuple_GET_SIZE(shape)) {
795 PyMem_Free(view.shape);
796 PyMem_Free(view.strides);
797
798 view.ndim = (int)PyTuple_GET_SIZE(shape);
799 view.shape = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
800 view.strides = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
801 }
802
803 for (Py_ssize_t idim = 0; idim < PyTuple_GET_SIZE(shape); ++idim) {
804 Py_ssize_t nlen = PyInt_AsSsize_t(PyTuple_GET_ITEM(shape, idim));
805 if (nlen == -1 && PyErr_Occurred())
806 return nullptr;
807
808 if (idim == 0) view.len = nlen * view.itemsize;
809
810 view.shape[idim] = nlen;
811 }
812
813 set_strides(view, itemsize, false /* by definition not fixed */);
814
816}
817
818
819//---------------------------------------------------------------------------
821{
822// Construct a numpy array from the lowlevelview (w/o copy if possible); this
823// uses the Python methods to avoid depending on numpy directly
824
825// Expect as most a dtype from the arguments;
826 static PyObject* npmod = PyImport_ImportModule("numpy"); // ref-count kept
827 if (!npmod)
828 return nullptr;
829
830 bool docopy = false;
831 if (kwds) {
832 PyObject* pycp = PyObject_GetItem(kwds, CPyCppyy::PyStrings::gCopy);
833 if (!pycp) {
834 PyErr_SetString(PyExc_TypeError, "__array__ only supports the \"copy\" keyword");
835 return nullptr;
836 }
837
838 docopy = PyObject_IsTrue(pycp);
839 Py_DECREF(pycp);
840 }
841
842 if (!docopy) { // view requested
843 // expect possible dtype from the arguments, otherwise take it from the type code
844 PyObject* dtype;
845 if (!args || PyTuple_GET_SIZE(args) != 1) {
846 PyObject* npdtype = PyObject_GetAttr(npmod, CPyCppyy::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 = PyObject_GetAttr(npmod, CPyCppyy::PyStrings::gFromBuffer);
860 PyObject* view = PyObject_CallFunctionObjArgs(npfrombuf, (PyObject*)self, dtype, nullptr);
861 Py_DECREF(dtype);
862 Py_DECREF(npfrombuf);
863
864 return view;
865
866 } else { // copy requested
867 PyObject* npcopy = PyObject_GetAttr(npmod, CPyCppyy::PyStrings::gCopy);
868 PyObject* newarr = PyObject_CallFunctionObjArgs(npcopy, (PyObject*)self, nullptr);
869 Py_DECREF(npcopy);
870
871 return newarr;
872 }
873
874// never get here
875 return nullptr;
876}
877
878
879//---------------------------------------------------------------------------
881{
882// Interpret memory as a null-terminated char string.
883 Py_buffer& view = self->fBufInfo;
884
885 if (strcmp(view.format, "b") != 0 || view.ndim != 1) {
886 PyErr_Format(PyExc_TypeError,
887 "as_string only supported for 1-dim char strings (format: %s, dim: %d)",
888 view.format, (int)view.ndim);
889 return nullptr;
890 }
891
892 char* buf = (char*)self->get_buf();
893 size_t sz = strnlen(buf, (size_t)view.shape[0]);
894 return CPyCppyy_PyText_FromStringAndSize(buf, sz);
895}
896
897//---------------------------------------------------------------------------
898static PyMethodDef ll_methods[] = {
899 {(char*)"reshape", (PyCFunction)ll_reshape, METH_O,
900 (char*)"change the shape (not layout) of the low level view"},
901 {(char*)"as_string", (PyCFunction)ll_as_string, METH_NOARGS,
902 (char*)"interpret memory as a null-terminated char string and return Python str"},
903 {(char*)"__array__", (PyCFunction)ll_array, METH_VARARGS | METH_KEYWORDS,
904 (char*)"return a numpy array from the low level view"},
905 {(char*)nullptr, nullptr, 0, nullptr}
906};
907
908//---------------------------------------------------------------------------
909static PyGetSetDef ll_getset[] = {
910 {(char*)"__python_owns__", (getter)ll_getownership, (setter)ll_setownership,
911 (char*)"If true, python manages the life time of this buffer", nullptr},
912 {(char*)"__cpp_array__", (getter)ll_getcpparray, (setter)ll_setcpparray,
913 (char*)"If true, this array was allocated with C++\'s new[]", nullptr},
914 {(char*)"format", (getter)ll_typecode, nullptr, nullptr, nullptr},
915 {(char*)"typecode", (getter)ll_typecode, nullptr, nullptr, nullptr},
916 {(char*)"shape", (getter)ll_shape, (setter)ll_reshape, nullptr, nullptr},
917 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr }
918};
919
920
921namespace CPyCppyy {
922
923//= CPyCppyy low level view type ============================================
924PyTypeObject LowLevelView_Type = {
925 PyVarObject_HEAD_INIT(&PyType_Type, 0)
926 (char*)"cppyy.LowLevelView", // tp_name
927 sizeof(CPyCppyy::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_CHECKTYPES |
945 Py_TPFLAGS_BASETYPE, // tp_flags
946 (char*)"memory view on C++ pointer", // tp_doc
947 0, // tp_traverse
948 0, // tp_clear
949 0, // tp_richcompare
950 0, // tp_weaklistoffset
951 (getiterfunc)ll_iter, // tp_iter
952 0, // tp_iternext
953 ll_methods, // tp_methods
954 0, // tp_members
955 ll_getset, // tp_getset
956 0, // tp_base
957 0, // tp_dict
958 0, // tp_descr_get
959 0, // tp_descr_set
960 0, // tp_dictoffset
961 0, // tp_init
962 0, // tp_alloc
963 (newfunc)ll_new, // tp_new
964 0, // tp_free
965 0, // tp_is_gc
966 0, // tp_bases
967 0, // tp_mro
968 0, // tp_cache
969 0, // tp_subclasses
970 0 // tp_weaklist
971#if PY_VERSION_HEX >= 0x02030000
972 , 0 // tp_del
973#endif
974#if PY_VERSION_HEX >= 0x02060000
975 , 0 // tp_version_tag
976#endif
977#if PY_VERSION_HEX >= 0x03040000
978 , 0 // tp_finalize
979#endif
980#if PY_VERSION_HEX >= 0x03080000
981 , 0 // tp_vectorcall
982#endif
983#if PY_VERSION_HEX >= 0x030c0000
984 , 0 // tp_watched
985#endif
986#if PY_VERSION_HEX >= 0x030d0000
987 , 0 // tp_versions_used
988#endif
989};
990
991} // namespace CPyCppyy
992
993namespace {
994
995template<typename T> struct typecode_traits {};
996template<> struct typecode_traits<bool> {
997 static constexpr const char* format = "?"; static constexpr const char* name = "bool"; };
998template<> struct typecode_traits<char> {
999 static constexpr const char* format = "b"; static constexpr const char* name = "char"; };
1000template<> struct typecode_traits<signed char> {
1001 static constexpr const char* format = "b"; static constexpr const char* name = "SCharAsInt"; };
1002template<> struct typecode_traits<unsigned char> {
1003 static constexpr const char* format = "B"; static constexpr const char* name = "UCharAsInt"; };
1004#if __cplusplus > 201402L
1005template<> struct typecode_traits<std::byte> {
1006 static constexpr const char* format = "B"; static constexpr const char* name = "UCharAsInt"; };
1007#endif
1008template<> struct typecode_traits<char*> {
1009 static constexpr const char* format = "b"; static constexpr const char* name = "char*"; };
1010template<> struct typecode_traits<const char*> {
1011 static constexpr const char* format = "b"; static constexpr const char* name = "const char*"; };
1012template<> struct typecode_traits<short> {
1013 static constexpr const char* format = "h"; static constexpr const char* name = "short"; };
1014template<> struct typecode_traits<unsigned short> {
1015 static constexpr const char* format = "H"; static constexpr const char* name = "unsigned short"; };
1016template<> struct typecode_traits<int> {
1017 static constexpr const char* format = "i"; static constexpr const char* name = "int"; };
1018template<> struct typecode_traits<unsigned int> {
1019 static constexpr const char* format = "I"; static constexpr const char* name = "unsigned int"; };
1020template<> struct typecode_traits<long> {
1021 static constexpr const char* format = "l"; static constexpr const char* name = "long"; };
1022template<> struct typecode_traits<unsigned long> {
1023 static constexpr const char* format = "L"; static constexpr const char* name = "unsigned long"; };
1024template<> struct typecode_traits<long long> {
1025 static constexpr const char* format = "q"; static constexpr const char* name = "long long"; };
1026template<> struct typecode_traits<unsigned long long> {
1027 static constexpr const char* format = "Q"; static constexpr const char* name = "unsigned long long"; };
1028template<> struct typecode_traits<float> {
1029 static constexpr const char* format = "f"; static constexpr const char* name = "float"; };
1030template<> struct typecode_traits<double> {
1031 static constexpr const char* format = "d"; static constexpr const char* name = "double"; };
1032template<> struct typecode_traits<long double> {
1033 static constexpr const char* format = "D"; static constexpr const char* name = "long double"; };
1034template<> struct typecode_traits<std::complex<float>> {
1035 static constexpr const char* format = "Zf"; static constexpr const char* name = "std::complex<float>"; };
1036template<> struct typecode_traits<std::complex<double>> {
1037 static constexpr const char* format = "Zd"; static constexpr const char* name = "std::complex<double>"; };
1038template<> struct typecode_traits<std::complex<int>> {
1039 static constexpr const char* format = "Zi"; static constexpr const char* name = "std::complex<int>"; };
1040template<> struct typecode_traits<std::complex<long>> {
1041 static constexpr const char* format = "Zl"; static constexpr const char* name = "std::complex<long>"; };
1042
1043} // unnamed namespace
1044
1045
1046//---------------------------------------------------------------------------
1048{
1049 Py_buffer& bi = this->fBufInfo;
1050 if (bi.ndim == 1 && bi.shape) {
1051 bi.len = sz * bi.itemsize;
1052 bi.shape[0] = sz;
1053 return true;
1054 }
1055
1056 return false;
1057}
1058
1059//---------------------------------------------------------------------------
1060template<typename T>
1062 T* address, CPyCppyy::cdims_t shape, const char* format = nullptr, const char* name = nullptr, Py_ssize_t itemsize = -1)
1063{
1064 using namespace CPyCppyy;
1065 Py_ssize_t nx = (shape.ndim() != UNKNOWN_SIZE) ? shape[0] : INT_MAX/sizeof(T);
1066 if (nx == UNKNOWN_SIZE) nx = INT_MAX/sizeof(T);
1067 PyObject* args = PyTuple_New(0);
1068 LowLevelView* llp =
1069 (LowLevelView*)LowLevelView_Type.tp_new(&LowLevelView_Type, args, nullptr);
1070 Py_DECREF(args);
1071
1072 Py_buffer& view = llp->fBufInfo;
1073 view.buf = address;
1074 view.obj = nullptr;
1075 view.readonly = 0;
1076 view.format = (char*)(format ? format : typecode_traits<T>::format);
1077 view.ndim = int(shape.ndim() != UNKNOWN_SIZE ? shape.ndim() : 1);
1078 view.shape = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
1079 view.shape[0] = nx; // view.len / view.itemsize
1080 view.strides = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
1081 view.suboffsets = nullptr;
1082 (intptr_t&)view.internal = CPyCppyy::LowLevelView::kIsCppArray; // assumed
1083 bool isfix = shape.ndim() != UNKNOWN_SIZE;
1084 if (isfix) {
1085 for (int i = 0; i < shape.ndim(); ++i)
1086 isfix = isfix && (shape[i] != UNKNOWN_SIZE);
1087 if (isfix) (intptr_t&)view.internal |= CPyCppyy::LowLevelView::kIsFixed;
1088 }
1089
1090 llp->fElemCnv = CreateConverter(name ? name : typecode_traits<T>::name);
1091 if (view.ndim == 1) {
1092 // simple 1-dim array of the declared type
1093 view.len = nx * sizeof(T);
1094 view.itemsize = (itemsize > 0 ? (size_t)itemsize : sizeof(T));
1095 llp->fConverter = llp->fElemCnv;
1096 } else {
1097 // multi-dim array; sub-views are projected by using more LLViews
1098 view.len = nx * sizeof(void*);
1099 view.itemsize = sizeof(void*);
1100 for (Py_ssize_t idim = 1; idim < view.ndim; ++idim)
1101 view.shape[idim] = shape[idim];
1102
1103 // peel off one dimension and create a new LLView converter
1104 std::string tname{name ? name : typecode_traits<T>::name};
1105 tname.append("[]"); // make sure to ask for another array
1106 // TODO: although this will work, it means that "naive" loops are expensive
1107 llp->fConverter = CreateConverter(tname, shape.sub());
1108 }
1109
1110 set_strides(view, sizeof(T), isfix);
1111
1112 return llp;
1113}
1114
1115//---------------------------------------------------------------------------
1116template<typename T>
1118 T** address, CPyCppyy::cdims_t shape, const char* format = nullptr, const char* name = nullptr)
1119{
1120 using namespace CPyCppyy;
1121 LowLevelView* llp = (LowLevelView*)CreateLowLevelViewT((T*)address, shape, format, name);
1122 llp->set_buf((void**)address);
1123 return llp;
1124}
1125
1126//---------------------------------------------------------------------------
1127#define CPPYY_RET_W_CREATOR(type, fname) \
1128 PyObject* (*c)(type, cdims_t) = &fname; \
1129 ll->fCreator = (LowLevelView::Creator_t)c; \
1130 return (PyObject*)ll
1131
1132#define CPPYY_IMPL_VIEW_CREATOR(type) \
1133PyObject* CPyCppyy::CreateLowLevelView(type* address, cdims_t shape) { \
1134 LowLevelView* ll = CreateLowLevelViewT<type>(address, shape); \
1135 CPPYY_RET_W_CREATOR(type*, CreateLowLevelView); \
1136} \
1137PyObject* CPyCppyy::CreateLowLevelView(type** address, cdims_t shape) { \
1138 LowLevelView* ll = CreateLowLevelViewT<type>(address, shape); \
1139 CPPYY_RET_W_CREATOR(type**, CreateLowLevelView); \
1140}
1141
1145#if __cplusplus > 201402L
1146CPPYY_IMPL_VIEW_CREATOR(std::byte);
1147#endif
1155CPPYY_IMPL_VIEW_CREATOR(unsigned long long);
1159CPPYY_IMPL_VIEW_CREATOR(std::complex<float>);
1160CPPYY_IMPL_VIEW_CREATOR(std::complex<double>);
1161CPPYY_IMPL_VIEW_CREATOR(std::complex<int>);
1162CPPYY_IMPL_VIEW_CREATOR(std::complex<long>);
1163
1165 LowLevelView* ll = CreateLowLevelViewT<char>(address, shape);
1167}
1168
1170 LowLevelView* ll = CreateLowLevelViewT<char>(address, shape);
1172}
1173
1175 LowLevelView* ll = CreateLowLevelViewT<char*>(address, shape, nullptr, nullptr, sizeof(char));
1177}
1178
1180 LowLevelView* ll = CreateLowLevelViewT<const char*>(address, shape, nullptr, nullptr, sizeof(char));
1182}
1183
1185 LowLevelView* ll = CreateLowLevelViewT<int8_t>(address, shape, "b", "int8_t");
1187}
1188
1190 LowLevelView* ll = CreateLowLevelViewT<int8_t>(address, shape, "b", "int8_t");
1192}
1193
1195 LowLevelView* ll = CreateLowLevelViewT<uint8_t>(address, shape, "B", "uint8_t");
1197}
1198
1200 LowLevelView* ll = CreateLowLevelViewT<uint8_t>(address, shape, "B", "uint8_t");
1202}
#define Py_TYPE(ob)
Definition CPyCppyy.h:196
#define PyInt_FromSsize_t
Definition CPyCppyy.h:217
#define CPyCppyy_PyText_FromStringAndSize
Definition CPyCppyy.h:85
Py_ssize_t PyNumber_AsSsize_t(PyObject *obj, PyObject *)
Definition CPyCppyy.h:230
#define lenfunc
Definition CPyCppyy.h:224
int Py_ssize_t
Definition CPyCppyy.h:215
#define PyInt_AsSsize_t
Definition CPyCppyy.h:216
#define CPyCppyy_PyText_AsString
Definition CPyCppyy.h:76
void CPyCppyy_PyBuffer_Release(PyObject *, Py_buffer *view)
Definition CPyCppyy.h:282
#define ssizeargfunc
Definition CPyCppyy.h:225
#define Py_RETURN_NONE
Definition CPyCppyy.h:268
#define CPyCppyy_PyText_AsStringChecked
Definition CPyCppyy.h:77
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:81
#define PyVarObject_HEAD_INIT(type, size)
Definition CPyCppyy.h:194
#define PyIndex_Check(obj)
Definition CPyCppyy.h:227
#define ADJUST_PTR(ptr, suboffsets, dim)
static PyObject * ll_typecode(CPyCppyy::LowLevelView *self, void *)
static void * ptr_from_index(CPyCppyy::LowLevelView *llview, Py_ssize_t index)
static int ll_ass_sub(CPyCppyy::LowLevelView *self, PyObject *key, PyObject *value)
static PyObject * ll_item_multi(CPyCppyy::LowLevelView *self, PyObject *tup)
static Py_ssize_t is_multiindex(PyObject *key)
static Py_ssize_t ll_oldgetbuf(CPyCppyy::LowLevelView *self, Py_ssize_t seg, void **pptr)
static PySequenceMethods ll_as_sequence
static Py_ssize_t ll_getsegcount(PyObject *, Py_ssize_t *lenp)
static PyObject * ll_reshape(CPyCppyy::LowLevelView *self, PyObject *shape)
static CPyCppyy::LowLevelView * CreateLowLevelViewT(T *address, CPyCppyy::cdims_t shape, const char *format=nullptr, const char *name=nullptr, Py_ssize_t itemsize=-1)
static PyObject * ll_getcpparray(CPyCppyy::LowLevelView *pyobj)
#define CPYCPPYY_LL_FLAG_GETSET(name, flag, doc)
static bool equiv_shape(const Py_buffer *dest, const Py_buffer *src)
#define CPPYY_RET_W_CREATOR(type, fname)
static int ll_getbuf(CPyCppyy::LowLevelView *self, Py_buffer *view, int flags)
static void * ptr_from_tuple(CPyCppyy::LowLevelView *llview, PyObject *tup)
static int last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
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_shape(CPyCppyy::LowLevelView *self)
static void ll_dealloc(CPyCppyy::LowLevelView *pyobj)
static int ll_setcpparray(CPyCppyy::LowLevelView *pyobj, PyObject *value, void *)
static PyMappingMethods ll_as_mapping
#define CPPYY_IMPL_VIEW_CREATOR(type)
static int ll_setownership(CPyCppyy::LowLevelView *pyobj, PyObject *value, void *)
static PyGetSetDef ll_getset[]
static CPyCppyy::LowLevelView * ll_new(PyTypeObject *subtype, PyObject *, PyObject *)
static PyObject * ll_item(CPyCppyy::LowLevelView *self, Py_ssize_t index)
static int copy_single(Py_buffer *dest, Py_buffer *src)
static PyObject * ll_iter(PyObject *self)
static int init_slice(Py_buffer *base, PyObject *_key, int dim)
static Py_ssize_t ll_length(CPyCppyy::LowLevelView *self)
static void set_strides(Py_buffer &view, size_t itemsize, bool isfix)
static bool is_multislice(PyObject *key)
static PyObject * ll_array(CPyCppyy::LowLevelView *self, PyObject *args, PyObject *kwds)
static PyObject * ll_getownership(CPyCppyy::LowLevelView *pyobj)
static PyObject * ll_subscript(CPyCppyy::LowLevelView *self, PyObject *key)
static char * lookup_dimension(Py_buffer &view, char *ptr, int dim, Py_ssize_t index)
static PyObject * ll_as_string(CPyCppyy::LowLevelView *self)
#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 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:110
#define free
Definition civetweb.c:1539
virtual bool HasState()
Definition API.h:122
virtual bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr)
virtual PyObject * FromMemory(void *address)
Dimensions sub() const
Definition Dimensions.h:76
dim_t ndim() const
Definition Dimensions.h:61
void set_buf(void **buf)
PyObject_HEAD Py_buffer fBufInfo
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
PyObject * gFromBuffer
Definition PyStrings.cxx:77
PyObject * CreateLowLevelView(bool *, cdims_t shape)
PyObject * CreateLowLevelViewString(char **, cdims_t shape)
static const dim_t UNKNOWN_SIZE
Definition Dimensions.h:11
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, cdims_t=0)
PyObject * CreateLowLevelView_i8(int8_t *, cdims_t shape)
PyTypeObject LowLevelView_Type
PyObject_HEAD PyObject * ii_container
unsigned char byte
Definition gifdecode.c:10