Logo ROOT  
Reference Guide
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 =========================
20 static 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 //---------------------------------------------------------------------------
50 static 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 //---------------------------------------------------------------------------
91 static 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 //---------------------------------------------------------------------------
115 static inline int last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
116 {
117  assert(dest->ndim > 0 && src->ndim > 0);
118  return (!HAVE_SUBOFFSETS_IN_LAST_DIM(dest) &&
120  dest->strides[dest->ndim-1] == dest->itemsize &&
121  src->strides[src->ndim-1] == src->itemsize);
122 }
123 
124 //---------------------------------------------------------------------------
125 static 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 //---------------------------------------------------------------------------
144 static 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 //---------------------------------------------------------------------------
159 static 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 //---------------------------------------------------------------------------
190 static 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 ----------------------------------------------------
221 static 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 //---------------------------------------------------------------------------
246 static 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 //---------------------------------------------------------------------------
254 static void* ptr_from_tuple(CPyCppyy::LowLevelView* llview, PyObject* tup)
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 //---------------------------------------------------------------------------
288 static 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 //---------------------------------------------------------------------------
320 static 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 //---------------------------------------------------------------------------
452 static 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 //---------------------------------------------------------------------------
566 static 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 ---------------------------------------------------------
612 static 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 --------------------------------------------------------
619 static 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 ----------------------------------------------------------
633 static 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 
645 namespace CPyCppyy {
646 
647 //= CPyCppyy low level view type ============================================
648 PyTypeObject 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 
708 namespace {
709 
710 template<typename T> struct typecode_traits {};
711 template<> struct typecode_traits<bool> {
712  static constexpr const char* format = "?"; static constexpr const char* name = "bool"; };
713 template<> struct typecode_traits<signed char> {
714  static constexpr const char* format = "b"; static constexpr const char* name = "signed char"; };
715 template<> struct typecode_traits<unsigned char> {
716  static constexpr const char* format = "B"; static constexpr const char* name = "UCharAsInt"; };
717 #if __cplusplus > 201402L
718 template<> struct typecode_traits<std::byte> {
719  static constexpr const char* format = "B"; static constexpr const char* name = "UCharAsInt"; };
720 #endif
721 template<> struct typecode_traits<const char*> {
722  static constexpr const char* format = "b"; static constexpr const char* name = "const char*"; };
723 template<> struct typecode_traits<short> {
724  static constexpr const char* format = "h"; static constexpr const char* name = "short"; };
725 template<> struct typecode_traits<unsigned short> {
726  static constexpr const char* format = "H"; static constexpr const char* name = "unsigned short"; };
727 template<> struct typecode_traits<int> {
728  static constexpr const char* format = "i"; static constexpr const char* name = "int"; };
729 template<> struct typecode_traits<unsigned int> {
730  static constexpr const char* format = "I"; static constexpr const char* name = "unsigned int"; };
731 template<> 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 };
739 template<> struct typecode_traits<unsigned long> {
740  static constexpr const char* format = "L"; static constexpr const char* name = "unsigned long"; };
741 template<> struct typecode_traits<long long> {
742  static constexpr const char* format = "q"; static constexpr const char* name = "long long"; };
743 template<> struct typecode_traits<unsigned long long> {
744  static constexpr const char* format = "Q"; static constexpr const char* name = "unsigned long long"; };
745 template<> struct typecode_traits<float> {
746  static constexpr const char* format = "f"; static constexpr const char* name = "float"; };
747 template<> struct typecode_traits<double> {
748  static constexpr const char* format = "d"; static constexpr const char* name = "double"; };
749 template<> struct typecode_traits<long double> {
750  static constexpr const char* format = "D"; static constexpr const char* name = "long double"; };
751 template<> struct typecode_traits<std::complex<float>> {
752  static constexpr const char* format = "Zf"; static constexpr const char* name = "std::complex<float>"; };
753 template<> struct typecode_traits<std::complex<double>> {
754  static constexpr const char* format = "Zd"; static constexpr const char* name = "std::complex<double>"; };
755 template<> struct typecode_traits<std::complex<int>> {
756  static constexpr const char* format = "Zi"; static constexpr const char* name = "std::complex<int>"; };
757 template<> 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 //---------------------------------------------------------------------------
764 template<typename T>
765 static 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);
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 //---------------------------------------------------------------------------
812 template<typename T>
813 static 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) \
824 PyObject* CPyCppyy::CreateLowLevelView(type* address, Py_ssize_t* shape) { \
825  return CreateLowLevelViewT<type>(address, shape); \
826 } \
827 PyObject* CPyCppyy::CreateLowLevelView(type** address, Py_ssize_t* shape) { \
828  return CreateLowLevelViewT<type>(address, shape); \
829 }
830 
833 CPPYY_IMPL_VIEW_CREATOR(unsigned char);
834 #if __cplusplus > 201402L
836 #endif
838 CPPYY_IMPL_VIEW_CREATOR(unsigned short);
842 CPPYY_IMPL_VIEW_CREATOR(unsigned long);
844 CPPYY_IMPL_VIEW_CREATOR(unsigned long long);
848 CPPYY_IMPL_VIEW_CREATOR(std::complex<float>);
849 CPPYY_IMPL_VIEW_CREATOR(std::complex<double>);
850 CPPYY_IMPL_VIEW_CREATOR(std::complex<int>);
851 CPPYY_IMPL_VIEW_CREATOR(std::complex<long>);
852 
853 PyObject* CPyCppyy::CreateLowLevelView(const char** address, Py_ssize_t* shape) {
854  return CreateLowLevelViewT<const char*>(address, shape);
855 }
CPyCppyy
Definition: TPython.cxx:99
CPPYY_IMPL_VIEW_CREATOR
#define CPPYY_IMPL_VIEW_CREATOR(type)
Definition: LowLevelViews.cxx:823
equiv_structure
static bool equiv_structure(const Py_buffer *dest, const Py_buffer *src)
Definition: LowLevelViews.cxx:144
ll_getset
static PyGetSetDef ll_getset[]
Definition: LowLevelViews.cxx:50
n
const Int_t n
Definition: legend1.C:16
is_multiindex
static Py_ssize_t is_multiindex(PyObject *key)
Definition: LowLevelViews.cxx:338
ll_new
static CPyCppyy::LowLevelView * ll_new(PyTypeObject *subtype, PyObject *, PyObject *)
Definition: LowLevelViews.cxx:20
PyVarObject_HEAD_INIT
#define PyVarObject_HEAD_INIT(type, size)
Definition: CPyCppyy.h:207
CPyCppyy::LowLevelView::fBuf
void ** fBuf
Definition: LowLevelViews.h:20
ll_item_multi
static PyObject * ll_item_multi(CPyCppyy::LowLevelView *self, PyObject *tup)
Definition: LowLevelViews.cxx:380
ll_ass_sub
static int ll_ass_sub(CPyCppyy::LowLevelView *self, PyObject *key, PyObject *value)
Definition: LowLevelViews.cxx:452
ll_item
static PyObject * ll_item(CPyCppyy::LowLevelView *self, Py_ssize_t index)
Definition: LowLevelViews.cxx:357
dest
#define dest(otri, vertexptr)
Definition: triangle.c:1040
CPyCppyy::LowLevelView::fConverter
Converter * fConverter
Definition: LowLevelViews.h:21
PyIndex_Check
#define PyIndex_Check(obj)
Definition: CPyCppyy.h:240
Py_ssize_t
int Py_ssize_t
Definition: CPyCppyy.h:228
PyObject
_object PyObject
Definition: PyMethodBase.h:41
ll_as_buffer
static PyBufferProcs ll_as_buffer
Definition: LowLevelViews.cxx:633
ll_getsegcount
static Py_ssize_t ll_getsegcount(PyObject *, Py_ssize_t *lenp)
Definition: LowLevelViews.cxx:558
lenfunc
#define lenfunc
Definition: CPyCppyy.h:237
ll_subscript
static PyObject * ll_subscript(CPyCppyy::LowLevelView *self, PyObject *key)
Definition: LowLevelViews.cxx:406
ssizeargfunc
#define ssizeargfunc
Definition: CPyCppyy.h:238
x
Double_t x[n]
Definition: legend1.C:17
ll_getbuf
static int ll_getbuf(CPyCppyy::LowLevelView *self, Py_buffer *view, int flags)
Definition: LowLevelViews.cxx:566
long
long
Definition: Converters.cxx:858
CPyCppyy_PyText_FromString
#define CPyCppyy_PyText_FromString
Definition: CPyCppyy.h:102
init_slice
static int init_slice(Py_buffer *base, PyObject *_key, int dim)
Definition: LowLevelViews.cxx:288
CPyCppyy::LowLevelView_Type
PyTypeObject LowLevelView_Type
Definition: LowLevelViews.cxx:648
bool
ll_length
static Py_ssize_t ll_length(CPyCppyy::LowLevelView *self)
Definition: LowLevelViews.cxx:280
ADJUST_PTR
#define ADJUST_PTR(ptr, suboffsets, dim)
Definition: LowLevelViews.cxx:106
CreateLowLevelViewT
static PyObject * CreateLowLevelViewT(T *address, Py_ssize_t *shape)
Definition: LowLevelViews.cxx:765
ll_oldgetbuf
static Py_ssize_t ll_oldgetbuf(CPyCppyy::LowLevelView *self, Py_ssize_t seg, void **pptr)
Definition: LowLevelViews.cxx:546
is_multislice
static bool is_multislice(PyObject *key)
Definition: LowLevelViews.cxx:320
CPyCppyy::LowLevelView::fBufInfo
PyObject_HEAD Py_buffer fBufInfo
Definition: LowLevelViews.h:19
ll_reshape
static PyObject * ll_reshape(CPyCppyy::LowLevelView *self, PyObject *shape)
Definition: LowLevelViews.cxx:58
PyInt_AsSsize_t
#define PyInt_AsSsize_t
Definition: CPyCppyy.h:229
last_dim_is_contiguous
static int last_dim_is_contiguous(const Py_buffer *dest, const Py_buffer *src)
Definition: LowLevelViews.cxx:115
HAVE_SUBOFFSETS_IN_LAST_DIM
#define HAVE_SUBOFFSETS_IN_LAST_DIM(view)
Definition: LowLevelViews.cxx:111
CPyCppyy::CreateConverter
CPYCPPYY_EXTERN Converter * CreateConverter(const std::string &name, Py_ssize_t *dims=nullptr)
equiv_shape
static bool equiv_shape(const Py_buffer *dest, const Py_buffer *src)
Definition: LowLevelViews.cxx:125
CPyCppyy.h
double
double
Definition: Converters.cxx:921
ll_dealloc
static void ll_dealloc(CPyCppyy::LowLevelView *pyobj)
Definition: LowLevelViews.cxx:33
ptr_from_index
static void * ptr_from_index(CPyCppyy::LowLevelView *llview, Py_ssize_t index)
Definition: LowLevelViews.cxx:246
ptr_from_tuple
static void * ptr_from_tuple(CPyCppyy::LowLevelView *llview, PyObject *tup)
Definition: LowLevelViews.cxx:254
byte
unsigned char byte
Definition: gifdecode.c:10
copy_single
static int copy_single(Py_buffer *dest, Py_buffer *src)
Definition: LowLevelViews.cxx:190
lookup_dimension
static char * lookup_dimension(Py_buffer &view, char *ptr, int dim, Py_ssize_t index)
Definition: LowLevelViews.cxx:221
ll_as_sequence
static PySequenceMethods ll_as_sequence
Definition: LowLevelViews.cxx:619
CPyCppyy::CreateLowLevelView
PyObject * CreateLowLevelView(const char **, Py_ssize_t *shape=nullptr)
Definition: LowLevelViews.cxx:853
CPyCppyy::LowLevelView::set_buf
void set_buf(void **buf)
Definition: LowLevelViews.h:25
short
l unsigned short
Definition: Converters.cxx:862
ll_methods
static PyMethodDef ll_methods[]
Definition: LowLevelViews.cxx:91
name
char name[80]
Definition: TGX11.cxx:109
CPyCppyy::LowLevelView::get_buf
void * get_buf()
Definition: LowLevelViews.h:24
ROOT::Math::Chebyshev::T
double T(double x)
Definition: ChebyshevPol.h:34
PyNumber_AsSsize_t
Py_ssize_t PyNumber_AsSsize_t(PyObject *obj, PyObject *)
Definition: CPyCppyy.h:243
LowLevelViews.h
copy_base
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)
Definition: LowLevelViews.cxx:159
CPyCppyy::Converter::HasState
virtual bool HasState()
Definition: API.h:105
ll_typecode
static PyObject * ll_typecode(CPyCppyy::LowLevelView *self, void *)
Definition: LowLevelViews.cxx:44
Py_TYPE
#define Py_TYPE(ob)
Definition: CPyCppyy.h:209
ll_as_mapping
static PyMappingMethods ll_as_mapping
Definition: LowLevelViews.cxx:612
Converters.h
CPyCppyy_PyText_AsStringChecked
#define CPyCppyy_PyText_AsStringChecked
Definition: CPyCppyy.h:98
Py_RETURN_NONE
#define Py_RETURN_NONE
Definition: CPyCppyy.h:281
int
CPyCppyy::LowLevelView
Definition: LowLevelViews.h:16