// 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 #include "voidptr.h" #include "pep384ext.h" #include "sbkconverter.h" #include "basewrapper.h" #include "basewrapper_p.h" extern "C" { // Void pointer object definition. struct SbkVoidPtrObject { PyObject_HEAD void *cptr; Py_ssize_t size; bool isWritable; }; PyObject *SbkVoidPtrObject_new(PyTypeObject *type, PyObject * /* args */, PyObject * /* kwds */) { // PYSIDE-560: It is much safer to first call a function and then do a // type cast than to do everything in one line. The bad construct looked // like this, actual call forgotten: // SbkVoidPtrObject *self = // reinterpret_cast(type->tp_alloc); auto *self = PepExt_TypeCallAlloc(type, 0); if (self != nullptr) { self->cptr = nullptr; self->size = -1; self->isWritable = false; } return reinterpret_cast(self); } #define SbkVoidPtr_Check(op) (Py_TYPE(op) == SbkVoidPtr_TypeF()) int SbkVoidPtrObject_init(PyObject *self, PyObject *args, PyObject *kwds) { PyObject *addressObject; Py_ssize_t size = -1; int isWritable = 0; auto *sbkSelf = reinterpret_cast(self); static const char *kwlist[] = {"address", "size", "writeable", nullptr}; if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|ni", const_cast(kwlist), &addressObject, &size, &isWritable)) return -1; // Void pointer. if (SbkVoidPtr_Check(addressObject)) { auto *sbkOther = reinterpret_cast(addressObject); sbkSelf->cptr = sbkOther->cptr; sbkSelf->size = sbkOther->size; sbkSelf->isWritable = sbkOther->isWritable; } // Python buffer interface. else if (PyObject_CheckBuffer(addressObject)) { Py_buffer bufferView; // Bail out if the object can't provide a simple contiguous buffer. if (PyObject_GetBuffer(addressObject, &bufferView, PyBUF_SIMPLE) < 0) return 0; sbkSelf->cptr = bufferView.buf; sbkSelf->size = bufferView.len > 0 ? bufferView.len : size; sbkSelf->isWritable = bufferView.readonly <= 0; // Release the buffer. PyBuffer_Release(&bufferView); } // Shiboken::Object wrapper. else if (Shiboken::Object::checkType(addressObject)) { auto *sbkOther = reinterpret_cast(addressObject); sbkSelf->cptr = sbkOther->d->cptr[0]; sbkSelf->size = size; sbkSelf->isWritable = isWritable > 0; } // An integer representing an address. else { if (addressObject == Py_None) { sbkSelf->cptr = nullptr; sbkSelf->size = 0; sbkSelf->isWritable = false; } else { void *cptr = PyLong_AsVoidPtr(addressObject); if (PyErr_Occurred()) { PyErr_SetString(PyExc_TypeError, "Creating a VoidPtr object requires an address of a C++ object, " "a wrapped Shiboken Object type, " "an object implementing the Python Buffer interface, " "or another VoidPtr object."); return -1; } sbkSelf->cptr = cptr; sbkSelf->size = size; sbkSelf->isWritable = isWritable > 0; } } return 0; } PyObject *SbkVoidPtrObject_richcmp(PyObject *obj1, PyObject *obj2, int op) { PyObject *result = Py_False; void *cptr1 = nullptr; void *cptr2 = nullptr; bool validObjects = true; if (SbkVoidPtr_Check(obj1)) cptr1 = reinterpret_cast(obj1)->cptr; else validObjects = false; if (SbkVoidPtr_Check(obj2)) cptr2 = reinterpret_cast(obj2)->cptr; else validObjects = false; if (validObjects) { switch (op) { case Py_EQ: if (cptr1 == cptr2) result = Py_True; break; case Py_NE: if (cptr1 != cptr2) result = Py_True; break; case Py_LT: case Py_LE: case Py_GT: case Py_GE: break; } } Py_INCREF(result); return result; } PyObject *SbkVoidPtrObject_int(PyObject *v) { auto *sbkObject = reinterpret_cast(v); return PyLong_FromVoidPtr(sbkObject->cptr); } PyObject *toBytes(PyObject *self, PyObject * /* args */) { auto *sbkObject = reinterpret_cast(self); if (sbkObject->size < 0) { PyErr_SetString(PyExc_IndexError, "VoidPtr does not have a size set."); return nullptr; } PyObject *bytes = PyBytes_FromStringAndSize(reinterpret_cast(sbkObject->cptr), sbkObject->size); Py_XINCREF(bytes); return bytes; } static struct PyMethodDef SbkVoidPtrObject_methods[] = { {"toBytes", toBytes, METH_NOARGS, nullptr}, {nullptr, nullptr, 0, nullptr} }; static Py_ssize_t SbkVoidPtrObject_length(PyObject *v) { auto *sbkObject = reinterpret_cast(v); if (sbkObject->size < 0) { PyErr_SetString(PyExc_IndexError, "VoidPtr does not have a size set."); return -1; } return sbkObject->size; } static const char trueString[] = "True" ; static const char falseString[] = "False" ; PyObject *SbkVoidPtrObject_repr(PyObject *v) { auto *sbkObject = reinterpret_cast(v); PyObject *s = PyUnicode_FromFormat("%s(%p, %zd, %s)", Py_TYPE(sbkObject)->tp_name, sbkObject->cptr, sbkObject->size, sbkObject->isWritable ? trueString : falseString); Py_XINCREF(s); return s; } PyObject *SbkVoidPtrObject_str(PyObject *v) { auto *sbkObject = reinterpret_cast(v); PyObject *s = PyUnicode_FromFormat("%s(Address %p, Size %zd, isWritable %s)", Py_TYPE(sbkObject)->tp_name, sbkObject->cptr, sbkObject->size, sbkObject->isWritable ? trueString : falseString); Py_XINCREF(s); return s; } static int SbkVoidPtrObject_getbuffer(PyObject *obj, Py_buffer *view, int flags) { if (view == nullptr) return -1; auto *sbkObject = reinterpret_cast(obj); if (sbkObject->size < 0) return -1; int readonly = sbkObject->isWritable ? 0 : 1; if (((flags & PyBUF_WRITABLE) == PyBUF_WRITABLE) && (readonly == 1)) { PyErr_SetString(PyExc_BufferError, "Object is not writable."); return -1; } view->obj = obj; if (obj) Py_XINCREF(obj); view->buf = sbkObject->cptr; view->len = sbkObject->size; view->readonly = readonly; view->itemsize = 1; view->format = nullptr; if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT) view->format = const_cast("B"); view->ndim = 1; view->shape = nullptr; if ((flags & PyBUF_ND) == PyBUF_ND) view->shape = &(view->len); view->strides = nullptr; if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES) view->strides = &(view->itemsize); view->suboffsets = nullptr; view->internal = nullptr; return 0; } static PyBufferProcs SbkVoidPtrObjectBufferProc = { (getbufferproc)SbkVoidPtrObject_getbuffer, // bf_getbuffer (releasebufferproc)nullptr // bf_releasebuffer }; static PyTypeObject *createVoidPtrType() { PyType_Slot SbkVoidPtrType_slots[] = { {Py_tp_repr, reinterpret_cast(SbkVoidPtrObject_repr)}, {Py_nb_int, reinterpret_cast(SbkVoidPtrObject_int)}, {Py_sq_length, reinterpret_cast(SbkVoidPtrObject_length)}, {Py_tp_str, reinterpret_cast(SbkVoidPtrObject_str)}, {Py_tp_richcompare, reinterpret_cast(SbkVoidPtrObject_richcmp)}, {Py_tp_init, reinterpret_cast(SbkVoidPtrObject_init)}, {Py_tp_new, reinterpret_cast(SbkVoidPtrObject_new)}, {Py_tp_dealloc, reinterpret_cast(Sbk_object_dealloc)}, {Py_tp_methods, reinterpret_cast(SbkVoidPtrObject_methods)}, {0, nullptr} }; PyType_Spec SbkVoidPtrType_spec = { "2:shiboken6.Shiboken.VoidPtr", sizeof(SbkVoidPtrObject), 0, Py_TPFLAGS_DEFAULT, SbkVoidPtrType_slots, }; return SbkType_FromSpec_BMDWB(&SbkVoidPtrType_spec, nullptr, nullptr, 0, 0, &SbkVoidPtrObjectBufferProc); } PyTypeObject *SbkVoidPtr_TypeF(void) { static auto *type = createVoidPtrType(); return type; } } // extern "C" namespace VoidPtr { static int voidPointerInitialized = false; void init() { if (PyType_Ready(SbkVoidPtr_TypeF()) < 0) Py_FatalError("[libshiboken] Failed to initialize Shiboken.VoidPtr type."); else voidPointerInitialized = true; } void addVoidPtrToModule(PyObject *module) { if (voidPointerInitialized) { Py_INCREF(SbkVoidPtr_TypeF()); PyModule_AddObject(module, PepType_GetNameStr(SbkVoidPtr_TypeF()), reinterpret_cast(SbkVoidPtr_TypeF())); } } static PyObject *createVoidPtr(void *cppIn, Py_ssize_t size = 0, bool isWritable = false) { if (!cppIn) Py_RETURN_NONE; SbkVoidPtrObject *result = PyObject_New(SbkVoidPtrObject, SbkVoidPtr_TypeF()); if (!result) Py_RETURN_NONE; result->cptr = cppIn; result->size = size; result->isWritable = isWritable; return reinterpret_cast(result); } static PyObject *toPython(const void *cppIn) { return createVoidPtr(const_cast(cppIn)); } static void VoidPtrToCpp(PyObject *pyIn, void *cppOut) { auto *sbkIn = reinterpret_cast(pyIn); *reinterpret_cast(cppOut) = sbkIn->cptr; } static PythonToCppFunc VoidPtrToCppIsConvertible(PyObject *pyIn) { return SbkVoidPtr_Check(pyIn) ? VoidPtrToCpp : nullptr; } static void SbkObjectToCpp(PyObject *pyIn, void *cppOut) { auto *sbkIn = reinterpret_cast(pyIn); *reinterpret_cast(cppOut) = sbkIn->d->cptr[0]; } static PythonToCppFunc SbkObjectToCppIsConvertible(PyObject *pyIn) { return Shiboken::Object::checkType(pyIn) ? SbkObjectToCpp : nullptr; } static void PythonBufferToCpp(PyObject *pyIn, void *cppOut) { if (PyObject_CheckBuffer(pyIn)) { Py_buffer bufferView; // Bail out if the object can't provide a simple contiguous buffer. if (PyObject_GetBuffer(pyIn, &bufferView, PyBUF_SIMPLE) < 0) return; *reinterpret_cast(cppOut) = bufferView.buf; // Release the buffer. PyBuffer_Release(&bufferView); } } static PythonToCppFunc PythonBufferToCppIsConvertible(PyObject *pyIn) { if (PyObject_CheckBuffer(pyIn)) { Py_buffer bufferView; // Bail out if the object can't provide a simple contiguous buffer. if (PyObject_GetBuffer(pyIn, &bufferView, PyBUF_SIMPLE) < 0) return nullptr; // Release the buffer. PyBuffer_Release(&bufferView); return PythonBufferToCpp; } return nullptr; } SbkConverter *createConverter() { SbkConverter *converter = Shiboken::Conversions::createConverter(SbkVoidPtr_TypeF(), toPython); Shiboken::Conversions::addPythonToCppValueConversion(converter, VoidPtrToCpp, VoidPtrToCppIsConvertible); Shiboken::Conversions::addPythonToCppValueConversion(converter, SbkObjectToCpp, SbkObjectToCppIsConvertible); Shiboken::Conversions::addPythonToCppValueConversion(converter, PythonBufferToCpp, PythonBufferToCppIsConvertible); return converter; } void setSize(PyObject *voidPtr, Py_ssize_t size) { assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast(voidPtr); voidPtrObj->size = size; } Py_ssize_t getSize(PyObject *voidPtr) { assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast(voidPtr); return voidPtrObj->size; } bool isWritable(PyObject *voidPtr) { assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast(voidPtr); return voidPtrObj->isWritable; } void setWritable(PyObject *voidPtr, bool isWritable) { assert(voidPtr->ob_type == SbkVoidPtr_TypeF()); auto *voidPtrObj = reinterpret_cast(voidPtr); voidPtrObj->isWritable = isWritable; } } // namespace VoidPtr