/****************************************************************************** * The MIT License (MIT) * * Copyright (c) 2019-2020 Baldur Karlsson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. ******************************************************************************/ #pragma once template inline std::map &obj2py(); template <> inline std::map &obj2py() { static std::map mapping; return mapping; } template <> inline std::map &obj2py() { static std::map mapping; return mapping; } // specialisations for structured data template struct ActiveRefcounter { static PyObject *GetPyObject(const refcountedType *c) { auto it = obj2py().find(c); if(it == obj2py().end()) { // not recognised - must be C++ side owned. Construct a non-owning PyObject and DON'T insert // it into the map. The map is only for objects python owns swig_type_info *type_info = TypeConversion::GetTypeInfo(); if(type_info == NULL) return NULL; return SWIG_InternalNewPointerObj((void *)c, type_info, 0); } // recognised - inc refcount on existing object and return Py_IncRef(it->second); return it->second; } static bool HasPyObject(const refcountedType *c) { return obj2py().find(c) != obj2py().end(); } static void NewPyObject(PyObject *py, const refcountedType *c) { obj2py()[c] = py; } static void DelPyObject(PyObject *py, refcountedType *c) { obj2py().erase(c); } static void Dec(const refcountedType *c) { auto it = obj2py().find(c); if(it != obj2py().end()) Py_DecRef(it->second); } static void Inc(const refcountedType *c) { auto it = obj2py().find(c); if(it != obj2py().end()) Py_IncRef(it->second); } }; template struct ExtRefcount { static void Dec(const T &t) {} static void Inc(const T &t) {} }; template struct RefcountConverter { static int ConvertFromPy(PyObject *in, refcountedType *&out) { // We just unbox the PyObject void *ptr = NULL; int res = 0; swig_type_info *typeInfo = TypeConversion::GetTypeInfo(); if(!typeInfo) return SWIG_RuntimeError; res = SWIG_ConvertPtr(in, &ptr, typeInfo, 0); if(SWIG_IsOK(res)) out = (refcountedType *)ptr; // increment the refcount to indicate that there's an externally stored reference. Py_IncRef(in); return res; } static PyObject *ConvertToPy(refcountedType *const &in) { return ExtRefcount::GetPyObject(in); } }; template <> struct ExtRefcount : public ActiveRefcounter { static void DelPyObject(PyObject *py, SDChunk *c) { // dec ref any python-owned objects in the children array, so the default destructor doesn't // just delete them. for(size_t i = 0; i < c->NumChildren(); i++) if(ActiveRefcounter::HasPyObject(c->GetChild(i))) ActiveRefcounter::Dec(c->GetChild(i)); // we clear the array, because anything still left is C++ owned. We're just borrowing a // reference to it, so C++ will control the lifetime. StructuredObjectList discard; c->TakeAllChildren(discard); ActiveRefcounter::DelPyObject(py, c); } }; template <> struct TypeConversion : public RefcountConverter { }; template <> struct ExtRefcount : public ActiveRefcounter { static void DelPyObject(PyObject *py, SDObject *o) { // dec ref any python-owned objects in the children array, so the default destructor doesn't // just delete them. for(size_t i = 0; i < o->NumChildren(); i++) if(ActiveRefcounter::HasPyObject(o->GetChild(i))) ActiveRefcounter::Dec(o->GetChild(i)); // we clear the array, because anything still left is C++ owned. We're just borrowing a // reference to it, so C++ will control the lifetime. StructuredObjectList discard; o->TakeAllChildren(discard); ActiveRefcounter::DelPyObject(py, o); } }; // mostly the same as the plain bytebuf conversion, but when converting from py we need to // allocate. This will only be used when assigning a buffer in an SDFile's StructuredBufferList, // which then takes ownership of the allocated object so it doesn't leak. template <> struct TypeConversion { static int ConvertFromPy(PyObject *in, bytebuf *&out, int *failIdx) { out = new bytebuf; return TypeConversion::ConvertFromPy(in, *out, failIdx); } static int ConvertFromPy(PyObject *in, bytebuf *&out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *list, const bytebuf *in, int *failIdx) { // can't modify bytes objects return SWIG_Py_Void(); } static PyObject *ConvertToPy(const bytebuf *in, int *failIdx) { return TypeConversion::ConvertToPy(*in, failIdx); } static PyObject *ConvertToPy(const bytebuf *in) { return ConvertToPy(in, NULL); } }; template <> struct ExtRefcount { static void Dec(const SDFile *t) {} static void Inc(const SDFile *t) {} static void NewPyObject(PyObject *py, const SDFile *f) {} static void DelPyObject(PyObject *py, SDFile *f) { // dec ref any python-owned objects in the children array, so the default destructor doesn't // just delete them. for(size_t i = 0; i < f->chunks.size(); i++) if(ActiveRefcounter::HasPyObject(f->chunks[i])) ActiveRefcounter::Dec(f->chunks[i]); // we clear the array, because anything still left is C++ owned. We're just borrowing a // reference to it, so C++ will control the lifetime. f->chunks.clear(); } }; template <> struct TypeConversion : public RefcountConverter { }; template <> struct TypeConversion { static swig_type_info *GetTypeInfo() { static swig_type_info *cached_type_info = NULL; if(cached_type_info) return cached_type_info; cached_type_info = SWIG_TypeQuery("StructuredBufferList *"); return cached_type_info; } // we add some extra parameters so the typemaps for array can use these to get // nicer failure error messages out with the index that failed static int ConvertFromPy(PyObject *in, StructuredBufferList &out, int *failIdx) { swig_type_info *own_type = GetTypeInfo(); if(own_type) { StructuredBufferList *ptr = NULL; int ret = SWIG_ConvertPtr(in, (void **)&ptr, own_type, 0); if(SWIG_IsOK(ret)) { // we need to duplicate the objects here, otherwise the owner of both lists will try and // delete the same things when they destruct. Avoiding copies must be done another way out.resize(ptr->size()); for(size_t i = 0; i < ptr->size(); i++) out[i] = new bytebuf(*ptr->at(i)); return SWIG_OK; } } if(!PyList_Check(in)) return SWIG_TypeError; out.resize((size_t)PyList_Size(in)); for(int i = 0; i < out.count(); i++) { PyObject *elem = PyList_GetItem(in, i); if(elem == Py_None) { out[i] = NULL; } else { out[i] = new bytebuf; int ret = TypeConversion::ConvertFromPy(elem, *out[i]); if(!SWIG_IsOK(ret)) { if(failIdx) *failIdx = i; return ret; } } } return SWIG_OK; } static int ConvertFromPy(PyObject *in, StructuredBufferList &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *list, const StructuredBufferList &in, int *failIdx) { for(int i = 0; i < in.count(); i++) { PyObject *elem = SWIG_Py_Void(); if(in[i]) elem = TypeConversion::ConvertToPy(*in[i]); if(elem) { PyList_Append(list, elem); // release our reference Py_DecRef(elem); } else { if(failIdx) *failIdx = i; return NULL; } } return list; } static PyObject *ConvertToPy(const StructuredBufferList &in, int *failIdx) { PyObject *list = PyList_New(0); if(!list) return NULL; PyObject *ret = ConvertToPyInPlace(list, in, failIdx); // if a failure happened, don't leak the list we created if(!ret) Py_XDECREF(list); return ret; } static PyObject *ConvertToPy(const StructuredBufferList &in) { return ConvertToPy(in, NULL); } }; template <> struct TypeConversion { static swig_type_info *GetTypeInfo() { static swig_type_info *cached_type_info = NULL; if(cached_type_info) return cached_type_info; cached_type_info = SWIG_TypeQuery("StructuredObjectList *"); return cached_type_info; } // we add some extra parameters so the typemaps for array can use these to get // nicer failure error messages out with the index that failed static int ConvertFromPy(PyObject *in, StructuredObjectList &out, int *failIdx) { swig_type_info *own_type = GetTypeInfo(); if(own_type) { StructuredObjectList *ptr = NULL; int ret = SWIG_ConvertPtr(in, (void **)&ptr, own_type, 0); if(SWIG_IsOK(ret)) { // we need to duplicate the objects here, otherwise the owner of both lists will try and // delete the same things when they destruct. Avoiding copies must be done another way out.resize(ptr->size()); for(size_t i = 0; i < ptr->size(); i++) { SDObject *obj = ptr->at(i); if(ActiveRefcounter::HasPyObject(obj)) { out[i] = obj; ActiveRefcounter::Inc(obj); } else { out[i] = obj->Duplicate(); } } return SWIG_OK; } } swig_type_info *type_info = TypeConversion::GetTypeInfo(); if(type_info == NULL) return SWIG_RuntimeError; if(!PyList_Check(in)) return SWIG_TypeError; out.resize((size_t)PyList_Size(in)); for(int i = 0; i < out.count(); i++) { PyObject *elem = PyList_GetItem(in, i); if(elem == Py_None) { out[i] = NULL; } else { SDObject *ptr = NULL; int ret = SWIG_ConvertPtr(elem, (void **)&ptr, type_info, 0); if(SWIG_IsOK(ret)) { if(ActiveRefcounter::HasPyObject(ptr)) { out[i] = ptr; Py_IncRef(elem); } else { out[i] = ptr->Duplicate(); } } else { if(failIdx) *failIdx = i; return ret; } } } return SWIG_OK; } static int ConvertFromPy(PyObject *in, StructuredObjectList &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *list, const StructuredObjectList &in, int *failIdx) { swig_type_info *type_info = TypeConversion::GetTypeInfo(); if(type_info == NULL) return NULL; for(int i = 0; i < in.count(); i++) { PyObject *elem = SWIG_Py_Void(); if(in[i]) { SDObject *pyCopy = in[i]->Duplicate(); elem = SWIG_InternalNewPointerObj((void *)pyCopy, type_info, SWIG_POINTER_OWN); } if(elem) { PyList_Append(list, elem); // release our reference Py_DecRef(elem); } else { if(failIdx) *failIdx = i; return NULL; } } return list; } static PyObject *ConvertToPy(const StructuredObjectList &in, int *failIdx) { PyObject *list = PyList_New(0); if(!list) return NULL; PyObject *ret = ConvertToPyInPlace(list, in, failIdx); // if a failure happened, don't leak the list we created if(!ret) Py_XDECREF(list); return ret; } static PyObject *ConvertToPy(const StructuredObjectList &in) { return ConvertToPy(in, NULL); } }; template <> struct TypeConversion { static swig_type_info *GetTypeInfo() { static swig_type_info *cached_type_info = NULL; if(cached_type_info) return cached_type_info; cached_type_info = SWIG_TypeQuery("StructuredChunkList *"); return cached_type_info; } // we add some extra parameters so the typemaps for array can use these to get // nicer failure error messages out with the index that failed static int ConvertFromPy(PyObject *in, StructuredChunkList &out, int *failIdx) { swig_type_info *own_type = GetTypeInfo(); if(own_type) { StructuredChunkList *ptr = NULL; int ret = SWIG_ConvertPtr(in, (void **)&ptr, own_type, 0); if(SWIG_IsOK(ret)) { // we need to duplicate the objects here, otherwise the owner of both lists will try and // delete the same things when they destruct. Avoiding copies must be done another way out.resize(ptr->size()); for(size_t i = 0; i < ptr->size(); i++) { SDChunk *obj = ptr->at(i); if(ActiveRefcounter::HasPyObject(obj)) { out[i] = obj; ActiveRefcounter::Inc(obj); } else { out[i] = obj->Duplicate(); } } return SWIG_OK; } } swig_type_info *type_info = TypeConversion::GetTypeInfo(); if(type_info == NULL) return SWIG_RuntimeError; if(!PyList_Check(in)) return SWIG_TypeError; out.resize((size_t)PyList_Size(in)); for(int i = 0; i < out.count(); i++) { PyObject *elem = PyList_GetItem(in, i); if(elem == Py_None) { out[i] = NULL; } else { SDChunk *ptr = NULL; int ret = SWIG_ConvertPtr(elem, (void **)&ptr, type_info, 0); if(SWIG_IsOK(ret)) { if(ActiveRefcounter::HasPyObject(ptr)) { out[i] = ptr; Py_IncRef(elem); } else { out[i] = ptr->Duplicate(); } } else { if(failIdx) *failIdx = i; return ret; } } } return SWIG_OK; } static int ConvertFromPy(PyObject *in, StructuredChunkList &out) { return ConvertFromPy(in, out, NULL); } static PyObject *ConvertToPyInPlace(PyObject *list, const StructuredChunkList &in, int *failIdx) { swig_type_info *type_info = TypeConversion::GetTypeInfo(); if(type_info == NULL) return NULL; for(int i = 0; i < in.count(); i++) { PyObject *elem = SWIG_Py_Void(); if(in[i]) { SDChunk *pyCopy = in[i]->Duplicate(); elem = SWIG_InternalNewPointerObj((void *)pyCopy, type_info, SWIG_POINTER_OWN); } if(elem) { PyList_Append(list, elem); // release our reference Py_DecRef(elem); } else { if(failIdx) *failIdx = i; return NULL; } } return list; } static PyObject *ConvertToPy(const StructuredChunkList &in, int *failIdx) { PyObject *list = PyList_New(0); if(!list) return NULL; PyObject *ret = ConvertToPyInPlace(list, in, failIdx); // if a failure happened, don't leak the list we created if(!ret) Py_XDECREF(list); return ret; } static PyObject *ConvertToPy(const StructuredChunkList &in) { return ConvertToPy(in, NULL); } };