Skip to content

Commit 1c7bee7

Browse files
committed
Sync LowLevelViews
1 parent 6e38498 commit 1c7bee7

File tree

2 files changed

+144
-26
lines changed

2 files changed

+144
-26
lines changed

src/LowLevelViews.cxx

Lines changed: 133 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
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
//---------------------------------------------------------------------------
827874
static 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> {
923978
template<> 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*"; };
926983
template<> struct typecode_traits<const char*> {
927984
static constexpr const char* format = "b"; static constexpr const char* name = "const char*"; };
928985
template<> 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
//---------------------------------------------------------------------------
9631033
template<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
//---------------------------------------------------------------------------
10181089
template<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) \
10291106
PyObject* 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
} \
10321110
PyObject* 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

10361115
CPPYY_IMPL_VIEW_CREATOR(bool);
@@ -1055,10 +1134,42 @@ CPPYY_IMPL_VIEW_CREATOR(std::complex<double>);
10551134
CPPYY_IMPL_VIEW_CREATOR(std::complex<int>);
10561135
CPPYY_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+
10581142
PyObject* 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
}

src/LowLevelViews.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,14 @@ class LowLevelView {
3131
Converter* fConverter;
3232
Converter* fElemCnv;
3333

34+
typedef LowLevelView* (*Creator_t)(void*, cdims_t);
35+
Creator_t fCreator; // for slicing, which requires copying
36+
3437
public:
3538
void* get_buf() { return fBuf ? *fBuf : fBufInfo.buf; }
3639
void set_buf(void** buf) { fBuf = buf; fBufInfo.buf = get_buf(); }
40+
41+
bool resize(size_t sz);
3742
};
3843

3944
#define CPPYY_DECL_VIEW_CREATOR(type) \
@@ -47,8 +52,10 @@ CPPYY_DECL_VIEW_CREATOR(unsigned char);
4752
#if __cplusplus > 201402L
4853
CPPYY_DECL_VIEW_CREATOR(std::byte);
4954
#endif
50-
CPPYY_DECL_VIEW_CREATOR(int8_t);
51-
CPPYY_DECL_VIEW_CREATOR(uint8_t);
55+
PyObject* CreateLowLevelView_i8(int8_t*, cdims_t shape);
56+
PyObject* CreateLowLevelView_i8(int8_t**, cdims_t shape);
57+
PyObject* CreateLowLevelView_i8(uint8_t*, cdims_t shape);
58+
PyObject* CreateLowLevelView_i8(uint8_t**, cdims_t shape);
5259
CPPYY_DECL_VIEW_CREATOR(short);
5360
CPPYY_DECL_VIEW_CREATOR(unsigned short);
5461
CPPYY_DECL_VIEW_CREATOR(int);
@@ -65,8 +72,8 @@ CPPYY_DECL_VIEW_CREATOR(std::complex<double>);
6572
CPPYY_DECL_VIEW_CREATOR(std::complex<int>);
6673
CPPYY_DECL_VIEW_CREATOR(std::complex<long>);
6774

68-
PyObject* CreateLowLevelView(char**, cdims_t shape = 0);
69-
PyObject* CreateLowLevelView(const char**, cdims_t shape = 0);
75+
PyObject* CreateLowLevelViewString(char**, cdims_t shape);
76+
PyObject* CreateLowLevelViewString(const char**, cdims_t shape);
7077

7178
inline PyObject* CreatePointerView(void* ptr, cdims_t shape = 0) {
7279
return CreateLowLevelView((uintptr_t*)ptr, shape);

0 commit comments

Comments
 (0)