diff options
Diffstat (limited to 'sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp')
-rw-r--r-- | sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp b/sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp new file mode 100644 index 000000000..835a97524 --- /dev/null +++ b/sources/shiboken6/libshiboken/sbknumpyarrayconverter.cpp @@ -0,0 +1,270 @@ +// Copyright (C) 2017 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// included by sbknumpy.cpp + +#include "sbkarrayconverter.h" +#include "helper.h" +#include "sbkconverter.h" +#include "sbkconverter_p.h" +#include "sbkarrayconverter_p.h" + +#include <algorithm> +#include <iostream> +#include <cstdint> + +enum { debugNumPy = 0 }; + +struct TypeCharMapping +{ + NPY_TYPES type; + const char *name; +}; + +static const TypeCharMapping typeCharMappings[] = { +{NPY_BYTE, "NPY_BYTE"}, +{NPY_UBYTE, "NPY_UBYTE"}, +{NPY_SHORT, "NPY_SHORT"}, +{NPY_USHORT, "NPY_USHORT"}, +{NPY_INT, "NPY_INT"}, +{NPY_UINT, "NPY_UINT"}, +{NPY_LONG, "NPY_LONG"}, +{NPY_ULONG, "NPY_ULONG"}, +{NPY_LONGLONG, "NPY_LONGLONG"}, +{NPY_ULONGLONG, "NPY_ULONGLONG"}, +{NPY_FLOAT, "NPY_FLOAT"}, +{NPY_DOUBLE, "NPY_DOUBLE"} +}; + +const char *npTypeName(npy_intp t) +{ + const TypeCharMapping *end = typeCharMappings + sizeof(typeCharMappings) / sizeof(typeCharMappings[0]); + const TypeCharMapping *result = + std::find_if(typeCharMappings, end, + [t] (const TypeCharMapping &m) { return m.type == t; }); + return result != end ? result->name : nullptr; +} + +std::ostream &operator<<(std::ostream &str, PyArrayObject *o) +{ + str << "PyArrayObject("; + if (o) { + const npy_intp npType = PyArray_TYPE(o); + if (const char *name = npTypeName(npType)) + str << name; + else + str << "type=" << npType; + const int nDim = PyArray_NDIM(o); + const npy_intp *dims = PyArray_DIMS(o); + for (int d = 0; d < nDim; ++d) + str << '[' << dims[d] << ']'; + str << ", "; + const int flags = PyArray_FLAGS(o); + if ((flags & NPY_ARRAY_C_CONTIGUOUS) != 0) + str << " NPY_ARRAY_C_CONTIGUOUS"; + if ((flags & NPY_ARRAY_F_CONTIGUOUS) != 0) + str << " NPY_ARRAY_F_CONTIGUOUS"; + if ((flags & NPY_ARRAY_OWNDATA) != 0) + str << " NPY_ARRAY_OWNDATA"; + if ((flags & NPY_ARRAY_FORCECAST) != 0) + str << " NPY_ARRAY_FORCECAST"; + if ((flags & NPY_ARRAY_ENSURECOPY) != 0) + str << " NPY_ARRAY_ENSURECOPY"; + if ((flags & NPY_ARRAY_ENSUREARRAY) != 0) + str << " NPY_ARRAY_ENSUREARRAY"; + if ((flags & NPY_ARRAY_ELEMENTSTRIDES) != 0) + str << " NPY_ARRAY_ELEMENTSTRIDES"; + if ((flags & NPY_ARRAY_ALIGNED) != 0) + str << " NPY_ARRAY_ALIGNED"; + if ((flags & NPY_ARRAY_NOTSWAPPED) != 0) + str << " NPY_ARRAY_NOTSWAPPED"; + if ((flags & NPY_ARRAY_WRITEABLE) != 0) + str << " NPY_ARRAY_WRITEABLE"; +#if NPY_VERSION >= 0x00000010 // NPY_1_23_API_VERSION + if ((flags & NPY_ARRAY_WRITEBACKIFCOPY) != 0) + str << " NPY_ARRAY_WRITEBACKIFCOPY"; +#else + if ((flags & NPY_ARRAY_UPDATEIFCOPY) != 0) + str << " NPY_ARRAY_UPDATEIFCOPY"; +#endif + } else { + str << '0'; + } + str << ')'; + return str; +} + +namespace Shiboken::Conversions { + +// Internals from sbkarrayconverter.cpp +SbkArrayConverter *createArrayConverter(IsArrayConvertibleToCppFunc toCppCheckFunc); +void setArrayTypeConverter(int index, int dimension, SbkArrayConverter *c); +SbkArrayConverter *unimplementedArrayConverter(); + +template <int dimension> +static bool isPrimitiveArray(PyObject *pyIn, int expectedNpType) +{ + Shiboken::Numpy::initNumPy(); + if (!PyArray_Check(pyIn)) + return false; + auto *pya = reinterpret_cast<PyArrayObject *>(pyIn); + if (debugNumPy) { + std::cerr << __FUNCTION__ << "(expectedNpType=" << expectedNpType; + if (const char *name = npTypeName(expectedNpType)) + std::cerr << " (" << name << ')'; + std::cerr << ' ' << pya << '\n'; + } + + const int dim = PyArray_NDIM(pya); + if (dim != dimension) { + warning(PyExc_RuntimeWarning, 0, + "%d dimensional numpy array passed to a function expecting a %d dimensional array.", + dim, dimension); + return false; + } + if ((PyArray_FLAGS(pya) & NPY_ARRAY_C_CONTIGUOUS) == 0) { + warning(PyExc_RuntimeWarning, 0, + "Cannot handle numpy arrays that do not have NPY_ARRAY_C_CONTIGUOUS set."); + return false; + } + const int actualNpType = PyArray_TYPE(pya); + if (actualNpType != expectedNpType) { + const char *actualName = npTypeName(actualNpType); + const char *expectedName = npTypeName(expectedNpType); + warning(PyExc_RuntimeWarning, 0, + "A numpy array of type %d (%s) was passed to a function expecting type %d (%s).", + actualNpType, actualName ? actualName : "", + expectedNpType, expectedName ? expectedName : ""); + return false; + } + return true; +} + +static inline bool primitiveArrayCheck1(PyObject *pyIn, int expectedNpType, int expectedSize) +{ + if (!isPrimitiveArray<1>(pyIn, expectedNpType)) + return false; + if (expectedSize >= 0) { + auto *pya = reinterpret_cast<PyArrayObject *>(pyIn); + const int size = int(PyArray_DIMS(pya)[0]); + if (size < expectedSize) { + warning(PyExc_RuntimeWarning, 0, "A numpy array of size %d was passed to a function expects %d.", + size, expectedSize); + return false; + } + } + return true; +} + +// Convert one-dimensional array +template <class T> +static void convertArray1(PyObject *pyIn, void *cppOut) +{ + auto *handle = reinterpret_cast<ArrayHandle<T> *>(cppOut); + auto *pya = reinterpret_cast<PyArrayObject *>(pyIn); + const npy_intp size = PyArray_DIMS(pya)[0]; + if (debugNumPy) + std::cerr << __FUNCTION__ << ' ' << size << '\n'; + handle->setData(reinterpret_cast<T *>(PyArray_DATA(pya)), size_t(size)); +} + +// Convert 2 dimensional array +template <class T> +static void convertArray2(PyObject *pyIn, void *cppOut) +{ + typedef typename Array2Handle<T, 1>::RowType RowType; + auto *handle = reinterpret_cast<Array2Handle<T, 1> *>(cppOut); + auto *pya = reinterpret_cast<PyArrayObject *>(pyIn); + handle->setData(reinterpret_cast<RowType *>(PyArray_DATA(pya))); +} + +template <class T, int NumPyType> +static PythonToCppFunc checkArray1(PyObject *pyIn, int dim1, int /* dim2 */) +{ + return primitiveArrayCheck1(pyIn, NumPyType, dim1) ? convertArray1<T> : nullptr; +} + +static inline bool primitiveArrayCheck2(PyObject *pyIn, int expectedNpType, int expectedDim1, int expectedDim2) +{ + if (!isPrimitiveArray<2>(pyIn, expectedNpType)) + return false; + if (expectedDim2 >= 0) { + auto *pya = reinterpret_cast<PyArrayObject *>(pyIn); + const int dim1 = int(PyArray_DIMS(pya)[0]); + const int dim2 = int(PyArray_DIMS(pya)[1]); + if (dim1 != expectedDim1 || dim2 != expectedDim2) { + warning(PyExc_RuntimeWarning, 0, "A numpy array of size %dx%d was passed to a function that expects %dx%d.", + dim1, dim2, expectedDim1, expectedDim2); + return false; + } + } + return true; +} + +template <class T, int NumPyType> +static PythonToCppFunc checkArray2(PyObject *pyIn, int dim1, int dim2) +{ + return primitiveArrayCheck2(pyIn, NumPyType, dim1, dim2) ? convertArray2<T> : nullptr; +} + +template <class T> +static void setOrExtendArrayConverter(int dimension, IsArrayConvertibleToCppFunc toCppCheckFunc) +{ + // PYSIDE-2404/FIXME: When adding a C++ -> Python conversion, be sure + // to delay-initialize numpy in the converter (similar to the + // initialization in check() for the Python -> C++ conversion). + SbkArrayConverter *arrayConverter = ArrayTypeConverter<T>(dimension); + if (arrayConverter == unimplementedArrayConverter()) { + arrayConverter = createArrayConverter(toCppCheckFunc); + setArrayTypeConverter(ArrayTypeIndex<T>::index, dimension, arrayConverter); + } else { + arrayConverter->toCppConversions.push_back(toCppCheckFunc); + } +} + +// Extend the converters for primitive type one-dimensional arrays by NumPy ones. +template <class T, int NumPyType> +static inline void extendArrayConverter1() +{ + setOrExtendArrayConverter<T>(1, checkArray1<T, NumPyType>); +} + +// Extend the converters for primitive type one-dimensional arrays by NumPy ones. +template <class T, int NumPyType> +static inline void extendArrayConverter2() +{ + setOrExtendArrayConverter<T>(2, checkArray2<T, NumPyType>); +} + +void initNumPyArrayConverters() +{ + // Extend the converters for primitive types by NumPy ones. + extendArrayConverter1<short, NPY_SHORT>(); + extendArrayConverter2<short, NPY_SHORT>(); + extendArrayConverter1<unsigned short, NPY_SHORT>(); + extendArrayConverter2<unsigned short, NPY_SHORT>(); + extendArrayConverter1<int, NPY_INT>(); + extendArrayConverter2<int, NPY_INT>(); + extendArrayConverter1<unsigned int, NPY_UINT>(); + extendArrayConverter2<unsigned int, NPY_UINT>(); + extendArrayConverter1<long long, NPY_LONGLONG>(); + extendArrayConverter2<long long, NPY_LONGLONG>(); + extendArrayConverter1<unsigned long long, NPY_ULONGLONG>(); + if (sizeof(long) == 8) { // UNIX/LP64: ints typically come as long + extendArrayConverter1<long long, NPY_LONG>(); + extendArrayConverter2<long long, NPY_LONG>(); + extendArrayConverter1<unsigned long long, NPY_ULONG>(); + extendArrayConverter2<unsigned long long, NPY_ULONG>(); + } else if (sizeof(long) == sizeof(int)) { + extendArrayConverter1<int, NPY_LONG>(); + extendArrayConverter1<unsigned, NPY_ULONG>(); + extendArrayConverter2<int, NPY_LONG>(); + extendArrayConverter2<unsigned, NPY_ULONG>(); + } + extendArrayConverter1<float, NPY_FLOAT>(); + extendArrayConverter2<float, NPY_FLOAT>(); + extendArrayConverter1<double, NPY_DOUBLE>(); + extendArrayConverter2<double, NPY_DOUBLE>(); +} + +} // namespace Shiboken::Conversions |