+
+// specific DataArray deallocator callback. This deallocator is used both in the constructor of DataArray and in the toNumPyArr
+// method. This dellocator uses weakref to determine if the linked numArr is still alive or not. If alive the ownership is given to it.
+// if no more alive the "standart" DataArray deallocator is called.
+void numarrdeal(void *pt, void *wron)
+{
+ void **wronc=(void **)wron;
+ PyObject *weakRefOnOwner=reinterpret_cast<PyObject *>(wronc[0]);
+ PyObject *obj=PyWeakref_GetObject(weakRefOnOwner);
+ if(obj!=Py_None)
+ {
+ Py_XINCREF(obj);
+ PyArrayObject *objC=reinterpret_cast<PyArrayObject *>(obj);
+ objC->flags|=NPY_OWNDATA;
+ Py_XDECREF(weakRefOnOwner);
+ Py_XDECREF(obj);
+ }
+ else
+ {
+ typedef void (*MyDeallocator)(void *,void *);
+ MyDeallocator deall=(MyDeallocator)wronc[1];
+ deall(pt,NULL);
+ Py_XDECREF(weakRefOnOwner);
+ }
+ delete [] wronc;
+}
+
+template<class MCData>
+struct PyCallBackDataArraySt {
+ PyObject_HEAD
+ MCData *_pt_mc;
+};
+
+typedef struct PyCallBackDataArraySt<ParaMEDMEM::DataArrayInt> PyCallBackDataArrayInt;
+typedef struct PyCallBackDataArraySt<ParaMEDMEM::DataArrayDouble> PyCallBackDataArrayDouble;
+
+extern "C"
+{
+ static int callbackmcdataarray___init__(PyObject *self, PyObject *args, PyObject *kwargs) { return 0; }
+
+ static PyObject *callbackmcdataarrayint___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+ {
+ PyCallBackDataArrayInt *self = (PyCallBackDataArrayInt *) ( type->tp_alloc(type, 0) );
+ return (PyObject *)self;
+ }
+
+ static PyObject *callbackmcdataarraydouble___new__(PyTypeObject *type, PyObject *args, PyObject *kwargs)
+ {
+ PyCallBackDataArrayDouble *self = (PyCallBackDataArrayDouble *) ( type->tp_alloc(type, 0) );
+ return (PyObject *)self;
+ }
+
+ static void callbackmcdataarray_dealloc(PyObject *self)
+ {
+ Py_TYPE(self)->tp_free(self);
+ }
+
+ // real callback called when a numpy arr having more than one DataArray instance client on it is destroyed.
+ // In this case, all the "weak" clients, except the first one, invoke this call back that desable the content of these "weak" clients.
+ static PyObject *callbackmcdataarrayint_call(PyCallBackDataArrayInt *self, PyObject *args, PyObject *kw)
+ {
+ if(self->_pt_mc)
+ {
+ ParaMEDMEM::MemArray<int>& mma=self->_pt_mc->accessToMemArray();
+ mma.destroy();
+ }
+ Py_XINCREF(Py_None);
+ return Py_None;
+ }
+
+ // real callback called when a numpy arr having more than one DataArray instance client on it is destroyed.
+ // In this case, all the "weak" clients, except the first one, invoke this call back that desable the content of these "weak" clients.
+ static PyObject *callbackmcdataarraydouble_call(PyCallBackDataArrayDouble *self, PyObject *args, PyObject *kw)
+ {
+ if(self->_pt_mc)
+ {
+ ParaMEDMEM::MemArray<double>& mma=self->_pt_mc->accessToMemArray();
+ mma.destroy();
+ }
+ Py_XINCREF(Py_None);
+ return Py_None;
+ }
+}
+
+PyTypeObject PyCallBackDataArrayInt_RefType = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "callbackmcdataarrayint",
+ sizeof(PyCallBackDataArrayInt),
+ 0,
+ callbackmcdataarray_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ (ternaryfunc)callbackmcdataarrayint_call, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ callbackmcdataarray___init__, /*tp_init*/
+ PyType_GenericAlloc, /*tp_alloc*/
+ callbackmcdataarrayint___new__, /*tp_new*/
+ PyObject_GC_Del, /*tp_free*/
+};
+
+PyTypeObject PyCallBackDataArrayDouble_RefType = {
+ PyVarObject_HEAD_INIT(&PyType_Type, 0)
+ "callbackmcdataarraydouble",
+ sizeof(PyCallBackDataArrayDouble),
+ 0,
+ callbackmcdataarray_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ 0, /*tp_repr*/
+ 0, /*tp_as_number*/
+ 0, /*tp_as_sequence*/
+ 0, /*tp_as_mapping*/
+ 0, /*tp_hash*/
+ (ternaryfunc)callbackmcdataarraydouble_call, /*tp_call*/
+ 0, /*tp_str*/
+ 0, /*tp_getattro*/
+ 0, /*tp_setattro*/
+ 0, /*tp_as_buffer*/
+ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_BASETYPE, /*tp_flags*/
+ 0, /*tp_doc*/
+ 0, /*tp_traverse*/
+ 0, /*tp_clear*/
+ 0, /*tp_richcompare*/
+ 0, /*tp_weaklistoffset*/
+ 0, /*tp_iter*/
+ 0, /*tp_iternext*/
+ 0, /*tp_methods*/
+ 0, /*tp_members*/
+ 0, /*tp_getset*/
+ 0, /*tp_base*/
+ 0, /*tp_dict*/
+ 0, /*tp_descr_get*/
+ 0, /*tp_descr_set*/
+ 0, /*tp_dictoffset*/
+ callbackmcdataarray___init__, /*tp_init*/
+ PyType_GenericAlloc, /*tp_alloc*/
+ callbackmcdataarraydouble___new__, /*tp_new*/
+ PyObject_GC_Del, /*tp_free*/
+};
+
+// this is the second type of specific deallocator, only valid for the constructor of DataArrays taking numpy array
+// in input when an another DataArray is already client of this.
+template<class MCData>
+void numarrdeal2(void *pt, void *obj)
+{
+ typedef struct PyCallBackDataArraySt<MCData> PyCallBackDataArray;
+ void **obj1=(void **)obj;
+ PyCallBackDataArray *cbdaic=reinterpret_cast<PyCallBackDataArray *>(obj1[0]);
+ PyObject *weakRefOnOwner=reinterpret_cast<PyObject *>(obj1[1]);
+ cbdaic->_pt_mc=0;
+ Py_XDECREF(weakRefOnOwner);
+ Py_XDECREF(cbdaic);
+ delete [] obj1;
+}
+
+template<class MCData, class T>
+MCData *BuildNewInstance(PyObject *elt0, int npyObjectType, PyTypeObject *pytype, const char *msg)
+{
+ int ndim=PyArray_NDIM(elt0);
+ if(ndim!=1 && ndim!=2)
+ throw INTERP_KERNEL::Exception("Input numpy array should have dimension equal to 1 or 2 !");
+ if(PyArray_ObjectType(elt0,0)!=npyObjectType)
+ {
+ std::ostringstream oss; oss << "Input numpy array has not the type " << msg << " at component #0 !";
+ throw INTERP_KERNEL::Exception(oss.str().c_str());
+ }
+ if(ndim==2)
+ if(PyArray_ObjectType(elt0,1)!=npyObjectType)
+ {
+ std::ostringstream oss; oss << "Input numpy array has not the type " << msg << " at component #1 !";
+ throw INTERP_KERNEL::Exception(oss.str().c_str());
+ }
+ npy_intp sz0=PyArray_DIM(elt0,0);
+ npy_intp sz1=ndim==2?PyArray_DIM(elt0,1):1;
+ //
+ int itemSize=PyArray_ITEMSIZE(elt0);
+ if(itemSize!=sizeof(T))
+ {
+ std::ostringstream oss; oss << "Input numpy array has not itemSize set to " << sizeof(T) << " !";
+ throw INTERP_KERNEL::Exception(oss.str().c_str());
+ }
+ if(itemSize*sz1!=PyArray_STRIDE(elt0,0))
+ throw INTERP_KERNEL::Exception("Input numpy array has stride that mismatches the item size ! Data are not packed in the right way for DataArrays !");
+ if(ndim==2)
+ if(itemSize!=PyArray_STRIDE(elt0,1))
+ throw INTERP_KERNEL::Exception("Input numpy array has stride that mismatches the item size ! Data are not packed in the right way for DataArrays for component #1 !");
+ const char *data=PyArray_BYTES(elt0);
+ typename ParaMEDMEM::MEDCouplingAutoRefCountObjectPtr<MCData> ret=MCData::New();
+ if(PyArray_ISBEHAVED(elt0))//aligned and writeable and in machine byte-order
+ {
+ PyArrayObject *elt0C=reinterpret_cast<PyArrayObject *>(elt0);
+ PyArrayObject *eltOwning=(PyArray_FLAGS(elt0C) & NPY_OWNDATA)?elt0C:NULL;
+ int mask=NPY_OWNDATA; mask=~mask;
+ elt0C->flags&=mask;
+ PyObject *deepestObj=elt0;
+ PyObject *base=elt0C->base;
+ if(base) deepestObj=base;
+ while(base)
+ {
+ if(PyArray_Check(base))
+ {
+ PyArrayObject *baseC=reinterpret_cast<PyArrayObject *>(base);
+ eltOwning=(PyArray_FLAGS(baseC) & NPY_OWNDATA)?baseC:eltOwning;
+ baseC->flags&=mask;
+ base=baseC->base;
+ if(base) deepestObj=base;
+ }
+ else
+ break;
+ }
+ typename ParaMEDMEM::MemArray<T>& mma=ret->accessToMemArray();
+ if(eltOwning==NULL)
+ {
+ PyCallBackDataArraySt<MCData> *cb=PyObject_GC_New(PyCallBackDataArraySt<MCData>,pytype);
+ cb->_pt_mc=ret;
+ ret->useArray(reinterpret_cast<const T *>(data),true,ParaMEDMEM::C_DEALLOC,sz0,sz1);
+ PyObject *ref=PyWeakref_NewRef(deepestObj,(PyObject *)cb);
+ void **objs=new void *[2]; objs[0]=cb; objs[1]=ref;
+ mma.setParameterForDeallocator(objs);
+ mma.setSpecificDeallocator(numarrdeal2<MCData>);
+ //"Impossible to share this numpy array chunk of data, because already shared by an another non numpy array object (maybe an another DataArrayInt instance) ! Release it, or perform a copy on the input array !");
+ }
+ else
+ {
+ ret->useArray(reinterpret_cast<const T *>(data),true,ParaMEDMEM::C_DEALLOC,sz0,sz1);
+ PyObject *ref=PyWeakref_NewRef(reinterpret_cast<PyObject *>(eltOwning),NULL);
+ void **objs=new void *[2]; objs[0]=ref; objs[1]=(void*) ParaMEDMEM::MemArray<T>::CDeallocator;
+ mma.setParameterForDeallocator(objs);
+ mma.setSpecificDeallocator(numarrdeal);
+ }
+ }
+ else if(PyArray_ISBEHAVED_RO(elt0))
+ ret->useArray(reinterpret_cast<const T *>(data),false,ParaMEDMEM::CPP_DEALLOC,sz0,sz1);
+ return ret.retn();
+}
+
+
+int NumpyArrSetBaseObjectExt(PyArrayObject *arr, PyObject *obj)
+{
+ if (obj == NULL) {
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot set the NumPy array 'base' "
+ "dependency to NULL after initialization");
+ return -1;
+ }
+ /*
+ * Allow the base to be set only once. Once the object which
+ * owns the data is set, it doesn't make sense to change it.
+ */
+ if (PyArray_BASE(arr) != NULL) {
+ Py_DECREF(obj);
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot set the NumPy array 'base' "
+ "dependency more than once");
+ return -1;
+ }
+
+ /*
+ * Don't allow infinite chains of views, always set the base
+ * to the first owner of the data.
+ * That is, either the first object which isn't an array,
+ * or the first object which owns its own data.
+ */
+
+ while (PyArray_Check(obj) && (PyObject *)arr != obj) {
+ PyArrayObject *obj_arr = (PyArrayObject *)obj;
+ PyObject *tmp;
+
+
+ /* If this array owns its own data, stop collapsing */
+ if (PyArray_CHKFLAGS(obj_arr, NPY_OWNDATA)) {
+ break;
+ }
+
+ tmp = PyArray_BASE(obj_arr);
+ /* If there's no base, stop collapsing */
+ if (tmp == NULL) {
+ break;
+ }
+ /* Stop the collapse new base when the would not be of the same
+ * type (i.e. different subclass).
+ */
+ if (Py_TYPE(tmp) != Py_TYPE(arr)) {
+ break;
+ }
+
+
+ Py_INCREF(tmp);
+ Py_DECREF(obj);
+ obj = tmp;
+ }
+
+ /* Disallow circular references */
+ if ((PyObject *)arr == obj) {
+ Py_DECREF(obj);
+ PyErr_SetString(PyExc_ValueError,
+ "Cannot create a circular NumPy array 'base' dependency");
+ return -1;
+ }
+
+ arr->base = obj;
+
+ return 0;
+}
+
+template<class MCData, class T>
+PyObject *ToNumPyArray(MCData *self, int npyObjectType, const char *MCDataStr)
+{
+ if(!self->isAllocated())
+ {
+ std::ostringstream oss; oss << MCDataStr << "::toNumPyArray : this is not allocated !";
+ throw INTERP_KERNEL::Exception(oss.str().c_str());
+ }
+ ParaMEDMEM::MemArray<T>& mem=self->accessToMemArray();
+ int nbComp=self->getNumberOfComponents();
+ if(nbComp==0)
+ {
+ std::ostringstream oss; oss << MCDataStr << "::toNumPyArray : number of components of this is 0 ! Should be > 0 !";
+ throw INTERP_KERNEL::Exception(oss.str().c_str());
+ }
+ int nbDims=nbComp==1?1:2;
+ npy_intp dim[2];
+ dim[0]=(npy_intp)self->getNumberOfTuples(); dim[1]=nbComp;
+ const T *bg=self->getConstPointer();
+ PyObject *ret=PyArray_SimpleNewFromData(nbDims,dim,npyObjectType,const_cast<T *>(bg));
+ if(mem.isDeallocatorCalled())
+ {
+ if(mem.getDeallocator()!=numarrdeal)
+ {// case for the first call of toNumPyArray
+ PyObject *ref=PyWeakref_NewRef(ret,NULL);
+ void **objs=new void *[2]; objs[0]=ref; objs[1]=(void*) mem.getDeallocator();
+ mem.setParameterForDeallocator(objs);
+ mem.setSpecificDeallocator(numarrdeal);
+ return ret;
+ }
+ else
+ {// case for the second and other call of toNumPyArray
+ void **objs=(void **)mem.getParameterForDeallocator();
+ PyObject *weakRefOnOwner=(PyObject *)objs[0];
+ PyObject *obj=PyWeakref_GetObject(weakRefOnOwner);
+ if(obj!=Py_None)
+ {//the previous numArray exists let numpy deals the numpy array each other by declaring the still alive instance as base
+ Py_XINCREF(obj);
+ NumpyArrSetBaseObjectExt((PyArrayObject*)ret,obj);
+ }
+ else
+ {//the previous numArray no more exists -> declare the newly created numpy array as the first one.
+ Py_XDECREF(weakRefOnOwner);
+ PyObject *ref=PyWeakref_NewRef(ret,NULL);
+ objs[0]=ref;
+ }
+ }
+ }
+ return ret;
+}
+