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