// Gestion de block de donnees avec partage de references // malheureusement tres mal concu... C.Magneville 04/99 // LAL (Orsay) / IN2P3-CNRS DAPNIA/SPP (Saclay) / CEA #include "sopnamsp.h" #include "machdefs.h" #include #include #include #include #include #include "pexceptions.h" #include "ndatablock.h" #include "thsafeop.h" // for ThreadSafe operations (Ref.Count/Share) /* ---- Pour renvoyer un identificateur unique ---- */ static uint_8 _ndrefid_ = 0; // Identificateur de NDREF cree uint_8 AnyDataObj::getUniqueId() { _ndrefid_++; return ( _ndrefid_ ); } /*! \class SOPHYA::NDataBlock \ingroup BaseTools Management of data blocks */ ////////////////////////////////// // Fonctionnement en mode debug // ////////////////////////////////// template int NDataBlock::Debug_NDataBlock = 0; template size_t NDataBlock::NallocData = 0; template size_t NDataBlock::NallocSRef = 0; template ThSafeOp* NDataBlock::gThsop = NULL; //! Set debug (and level print) for allocation and references debug. /*! \param prtlevel : activate/des-activate debug mode and select print level \arg prtlevel <= 0 : no debug \arg prtlevel == 1 : debug activated, no print \arg prtlevel >=2 : debug activated, print infos in all routines that have something to do with allocations or des-allocation of datas or references. */ template void NDataBlock::SetPrintDebug(int prtdbglevel) { Debug_NDataBlock = prtdbglevel; } //! Reset debug counter values. /*! \param nallocdata : reset number of allocated data structures to \b nallocdata \param nallocsref : reset number of allocated references to \b nallocsref \warning In principle this routine should not be use (only experts) */ template void NDataBlock::ResetDebug(size_t nallocdata, size_t nallocsref) { NallocData = nallocdata; NallocSRef = nallocsref; } //! Print debug current status. /*! Print debug current status for number of allocated data structures and number of allocated references. */ template void NDataBlock::PrintDebug() { cout<<"... ... ... NallocData = "< NDataBlock::NDataBlock(size_t n, bool fzero) // Createur d'une structure de "n" donnees : mSz(0), mSRef(NULL), mIsTemp(false) { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::NDataBlock("< NDataBlock::NDataBlock(size_t n, T* data, Bridge* br) // Createur d'une structure de "n" donnees, avec donnees preallouees. // Attention createur TRES DANGEREUX (Voir explications dans Alloc()). : mSz(0), mSRef(NULL), mIsTemp(false) { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::NDataBlock("< NDataBlock::NDataBlock() // Createur par default : mSz(0), mSRef(NULL), mIsTemp(false) { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::NDataBlock("< NDataBlock::NDataBlock(const NDataBlock& a) // Createur par copie: partage les donnees dans tous les cas : mSz(0), mSRef(NULL), mIsTemp(false) { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::NDataBlock("<0) Share(a); } //! Copy constructor with \b share option /*! \warning datas are shared if \b share is \b true, cloned if not. */ template NDataBlock::NDataBlock(const NDataBlock& a,bool share) // Createur avec choix de partager ou non selon "share" : mSz(0), mSRef(NULL), mIsTemp(false) { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::NDataBlock("<0) {if(share) Share(a); else Clone(a);} } //! Destructor template NDataBlock::~NDataBlock() // Destructeur { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::~NDataBlock("< void NDataBlock::Clone(const NDataBlock& a) // Clone: copie de donnees a partir de "a" { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::Clone("< void NDataBlock::Share(const NDataBlock& a) // Share: Partage les donnees avec "a" { if(Debug_NDataBlock>1) { cout<<"?_NDataBlock::Share("<lock(); // (ThreadSafe) if(mSRef) Delete(); mSz = a.mSz; mSRef = a.mSRef; mSRef->nref++; gThsop->unlock(); // (ThreadSafe) //--- End of atomic operation if(Debug_NDataBlock>1) cout<<"...?_NDataBlock::Share mSz="<nref="<nref<<" mSRef->data="<< mSRef->data <<" mSRef->bridge="<bridge <<" IsTemp="< result; ...; return result;} ne va pas allouer de la memoire pour retourner "result". - La gestion de temporaire sert quand on enchaine plusieurs operations sur la meme ligne, par exemple : A = B+C+D; Dans ce cas l'objet CD=C+D est d'abord alloue et rempli avec C+D, puis CD est mis a "temporaire". Quand on ajoute B a CD, la methode d'addition va se rendre compte que CD est "temporaire" et additionner B "in-place" dans CD sans allouer une fois de plus de la place (pas d'allocation de place BCD pour mettre B+CD mais une operation CD += B). Si la notion d'objet "temporaire" n'avait pas ete consideree l'addition A = B+C+D aurait alloue de la place pour "CD=C+D" puis pour BCD=B+CD : 2 allocations auraient ete necessaires contre 1 seule dans notre cas de geston de "temporaire". \endverbatim */ template void NDataBlock::CloneOrShare(const NDataBlock& a) // CloneOrShare: Share si "a" temporaire, Clone sinon. { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::CloneOrShare("< float *x = new float[5]; ... remplissage de x[] ...; 1- On veut que NDataBlock NE DESALLOUE PAS le tableau "x[]" a- Premiere solution > NDataBlock A(5,x,new Bridge); ...... > delete [] x; - Il faut deleter x[] explicitement. - Le destructeur de "A" ne detruit pas x[]. ATTENTION: Une fois x[] detruit, "A" ne peut plus acceder les donnees! - Bridge est detruit par le destructeur de "A" b- Autre solution: > NDataBlock A(5); A.FillFrom(5,x); > delete [] x; ...... - Il faut deleter x[] explicitement. - "A" possede une copie en local de x[]. - Le destructeur de "A" ne detruit pas x[] mais la copie locale. 2- On veut que NDataBlock desalloue le tableau > NDataBlock A(5,x); - Ne Pas Faire "delete [] x;" - "A" partage les donnees avec x[]. - Le destructeur de "A" detruit x[]. --- REMARQUE SUR LE DANGER DE CERTAINES SITUATIONS (CMV): 1-/ x = new float[n1]; NDataBlock A(n2,x); 1er danger: si n2>n1 depassement de tableaux (core dump) 2sd danger: celui qui alloue x[] ne doit pas faire le "delete" en desaccord avec toutes les regles de bonne conduite. 2-/ float x[5]={1,2,3,4,5}; {NDataBlock A(n2,&x[0]);} cout<n1 depassement de tableaux (core dump) 2sd danger: si la methode bridgee (blitz?) detruit x[] "A" n'a plus de donnees connectees! --- CONCLUSION Cette classe est franchement merdique. - On peut accepter la prise de risque liee a NDataBlock(n2,x,new Bridge); car je ne vois pas comment on pourrait faire autrement pour connecter un tableau de type blitz par exemple. - Par contre le createur NDataBlock(n2,x); doit etre interdit dans sa forme actelle car trop dangereux et il me semble inutile. - Dans cette nouvelle optique: NDataBlock(n2,x,new Bridge) et NDataBlock(n2,x) disparaissent On remplace par NDataBlock(n2,x) {Alloc(n2,x,new Bridge);} qui force le Bridge dans tout les cas puisque NDataBlock ne possede pas les donnees. Mais puis-je encore le faire vu que NDataBlock est a la base de TVector,TMatrix et qu'il faut donc reprendre tout le code DPC - Quoiqu'il arrive Alloc est une methode privee et peut donc rester sous sa forme actuelle. \endverbatim */ template void NDataBlock::Alloc(size_t n,T* data,Bridge* br,bool zero) { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::Alloc("<lock(); // (ThreadSafe) if(mSRef) Delete(); mSz = n; mSRef = new NDREF; mSRef->nref = 1; mSRef->dsid = AnyDataObj::getUniqueId(); if(data) mSRef->data = data; else {mSRef->data = new T[n]; if (zero) memset(mSRef->data,0,n*sizeof(T));} mSRef->bridge = br; gThsop->unlock(); // (ThreadSafe) //--- End of atomic operation (ThreadSafe) if(Debug_NDataBlock>0) { // Meme dans le cas data!=0 et br==0 (connexion d'un tableau // avec destruction geree par ~NDataBlock (cas 2-) on compte // comme si on avait fait une allocation du tableau (ce qui a ete // fait au niveau du dessus!). if(!br) NallocData++; NallocSRef++; if(Debug_NDataBlock>1) cout<<"...?_NDataBlock::Alloc mSz="<nref="<nref<<" mSRef->data="<data <<" mSRef->bridge="<bridge <<" IsTemp="< void NDataBlock::Delete(void) // Pour detruire les pointeurs en tenant compte des references { if(Debug_NDataBlock>1) { cout<<"?_NDataBlock::Delete("<nref="<nref<<" mSRef->data=" <data<<" mSRef->bridge="<bridge; cout<nref--; if(mSRef->nref != 0) { if(Debug_NDataBlock>1) cout<<"...?_NDataBlock::Delete() pas de desallocation il reste nref=" <nref<<" Total("<0) { if(!mSRef->bridge) NallocData--; NallocSRef--; if(Debug_NDataBlock>1) cout<<"...?_NDataBlock::Delete() desallocation complete il reste nref=" <nref<<" Total("<bridge) { if(Debug_NDataBlock>1) cout<<"...?_NDataBlock::Delete() Bridge "<bridge<<" deleted"<bridge; } else { if(Debug_NDataBlock>1) cout<<"...?_NDataBlock::Delete() data "<data<<" deleted"<data; } mSRef->bridge=NULL; mSRef->data=NULL; delete mSRef; mSRef=NULL; mSz = 0; } //! Fill dats of this NDataBlock with the \b n datas pointed by \b data /*! \warning If class empty : allocate space in memory \warning If class already connected : overwrite with minimum size (\b n or \b mSz) */ template void NDataBlock::FillFrom(size_t n,T* data) // Remplissage par un tableau de donnees // - Si classe vide : creation de l'espace memoire // - Si classe connectee : on ecrit selon la longueur minimale // (cad this->mSz ou "n") { if(data==NULL) throw(NullPtrError("NDataBlock::FillFrom data==NULL\n")); if(n==0) throw(ParmError("NDataBlock::FillFrom n<=0\n")); if(mSRef==NULL) Alloc(n, NULL, NULL, false); // Pas de mise a zero if(mSznold force=quelconque *** place re-allouee, donnees [0,nold[ copiees, surplus [nold,new[ mis a zero b-/ *** nnew<=nold force=true *** place re-allouee, donnees [0,nnew[ copiees, pas de surplus c-/ *** nnew<=nold force=false *** place non re-allouee, seule la valeur de la taille est diminuee - On tient compte du partage des donnees dans tous les cas. - Si il n'y a pas de donnees connectees a la classe, on re-alloue dans tous les cas \endverbatim */ template void NDataBlock::Realloc(size_t nnew,bool force) { if(nnew==0) throw(ParmError("NDataBlock::Realloc n<=0\n")); // Cas sans re-allocation memoire if(mSRef && nnew<=mSz && ! force) { mSz=nnew; return;} // Cas avec re-allocation memoire size_t ncop; if(!mSRef || mSz==0) ncop=0; else if(mSz0) memcpy(dataloc,mSRef->data,ncop*sizeof(T)); if(nnew>ncop) memset(dataloc+ncop,0,(nnew-ncop)*sizeof(T)); Alloc(nnew,dataloc,NULL); //Alloc gere partage de reference et bridge } /*! \brief Calls the protected Delete() method to set the size to zero This is the public - thread safe version - of the Delete() method The memory is freed if last referenced structure. */ template void NDataBlock::Dealloc() { gThsop->lock(); Delete(); gThsop->unlock(); } //////////////// // Impression // //////////////// //! Give infos and print \b n datas beginning at \b i1 on stream \b os. template void NDataBlock::Print(ostream& os,size_t i1,size_t n) const // Impression de n elements a partir de i1 { size_t nr = 0; T* p = NULL; Bridge* br = NULL; if(mSRef) {nr = mSRef->nref; p = mSRef->data; br = mSRef->bridge;} os<<"NDataBlock::Print("<=mSz || n<=0 || !p) return; size_t i2 = i1+n; if(i2>mSz) i2=mSz; size_t im = 1; bool enl=false; while(i1 T NDataBlock::Sum(size_t i1,size_t n) const // Somme des elements de i1 a i1+n-1 { if(i1>=mSz) return 0; if(n>mSz) n = mSz; if(n==0) n = mSz-i1; T const *p=Begin()+i1, *pe=p+n; T val = 0; while (p T NDataBlock::Product(size_t i1,size_t n) const // Produit des elements de i1 a i1+n-1 { if(i1>=mSz) return 0; if(n>mSz) n = mSz; if(n==0) n = mSz-i1; T const *p=Begin()+i1, *pe=p+n; T val = 0; while (p b; // /////////////////////////////////////////////////////////////// //! Operator = : ND = NDa /*! \warning Datas are copied (cloned) from \b a. */ template NDataBlock& NDataBlock::operator = (const NDataBlock& a) // Affectation: partage des donnees si "a" temporaire, clone sinon. { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::operator=("< NDataBlock& NDataBlock::operator = (T v) // Affectation de tous les elements a une constante "v" { if(Debug_NDataBlock>1) cout<<"?_NDataBlock::operator=("< b; // ////////////////////////////////////////////////////////////// //! Add a constant : ND += b template NDataBlock& NDataBlock::operator += (T b) { if(mSz==0) throw(SzMismatchError("NDataBlock::operator+=v null size\n")); T *p=Begin(), *pe=End(); while (p NDataBlock& NDataBlock::operator -= (T b) { if(mSz==0) throw(SzMismatchError("NDataBlock::operator-=v null size\n")); T *p=Begin(), *pe=End(); while (p NDataBlock& NDataBlock::operator *= (T b) { if(mSz==0) throw(SzMismatchError("NDataBlock::operator*=v null size\n")); T *p=Begin(), *pe=End(); while (p NDataBlock& NDataBlock::operator /= (T b) { if(b==(T) 0) throw(ParmError("NDataBlock::operator/=v divide by zero\n")); if(mSz==0) throw(SzMismatchError("NDataBlock::operator/=v null size\n")); T *p=Begin(), *pe=End(); while (p NDataBlock& NDataBlock::operator += (const NDataBlock& a) { if(mSz==0 || mSz!=a.mSz) throw(SzMismatchError("NDataBlock::operator+=A size mismatch/null")); T *p=Begin(), *pe=End(); T const * pa=a.Begin(); while (p NDataBlock& NDataBlock::operator -= (const NDataBlock& a) { if(mSz==0 || mSz!=a.mSz) throw(SzMismatchError("NDataBlock::operator-=A size mismatch/null")); T *p=Begin(), *pe=End(); T const *pa=a.Begin(); while (p NDataBlock& NDataBlock::operator *= (const NDataBlock& a) { if(mSz==0 || mSz!=a.mSz) throw(SzMismatchError("NDataBlock::operator*=A size mismatch/null")); T *p=Begin(), *pe=End(); T const *pa=a.Begin(); while (p NDataBlock& NDataBlock::operator /= (const NDataBlock& a) // Attention, aucune protection si un element de "a" est nul. { if(mSz==0 || mSz!=a.mSz) throw(SzMismatchError("NDataBlock::operator/=A size mismatch/null")); T *p=Begin(), *pe=End(); T const *pa=a.Begin(); while (pb; // // NDataBlock = b+NDataBlock1; // // Pour la notion de "temporaire" voir blabla dans CloneOrShare // ////////////////////////////////////////////////////////////////// //! Add a constant and return NDataBlock : NDret = ND + b template NDataBlock NDataBlock::Add(T b) const // Pour A+b { NDataBlock result; result.CloneOrShare(*this); result.SetTemp(true); result += b; return result; } //! Substract a constant and return NDataBlock : NDret = ND - b or NDret = b - ND /*! Substract a constant or from a constant \param fginv==false : performs NDret = ND - b (default) \param fginv==true : performs NDret = b - ND */ template NDataBlock NDataBlock::Sub(T b,bool fginv) const // Pour A-b sauf si fginv==true b-A (- n'est pas commutatif!) { NDataBlock result; result.CloneOrShare(*this); result.SetTemp(true); if(fginv) { T *p=result.Begin(), *pe=result.End(); T const *pa=this->Begin(); while(p NDataBlock NDataBlock::Mul(T b) const // Pour A*b { NDataBlock result; result.CloneOrShare(*this); result.SetTemp(true); result *= b; return result; } //! Divide by a constant and return NDataBlock : NDret = ND / b or NDret = b / ND /*! Divide by a constant or from a constant \param fginv==false : performs NDret = ND / b (default) \param fginv==true : performs NDret = b / ND */ template NDataBlock NDataBlock::Div(T b,bool fginv) const // Pour A/b sauf si fginv==true b/A (/ n'est pas commutatif!) { NDataBlock result; result.CloneOrShare(*this); result.SetTemp(true); if(fginv) { T *p=result.Begin(), *pe=result.End(); T const *pa = this->Begin(); while(p::Div(T) - Divide by zero ! "); result /= b; } return result; } /////////////////////////////////////////////////////////////////////// // Pour surcharge de +,-,*,/ : NDataBlock = NDataBlock1+NDataBlock2; // /////////////////////////////////////////////////////////////////////// //! Add a NDataBlock and return a NDataBlock: ND = NDthis + NDb template NDataBlock NDataBlock::Add(const NDataBlock& b) const // Pour A+B { if(mSz!=b.mSz) throw(SzMismatchError("NDataBlock operator C=A+B size mismatch/null\n")); NDataBlock result; result.SetTemp(true); if(b.IsTemp()) {result.Share(b); result += *this;} else {result.CloneOrShare(*this); result += b;} return result; } //! Multiply by a NDataBlock and return a NDataBlock: ND = NDthis * NDb template NDataBlock NDataBlock::Mul(const NDataBlock& b) const // Pour A*B { if(mSz!=b.mSz) throw(SzMismatchError("NDataBlock operator C=A*B size mismatch/null\n")); NDataBlock result; result.SetTemp(true); if(b.IsTemp()) {result.Share(b); result *= *this;} else {result.CloneOrShare(*this); result *= b;} return result; } //! Substract a NDataBlock and return a NDataBlock: ND = NDthis - NDb template NDataBlock NDataBlock::Sub(const NDataBlock& b) const // Pour A-B { if(mSz!=b.mSz) throw(SzMismatchError("NDataBlock operator C=A-B size mismatch/null\n")); NDataBlock result; result.SetTemp(true); if(b.IsTemp()) { result.Share(b); T *p=result.Begin(), *pe=result.End(); T const *pa=Begin(); while(p NDataBlock NDataBlock::Div(const NDataBlock& b) const // Pour A/B { if(mSz!=b.mSz) throw(SzMismatchError("NDataBlock operator C=A/B size mismatch/null\n")); NDataBlock result; result.SetTemp(true); if(b.IsTemp()) { result.Share(b); T *p=result.Begin(), *pe=result.End(); T const *pa=Begin(); while(p #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock #pragma define_template NDataBlock< complex > #pragma define_template NDataBlock< complex > #ifdef SO_LDBLE128 #pragma define_template NDataBlock #pragma define_template NDataBlock< complex > #endif #endif #if defined(ANSI_TEMPLATES) || defined(GNU_TEMPLATES) namespace SOPHYA { template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock; template class NDataBlock< complex >; template class NDataBlock< complex >; #ifdef SO_LDBLE128 template class NDataBlock; template class NDataBlock< complex >; #endif } #endif