88// Standard
99#include < map>
1010#include < assert.h>
11+ #include < string.h>
1112#include < limits.h>
1213
1314
@@ -246,14 +247,14 @@ static char* lookup_dimension(Py_buffer& view, char* ptr, int dim, Py_ssize_t in
246247 index += nitems;
247248 else {
248249 PyErr_Format (PyExc_IndexError,
249- " negative index not supporte on dimension %d with unknown size" , dim + 1 );
250+ " negative index not supported on dimension %d with unknown size" , dim + 1 );
250251 return nullptr ;
251252 }
252253 }
253254
254255 if (view.strides [dim] == CPyCppyy::UNKNOWN_SIZE) {
255256 PyErr_Format (PyExc_IndexError,
256- " multi index not supporte on dimension %d with unknown stride" , dim + 1 );
257+ " multi index not supported on dimension %d with unknown stride" , dim + 1 );
257258 return nullptr ;
258259 }
259260
@@ -466,11 +467,38 @@ static PyObject* ll_subscript(CPyCppyy::LowLevelView* self, PyObject* key)
466467 return ll_item (self, index);
467468 }
468469 else if (PySlice_Check (key)) {
469- // TODO: handle slicing. This should be simpler than the memoryview
470- // case as there is no Python object holding the buffer.
471- PyErr_SetString (PyExc_NotImplementedError,
472- " multi-dimensional slicing is not implemented" );
473- return nullptr ;
470+ if (view.ndim == 1 ) {
471+ Py_ssize_t start, stop, step, slicelen;
472+ if (PySlice_Unpack (key, &start, &stop, &step) < 0 )
473+ return nullptr ;
474+
475+ slicelen = PySlice_AdjustIndices (view.shape [0 ], &start, &stop, step);
476+ if (slicelen <= 0 )
477+ slicelen = view.shape [0 ];
478+
479+ char * buf = (char *)self->get_buf ();
480+ char * slice_buf = new char [slicelen*view.itemsize ];
481+ size_t isize = view.itemsize ;
482+ for (size_t i=0 , cur=0 ; i < (size_t )slicelen; cur += step, ++i) {
483+ for (size_t j=0 ; j < isize; ++j)
484+ slice_buf[i*isize+j] = buf[(start+cur)*isize + j];
485+ }
486+
487+ CPyCppyy::LowLevelView* ll = self->fCreator (slice_buf, {1 , slicelen});
488+ if (!ll)
489+ delete [] slice_buf;
490+ else
491+ (intptr_t &)ll->fBufInfo .internal |= CPyCppyy::LowLevelView::kIsOwner ;
492+
493+ return (PyObject*)ll;
494+
495+ } else {
496+ // TODO: handle slicing. This should be simpler than the memoryview
497+ // case as there is no Python object holding the buffer.
498+ PyErr_SetString (PyExc_NotImplementedError,
499+ " multi-dimensional slicing is not implemented" );
500+ return nullptr ;
501+ }
474502 }
475503 else if (is_multiindex (key)) {
476504 return ll_item_multi (self, key);
@@ -823,10 +851,31 @@ static PyObject* ll_array(CPyCppyy::LowLevelView* self, PyObject* args, PyObject
823851 return view;
824852}
825853
854+
855+ // ---------------------------------------------------------------------------
856+ static PyObject* ll_as_string (CPyCppyy::LowLevelView* self)
857+ {
858+ // Interpret memory as a null-terminated char string.
859+ Py_buffer& view = self->fBufInfo ;
860+
861+ if (strcmp (view.format , " b" ) != 0 || view.ndim != 1 ) {
862+ PyErr_Format (PyExc_TypeError,
863+ " as_string only supported for 1-dim char strings (format: %s, dim: %d)" ,
864+ view.format , (int )view.ndim );
865+ return nullptr ;
866+ }
867+
868+ char * buf = (char *)self->get_buf ();
869+ size_t sz = strnlen (buf, (size_t )view.shape [0 ]);
870+ return CPyCppyy_PyText_FromStringAndSize (buf, sz);
871+ }
872+
826873// ---------------------------------------------------------------------------
827874static PyMethodDef ll_methods[] = {
828875 {(char *)" reshape" , (PyCFunction)ll_reshape, METH_O,
829876 (char *)" change the shape (not layout) of the low level view" },
877+ {(char *)" as_string" , (PyCFunction)ll_as_string, METH_NOARGS,
878+ (char *)" interpret memory as a null-terminated char string and return Python str" },
830879 {(char *)" __array__" , (PyCFunction)ll_array, METH_VARARGS | METH_KEYWORDS,
831880 (char *)" return a numpy array from the low level view" },
832881 {(char *)nullptr , nullptr , 0 , nullptr }
@@ -904,6 +953,12 @@ PyTypeObject LowLevelView_Type = {
904953#if PY_VERSION_HEX >= 0x03040000
905954 , 0 // tp_finalize
906955#endif
956+ #if PY_VERSION_HEX >= 0x03080000
957+ , 0 // tp_vectorcall
958+ #endif
959+ #if PY_VERSION_HEX >= 0x030c0000
960+ , 0 // tp_watched
961+ #endif
907962};
908963
909964} // namespace CPyCppyy
@@ -923,6 +978,8 @@ template<> struct typecode_traits<unsigned char> {
923978template <> struct typecode_traits <std::byte> {
924979 static constexpr const char * format = " B" ; static constexpr const char * name = " UCharAsInt" ; };
925980#endif
981+ template <> struct typecode_traits <char *> {
982+ static constexpr const char * format = " b" ; static constexpr const char * name = " char*" ; };
926983template <> struct typecode_traits <const char *> {
927984 static constexpr const char * format = " b" ; static constexpr const char * name = " const char*" ; };
928985template <> struct typecode_traits <short > {
@@ -959,9 +1016,23 @@ template<> struct typecode_traits<std::complex<long>> {
9591016} // unnamed namespace
9601017
9611018
1019+ // ---------------------------------------------------------------------------
1020+ bool CPyCppyy::LowLevelView::resize (size_t sz)
1021+ {
1022+ Py_buffer& bi = this ->fBufInfo ;
1023+ if (bi.ndim == 1 && bi.shape ) {
1024+ bi.len = sz * bi.itemsize ;
1025+ bi.shape [0 ] = sz;
1026+ return true ;
1027+ }
1028+
1029+ return false ;
1030+ }
1031+
9621032// ---------------------------------------------------------------------------
9631033template <typename T>
964- static inline PyObject* CreateLowLevelViewT (T* address, CPyCppyy::cdims_t shape)
1034+ static inline CPyCppyy::LowLevelView* CreateLowLevelViewT (
1035+ T* address, CPyCppyy::cdims_t shape, const char * format = nullptr , const char * name = nullptr , Py_ssize_t itemsize = -1 )
9651036{
9661037 using namespace CPyCppyy ;
9671038 Py_ssize_t nx = (shape.ndim () != UNKNOWN_SIZE) ? shape[0 ] : INT_MAX/sizeof (T);
@@ -975,7 +1046,7 @@ static inline PyObject* CreateLowLevelViewT(T* address, CPyCppyy::cdims_t shape)
9751046 view.buf = address;
9761047 view.obj = nullptr ;
9771048 view.readonly = 0 ;
978- view.format = (char *)typecode_traits<T>::format;
1049+ view.format = (char *)(format ? format : typecode_traits<T>::format) ;
9791050 view.ndim = int (shape.ndim () != UNKNOWN_SIZE ? shape.ndim () : 1 );
9801051 view.shape = (Py_ssize_t*)PyMem_Malloc (view.ndim * sizeof (Py_ssize_t));
9811052 view.shape [0 ] = nx; // view.len / view.itemsize
@@ -989,11 +1060,11 @@ static inline PyObject* CreateLowLevelViewT(T* address, CPyCppyy::cdims_t shape)
9891060 if (isfix) (intptr_t &)view.internal |= CPyCppyy::LowLevelView::kIsFixed ;
9901061 }
9911062
992- llp->fElemCnv = CreateConverter (typecode_traits<T>::name);
1063+ llp->fElemCnv = CreateConverter (name ? name : typecode_traits<T>::name);
9931064 if (view.ndim == 1 ) {
9941065 // simple 1-dim array of the declared type
9951066 view.len = nx * sizeof (T);
996- view.itemsize = sizeof (T);
1067+ view.itemsize = (itemsize > 0 ? ( size_t )itemsize : sizeof (T) );
9971068 llp->fConverter = llp->fElemCnv ;
9981069 } else {
9991070 // multi-dim array; sub-views are projected by using more LLViews
@@ -1003,34 +1074,42 @@ static inline PyObject* CreateLowLevelViewT(T* address, CPyCppyy::cdims_t shape)
10031074 view.shape [idim] = shape[idim];
10041075
10051076 // peel off one dimension and create a new LLView converter
1006- std::string tname{typecode_traits<T>::name};
1007- tname.append (" * " ); // make sure to ask for another array
1077+ std::string tname{name ? name : typecode_traits<T>::name};
1078+ tname.append (" [] " ); // make sure to ask for another array
10081079 // TODO: although this will work, it means that "naive" loops are expensive
10091080 llp->fConverter = CreateConverter (tname, shape.sub ());
10101081 }
10111082
10121083 set_strides (view, sizeof (T), isfix);
10131084
1014- return (PyObject*) llp;
1085+ return llp;
10151086}
10161087
10171088// ---------------------------------------------------------------------------
10181089template <typename T>
1019- static inline PyObject* CreateLowLevelViewT (T** address, CPyCppyy::cdims_t shape)
1090+ static inline CPyCppyy::LowLevelView* CreateLowLevelViewT (
1091+ T** address, CPyCppyy::cdims_t shape, const char * format = nullptr , const char * name = nullptr )
10201092{
10211093 using namespace CPyCppyy ;
1022- LowLevelView* llp = (LowLevelView*)CreateLowLevelViewT ((T*)address, shape);
1094+ LowLevelView* llp = (LowLevelView*)CreateLowLevelViewT ((T*)address, shape, format, name );
10231095 llp->set_buf ((void **)address);
1024- return (PyObject*) llp;
1096+ return llp;
10251097}
10261098
10271099// ---------------------------------------------------------------------------
1100+ #define CPPYY_RET_W_CREATOR (type, fname ) \
1101+ PyObject* (*c)(type, cdims_t ) = &fname; \
1102+ ll->fCreator = (LowLevelView::Creator_t)c; \
1103+ return (PyObject*)ll
1104+
10281105#define CPPYY_IMPL_VIEW_CREATOR (type ) \
10291106PyObject* CPyCppyy::CreateLowLevelView (type* address, cdims_t shape) { \
1030- return CreateLowLevelViewT<type>(address, shape); \
1107+ LowLevelView* ll = CreateLowLevelViewT<type>(address, shape); \
1108+ CPPYY_RET_W_CREATOR (type*, CreateLowLevelView); \
10311109} \
10321110PyObject* CPyCppyy::CreateLowLevelView (type** address, cdims_t shape) { \
1033- return CreateLowLevelViewT<type>(address, shape); \
1111+ LowLevelView* ll = CreateLowLevelViewT<type>(address, shape); \
1112+ CPPYY_RET_W_CREATOR (type**, CreateLowLevelView); \
10341113}
10351114
10361115CPPYY_IMPL_VIEW_CREATOR (bool );
@@ -1055,10 +1134,42 @@ CPPYY_IMPL_VIEW_CREATOR(std::complex<double>);
10551134CPPYY_IMPL_VIEW_CREATOR (std::complex <int >);
10561135CPPYY_IMPL_VIEW_CREATOR (std::complex <long >);
10571136
1137+ PyObject* CPyCppyy::CreateLowLevelView (char * address, cdims_t shape) {
1138+ LowLevelView* ll = CreateLowLevelViewT<char >(address, shape);
1139+ CPPYY_RET_W_CREATOR (char *, CreateLowLevelView);
1140+ }
1141+
10581142PyObject* CPyCppyy::CreateLowLevelView (char ** address, cdims_t shape) {
1059- return CreateLowLevelViewT<char >((char *)address, shape);
1143+ LowLevelView* ll = CreateLowLevelViewT<char >(address, shape);
1144+ CPPYY_RET_W_CREATOR (char **, CreateLowLevelView);
1145+ }
1146+
1147+ PyObject* CPyCppyy::CreateLowLevelViewString (char ** address, cdims_t shape) {
1148+ LowLevelView* ll = CreateLowLevelViewT<char *>(address, shape, nullptr , nullptr , sizeof (char ));
1149+ CPPYY_RET_W_CREATOR (char **, CreateLowLevelViewString);
1150+ }
1151+
1152+ PyObject* CPyCppyy::CreateLowLevelViewString (const char ** address, cdims_t shape) {
1153+ LowLevelView* ll = CreateLowLevelViewT<const char *>(address, shape, nullptr , nullptr , sizeof (char ));
1154+ CPPYY_RET_W_CREATOR (const char **, CreateLowLevelViewString);
1155+ }
1156+
1157+ PyObject* CPyCppyy::CreateLowLevelView_i8 (int8_t * address, cdims_t shape) {
1158+ LowLevelView* ll = CreateLowLevelViewT<int8_t >(address, shape, " b" , " int8_t" );
1159+ CPPYY_RET_W_CREATOR (int8_t *, CreateLowLevelView_i8);
1160+ }
1161+
1162+ PyObject* CPyCppyy::CreateLowLevelView_i8 (int8_t ** address, cdims_t shape) {
1163+ LowLevelView* ll = CreateLowLevelViewT<int8_t >(address, shape, " b" , " int8_t" );
1164+ CPPYY_RET_W_CREATOR (int8_t **, CreateLowLevelView_i8);
1165+ }
1166+
1167+ PyObject* CPyCppyy::CreateLowLevelView_i8 (uint8_t * address, cdims_t shape) {
1168+ LowLevelView* ll = CreateLowLevelViewT<uint8_t >(address, shape, " B" , " uint8_t" );
1169+ CPPYY_RET_W_CREATOR (uint8_t *, CreateLowLevelView_i8);
10601170}
10611171
1062- PyObject* CPyCppyy::CreateLowLevelView (const char ** address, cdims_t shape) {
1063- return CreateLowLevelViewT<const char *>(address, shape);
1172+ PyObject* CPyCppyy::CreateLowLevelView_i8 (uint8_t ** address, cdims_t shape) {
1173+ LowLevelView* ll = CreateLowLevelViewT<uint8_t >(address, shape, " B" , " uint8_t" );
1174+ CPPYY_RET_W_CREATOR (uint8_t **, CreateLowLevelView_i8);
10641175}
0 commit comments