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
6// Standard
7#include <map>
8#include <assert.h>
9#include <limits.h>
10
11
12//= memoryview-like object ===================================================
13// This is largely setup just like Python builtin memory view objects, with
14// the exceptions that there is no need of a "base" object (it views on C++
15// memory, not a Python object with a buffer interface), it uses the CPyCppyy
16// converters, and typed results and assignments are supported. Code reused
17// under PSF License Version 2.
18
19//= CPyCppyy low level view construction/destruction =========================
20static CPyCppyy::LowLevelView* ll_new(PyTypeObject* subtype, PyObject*, PyObject*)
21{
22// Create a new low level ptr type
23 CPyCppyy::LowLevelView* pyobj = (CPyCppyy::LowLevelView*)subtype->tp_alloc(subtype, 0);
24 if (!pyobj) {
25 PyErr_Print();
26 return nullptr;
27 }
28 memset(&pyobj->fBufInfo, 0, sizeof(Py_buffer));
29 pyobj->fBuf = nullptr;
30 pyobj->fConverter = nullptr;
31
32 return pyobj;
33}
34
35//----------------------------------------------------------------------------
37{
38// Destruction requires the deletion of the converter (if any)
39 PyMem_Free(pyobj->fBufInfo.shape);
40 PyMem_Free(pyobj->fBufInfo.strides);
41 if (pyobj->fConverter && pyobj->fConverter->HasState()) delete pyobj->fConverter;
42 Py_TYPE(pyobj)->tp_free((PyObject*)pyobj);
43}
44
45
46//---------------------------------------------------------------------------
48{
49 return CPyCppyy_PyText_FromString((char*)self->fBufInfo.format);
50}
51
52//---------------------------------------------------------------------------
53static PyGetSetDef ll_getset[] = {
54 {(char*)"format", (getter)ll_typecode, nullptr, nullptr, nullptr},
55 {(char*)"typecode", (getter)ll_typecode, nullptr, nullptr, nullptr},
56 {(char*)nullptr, nullptr, nullptr, nullptr, nullptr }
57};
58
59
60//---------------------------------------------------------------------------
62{
63// Allow the user to fix up the actual (type-strided) size of the buffer.
64 if (!PyTuple_Check(shape) || PyTuple_GET_SIZE(shape) != 1) {
65 if (shape) {
66 PyObject* pystr = PyObject_Str(shape);
67 if (pystr) {
68 PyErr_Format(PyExc_TypeError, "tuple object of length 1 expected, received %s",
70 Py_DECREF(pystr);
71 return nullptr;
72 }
73 }
74 PyErr_SetString(PyExc_TypeError, "tuple object of length 1 expected");
75 return nullptr;
76 }
77
78 Py_ssize_t nlen = PyInt_AsSsize_t(PyTuple_GET_ITEM(shape, 0));
79 if (nlen == -1 && PyErr_Occurred())
80 return nullptr;
81
82 self->fBufInfo.len = nlen * self->fBufInfo.itemsize;
83 if (self->fBufInfo.ndim == 1 && self->fBufInfo.shape)
84 self->fBufInfo.shape[0] = nlen;
85 else {
86 PyErr_SetString(PyExc_TypeError, "unsupported buffer dimensions");
87 return nullptr;
88 }
89
91}
92
93//---------------------------------------------------------------------------
94static PyMethodDef ll_methods[] = {
95 {(char*)"reshape", (PyCFunction)ll_reshape, METH_O, nullptr},
96 {(char*)nullptr, nullptr, 0, nullptr}
97};
98
99
100//- Copy memoryview buffers =================================================
101
102// The functions in this section take a source and a destination buffer
103// with the same logical structure: format, itemsize, ndim and shape
104// are identical, with ndim > 0.
105
106// Check for the presence of suboffsets in the first dimension.
107#define HAVE_PTR(suboffsets, dim) (suboffsets && suboffsets[dim] >= 0)
108// Adjust ptr if suboffsets are present.
109#define ADJUST_PTR(ptr, suboffsets, dim) \
110 (HAVE_PTR(suboffsets, dim) ? *((char**)ptr) + suboffsets[dim] : ptr)
111
112// Assumptions: ndim >= 1. The macro tests for a corner case that should
113// perhaps be explicitly forbidden in the PEP.
114#define HAVE_SUBOFFSETS_IN_LAST_DIM(view) \
115 (view->suboffsets && view->suboffsets[dest->ndim-1] >= 0)
116
117//---------------------------------------------------------------------------
118static inline int last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
119{
120 assert(dest->ndim > 0 && src->ndim > 0);
123 dest->strides[dest->ndim-1] == dest->itemsize &&
124 src->strides[src->ndim-1] == src->itemsize);
125}
126
127//---------------------------------------------------------------------------
128static inline bool equiv_shape(const Py_buffer* dest, const Py_buffer* src)
129{
130// Two shapes are equivalent if they are either equal or identical up
131// to a zero element at the same position. For example, in NumPy arrays
132// the shapes [1, 0, 5] and [1, 0, 7] are equivalent.
133 if (dest->ndim != src->ndim)
134 return false;
135
136 for (int i = 0; i < dest->ndim; i++) {
137 if (dest->shape[i] != src->shape[i])
138 return 0;
139 if (dest->shape[i] == 0)
140 break;
141 }
142
143 return true;
144}
145
146//---------------------------------------------------------------------------
147static bool equiv_structure(const Py_buffer* dest, const Py_buffer* src)
148{
149// Check that the logical structure of the destination and source buffers
150// is identical.
151 if (strcmp(dest->format, src->format) != 0 || dest->itemsize != src->itemsize ||
152 !equiv_shape(dest, src)) {
153 PyErr_SetString(PyExc_ValueError,
154 "low level pointer assignment: lvalue and rvalue have different structures");
155 return false;
156 }
157
158 return true;
159}
160
161//---------------------------------------------------------------------------
162static void copy_base(const Py_ssize_t* shape, Py_ssize_t itemsize,
163 char* dptr, const Py_ssize_t* dstrides, const Py_ssize_t* dsuboffsets,
164 char* sptr, const Py_ssize_t* sstrides, const Py_ssize_t* ssuboffsets,
165 char* mem)
166{
167// Base case for recursive multi-dimensional copying. Contiguous arrays are
168// copied with very little overhead. Assumptions: ndim == 1, mem == nullptr or
169// sizeof(mem) == shape[0] * itemsize.
170 if (!mem) { // contiguous
171 Py_ssize_t size = shape[0] * itemsize;
172 if (dptr + size < sptr || sptr + size < dptr)
173 memcpy(dptr, sptr, size); // no overlapping
174 else
175 memmove(dptr, sptr, size);
176 }
177 else {
178 char *p;
179 Py_ssize_t i;
180 for (i=0, p=mem; i < shape[0]; p+=itemsize, sptr+=sstrides[0], i++) {
181 char *xsptr = ADJUST_PTR(sptr, ssuboffsets, 0);
182 memcpy(p, xsptr, itemsize);
183 }
184 for (i=0, p=mem; i < shape[0]; p+=itemsize, dptr+=dstrides[0], i++) {
185 char *xdptr = ADJUST_PTR(dptr, dsuboffsets, 0);
186 memcpy(xdptr, p, itemsize);
187 }
188 }
189
190}
191
192//---------------------------------------------------------------------------
193static int copy_single(Py_buffer* dest, Py_buffer* src)
194{
195// Faster copying of one-dimensional arrays.
196 char* mem = nullptr;
197
198 assert(dest->ndim == 1);
199
200 if (!equiv_structure(dest, src))
201 return -1;
202
204 mem = (char*)PyMem_Malloc(dest->shape[0] * dest->itemsize);
205 if (!mem) {
206 PyErr_NoMemory();
207 return -1;
208 }
209 }
210
211 copy_base(dest->shape, dest->itemsize,
212 (char*)dest->buf, dest->strides, dest->suboffsets,
213 (char*)src->buf, src->strides, src->suboffsets,
214 mem);
215
216 if (mem)
217 PyMem_Free(mem);
218
219 return 0;
220}
221
222
223//- Indexing and slicing ----------------------------------------------------
224static char* lookup_dimension(Py_buffer& view, char* ptr, int dim, Py_ssize_t index)
225{
226 Py_ssize_t nitems; // items in the given dimension
227
228 assert(view.shape);
229 assert(view.strides);
230
231 nitems = view.shape[dim];
232 if (index < 0)
233 index += nitems;
234
235 if (index < 0 || index >= nitems) {
236 PyErr_Format(PyExc_IndexError,
237 "index out of bounds on dimension %d", dim + 1);
238 return nullptr;
239 }
240
241 ptr += view.strides[dim] * index;
242 ptr = ADJUST_PTR(ptr, view.suboffsets, dim);
243
244 return ptr;
245}
246
247// Get the pointer to the item at index.
248//---------------------------------------------------------------------------
250{
251 Py_buffer& view = llview->fBufInfo;
252 return lookup_dimension(view, (char*)llview->get_buf(), 0, index);
253}
254
255// Get the pointer to the item at tuple.
256//---------------------------------------------------------------------------
258{
259 Py_buffer& view = llview->fBufInfo;
260 Py_ssize_t nindices = PyTuple_GET_SIZE(tup);
261 if (nindices > view.ndim) {
262 PyErr_Format(PyExc_TypeError,
263 "cannot index %d-dimension view with %zd-element tuple", view.ndim, nindices);
264 return nullptr;
265 }
266
267 char* ptr = (char*)llview->get_buf();
268 for (Py_ssize_t dim = 0; dim < nindices; dim++) {
270 index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(tup, dim),
271 PyExc_IndexError);
272 if (index == -1 && PyErr_Occurred())
273 return nullptr;
274 ptr = lookup_dimension(view, ptr, (int)dim, index);
275 if (!ptr)
276 return nullptr;
277 }
278 return ptr;
279}
280
281
282//= mapping methods =========================================================
284{
285 if (!self->get_buf())
286 return 0;
287 return self->fBufInfo.ndim == 0 ? 1 : self->fBufInfo.shape[0];
288}
289
290//---------------------------------------------------------------------------
291static inline int init_slice(Py_buffer* base, PyObject* _key, int dim)
292{
293 Py_ssize_t start, stop, step, slicelength;
294
295#if PY_VERSION_HEX < 0x03000000
296 PySliceObject* key = (PySliceObject*)_key;
297#else
298 PyObject* key = _key;
299#endif
300
301 if (PySlice_GetIndicesEx(key, base->shape[dim], &start, &stop, &step, &slicelength) < 0)
302 return -1;
303
304 if (!base->suboffsets || dim == 0) {
305 adjust_buf:
306 base->buf = (char *)base->buf + base->strides[dim] * start;
307 }
308 else {
309 Py_ssize_t n = dim-1;
310 while (n >= 0 && base->suboffsets[n] < 0)
311 n--;
312 if (n < 0)
313 goto adjust_buf; // all suboffsets are negative
314 base->suboffsets[n] = base->suboffsets[n] + base->strides[dim] * start;
315 }
316 base->shape[dim] = slicelength;
317 base->strides[dim] = base->strides[dim] * step;
318
319 return 0;
320}
321
322//---------------------------------------------------------------------------
323static bool is_multislice(PyObject* key)
324{
325 if (!PyTuple_Check(key))
326 return false;
327
328 Py_ssize_t size = PyTuple_GET_SIZE(key);
329 if (size == 0)
330 return false;
331
332 for (Py_ssize_t i = 0; i < size; i++) {
333 PyObject *x = PyTuple_GET_ITEM(key, i);
334 if (!PySlice_Check(x))
335 return false;
336 }
337 return true;
338}
339
340//---------------------------------------------------------------------------
342{
343 if (!PyTuple_Check(key))
344 return 0;
345
346 Py_ssize_t size = PyTuple_GET_SIZE(key);
347 for (Py_ssize_t i = 0; i < size; i++) {
348 PyObject *x = PyTuple_GET_ITEM(key, i);
349 if (!PyIndex_Check(x))
350 return 0;
351 }
352 return 1;
353}
354
355
356// Return the item at index. In a one-dimensional view, this is an object
357// with the type specified by view->format. Otherwise, the item is a sub-view.
358// The function is used in ll_subscript() and ll_as_sequence.
359//---------------------------------------------------------------------------
361{
362 Py_buffer& view = self->fBufInfo;
363
364 if (!self->get_buf()) {
365 PyErr_SetString(PyExc_ReferenceError, "attempt to access a null-pointer");
366 return nullptr;
367 }
368
369 if (view.ndim == 0) {
370 PyErr_SetString(PyExc_TypeError, "invalid indexing of 0-dim memory");
371 return nullptr;
372 }
373
374 void* ptr = ptr_from_index(self, index);
375 if (ptr)
376 return self->fConverter->FromMemory(ptr);
377
378 return nullptr; // error already set by lookup_dimension
379}
380
381// Return the item at position *key* (a tuple of indices).
382//---------------------------------------------------------------------------
384{
385 Py_buffer& view = self->fBufInfo;
386 Py_ssize_t nindices = PyTuple_GET_SIZE(tup);
387
388 if (nindices < view.ndim) {
389 // TODO: implement
390 PyErr_SetString(PyExc_NotImplementedError,
391 "sub-views are not implemented");
392 return nullptr;
393 }
394
395 void* ptr = ptr_from_tuple(self, tup);
396 if (!ptr)
397 return nullptr;
398 return self->fConverter->FromMemory(ptr);
399}
400
401
402// llp[obj] returns an object holding the data for one element if obj
403// fully indexes the lowlevelptr or another lowlevelptr object if it
404// does not.
405//
406// 0-d lowlevelptr objects can be referenced using llp[...] or llp[()]
407// but not with anything else.
408//---------------------------------------------------------------------------
410{
411 Py_buffer& view = self->fBufInfo;
412
413 if (view.ndim == 0) {
414 if (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0) {
415 return self->fConverter->FromMemory(self->get_buf());
416 }
417 else if (key == Py_Ellipsis) {
418 Py_INCREF(self);
419 return (PyObject*)self;
420 }
421 else {
422 PyErr_SetString(PyExc_TypeError,
423 "invalid indexing of 0-dim memory");
424 return nullptr;
425 }
426 }
427
428 if (PyIndex_Check(key)) {
429 Py_ssize_t index = PyNumber_AsSsize_t(key, PyExc_IndexError);
430 if (index == -1 && PyErr_Occurred())
431 return nullptr;
432 return ll_item(self, index);
433 }
434 else if (PySlice_Check(key)) {
435 // TODO: handle slicing. This should be simpler than the memoryview
436 // case as there is no Python object holding the buffer.
437 PyErr_SetString(PyExc_NotImplementedError,
438 "multi-dimensional slicing is not implemented");
439 return nullptr;
440 }
441 else if (is_multiindex(key)) {
442 return ll_item_multi(self, key);
443 }
444 else if (is_multislice(key)) {
445 PyErr_SetString(PyExc_NotImplementedError,
446 "multi-dimensional slicing is not implemented");
447 return nullptr;
448 }
449
450 PyErr_SetString(PyExc_TypeError, "invalid slice key");
451 return nullptr;
452}
453
454//---------------------------------------------------------------------------
456{
457 Py_buffer& view = self->fBufInfo;
458 Py_buffer src;
459
460 if (view.readonly) {
461 PyErr_SetString(PyExc_TypeError, "cannot modify read-only memory");
462 return -1;
463 }
464
465 if (value == nullptr) {
466 PyErr_SetString(PyExc_TypeError, "cannot delete memory");
467 return -1;
468 }
469
470 if (view.ndim == 0) {
471 if (key == Py_Ellipsis ||
472 (PyTuple_Check(key) && PyTuple_GET_SIZE(key) == 0)) {
473 return self->fConverter->ToMemory(value, self->get_buf()) ? 0 : -1;
474 }
475 else {
476 PyErr_SetString(PyExc_TypeError,
477 "invalid indexing of 0-dim memory");
478 return -1;
479 }
480 }
481
482 if (PyIndex_Check(key)) {
484 if (1 < view.ndim) {
485 PyErr_SetString(PyExc_NotImplementedError,
486 "sub-views are not implemented");
487 return -1;
488 }
489 index = PyNumber_AsSsize_t(key, PyExc_IndexError);
490 if (index == -1 && PyErr_Occurred())
491 return -1;
492 void* ptr = ptr_from_index(self, index);
493 if (ptr == nullptr)
494 return -1;
495 return self->fConverter->ToMemory(value, ptr) ? 0 : -1;
496 }
497
498 // one-dimensional: fast path
499 if (PySlice_Check(key) && view.ndim == 1) {
500 Py_buffer dest; // sliced view
501 Py_ssize_t arrays[3];
502 int ret = -1;
503
504 // rvalue must be an exporter
505 if (PyObject_GetBuffer(value, &src, PyBUF_FULL_RO) < 0)
506 return ret;
507
508 dest = view;
509 dest.shape = &arrays[0]; dest.shape[0] = view.shape[0];
510 dest.strides = &arrays[1]; dest.strides[0] = view.strides[0];
511 if (view.suboffsets) {
512 dest.suboffsets = &arrays[2]; dest.suboffsets[0] = view.suboffsets[0];
513 }
514
515 if (init_slice(&dest, key, 0) < 0)
516 return -1;
517 dest.len = dest.shape[0] * dest.itemsize;
518
519 return copy_single(&dest, &src);
520 }
521
522 if (is_multiindex(key)) {
523 // TODO: implement
524 if (PyTuple_GET_SIZE(key) < view.ndim) {
525 PyErr_SetString(PyExc_NotImplementedError,
526 "sub-views are not implemented");
527 return -1;
528 }
529 void* ptr = ptr_from_tuple(self, key);
530 if (ptr == nullptr)
531 return -1;
532 return self->fConverter->ToMemory(value, ptr) ? 0 : -1;
533 }
534
535 if (PySlice_Check(key) || is_multislice(key)) {
536 // TODO: implement
537 PyErr_SetString(PyExc_NotImplementedError,
538 "LowLevelView slice assignments are currently restricted "
539 "to ndim = 1");
540 return -1;
541 }
542
543 PyErr_SetString(PyExc_TypeError, "invalid slice key");
544 return -1;
545}
546
547#if PY_VERSION_HEX < 0x03000000
548//---------------------------------------------------------------------------
550{
551 if (seg != 0) {
552 PyErr_SetString(PyExc_TypeError, "accessing non-existent segment");
553 return -1;
554 }
555
556 *pptr = self->get_buf();
557 return self->fBufInfo.len;
558}
559
560//---------------------------------------------------------------------------
562{
563 if (lenp) *lenp = 1;
564 return 1;
565}
566#endif
567
568//---------------------------------------------------------------------------
569static int ll_getbuf(CPyCppyy::LowLevelView* self, Py_buffer* view, int flags)
570{
571// Simplified from memoryobject, as we're always dealing with C arrays.
572
573// start with full copy
574 *view = self->fBufInfo;
575
576 if (!(flags & PyBUF_FORMAT)) {
577 /* NULL indicates that the buffer's data type has been cast to 'B'.
578 view->itemsize is the _previous_ itemsize. If shape is present,
579 the equality product(shape) * itemsize = len still holds at this
580 point. The equality calcsize(format) = itemsize does _not_ hold
581 from here on! */
582 view->format = NULL;
583 }
584
585 if ((flags & PyBUF_F_CONTIGUOUS) == PyBUF_F_CONTIGUOUS) {
586 PyErr_SetString(PyExc_BufferError,
587 "underlying buffer is not Fortran contiguous");
588 return -1;
589 }
590
591 if (!(flags & PyBUF_FORMAT)) {
592 /* PyBUF_SIMPLE or PyBUF_WRITABLE: at this point buf is C-contiguous,
593 so base->buf = ndbuf->data. */
594 if (view->format != NULL) {
595 /* PyBUF_SIMPLE|PyBUF_FORMAT and PyBUF_WRITABLE|PyBUF_FORMAT do
596 not make sense. */
597 PyErr_Format(PyExc_BufferError,
598 "cannot cast to unsigned bytes if the format flag is present");
599 return -1;
600 }
601 /* product(shape) * itemsize = len and calcsize(format) = itemsize
602 do _not_ hold from here on! */
603 view->ndim = 1;
604 view->shape = NULL;
605 }
606
607 view->obj = (PyObject*)self;
608 Py_INCREF(view->obj);
609
610 return 0;
611}
612
613
614//- mapping methods ---------------------------------------------------------
615static PyMappingMethods ll_as_mapping = {
616 (lenfunc) ll_length, // mp_length
617 (binaryfunc) ll_subscript, // mp_subscript
618 (objobjargproc)ll_ass_sub, // mp_ass_subscript
619};
620
621//- sequence methods --------------------------------------------------------
622static PySequenceMethods ll_as_sequence = {
623 (lenfunc)ll_length, // sq_length
624 0, // sq_concat
625 0, // sq_repeat
626 (ssizeargfunc)ll_item, // sq_item
627 0, // sq_slice
628 0, // sq_ass_item
629 0, // sq_ass_slice
630 0, // sq_contains
631 0, // sq_inplace_concat
632 0, // sq_inplace_repeat
633};
634
635//- buffer methods ----------------------------------------------------------
636static PyBufferProcs ll_as_buffer = {
637#if PY_VERSION_HEX < 0x03000000
638 (readbufferproc)ll_oldgetbuf, // bf_getreadbuffer
639 (writebufferproc)ll_oldgetbuf, // bf_getwritebuffer
640 (segcountproc)ll_getsegcount, // bf_getsegcount
641 0, // bf_getcharbuffer
642#endif
643 (getbufferproc)ll_getbuf, // bf_getbuffer
644 0, // bf_releasebuffer
645};
646
647
648namespace CPyCppyy {
649
650//= CPyCppyy low level view type ============================================
651PyTypeObject LowLevelView_Type = {
652 PyVarObject_HEAD_INIT(&PyType_Type, 0)
653 (char*)"cppyy.LowLevelView", // tp_name
654 sizeof(CPyCppyy::LowLevelView),// tp_basicsize
655 0, // tp_itemsize
656 (destructor)ll_dealloc, // tp_dealloc
657 0, // tp_print
658 0, // tp_getattr
659 0, // tp_setattr
660 0, // tp_compare
661 0, // tp_repr
662 0, // tp_as_number
663 &ll_as_sequence, // tp_as_sequence
664 &ll_as_mapping, // tp_as_mapping
665 0, // tp_hash
666 0, // tp_call
667 0, // tp_str
668 0, // tp_getattro
669 0, // tp_setattro
670 &ll_as_buffer, // tp_as_buffer
671 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES |
672 Py_TPFLAGS_BASETYPE, // tp_flags
673 (char*)"memory view on C++ pointer", // tp_doc
674 0, // tp_traverse
675 0, // tp_clear
676 0, // tp_richcompare
677 0, // tp_weaklistoffset
678 0, // tp_iter
679 0, // tp_iternext
680 ll_methods, // tp_methods
681 0, // tp_members
682 ll_getset, // tp_getset
683 0, // tp_base
684 0, // tp_dict
685 0, // tp_descr_get
686 0, // tp_descr_set
687 0, // tp_dictoffset
688 0, // tp_init
689 0, // tp_alloc
690 (newfunc)ll_new, // tp_new
691 0, // tp_free
692 0, // tp_is_gc
693 0, // tp_bases
694 0, // tp_mro
695 0, // tp_cache
696 0, // tp_subclasses
697 0 // tp_weaklist
698#if PY_VERSION_HEX >= 0x02030000
699 , 0 // tp_del
700#endif
701#if PY_VERSION_HEX >= 0x02060000
702 , 0 // tp_version_tag
703#endif
704#if PY_VERSION_HEX >= 0x03040000
705 , 0 // tp_finalize
706#endif
707};
708
709} // namespace CPyCppyy
710
711namespace {
712
713template<typename T> struct typecode_traits {};
714template<> struct typecode_traits<bool> {
715 static constexpr const char* format = "?"; static constexpr const char* name = "bool"; };
716template<> struct typecode_traits<signed char> {
717 static constexpr const char* format = "b"; static constexpr const char* name = "signed char"; };
718template<> struct typecode_traits<unsigned char> {
719 static constexpr const char* format = "B"; static constexpr const char* name = "UCharAsInt"; };
720#if __cplusplus > 201402L
721template<> struct typecode_traits<std::byte> {
722 static constexpr const char* format = "B"; static constexpr const char* name = "UCharAsInt"; };
723#endif
724template<> struct typecode_traits<const char*> {
725 static constexpr const char* format = "b"; static constexpr const char* name = "const char*"; };
726template<> struct typecode_traits<short> {
727 static constexpr const char* format = "h"; static constexpr const char* name = "short"; };
728template<> struct typecode_traits<unsigned short> {
729 static constexpr const char* format = "H"; static constexpr const char* name = "unsigned short"; };
730template<> struct typecode_traits<int> {
731 static constexpr const char* format = "i"; static constexpr const char* name = "int"; };
732template<> struct typecode_traits<unsigned int> {
733 static constexpr const char* format = "I"; static constexpr const char* name = "unsigned int"; };
734template<> struct typecode_traits<long> {
735 static constexpr const char* format = "l"; static constexpr const char*
736#if PY_VERSION_HEX < 0x03000000
737 name = "int";
738#else
739 name = "long";
740#endif
741};
742template<> struct typecode_traits<unsigned long> {
743 static constexpr const char* format = "L"; static constexpr const char* name = "unsigned long"; };
744template<> struct typecode_traits<long long> {
745 static constexpr const char* format = "q"; static constexpr const char* name = "long long"; };
746template<> struct typecode_traits<unsigned long long> {
747 static constexpr const char* format = "Q"; static constexpr const char* name = "unsigned long long"; };
748template<> struct typecode_traits<float> {
749 static constexpr const char* format = "f"; static constexpr const char* name = "float"; };
750template<> struct typecode_traits<double> {
751 static constexpr const char* format = "d"; static constexpr const char* name = "double"; };
752template<> struct typecode_traits<long double> {
753 static constexpr const char* format = "D"; static constexpr const char* name = "long double"; };
754template<> struct typecode_traits<std::complex<float>> {
755 static constexpr const char* format = "Zf"; static constexpr const char* name = "std::complex<float>"; };
756template<> struct typecode_traits<std::complex<double>> {
757 static constexpr const char* format = "Zd"; static constexpr const char* name = "std::complex<double>"; };
758template<> struct typecode_traits<std::complex<int>> {
759 static constexpr const char* format = "Zi"; static constexpr const char* name = "std::complex<int>"; };
760template<> struct typecode_traits<std::complex<long>> {
761 static constexpr const char* format = "Zl"; static constexpr const char* name = "std::complex<long>"; };
762
763} // unnamed namespace
764
765
766//---------------------------------------------------------------------------
767template<typename T>
768static inline PyObject* CreateLowLevelViewT(T* address, Py_ssize_t* shape)
769{
770 using namespace CPyCppyy;
771 Py_ssize_t nx = (shape && 0 <= shape[1]) ? shape[1] : INT_MAX/sizeof(T);
772 PyObject* args = PyTuple_New(0);
773 LowLevelView* llp =
774 (LowLevelView*)LowLevelView_Type.tp_new(&LowLevelView_Type, args, nullptr);
775 Py_DECREF(args);
776
777 Py_buffer& view = llp->fBufInfo;
778 view.buf = address;
779 view.obj = nullptr;
780 view.readonly = 0;
781 view.format = (char*)typecode_traits<T>::format;
782 view.ndim = shape ? (int)shape[0] : 1;
783 view.shape = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
784 view.shape[0] = nx; // view.len / view.itemsize
785 view.strides = (Py_ssize_t*)PyMem_Malloc(view.ndim * sizeof(Py_ssize_t));
786 view.suboffsets = nullptr;
787 view.internal = nullptr;
788
789 if (view.ndim == 1) {
790 // simple 1-dim array of the declared type
791 view.len = nx * sizeof(T);
792 view.itemsize = sizeof(T);
793 llp->fConverter = CreateConverter(typecode_traits<T>::name);
794 } else {
795 // multi-dim array; sub-views are projected by using more LLViews
796 view.len = nx * sizeof(void*);
797 view.itemsize = sizeof(void*);
798
799 // peel off one dimension and create a new LLView converter
800 Py_ssize_t res = shape[1];
801 shape[1] = shape[0] - 1;
802 std::string tname{typecode_traits<T>::name};
803 tname.append("*"); // make sure to ask for another array
804 // TODO: although this will work, it means that "naive" loops are expensive
805 llp->fConverter = CreateConverter(tname, &shape[1]);
806 shape[1] = res;
807 }
808
809 view.strides[0] = view.itemsize;
810
811 return (PyObject*)llp;
812}
813
814//---------------------------------------------------------------------------
815template<typename T>
816static inline PyObject* CreateLowLevelViewT(T** address, Py_ssize_t* shape)
817{
818 using namespace CPyCppyy;
819 T* buf = address ? *address : nullptr;
820 LowLevelView* llp = (LowLevelView*)CreateLowLevelViewT(buf, shape);
821 llp->set_buf((void**)address);
822 return (PyObject*)llp;
823}
824
825//---------------------------------------------------------------------------
826#define CPPYY_IMPL_VIEW_CREATOR(type) \
827PyObject* CPyCppyy::CreateLowLevelView(type* address, Py_ssize_t* shape) { \
828 return CreateLowLevelViewT<type>(address, shape); \
829} \
830PyObject* CPyCppyy::CreateLowLevelView(type** address, Py_ssize_t* shape) { \
831 return CreateLowLevelViewT<type>(address, shape); \
832}
833
837#if __cplusplus > 201402L
838CPPYY_IMPL_VIEW_CREATOR(std::byte);
839#endif
847CPPYY_IMPL_VIEW_CREATOR(unsigned long long);
851CPPYY_IMPL_VIEW_CREATOR(std::complex<float>);
852CPPYY_IMPL_VIEW_CREATOR(std::complex<double>);
853CPPYY_IMPL_VIEW_CREATOR(std::complex<int>);
854CPPYY_IMPL_VIEW_CREATOR(std::complex<long>);
855
856PyObject* CPyCppyy::CreateLowLevelView(const char** address, Py_ssize_t* shape) {
857 return CreateLowLevelViewT<const char*>(address, shape);
858}
#define Py_TYPE(ob)
Definition CPyCppyy.h:217
Py_ssize_t PyNumber_AsSsize_t(PyObject *obj, PyObject *)
Definition CPyCppyy.h:251
#define lenfunc
Definition CPyCppyy.h:245
int Py_ssize_t
Definition CPyCppyy.h:236
#define PyInt_AsSsize_t
Definition CPyCppyy.h:237
#define ssizeargfunc
Definition CPyCppyy.h:246
#define Py_RETURN_NONE
Definition CPyCppyy.h:289
#define CPyCppyy_PyText_AsStringChecked
Definition CPyCppyy.h:98
#define CPyCppyy_PyText_FromString
Definition CPyCppyy.h:102
#define PyVarObject_HEAD_INIT(type, size)
Definition CPyCppyy.h:215
#define PyIndex_Check(obj)
Definition CPyCppyy.h:248
static PyObject * CreateLowLevelViewT(T *address, Py_ssize_t *shape)
#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 bool equiv_shape(const Py_buffer *dest, const Py_buffer *src)
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 void ll_dealloc(CPyCppyy::LowLevelView *pyobj)
static PyMappingMethods ll_as_mapping
#define CPPYY_IMPL_VIEW_CREATOR(type)
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 int init_slice(Py_buffer *base, PyObject *_key, int dim)
static Py_ssize_t ll_length(CPyCppyy::LowLevelView *self)
static bool is_multislice(PyObject *key)
static PyObject * ll_subscript(CPyCppyy::LowLevelView *self, 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 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
virtual bool HasState()
Definition API.h:105
virtual bool ToMemory(PyObject *value, void *address, PyObject *ctxt=nullptr)
virtual PyObject * FromMemory(void *address)
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
Set of helper functions that are invoked from the pythonizors, on the Python side.
PyObject * CreateLowLevelView(bool *, Py_ssize_t *shape=nullptr)
PyTypeObject LowLevelView_Type
#define dest(otri, vertexptr)
Definition triangle.c:1041
unsigned char byte
Definition gifdecode.c:10