diff --git a/core/meta/inc/TClass.h b/core/meta/inc/TClass.h index ff250aae7ef5a..56365cd720a0d 100644 --- a/core/meta/inc/TClass.h +++ b/core/meta/inc/TClass.h @@ -406,6 +406,7 @@ friend class TStreamerInfo; void ForceReload (TClass* oldcl); Bool_t HasDataMemberInfo() const { return fIsSyntheticPair || fHasRootPcmInfo || HasInterpreterInfo(); } Bool_t HasDefaultConstructor(Bool_t testio = kFALSE) const; + Bool_t HasDirectStreamerInfoUse() const { return fStreamerImpl == &TClass::StreamerStreamerInfo; } Bool_t HasInterpreterInfoInMemory() const { return nullptr != fClassInfo; } Bool_t HasInterpreterInfo() const { return fCanLoadClassInfo || fClassInfo; } UInt_t GetCheckSum(ECheckSum code = kCurrentCheckSum) const; diff --git a/core/meta/inc/TStreamerElement.h b/core/meta/inc/TStreamerElement.h index 16b41c3e7c51e..773f005655e1b 100644 --- a/core/meta/inc/TStreamerElement.h +++ b/core/meta/inc/TStreamerElement.h @@ -52,6 +52,8 @@ class TStreamerElement : public TNamed { Double_t fXmax; //!Maximum of data member if a range is specified [xmin,xmax,nbits] Double_t fFactor; //!Conversion factor if a range is specified fFactor = (1<ClassInfo_IsEnum(arglist.fElements[1].c_str())) { + fCtype = enumdesc ? enumdesc->GetUnderlyingType() : 3; + } + } + if (TStreamerSTL::IsaPointer()) { + fType = TVirtualStreamerInfo::kSTLp; + fNewType = fType; } - if (TStreamerSTL::IsaPointer()) fType = TVirtualStreamerInfo::kSTLp; } //////////////////////////////////////////////////////////////////////////////// @@ -1776,8 +1789,11 @@ TStreamerSTL::TStreamerSTL(const char *name, const char *title, Int_t offset, if (isPointer) fCtype = TVirtualStreamerInfo::kObjectp; else fCtype = TVirtualStreamerInfo::kObject; } else { - if (gCling->ClassInfo_IsEnum(intype.c_str())) { - if (isPointer) fCtype += TVirtualStreamerInfo::kOffsetP; + auto enumdesc = TEnum::GetEnum(intype.c_str()); + if (enumdesc || gCling->ClassInfo_IsEnum(intype.c_str())) { + fCtype = enumdesc ? enumdesc->GetUnderlyingType() : 3; + if (isPointer) + fCtype += TVirtualStreamerInfo::kOffsetP; } else { if (intype != "string") { // This case can happens when 'this' is a TStreamerElement for @@ -1794,7 +1810,10 @@ TStreamerSTL::TStreamerSTL(const char *name, const char *title, Int_t offset, } } - if (TStreamerSTL::IsaPointer()) fType = TVirtualStreamerInfo::kSTLp; + if (TStreamerSTL::IsaPointer()) { + fType = TVirtualStreamerInfo::kSTLp; + fNewType = fType; + } } //////////////////////////////////////////////////////////////////////////////// @@ -1844,6 +1863,41 @@ Bool_t TStreamerSTL::IsBase() const if (strcmp(ts.Data(),GetTypeNameBasic())==0) return kTRUE; return kFALSE; } + +//////////////////////////////////////////////////////////////////////////////// +/// Returns a pointer to the TClass of this element. + +TClass *TStreamerSTL::GetClassPointer() const +{ + if (fClassObject!=(TClass*)(-1)) + return fClassObject; + + bool quiet = (fType == TVirtualStreamerInfo::kArtificial); + + TString className(ExtractClassName(fTypeName)); + TClass *cl = TClass::GetClass(className, kTRUE, quiet); + + auto proxy = cl->GetCollectionProxy(); + if (fNewClass && proxy->GetValueClass() == nullptr) { + // Collection of numerical type, let check if it is an enum. + TClassEdit::TSplitType arglist(fTypeName, TClassEdit::kDropStlDefault); + if ( arglist.fElements[1].size() >= 2 ) { + auto enumdesc = TEnum::GetEnum(arglist.fElements[1].c_str()); + if (enumdesc || gCling->ClassInfo_IsEnum(arglist.fElements[1].c_str())) { + if (fNewClass == nullptr) { + ((TStreamerElement*)this)->fNewClass = cl; + if (proxy->HasPointers()) + cl = TClass::GetClass("vector"); + else + cl = TClass::GetClass("vector"); + } + } + } + } + ((TStreamerElement*)this)->fClassObject = cl; + return fClassObject; +} + //////////////////////////////////////////////////////////////////////////////// /// Returns size of STL container in bytes. diff --git a/io/io/inc/TStreamerInfoActions.h b/io/io/inc/TStreamerInfoActions.h index ea1cc24dfdcda..40f2ac9cf8419 100644 --- a/io/io/inc/TStreamerInfoActions.h +++ b/io/io/inc/TStreamerInfoActions.h @@ -175,7 +175,7 @@ namespace TStreamerInfoActions { typedef std::vector ActionContainer_t; class TActionSequence : public TObject { - TActionSequence() {}; + TActionSequence() = delete; public: enum class EStatusBits { kVectorPtrLooper = BIT(14) @@ -216,7 +216,9 @@ namespace TStreamerInfoActions { TActionSequence *CreateCopy(); static TActionSequence *CreateReadMemberWiseActions(TVirtualStreamerInfo *info, TVirtualCollectionProxy &proxy); + static TActionSequence *CreateReadMemberWiseActions(TVirtualStreamerInfo &info, TLoopConfiguration *loopConfig); // 2nd arg should be unique_ptr static TActionSequence *CreateWriteMemberWiseActions(TVirtualStreamerInfo *info, TVirtualCollectionProxy &proxy); + static TActionSequence *CreateWriteMemberWiseActions(TVirtualStreamerInfo &info, TLoopConfiguration *loopConfig); // 2nd arg should be unique_ptr TActionSequence *CreateSubSequence(const std::vector &element_ids, size_t offset); TActionSequence *CreateSubSequence(const TIDs &element_ids, size_t offset, SequenceGetter_t create); diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 401793f98e50e..534ae10f2cc49 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -535,8 +535,10 @@ void TStreamerInfo::Build(Bool_t isTransient) element = new TStreamerSTLstring(dmName, dmTitle, offset, dmFull, dmIsPtr); } else if (dm->IsSTLContainer()) { TVirtualCollectionProxy *proxy = TClass::GetClass(dmType /* the underlying type */)->GetCollectionProxy(); - if (proxy) element = new TStreamerSTL(dmName, dmTitle, offset, dmFull, *proxy, dmIsPtr); - else element = new TStreamerSTL(dmName, dmTitle, offset, dmFull, dmFull, dmIsPtr); + if (proxy) + element = new TStreamerSTL(dmName, dmTitle, offset, dmFull, *proxy, dmIsPtr); + else + element = new TStreamerSTL(dmName, dmTitle, offset, dmFull, dmFull, dmIsPtr); bool hasCustomAlloc = proxy ? proxy->GetProperties() & TVirtualCollectionProxy::kCustomAlloc : kFALSE; if (((TStreamerSTL*)element)->GetSTLtype() != ROOT::kSTLvector || hasCustomAlloc) { auto printErrorMsg = [&](const char* category) diff --git a/io/io/src/TStreamerInfoActions.cxx b/io/io/src/TStreamerInfoActions.cxx index cb01ef9ee14ac..8b6e556716073 100644 --- a/io/io/src/TStreamerInfoActions.cxx +++ b/io/io/src/TStreamerInfoActions.cxx @@ -44,6 +44,12 @@ using namespace TStreamerInfoActions; namespace TStreamerInfoActions { + enum class EMode + { + kRead, + kWrite + }; + bool IsDefaultVector(TVirtualCollectionProxy &proxy) { const auto props = proxy.GetProperties(); @@ -188,6 +194,59 @@ namespace TStreamerInfoActions }; + struct TConfObject : public TConfiguration + { + TClassRef fOnfileClass; + TClassRef fInMemoryClass; + + TConfObject(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, + TClass *onfileClass, TClass *inMemoryClass) : + TConfiguration(info, id, compinfo, offset), + fOnfileClass(onfileClass), + fInMemoryClass(inMemoryClass ? inMemoryClass : onfileClass) {} + TConfiguration *Copy() override { return new TConfObject(*this); } + }; + + struct TConfSubSequence : public TConfiguration + { + std::unique_ptr fActions; + + TConfSubSequence(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, + std::unique_ptr actions) : + TConfiguration(info, id, compinfo, offset), + fActions(std::move(actions)) + {} + + TConfSubSequence(const TConfSubSequence &input) : TConfiguration(input), + fActions(input.fActions->CreateCopy()) + {} + + void AddToOffset(Int_t delta) override + { + // Add the (potentially negative) delta to all the configuration's offset. This is used by + // TBranchElement in the case of split sub-object. + + if (fOffset != TVirtualStreamerInfo::kMissing) { + fOffset += delta; + if (fActions) + fActions->AddToOffset(delta); + } + } + + TConfiguration *Copy() override { return new TConfSubSequence(*this); }; + }; + + struct TConfStreamerLoop : public TConfiguration { + bool fIsPtrPtr = false; // Which are we, an array of objects or an array of pointers to objects? + + TConfStreamerLoop(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, bool isPtrPtr) + : TConfiguration(info, id, compinfo, offset), fIsPtrPtr(isPtrPtr) + { + } + + TConfiguration *Copy() override { return new TConfStreamerLoop(*this); }; + }; + Int_t GenericReadAction(TBuffer &buf, void *addr, const TConfiguration *config) { char *obj = (char*)addr; @@ -231,6 +290,71 @@ namespace TStreamerInfoActions } } + inline Int_t ReadViaExtStreamer(TBuffer &buf, void *addr, const TConfiguration *config) + { + void *x = (void *)(((char *)addr) + config->fOffset); + TMemberStreamer *pstreamer = config->fCompInfo->fStreamer; + (*pstreamer)(buf, x, config->fCompInfo->fLength); + return 0; + } + + inline Int_t WriteViaExtStreamer(TBuffer &buf, void *addr, const TConfiguration *config) + { + void *x = (void *)(((char *)addr) + config->fOffset); + TMemberStreamer *pstreamer = config->fCompInfo->fStreamer; + (*pstreamer)(buf, x, config->fCompInfo->fLength); + return 0; + } + + Int_t ReadStreamerCase(TBuffer &buf, void *addr, const TConfiguration *config) + { + UInt_t start, count; + /* Version_t v = */ buf.ReadVersion(&start, &count, config->fInfo->IsA()); + + ReadViaExtStreamer(buf, addr, config); + + buf.CheckByteCount(start, count, config->fCompInfo->fElem->GetFullName()); + return 0; + } + + Int_t WriteStreamerCase(TBuffer &buf, void *addr, const TConfiguration *config) + { + UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + WriteViaExtStreamer(buf, addr, config); + + buf.SetByteCount(pos, kTRUE); + return 0; + } + + // Read a full object objectwise including reading the version and + // selecting any conversion. + Int_t ReadViaClassBuffer(TBuffer &buf, void *addr, const TConfiguration *config) + { + auto conf = (TConfObject*)config; + auto memoryClass = conf->fInMemoryClass; + auto onfileClass = conf->fOnfileClass; + + char * const where = (((char*)addr)+config->fOffset); + buf.ReadClassBuffer( memoryClass, where, onfileClass ); + return 0; + } + + // Write a full object objectwise including reading the version and + // selecting any conversion. + Int_t WriteViaClassBuffer(TBuffer &buf, void *addr, const TConfiguration *config) + { + auto conf = (TConfObject*)config; + [[maybe_unused]] auto memoryClass = conf->fInMemoryClass; + auto onfileClass = conf->fOnfileClass; + assert((memoryClass == nullptr || memoryClass == onfileClass) + && "Need to new TBufferFile::WriteClassBuffer overload to support this case"); + + char * const where = (((char*)addr)+config->fOffset); + buf.WriteClassBuffer( onfileClass, where); + return 0; + } + template <> INLINE_TEMPLATE_ARGS Int_t ReadBasicType(TBuffer &buf, void *addr, const TConfiguration *config) { @@ -247,6 +371,13 @@ namespace TStreamerInfoActions return 0; } + template + static INLINE_TEMPLATE_ARGS Int_t WriteBasicZero(TBuffer &buf, void *, const TConfiguration *) + { + buf << T{0}; + return 0; + } + template INLINE_TEMPLATE_ARGS Int_t WriteBasicType(TBuffer &buf, void *addr, const TConfiguration *config) { @@ -256,6 +387,18 @@ namespace TStreamerInfoActions return 0; } + template + struct WriteConvertBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory. + Onfile temp; + temp = (Onfile)(*(Memory*)( ((char*)addr) + config->fOffset )); + buf << temp; + return 0; + } + }; + INLINE_TEMPLATE_ARGS Int_t WriteTextTNamed(TBuffer &buf, void *addr, const TConfiguration *config) { void *x = (void *)(((char *)addr) + config->fOffset); @@ -280,16 +423,6 @@ namespace TStreamerInfoActions return 0; } - INLINE_TEMPLATE_ARGS Int_t WriteTextStreamer(TBuffer &buf, void *addr, const TConfiguration *config) - { - void *x = (void *)(((char *)addr) + config->fOffset); - TMemberStreamer *pstreamer = config->fCompInfo->fStreamer; - UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); - (*pstreamer)(buf, x, config->fCompInfo->fLength); - buf.SetByteCount(pos, kTRUE); - return 0; - } - INLINE_TEMPLATE_ARGS Int_t ReadTextObject(TBuffer &buf, void *addr, const TConfiguration *config) { void *x = (void *)(((char *)addr) + config->fOffset); @@ -312,18 +445,6 @@ namespace TStreamerInfoActions return 0; } - INLINE_TEMPLATE_ARGS Int_t ReadTextStreamer(TBuffer &buf, void *addr, const TConfiguration *config) - { - void *x = (void *)(((char *)addr) + config->fOffset); - TMemberStreamer *pstreamer = config->fCompInfo->fStreamer; - - UInt_t start, count; - /* Version_t v = */ buf.ReadVersion(&start, &count, config->fCompInfo->fClass); - (*pstreamer)(buf, x, config->fCompInfo->fLength); - buf.CheckByteCount(start, count, config->fCompInfo->fElem->GetFullName()); - return 0; - } - INLINE_TEMPLATE_ARGS Int_t ReadTextTObjectBase(TBuffer &buf, void *addr, const TConfiguration *config) { // action required to call custom code for TObject as base class @@ -339,637 +460,165 @@ namespace TStreamerInfoActions return 0; } - /** Direct copy of code from TStreamerInfo::WriteBufferAux, - * potentially can be used later for non-text streaming */ - template - INLINE_TEMPLATE_ARGS Int_t WriteSTLp(TBuffer &buf, void *addr, const TConfiguration *config) + static INLINE_TEMPLATE_ARGS void WriteCompressed(TBuffer &buf, float *addr, const TStreamerElement *elem) + { + buf.WriteFloat16(addr, const_cast(elem)); + } + + static INLINE_TEMPLATE_ARGS void WriteCompressed(TBuffer &buf, double *addr, const TStreamerElement *elem) + { + buf.WriteDouble32(addr, const_cast(elem)); + } + + INLINE_TEMPLATE_ARGS Int_t TextWriteSTLp(TBuffer &buf, void *addr, const TConfiguration *config) { - TClass *cl = config->fCompInfo->fClass; + TClass *cl = config->fCompInfo->fClass; TMemberStreamer *pstreamer = config->fCompInfo->fStreamer; - TVirtualCollectionProxy *proxy = cl->GetCollectionProxy(); - TClass* vClass = proxy ? proxy->GetValueClass() : 0; - UInt_t eoffset = 0; // extra parameter of TStreamerInfo::WriteBufferAux, 0 for all kind of objects writing - UInt_t ioffset = eoffset + config->fOffset; + UInt_t ioffset = config->fOffset; - if (!buf.TestBit(TBuffer::kCannotHandleMemberWiseStreaming) - && proxy && vClass - && config->fInfo->GetStreamMemberWise() - && cl->CanSplit() - && !(strspn(config->fCompInfo->fElem->GetTitle(),"||") == 2) - && !(vClass->HasCustomStreamerMember()) ) { - // Let's save the collection member-wise. - - UInt_t pos = buf.WriteVersionMemberWise(config->fInfo->IsA(),kTRUE); - buf.WriteVersion( vClass, kFALSE ); - - // TODO: subinfo used for WriteBufferSTL call, which is private for the moment - //TStreamerInfo *subinfo = (TStreamerInfo*)vClass->GetStreamerInfo(); - - //for (int k = 0; k < narr; ++k) { - char **contp = (char **)((char *)addr + ioffset); - for(int j=0;jfCompInfo->fLength;++j) { - char *cont = contp[j]; - TVirtualCollectionProxy::TPushPop helper( proxy, cont ); - Int_t nobjects = cont ? proxy->Size() : 0; - buf << nobjects; - - // TODO: method is private, should be made accesible from here - // subinfo->WriteBufferSTL(buf,proxy,nobjects); - } - //} - buf.SetByteCount(pos,kTRUE); - return 0; - } UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); - if (kIsTextT) { - // use same method which is used in kSTL - buf.WriteFastArray((void **)((char *)addr + ioffset), cl, config->fCompInfo->fLength, kFALSE, pstreamer); - } else if (pstreamer == nullptr) { - // for (int k = 0; k < narr; ++k) { - char **contp = (char **)((char *)addr + ioffset); - for (int j = 0; j < config->fCompInfo->fLength; ++j) { - char *cont = contp[j]; - cl->Streamer(cont, buf); - } - // } - } else { - // for (int k = 0; k < narr; ++k) { - (*pstreamer)(buf, (char *)addr + ioffset, config->fCompInfo->fLength); - //} - } + + // use same method which is used in kSTL + buf.WriteFastArray((void **)((char *)addr + ioffset), cl, config->fCompInfo->fLength, kFALSE, pstreamer); + buf.SetByteCount(pos, kTRUE); return 0; } - - /** Direct copy of code from TStreamerInfo::WriteBufferAux, - * potentially can be used later for non-text streaming */ - template - INLINE_TEMPLATE_ARGS Int_t ReadSTLp(TBuffer &buf, void *addr, const TConfiguration *config) + INLINE_TEMPLATE_ARGS Int_t TextReadSTLp(TBuffer &buf, void *addr, const TConfiguration *config) { - TClass *cle = config->fCompInfo->fClass; - TStreamerElement * aElement = (TStreamerElement*) config->fCompInfo->fElem; + TClass *cle = config->fCompInfo->fClass; TMemberStreamer *pstreamer = config->fCompInfo->fStreamer; - //TVirtualCollectionProxy *proxy = cl->GetCollectionProxy(); - //TClass* vClass = proxy ? proxy->GetValueClass() : 0; - - UInt_t eoffset = 0; // extra parameter of TStreamerInfo::WriteBufferAux, 0 for all kind of objects writing - UInt_t ioffset = eoffset + config->fOffset; + TStreamerElement *aElement = (TStreamerElement *)config->fCompInfo->fElem; + UInt_t ioffset = config->fOffset; + UInt_t start, count; + /* Version_t vers = */ buf.ReadVersion(&start, &count, cle); + // use same method which is used in kSTL + buf.ReadFastArray((void **)((char *)addr + ioffset), cle, config->fCompInfo->fLength, kFALSE, pstreamer); + buf.CheckByteCount(start, count, aElement->GetFullName()); + return 0; + } - UInt_t start,count; - Version_t vers = buf.ReadVersion(&start, &count, cle); + class TConfWithFactor : public TConfiguration { + // Configuration object for the Float16/Double32 where a factor has been specified. + public: + Double_t fFactor; + Double_t fXmin; + TConfWithFactor(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, Double_t factor, Double_t xmin) : TConfiguration(info,id,compinfo,offset),fFactor(factor),fXmin(xmin) {}; + TConfiguration *Copy() override { return new TConfWithFactor(*this); } + }; - if (!kIsTextT && (vers & TBufferFile::kStreamedMemberWise) ) { - // Collection was saved member-wise + template + INLINE_TEMPLATE_ARGS Int_t ReadBasicType_WithFactor(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Stream a Float16 or Double32 where a factor has been specified. + //a range was specified. We read an integer and convert it back to a double. - vers &= ~( TBufferFile::kStreamedMemberWise ); + TConfWithFactor *conf = (TConfWithFactor *)config; + buf.ReadWithFactor((T*)( ((char*)addr) + config->fOffset ), conf->fFactor, conf->fXmin); + return 0; + } - TClass *newClass = aElement->GetNewClass(); - TClass *oldClass = aElement->GetClassPointer(); - if( vers < 9 && newClass && newClass!=oldClass ) { - Error( "ReadBuffer", "Unfortunately, version %d of TStreamerInfo (used in %s) did not record enough information to convert a %s%s into a %s.", - vers, buf.GetParent() ? buf.GetParent()->GetName() : "memory/socket", oldClass ? oldClass->GetName() : aElement->GetTypeName(), oldClass ? "" : " (could not find the corresponding TClass)", newClass->GetName() ); - return 0; - } + class TConfNoFactor : public TConfiguration { + // Configuration object for the Float16/Double32 where a factor has been specified. + public: + Int_t fNbits; + TConfNoFactor(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, Int_t nbits) : TConfiguration(info,id,compinfo,offset),fNbits(nbits) {}; + TConfiguration *Copy() override { return new TConfNoFactor(*this); } + }; - Version_t vClVersion = 0; // For vers less than 9, we have to use the current version. - if( vers >= 9 ) { - vClVersion = buf.ReadVersionForMemberWise( cle->GetCollectionProxy()->GetValueClass() ); - } + template + INLINE_TEMPLATE_ARGS Int_t ReadBasicType_NoFactor(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Stream a Float16 or Double32 where a factor has not been specified. - TVirtualCollectionProxy *newProxy = (newClass ? newClass->GetCollectionProxy() : nullptr); - TVirtualCollectionProxy *oldProxy = (oldClass ? oldClass->GetCollectionProxy() : nullptr); - TStreamerInfo *subinfo = nullptr; + TConfNoFactor *conf = (TConfNoFactor *)config; + Int_t nbits = conf->fNbits; - if( newProxy ) { - // coverity[dereference] oldProxy->GetValueClass() can not be null since this was streamed memberwise. - subinfo = (TStreamerInfo*)newProxy->GetValueClass()->GetConversionStreamerInfo( oldProxy->GetValueClass(), vClVersion ); - } else if ( oldProxy ) { - subinfo = (TStreamerInfo*)oldProxy->GetValueClass()->GetStreamerInfo( vClVersion ); - newProxy = oldProxy; - } - if (subinfo) { - // DOLOOP { - void* env; - void **contp = (void**)((char *) addr + ioffset); - for(int j=0;jfCompInfo->fLength;j++) { - void *cont = contp[j]; - if (cont==nullptr) { - contp[j] = cle->New(); - cont = contp[j]; - } - TVirtualCollectionProxy::TPushPop helper( newProxy, cont ); - Int_t nobjects; - buf >> nobjects; - env = newProxy->Allocate(nobjects,true); - subinfo->ReadBufferSTL(buf,newProxy,nobjects,/* offset */ 0, vers>=7 ); - newProxy->Commit(env); - } - // } // DOLOOP - } - buf.CheckByteCount(start,count,aElement->GetFullName()); - return 0; - } + buf.ReadWithNbits( (T*)( ((char*)addr) + config->fOffset ), nbits ); + return 0; + } - if (kIsTextT) { - // use same method which is used in kSTL - buf.ReadFastArray((void **)((char *)addr + ioffset), cle, config->fCompInfo->fLength, kFALSE, pstreamer); - } else if (pstreamer == nullptr) { - // DOLOOP { - void **contp = (void **)((char *)addr + ioffset); - for (int j = 0; j < config->fCompInfo->fLength; j++) { - void *cont = contp[j]; - if (cont == nullptr) { - // int R__n; - // b >> R__n; - // b.SetOffset(b.GetOffset()-4); // rewind to the start of the int - // if (R__n) continue; - contp[j] = cle->New(); - cont = contp[j]; - } - cle->Streamer(cont, buf); - } - // } - } else { - (*pstreamer)(buf, (char *)addr + ioffset, config->fCompInfo->fLength); - } - buf.CheckByteCount(start, count, aElement->GetFullName()); + INLINE_TEMPLATE_ARGS Int_t ReadTString(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Read in a TString object. + // Idea: We could separate the TString Streamer in its two parts and + // avoid the if (buf.IsReading()) and try having it inlined. + ((TString*)(((char*)addr)+config->fOffset))->TString::Streamer(buf); return 0; } - /** Direct copy of code from TStreamerInfo::WriteBufferAux, - * potentially can be used later for non-text streaming */ - template - INLINE_TEMPLATE_ARGS Int_t WriteStreamerLoop(TBuffer &buf, void *addr, const TConfiguration *config) + inline Int_t WriteTString(TBuffer &buf, void *addr, const TConfiguration *config) { - UInt_t eoffset = 0; // extra parameter of TStreamerInfo::WriteBufferAux, 0 for all kind of objects writing - UInt_t ioffset = eoffset + config->fOffset; + // Read in a TString object. - if (!kIsTextT && config->fCompInfo->fStreamer) { - // Get any private streamer which was set for the data member. - TMemberStreamer* pstreamer = config->fCompInfo->fStreamer; - // -- We have a private streamer. - UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); - // Loop over the entries in the clones array or the STL container. - //for (int k = 0; k < narr; ++k) { - // Get a pointer to the counter for the varying length array. - Int_t* counter = (Int_t*) ((char *) addr /*entry pointer*/ + eoffset /*entry offset*/ + config->fCompInfo->fMethod /*counter offset*/); - - // And call the private streamer, passing it the buffer, the object, and the counter. - (*pstreamer)(buf, (char *) addr /*entry pointer*/ + ioffset /*object offset*/, *counter); - //} for k - buf.SetByteCount(pos, kTRUE); - // We are done, next streamer element. - return 0; - } + // Idea: We could separate the TString Streamer in its two parts and + // avoid the if (buf.IsReading()) and try having it inlined. + ((TString*)(((char*)addr)+config->fOffset))->TString::Streamer(buf); + return 0; + } - // Get the class of the data member. - TClass* cl = config->fCompInfo->fClass; - // Which are we, an array of objects or an array of pointers to objects? - Bool_t isPtrPtr = (strstr(config->fCompInfo->fElem->GetTypeName(), "**") != 0); + INLINE_TEMPLATE_ARGS Int_t ReadTObject(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Read in a TObject object part. - // By default assume the file version is the newest. - Int_t fileVersion = kMaxInt; + // Idea: We could separate the TObject Streamer in its two parts and + // avoid the if (buf.IsReading()). + ((TObject*)(((char*)addr)+config->fOffset))->TObject::Streamer(buf); + return 0; + } - if (!kIsTextT) { - // At this point we do *not* have a private streamer. - // Get the version of the file we are writing to. - TFile* file = (TFile*) buf.GetParent(); - if (file) { - fileVersion = file->GetVersion(); - } - } - // Write the class version to the buffer. - UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); - if (fileVersion > 51508) { - // -- Newer versions allow polymorphic pointers to objects. - // Loop over the entries in the clones array or the STL container. - //for (int k = 0; k < narr; ++k) { - // Get the counter for the varying length array. - Int_t vlen = *((Int_t*) ((char *) addr /*entry pointer*/ + eoffset /*entry offset*/ + config->fCompInfo->fMethod /*counter offset*/)); - - //b << vlen; - if (vlen) { - // Get a pointer to the array of pointers. - char** pp = (char**) ((char *) addr /*entry pointer*/ + ioffset /*object offset*/); - // Loop over each element of the array of pointers to varying-length arrays. - for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { - if (!pp[ndx]) { - // -- We do not have a pointer to a varying-length array. - // Error("WriteBufferAux", "The pointer to element %s::%s type %d (%s) is null\n", GetName(), aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); - // ::ErrorHandler(kError, "::WriteStreamerLoop", Form("The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName())); - printf("WriteStreamerLoop - The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName()); - continue; - } - if (!isPtrPtr) { - // -- We are a varying-length array of objects. - // Write the entire array of objects to the buffer. - // Note: Polymorphism is not allowed here. - buf.WriteFastArray(pp[ndx], cl, vlen, nullptr); - } else { - // -- We are a varying-length array of pointers to objects. - // Write the entire array of object pointers to the buffer. - // Note: The object pointers are allowed to be polymorphic. - buf.WriteFastArray((void **)pp[ndx], cl, vlen, kFALSE, nullptr); - } // isPtrPtr - } // ndx - } else // vlen - if (kIsTextT) { - // special handling for the text-based streamers - for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) - buf.WriteFastArray((void *)nullptr, cl, -1, nullptr); - } - //} // k - } - else { - // -- Older versions do *not* allow polymorphic pointers to objects. - // Loop over the entries in the clones array or the STL container. - //for (int k = 0; k < narr; ++k) { - // Get the counter for the varying length array. - Int_t vlen = *((Int_t*) ((char *) addr /*entry pointer*/ + eoffset /*entry offset*/ + config->fCompInfo->fMethod /*counter offset*/)); - //b << vlen; - if (vlen) { - // Get a pointer to the array of pointers. - char** pp = (char**) ((char *) addr /*entry pointer*/ + ioffset /*object offset*/); - // -- Older versions do *not* allow polymorphic pointers to objects. - // Loop over each element of the array of pointers to varying-length arrays. - for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { - if (!pp[ndx]) { - // -- We do not have a pointer to a varying-length array. - //Error("WriteBufferAux", "The pointer to element %s::%s type %d (%s) is null\n", GetName(), aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); - // ::ErrorHandler(kError, "::WriteTextStreamerLoop", Form("The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName())); - printf("WriteStreamerLoop - The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName()); - continue; - } - if (!isPtrPtr) { - // -- We are a varying-length array of objects. - // Loop over the elements of the varying length array. - for (Int_t v = 0; v < vlen; ++v) { - // Write the object to the buffer. - cl->Streamer(pp[ndx] + (v * cl->Size()), buf); - } // v - } - else { - // -- We are a varying-length array of pointers to objects. - // Loop over the elements of the varying length array. - for (Int_t v = 0; v < vlen; ++v) { - // Get a pointer to the object pointer. - char** r = (char**) pp[ndx]; - // Write the object to the buffer. - cl->Streamer(r[v], buf); - } // v - } // isPtrPtr - } // ndx - } // vlen - //} // k - } // fileVersion - // Backpatch the byte count into the buffer. - buf.SetByteCount(pos, kTRUE); + inline Int_t WriteTObject(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Read in a TObject object part. + // Idea: We could separate the TObject Streamer in its two parts and + // avoid the if (buf.IsReading()). + ((TObject*)(((char*)addr)+config->fOffset))->TObject::Streamer(buf); return 0; } - - /** Direct copy of code from TStreamerInfo::WriteBufferAux, - * potentially can be used later for non-text streaming */ - template - INLINE_TEMPLATE_ARGS Int_t ReadStreamerLoop(TBuffer &buf, void *addr, const TConfiguration *config) + INLINE_TEMPLATE_ARGS Int_t ReadTNamed(TBuffer &buf, void *addr, const TConfiguration *config) { - UInt_t eoffset = 0; // extra parameter of TStreamerInfo::WriteBufferAux, 0 for all kind of objects writing - UInt_t ioffset = eoffset + config->fOffset; + // Read in a TNamed object part. + // Since the TNamed streamer is solely delegating back to the StreamerInfo we + // can skip the streamer. - // Get the class of the data member. - TClass* cl = config->fCompInfo->fClass; + // Idea: We could extract the code from ReadClassBuffer and avoid one function + // code. + static const TClass *TNamed_cl = TNamed::Class(); + return buf.ReadClassBuffer(TNamed_cl,(((char*)addr)+config->fOffset)); + } - // Check for a private streamer. - if (!kIsTextT && config->fCompInfo->fStreamer) { - // Get any private streamer which was set for the data member. - TMemberStreamer* pstreamer = config->fCompInfo->fStreamer; - // -- We have a private streamer. - // Read the class version and byte count from the buffer. - UInt_t start = 0; - UInt_t count = 0; - buf.ReadVersion(&start, &count, cl); - // Loop over the entries in the clones array or the STL container. - //for (Int_t k = 0; k < narr; ++k) { - - Int_t* counter = (Int_t*) ((char *) addr /*entry pointer*/ + eoffset /*entry offset*/ + config->fCompInfo->fMethod /*counter offset*/); - // And call the private streamer, passing it the buffer, the object, and the counter. - (*pstreamer)(buf, (char *) addr /*entry pointer*/ + ioffset /*object offset*/, *counter); - - // } // for k - buf.CheckByteCount(start, count, config->fCompInfo->fElem->GetFullName()); - // We are done, next streamer element. - return 0; - } + inline Int_t WriteTNamed(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Read in a TNamed object part. + // Since the TNamed streamer is solely delegating back to the StreamerInfo we + // can skip the streamer. - // Which are we, an array of objects or an array of pointers to objects? - Bool_t isPtrPtr = (strstr(config->fCompInfo->fElem->GetTypeName(), "**") != 0); + // Idea: We could extract the code from ReadClassBuffer and avoid one function + // code. + static const TClass *TNamed_cl = TNamed::Class(); + return buf.WriteClassBuffer(TNamed_cl,(((char*)addr)+config->fOffset)); + } - // By default assume the file version is the newest. - Int_t fileVersion = kMaxInt; - if (!kIsTextT) { - // At this point we do *not* have a private streamer. - // Get the version of the file we are reading from. - TFile* file = (TFile*) buf.GetParent(); - if (file) { - fileVersion = file->GetVersion(); - } - } - // Read the class version and byte count from the buffer. - UInt_t start = 0; - UInt_t count = 0; - buf.ReadVersion(&start, &count, cl); - if (fileVersion > 51508) { - // -- Newer versions allow polymorphic pointers. - // Loop over the entries in the clones array or the STL container. - // for (Int_t k = 0; k < narr; ++k) { - // Get the counter for the varying length array. - Int_t vlen = *((Int_t *)((char *)addr /*entry pointer*/ + eoffset /*entry offset*/ + - config->fCompInfo->fMethod /*counter offset*/)); - // Int_t realLen; - // b >> realLen; - // if (realLen != vlen) { - // fprintf(stderr, "read vlen: %d realLen: %s\n", vlen, realLen); - //} - // Get a pointer to the array of pointers. - char **pp = (char **)((char *)addr /*entry pointer*/ + ioffset /*object offset*/); - // Loop over each element of the array of pointers to varying-length arrays. - // if (!pp) { - // continue; - // } - - if (pp) // SL: place it here instead of continue, which is related to for(k) loop - for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { - // if (!pp[ndx]) { - // -- We do not have a pointer to a varying-length array. - // Error("ReadBuffer", "The pointer to element %s::%s type %d (%s) is null\n", thisVar->GetName(), - // aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); - // continue; - //} - // Delete any memory at pp[ndx]. - if (!isPtrPtr) { - cl->DeleteArray(pp[ndx]); - pp[ndx] = 0; - } else { - // Using vlen is wrong here because it has already - // been overwritten with the value needed to read - // the current record. Fixing this will require - // doing a pass over the object at the beginning - // of the I/O and releasing all the buffer memory - // for varying length arrays before we overwrite - // the counter values. - // - // For now we will just leak memory, just as we - // have always done in the past. Fix this. - // - // char** r = (char**) pp[ndx]; - // if (r) { - // for (Int_t v = 0; v < vlen; ++v) { - // cl->Destructor(r[v]); - // r[v] = 0; - // } - //} - delete[] pp[ndx]; - pp[ndx] = 0; - } - if (!vlen) { - if (kIsTextT) { - // special handling for the text-based streamers - keep calling to shift array index - buf.ReadFastArray((void *)nullptr, cl, -1, nullptr); - } - continue; - } - // Note: We now have pp[ndx] is null. - // Allocate memory to read into. - if (!isPtrPtr) { - // -- We are a varying-length array of objects. - // Note: Polymorphism is not allowed here. - // Allocate a new array of objects to read into. - pp[ndx] = (char *)cl->NewArray(vlen); - if (!pp[ndx]) { - Error("ReadBuffer", "Memory allocation failed!\n"); - continue; - } - } else { - // -- We are a varying-length array of pointers to objects. - // Note: The object pointers are allowed to be polymorphic. - // Allocate a new array of pointers to objects to read into. - pp[ndx] = (char *)new char *[vlen]; - if (!pp[ndx]) { - Error("ReadBuffer", "Memory allocation failed!\n"); - continue; - } - // And set each pointer to null. - memset(pp[ndx], 0, vlen * sizeof(char *)); // This is the right size we really have a char**: pp[ndx] - // = (char*) new char*[vlen]; - } - if (!isPtrPtr) { - // -- We are a varying-length array of objects. - buf.ReadFastArray(pp[ndx], cl, vlen, nullptr); - } else { - // -- We are a varying-length array of object pointers. - buf.ReadFastArray((void **)pp[ndx], cl, vlen, kFALSE, nullptr); - } // isPtrPtr - } // ndx - // } // k - } else { - // -- Older versions do *not* allow polymorphic pointers. - // Loop over the entries in the clones array or the STL container. - // for (Int_t k = 0; k < narr; ++k) { - // Get the counter for the varying length array. - Int_t vlen = *((Int_t *)((char *)addr /*entry pointer*/ + eoffset /*entry offset*/ + - config->fCompInfo->fMethod /*counter offset*/)); - // Int_t realLen; - // b >> realLen; - // if (realLen != vlen) { - // fprintf(stderr, "read vlen: %d realLen: %s\n", vlen, realLen); - //} - // Get a pointer to the array of pointers. - char **pp = (char **)((char *)addr /*entry pointer*/ + ioffset /*object offset*/); - // if (!pp) { - // continue; - //} - - if (pp) // SL: place it here instead of continue, which is related to for(k) loop - - // Loop over each element of the array of pointers to varying-length arrays. - for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { - // if (!pp[ndx]) { - // -- We do not have a pointer to a varying-length array. - // Error("ReadBuffer", "The pointer to element %s::%s type %d (%s) is null\n", thisVar->GetName(), - // aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); - // continue; - //} - // Delete any memory at pp[ndx]. - if (!isPtrPtr) { - cl->DeleteArray(pp[ndx]); - pp[ndx] = 0; - } else { - // Using vlen is wrong here because it has already - // been overwritten with the value needed to read - // the current record. Fixing this will require - // doing a pass over the object at the beginning - // of the I/O and releasing all the buffer memory - // for varying length arrays before we overwrite - // the counter values. - // - // For now we will just leak memory, just as we - // have always done in the past. Fix this. - // - // char** r = (char**) pp[ndx]; - // if (r) { - // for (Int_t v = 0; v < vlen; ++v) { - // cl->Destructor(r[v]); - // r[v] = 0; - // } - //} - delete[] pp[ndx]; - pp[ndx] = 0; - } - if (!vlen) { - continue; - } - // Note: We now have pp[ndx] is null. - // Allocate memory to read into. - if (!isPtrPtr) { - // -- We are a varying-length array of objects. - // Note: Polymorphism is not allowed here. - // Allocate a new array of objects to read into. - pp[ndx] = (char *)cl->NewArray(vlen); - if (!pp[ndx]) { - Error("ReadBuffer", "Memory allocation failed!\n"); - continue; - } - } else { - // -- We are a varying-length array of pointers to objects. - // Note: The object pointers are allowed to be polymorphic. - // Allocate a new array of pointers to objects to read into. - pp[ndx] = (char *)new char *[vlen]; - if (!pp[ndx]) { - Error("ReadBuffer", "Memory allocation failed!\n"); - continue; - } - // And set each pointer to null. - memset(pp[ndx], 0, vlen * sizeof(char *)); // This is the right size we really have a char**: pp[ndx] - // = (char*) new char*[vlen]; - } - if (!isPtrPtr) { - // -- We are a varying-length array of objects. - // Loop over the elements of the varying length array. - for (Int_t v = 0; v < vlen; ++v) { - // Read the object from the buffer. - cl->Streamer(pp[ndx] + (v * cl->Size()), buf); - } // v - } else { - // -- We are a varying-length array of object pointers. - // Get a pointer to the object pointer array. - char **r = (char **)pp[ndx]; - // Loop over the elements of the varying length array. - for (Int_t v = 0; v < vlen; ++v) { - // Allocate an object to read into. - r[v] = (char *)cl->New(); - if (!r[v]) { - // Do not print a second error message here. - // Error("ReadBuffer", "Memory allocation failed!\n"); - continue; - } - // Read the object from the buffer. - cl->Streamer(r[v], buf); - } // v - } // isPtrPtr - } // ndx - // } // k - } // fileVersion - buf.CheckByteCount(start, count, config->fCompInfo->fElem->GetFullName()); - return 0; - } - - class TConfWithFactor : public TConfiguration { - // Configuration object for the Float16/Double32 where a factor has been specified. - public: - Double_t fFactor; - Double_t fXmin; - TConfWithFactor(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, Double_t factor, Double_t xmin) : TConfiguration(info,id,compinfo,offset),fFactor(factor),fXmin(xmin) {}; - TConfiguration *Copy() override { return new TConfWithFactor(*this); } - }; - - template - INLINE_TEMPLATE_ARGS Int_t ReadBasicType_WithFactor(TBuffer &buf, void *addr, const TConfiguration *config) - { - // Stream a Float16 or Double32 where a factor has been specified. - //a range was specified. We read an integer and convert it back to a double. - - TConfWithFactor *conf = (TConfWithFactor *)config; - buf.ReadWithFactor((T*)( ((char*)addr) + config->fOffset ), conf->fFactor, conf->fXmin); - return 0; - } - - class TConfNoFactor : public TConfiguration { - // Configuration object for the Float16/Double32 where a factor has been specified. - public: - Int_t fNbits; - TConfNoFactor(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, Int_t nbits) : TConfiguration(info,id,compinfo,offset),fNbits(nbits) {}; - TConfiguration *Copy() override { return new TConfNoFactor(*this); } - }; - - template - INLINE_TEMPLATE_ARGS Int_t ReadBasicType_NoFactor(TBuffer &buf, void *addr, const TConfiguration *config) - { - // Stream a Float16 or Double32 where a factor has not been specified. - - TConfNoFactor *conf = (TConfNoFactor *)config; - Int_t nbits = conf->fNbits; - - buf.ReadWithNbits( (T*)( ((char*)addr) + config->fOffset ), nbits ); - return 0; - } - - INLINE_TEMPLATE_ARGS Int_t ReadTString(TBuffer &buf, void *addr, const TConfiguration *config) - { - // Read in a TString object. - - // Idea: We could separate the TString Streamer in its two parts and - // avoid the if (buf.IsReading()) and try having it inlined. - ((TString*)(((char*)addr)+config->fOffset))->TString::Streamer(buf); - return 0; - } - - INLINE_TEMPLATE_ARGS Int_t ReadTObject(TBuffer &buf, void *addr, const TConfiguration *config) - { - // Read in a TObject object part. - - // Idea: We could separate the TObject Streamer in its two parts and - // avoid the if (buf.IsReading()). - ((TObject*)(((char*)addr)+config->fOffset))->TObject::Streamer(buf); - return 0; - } - - INLINE_TEMPLATE_ARGS Int_t ReadTNamed(TBuffer &buf, void *addr, const TConfiguration *config) - { - // Read in a TNamed object part. - // Since the TNamed streamer is solely delegating back to the StreamerInfo we - // can skip the streamer. - - // Idea: We could extract the code from ReadClassBuffer and avoid one function - // code. - static const TClass *TNamed_cl = TNamed::Class(); - return buf.ReadClassBuffer(TNamed_cl,(((char*)addr)+config->fOffset)); - } - - class TConfigSTL : public TConfiguration { - // Configuration object for the kSTL case - private: - void Init() { - TVirtualCollectionProxy *proxy = fNewClass->GetCollectionProxy(); - if (proxy) { - fCreateIterators = proxy->GetFunctionCreateIterators(); - fCreateWriteIterators = proxy->GetFunctionCreateIterators(kFALSE); - fCopyIterator = proxy->GetFunctionCopyIterator(); - fDeleteIterator = proxy->GetFunctionDeleteIterator(); - fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(); + class TConfigSTL : public TConfiguration { + // Configuration object for the kSTL case + private: + void Init(bool read) { + TVirtualCollectionProxy *proxy = fNewClass->GetCollectionProxy(); + if (proxy) { + fCreateIterators = proxy->GetFunctionCreateIterators(read); + fCopyIterator = proxy->GetFunctionCopyIterator(); + fDeleteIterator = proxy->GetFunctionDeleteIterator(); + fDeleteTwoIterators = proxy->GetFunctionDeleteTwoIterators(); + if (proxy->HasPointers()) { + fNext = TVirtualCollectionPtrIterators::Next; + } else { + fNext = proxy->GetFunctionNext(read); + } } } @@ -981,26 +630,27 @@ namespace TStreamerInfoActions Bool_t fIsSTLBase; // aElement->IsBase() && aElement->IsA()!=TStreamerBase::Class() TVirtualCollectionProxy::CreateIterators_t fCreateIterators; - TVirtualCollectionProxy::CreateIterators_t fCreateWriteIterators; TVirtualCollectionProxy::CopyIterator_t fCopyIterator; TVirtualCollectionProxy::DeleteIterator_t fDeleteIterator; TVirtualCollectionProxy::DeleteTwoIterators_t fDeleteTwoIterators; + TVirtualCollectionProxy::Next_t fNext; + - TConfigSTL(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, const char *type_name, Bool_t isbase) : + TConfigSTL(Bool_t read, TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, const char *type_name, Bool_t isbase) : TConfiguration(info,id,compinfo,offset,length), fOldClass(oldClass), fNewClass(oldClass), fStreamer(0), fTypeName(type_name), fIsSTLBase(isbase), - fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(); } + fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(read); } - TConfigSTL(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, TClass *newClass, const char *type_name, Bool_t isbase) : + TConfigSTL(Bool_t read, TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, TClass *newClass, const char *type_name, Bool_t isbase) : TConfiguration(info,id,compinfo,offset,length), fOldClass(oldClass), fNewClass(newClass), fStreamer(0), fTypeName(type_name), fIsSTLBase(isbase), - fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(); } + fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(read); } - TConfigSTL(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, TMemberStreamer* streamer, const char *type_name, Bool_t isbase) : + TConfigSTL(Bool_t read, TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, TMemberStreamer* streamer, const char *type_name, Bool_t isbase) : TConfiguration(info,id,compinfo,offset,length), fOldClass(oldClass), fNewClass(oldClass), fStreamer(streamer), fTypeName(type_name), fIsSTLBase(isbase), - fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(); } + fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(read); } - TConfigSTL(TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, TClass *newClass, TMemberStreamer* streamer, const char *type_name, Bool_t isbase) : + TConfigSTL(Bool_t read, TVirtualStreamerInfo *info, UInt_t id, TCompInfo_t *compinfo, Int_t offset, UInt_t length, TClass *oldClass, TClass *newClass, TMemberStreamer* streamer, const char *type_name, Bool_t isbase) : TConfiguration(info,id,compinfo,offset,length), fOldClass(oldClass), fNewClass(newClass), fStreamer(streamer), fTypeName(type_name), fIsSTLBase(isbase), - fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(); } + fCreateIterators(0), fCopyIterator(0), fDeleteIterator(0), fDeleteTwoIterators(0) { Init(read); } TConfiguration *Copy() override { return new TConfigSTL(*this); } }; @@ -1010,7 +660,7 @@ namespace TStreamerInfoActions public: Double_t fFactor; Double_t fXmin; - TConfSTLWithFactor(TConfigSTL *orig, Double_t factor, Double_t xmin) : TConfigSTL(*orig),fFactor(factor),fXmin(xmin) {}; + TConfSTLWithFactor(TConfigSTL *orig, Double_t factor, Double_t xmin) : TConfigSTL(*orig), fFactor(factor), fXmin(xmin) {}; TConfiguration *Copy() override { return new TConfSTLWithFactor(*this); } }; @@ -1181,46 +831,303 @@ namespace TStreamerInfoActions subinfo->ReadBufferSTL(buf, oldProxy, nobjects, /* offset */ 0, /* v7 */ kFALSE); } - oldProxy->Commit(env); + oldProxy->Commit(env); + } + } + + INLINE_TEMPLATE_ARGS void ReadArraySTLMemberWiseSameClass(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers) + { + // Collection was saved member-wise + + TConfigSTL *config = (TConfigSTL*)conf; + vers &= ~( TBufferFile::kStreamedMemberWise ); + + if( vers >= 8 ) { + + TClass *oldClass = config->fOldClass; + + TVirtualCollectionProxy *oldProxy = oldClass ? oldClass->GetCollectionProxy() : nullptr; + if (!oldProxy) { + // Missing information, broken file ... give up + return; + } + TClass *valueClass = oldProxy->GetValueClass(); + Version_t vClVersion = buf.ReadVersionForMemberWise( valueClass ); + + TActionSequence *actions = oldProxy->GetReadMemberWiseActions( vClVersion ); + + int objectSize = oldClass->Size(); + char *obj = (char*)addr; + char *endobj = obj + conf->fLength*objectSize; + + for(; objAllocate(nobjects,true); + if (nobjects) { + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin = &(startbuf[0]); + void *end = &(endbuf[0]); + config->fCreateIterators(alternative, &begin, &end, oldProxy); + // We can not get here with a split vector of pointer, so we can indeed assume + // that actions->fConfiguration != null. + buf.ApplySequence(*actions, begin, end); + if (begin != &(startbuf[0])) { + // assert(end != endbuf); + config->fDeleteTwoIterators(begin,end); + } + } + oldProxy->Commit(alternative); + } + + } else { + + TClass *oldClass = config->fOldClass; + + TVirtualCollectionProxy *oldProxy = oldClass ? oldClass->GetCollectionProxy() : nullptr; + if (!oldProxy) { + // Missing information, broken file ... give up + return; + } + + int objectSize = oldClass->Size(); + char *obj = (char*)addr; + char *endobj = obj + conf->fLength*objectSize; + + for(; objAllocate(nobjects,true); + + if (nobjects || vers < 7 ) { + // coverity[dereference] since this is a member streaming action by definition the collection contains objects. + TStreamerInfo *subinfo = (TStreamerInfo*)oldProxy->GetValueClass()->GetStreamerInfo( 0 ); + + subinfo->ReadBufferSTL(buf, oldProxy, nobjects, /* offset */ 0, /* v7 */ kFALSE); + } + oldProxy->Commit(env); + } + } + } + + INLINE_TEMPLATE_ARGS void ReadSTLMemberWiseChangedClass(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers) + { + // Collection was saved member-wise + + TConfigSTL *config = (TConfigSTL*)conf; + + vers &= ~( TBufferFile::kStreamedMemberWise ); + + TClass *newClass = config->fNewClass; + TClass *oldClass = config->fOldClass; + + if( vers < 8 ) { + Error( "ReadSTLMemberWiseChangedClass", "Unfortunately, version %d of TStreamerInfo (used in %s) did not record enough information to convert a %s into a %s.", + vers, buf.GetParent() ? buf.GetParent()->GetName() : "memory/socket", oldClass ? oldClass->GetName() : "(could not find the origin TClass)", newClass ? newClass->GetName() : "(could not find the destination TClass)" ); + } else if (newClass && oldClass){ + + Version_t vClVersion = buf.ReadVersionForMemberWise( oldClass->GetCollectionProxy()->GetValueClass() ); + + TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); + TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy(); + + TVirtualCollectionProxy::TPushPop helper( newProxy, (char*)addr ); + Int_t nobjects; + buf.ReadInt(nobjects); + void* alternative = newProxy->Allocate(nobjects,true); + if (nobjects) { + TActionSequence *actions = newProxy->GetConversionReadMemberWiseActions( oldProxy->GetValueClass(), vClVersion ); + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin = &(startbuf[0]); + void *end = &(endbuf[0]); + config->fCreateIterators( alternative, &begin, &end, newProxy); + // We can not get here with a split vector of pointer, so we can indeed assume + // that actions->fConfiguration != null. + buf.ApplySequence(*actions, begin, end); + if (begin != &(startbuf[0])) { + // assert(end != endbuf); + config->fDeleteTwoIterators(begin,end); + } + } + newProxy->Commit(alternative); + } + } + + INLINE_TEMPLATE_ARGS void ReadArraySTLMemberWiseChangedClass(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers) + { + // Collection was saved member-wise + + TConfigSTL *config = (TConfigSTL*)conf; + + vers &= ~( TBufferFile::kStreamedMemberWise ); + + TClass *newClass = config->fNewClass; + TClass *oldClass = config->fOldClass; + + if( vers < 8 ) { + Error( "ReadSTLMemberWiseChangedClass", "Unfortunately, version %d of TStreamerInfo (used in %s) did not record enough information to convert a %s into a %s.", + vers, buf.GetParent() ? buf.GetParent()->GetName() : "memory/socket", oldClass ? oldClass->GetName() : "(could not find the origin TClass)", newClass ? newClass->GetName() : "(could not find the destination TClass)" ); + } else if (newClass && oldClass) { + + Version_t vClVersion = buf.ReadVersionForMemberWise( oldClass->GetCollectionProxy()->GetValueClass() ); + + TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); + TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy(); + + int objectSize = newClass->Size(); + char *obj = (char*)addr; + char *endobj = obj + conf->fLength*objectSize; + + for(; objAllocate(nobjects,true); + if (nobjects) { + TActionSequence *actions = newProxy->GetConversionReadMemberWiseActions( oldProxy->GetValueClass(), vClVersion ); + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin = &(startbuf[0]); + void *end = &(endbuf[0]); + config->fCreateIterators( alternative, &begin, &end, newProxy); + // We can not get here with a split vector of pointer, so we can indeed assume + // that actions->fConfiguration != null. + buf.ApplySequence(*actions, begin, end); + if (begin != &(startbuf[0])) { + // assert(end != endbuf); + config->fDeleteTwoIterators(begin,end); + } + } + newProxy->Commit(alternative); + } + } + } + + INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseFastArray(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t /* vers */, UInt_t /* start */) + { + TConfigSTL *config = (TConfigSTL*)conf; + // Idea: This needs to be unrolled, it currently calls the TGenCollectionStreamer .... + buf.ReadFastArray(addr,config->fNewClass,conf->fLength,(TMemberStreamer*)0,config->fOldClass); + } + INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseStreamer(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t /* vers */, UInt_t /* start */) + { + TConfigSTL *config = (TConfigSTL*)conf; + (*config->fStreamer)(buf,addr,conf->fLength); + } + INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseFastArrayV2(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers, UInt_t start) + { + // case of old TStreamerInfo + + TConfigSTL *config = (TConfigSTL*)conf; + // Backward compatibility. Some TStreamerElement's where without + // Streamer but were not removed from element list + if (config->fIsSTLBase || vers == 0) { + buf.SetBufferOffset(start); //there is no byte count + } + // Idea: This needs to be unrolled, it currently calls the TGenCollectionStreamer .... + buf.ReadFastArray(addr,config->fNewClass,conf->fLength,(TMemberStreamer*)0,config->fOldClass); + } + INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseStreamerV2(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers, UInt_t start) + { + // case of old TStreamerInfo + + TConfigSTL *config = (TConfigSTL*)conf; + // Backward compatibility. Some TStreamerElement's where without + // Streamer but were not removed from element list + if (config->fIsSTLBase || vers == 0) { + buf.SetBufferOffset(start); //there is no byte count + } + (*config->fStreamer)(buf,addr,conf->fLength); + } + + // To Upgrade this to WriteSTLMemberWiseChangedClass (see ReadSTLMemberWiseChangedClass) + // we would need to a TVirtualCollectionProxy::GetConversionWriteMemberWiseActions + INLINE_TEMPLATE_ARGS void WriteSTLMemberWise(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // Collection was saved member-wise + // newClass -> in memory representation + // oldClass -> onfile representation + + TConfigSTL *config = (TConfigSTL*)conf; + TClass *newClass = config->fNewClass; + TClass *onfileClass = config->fOldClass; + + // Until the writing of TVirtualCollectionProxy::GetConversionWriteMemberWiseActions + assert(newClass && onfileClass && newClass == onfileClass && + "Class level transformation while writing is not yet supported"); + + if (newClass && onfileClass) { + + buf.WriteVersion( onfileClass->GetCollectionProxy()->GetValueClass(), kFALSE ); + + TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); + TVirtualCollectionProxy *onfileProxy = onfileClass->GetCollectionProxy(); + + TVirtualCollectionProxy::TPushPop helper( newProxy, (char*)addr ); + Int_t nobjects = newProxy->Size(); + buf.WriteInt(nobjects); + if (nobjects) { + TActionSequence *actions = onfileProxy->GetWriteMemberWiseActions(); + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin = &(startbuf[0]); + void *end = &(endbuf[0]); + config->fCreateIterators( addr, &begin, &end, newProxy); + // We can not get here with a split vector of pointer, so we can indeed assume + // that actions->fConfiguration != null. + buf.ApplySequence(*actions, begin, end); + if (begin != &(startbuf[0])) { + // assert(end != endbuf); + config->fDeleteTwoIterators(begin,end); + } + } } } - INLINE_TEMPLATE_ARGS void ReadArraySTLMemberWiseSameClass(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers) + // This routine could/should be merge with WriteSTLMemberWise, the only difference + // is the for loop over the objects. We are not using this version for the case + // where `conf->fLength is 1` as we know this information at StreamerInfo build time + // and thus we can avoid the 'waste' of CPU cycles involved in doing a loop of length 1 + INLINE_TEMPLATE_ARGS void WriteArraySTLMemberWise(TBuffer &buf, void *addr, const TConfiguration *conf) { // Collection was saved member-wise + // newClass -> in memory representation + // oldClass -> onfile representation TConfigSTL *config = (TConfigSTL*)conf; - vers &= ~( TBufferFile::kStreamedMemberWise ); + TClass *newClass = config->fNewClass; + TClass *onfileClass = config->fOldClass; - if( vers >= 8 ) { + // Until the writing of TVirtualCollectionProxy::GetConversionWriteMemberWiseActions + assert(newClass && onfileClass && newClass == onfileClass && + "Class level transformation while writing is not yet supported"); - TClass *oldClass = config->fOldClass; + if (newClass && onfileClass) { - TVirtualCollectionProxy *oldProxy = oldClass ? oldClass->GetCollectionProxy() : nullptr; - if (!oldProxy) { - // Missing information, broken file ... give up - return; - } - TClass *valueClass = oldProxy->GetValueClass(); - Version_t vClVersion = buf.ReadVersionForMemberWise( valueClass ); + buf.WriteVersion( onfileClass->GetCollectionProxy()->GetValueClass(), kFALSE ); - TActionSequence *actions = oldProxy->GetReadMemberWiseActions( vClVersion ); + TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); + TVirtualCollectionProxy *onfileProxy = onfileClass->GetCollectionProxy(); - int objectSize = oldClass->Size(); + int objectSize = newClass->Size(); char *obj = (char*)addr; - char *endobj = obj + conf->fLength*objectSize; + char *endobj = obj + conf->fLength * objectSize; - for(; objAllocate(nobjects,true); + for (; obj < endobj; obj += objectSize) { + TVirtualCollectionProxy::TPushPop helper( newProxy, (char*)obj ); + Int_t nobjects = newProxy->Size(); + buf.WriteInt(nobjects); if (nobjects) { + TActionSequence *actions = onfileProxy->GetWriteMemberWiseActions(); char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; void *begin = &(startbuf[0]); void *end = &(endbuf[0]); - config->fCreateIterators(alternative, &begin, &end, oldProxy); + config->fCreateIterators( addr, &begin, &end, newProxy); // We can not get here with a split vector of pointer, so we can indeed assume // that actions->fConfiguration != null. buf.ApplySequence(*actions, begin, end); @@ -1229,494 +1136,873 @@ namespace TStreamerInfoActions config->fDeleteTwoIterators(begin,end); } } - oldProxy->Commit(alternative); } + } + } + + INLINE_TEMPLATE_ARGS void WriteSTLObjectWiseFastArray(TBuffer &buf, void *addr, const TConfiguration *conf) + { + TConfigSTL *config = (TConfigSTL*)conf; + // Idea: This needs to be unrolled, it currently calls the TGenCollectionStreamer .... + buf.WriteFastArray(addr,config->fNewClass,conf->fLength,(TMemberStreamer*)0); + } + INLINE_TEMPLATE_ARGS void WriteSTLObjectWiseStreamer(TBuffer &buf, void *addr, const TConfiguration *conf) + { + TConfigSTL *config = (TConfigSTL*)conf; + (*config->fStreamer)(buf,addr,conf->fLength); + } + template + INLINE_TEMPLATE_ARGS Int_t ReadSTL(TBuffer &buf, void *addr, const TConfiguration *conf) + { + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start, count; + Version_t vers = buf.ReadVersion(&start, &count, config->fOldClass); + if ( vers & TBufferFile::kStreamedMemberWise ) { + memberwise(buf,((char*)addr)+config->fOffset,config, vers); } else { + objectwise(buf,((char*)addr)+config->fOffset,config, vers, start); + } + buf.CheckByteCount(start,count,config->fTypeName); + return 0; + } - TClass *oldClass = config->fOldClass; + template + INLINE_TEMPLATE_ARGS Int_t WriteSTL(TBuffer &buf, void *addr, const TConfiguration *conf) + { + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start; + TClass *onfileClass = config->fOldClass; + TStreamerElement *aElement = config->fCompInfo->fElem; + TVirtualCollectionProxy *proxy = onfileClass->GetCollectionProxy(); + TClass* vClass = proxy ? proxy->GetValueClass() : 0; - TVirtualCollectionProxy *oldProxy = oldClass ? oldClass->GetCollectionProxy() : nullptr; - if (!oldProxy) { - // Missing information, broken file ... give up - return; + // See test in TStreamerInfo::kSTL case in TStreamerInfoWriteBuffer.cxx + if (!buf.TestBit(TBuffer::kCannotHandleMemberWiseStreaming) + && proxy && vClass + && config->fInfo->GetStreamMemberWise() && onfileClass->CanSplit() + && !(strspn(aElement->GetTitle(),"||") == 2) + && !(vClass->HasCustomStreamerMember()) ) + { + start = buf.WriteVersionMemberWise(config->fInfo->IsA(), kTRUE); + memberwise(buf, ((char *)addr) + config->fOffset, config); + } else { + start = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + objectwise(buf, ((char *)addr) + config->fOffset, config); + } + buf.SetByteCount(start); + return 0; + } + + template + struct ConvertBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory. + From temp; + buf >> temp; + *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; + return 0; + } + }; + + template + struct ConvertBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory + UInt_t temp; + buf >> temp; + + if ((temp & kIsReferenced) != 0) { + HandleReferencedTObject(buf,addr,config); } - int objectSize = oldClass->Size(); - char *obj = (char*)addr; - char *endobj = obj + conf->fLength*objectSize; + *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; + return 0; + } + }; - for(; objAllocate(nobjects,true); + template + struct ConvertBasicType,To> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory. + TConfWithFactor *conf = (TConfWithFactor *)config; + From temp; + buf.ReadWithFactor(&temp, conf->fFactor, conf->fXmin); + *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; + return 0; + } + }; - if (nobjects || vers < 7 ) { - // coverity[dereference] since this is a member streaming action by definition the collection contains objects. - TStreamerInfo *subinfo = (TStreamerInfo*)oldProxy->GetValueClass()->GetStreamerInfo( 0 ); + template + struct ConvertBasicType,To> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory. + TConfNoFactor *conf = (TConfNoFactor *)config; + From temp; + buf.ReadWithNbits(&temp, conf->fNbits); + *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; + return 0; + } + }; + + class TConfigurationPushDataCache : public TConfiguration { + // Configuration object for the PushDataCache case. + public: + TVirtualArray *fOnfileObject; + + TConfigurationPushDataCache(TVirtualStreamerInfo *info, TVirtualArray *onfileObject, Int_t offset) : + TConfiguration(info, -1, nullptr, offset), fOnfileObject(onfileObject) + {} + + void Print() const override + { + TStreamerInfo *info = (TStreamerInfo*)fInfo; + if (fOnfileObject) + printf("StreamerInfoAction, class:%s, PushDataCache offset=%d\n", + info->GetClass()->GetName(), fOffset); + else + printf("StreamerInfoAction, class:%s, PopDataCache offset=%d\n", + info->GetClass()->GetName(), fOffset); + } + void PrintDebug(TBuffer &buffer, void *object) const override + { + if (gDebug > 1) { + TStreamerInfo *info = (TStreamerInfo*)fInfo; + printf("StreamerInfoAction, class:%s, %sDataCache, bufpos=%d, arr=%p, offset=%d, onfileObject=%p\n", + info->GetClass()->GetName(), fOnfileObject ? "Push" : "Pop", buffer.Length(), object, fOffset, fOnfileObject); - subinfo->ReadBufferSTL(buf, oldProxy, nobjects, /* offset */ 0, /* v7 */ kFALSE); - } - oldProxy->Commit(env); } } + }; + + Int_t PushDataCache(TBuffer &b, void *, const TConfiguration *conf) + { + TConfigurationPushDataCache *config = (TConfigurationPushDataCache*)conf; + auto onfileObject = config->fOnfileObject; + + // onfileObject->SetSize(1); + b.PushDataCache( onfileObject ); + + return 0; } - INLINE_TEMPLATE_ARGS void ReadSTLMemberWiseChangedClass(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers) + Int_t PushDataCacheVectorPtr(TBuffer &b, void *, const void *, const TConfiguration *conf) { - // Collection was saved member-wise + TConfigurationPushDataCache *config = (TConfigurationPushDataCache*)conf; + auto onfileObject = config->fOnfileObject; - TConfigSTL *config = (TConfigSTL*)conf; + // onfileObject->SetSize(n); + b.PushDataCache( onfileObject ); - vers &= ~( TBufferFile::kStreamedMemberWise ); + return 0; + } - TClass *newClass = config->fNewClass; - TClass *oldClass = config->fOldClass; + Int_t PushDataCacheGenericCollection(TBuffer &b, void *, const void *, const TLoopConfiguration *loopconfig, const TConfiguration *conf) + { + TConfigurationPushDataCache *config = (TConfigurationPushDataCache*)conf; + auto onfileObject = config->fOnfileObject; - if( vers < 8 ) { - Error( "ReadSTLMemberWiseChangedClass", "Unfortunately, version %d of TStreamerInfo (used in %s) did not record enough information to convert a %s into a %s.", - vers, buf.GetParent() ? buf.GetParent()->GetName() : "memory/socket", oldClass ? oldClass->GetName() : "(could not find the origin TClass)", newClass ? newClass->GetName() : "(could not find the destination TClass)" ); - } else if (newClass && oldClass){ + TVirtualCollectionProxy *proxy = ((TGenericLoopConfig*)loopconfig)->fProxy; + UInt_t n = proxy->Size(); - Version_t vClVersion = buf.ReadVersionForMemberWise( oldClass->GetCollectionProxy()->GetValueClass() ); + onfileObject->SetSize(n); + b.PushDataCache( onfileObject ); - TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); - TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy(); + return 0; + } - TVirtualCollectionProxy::TPushPop helper( newProxy, (char*)addr ); - Int_t nobjects; - buf.ReadInt(nobjects); - void* alternative = newProxy->Allocate(nobjects,true); - if (nobjects) { - TActionSequence *actions = newProxy->GetConversionReadMemberWiseActions( oldProxy->GetValueClass(), vClVersion ); - char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; - char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; - void *begin = &(startbuf[0]); - void *end = &(endbuf[0]); - config->fCreateIterators( alternative, &begin, &end, newProxy); - // We can not get here with a split vector of pointer, so we can indeed assume - // that actions->fConfiguration != null. - buf.ApplySequence(*actions, begin, end); - if (begin != &(startbuf[0])) { - // assert(end != endbuf); - config->fDeleteTwoIterators(begin,end); - } + Int_t PopDataCache(TBuffer &b, void *, const TConfiguration *) + { + b.PopDataCache(); + return 0; + } + + Int_t PopDataCacheVectorPtr(TBuffer &b, void *, const void *, const TConfiguration *) + { + b.PopDataCache(); + return 0; + } + + Int_t PopDataCacheGenericCollection(TBuffer &b, void *, const void *, const TLoopConfiguration *, const TConfiguration *) + { + b.PopDataCache(); + return 0; + } + + class TConfigurationUseCache : public TConfiguration { + // Configuration object for the UseCache case. + public: + TConfiguredAction fAction; + Bool_t fNeedRepeat; + + TConfigurationUseCache(TVirtualStreamerInfo *info, TConfiguredAction &action, Bool_t repeat) : + TConfiguration(info,action.fConfiguration->fElemId,action.fConfiguration->fCompInfo,action.fConfiguration->fOffset),fAction(action),fNeedRepeat(repeat) {}; + void PrintDebug(TBuffer &b, void *addr) const override + { + if (gDebug > 1) { + // Idea: We should print the name of the action function. + TStreamerInfo *info = (TStreamerInfo*)fInfo; + TStreamerElement *aElement = fCompInfo->fElem; + fprintf(stdout,"StreamerInfoAction, class:%s, name=%s, fType[%d]=%d," + " %s, bufpos=%d, arr=%p, eoffset=%d, Redirect=%p\n", + info->GetClass()->GetName(),aElement->GetName(),fElemId,fCompInfo->fType, + aElement->ClassName(),b.Length(),addr, 0,b.PeekDataCache() ? b.PeekDataCache()->GetObjectAt(0) : 0); } - newProxy->Commit(alternative); + } - } + ~TConfigurationUseCache() override {} + TConfiguration *Copy() override + { + TConfigurationUseCache *copy = new TConfigurationUseCache(*this); + fAction.fConfiguration = copy->fAction.fConfiguration->Copy(); // since the previous allocation did a 'move' of fAction we need to fix it. + return copy; + } + }; - INLINE_TEMPLATE_ARGS void ReadArraySTLMemberWiseChangedClass(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers) + INLINE_TEMPLATE_ARGS Int_t UseCache(TBuffer &b, void *addr, const TConfiguration *conf) { - // Collection was saved member-wise + TConfigurationUseCache *config = (TConfigurationUseCache*)conf; - TConfigSTL *config = (TConfigSTL*)conf; + Int_t bufpos = b.Length(); + TVirtualArray *cached = b.PeekDataCache(); + if (cached==0) { + TStreamerElement *aElement = conf->fCompInfo->fElem; + TStreamerInfo *info = (TStreamerInfo*)conf->fInfo; + Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); + char *ptr = (char*)addr; + info->ReadBufferSkip(b,&ptr,config->fCompInfo,config->fCompInfo->fType+TStreamerInfo::kSkip,aElement,1,0); + } else { + config->fAction(b, (*cached)[0]); + } + // Idea: Factor out this 'if' to a UseCacheRepeat function + if (config->fNeedRepeat) { + b.SetBufferOffset(bufpos); + } + return 0; + } - vers &= ~( TBufferFile::kStreamedMemberWise ); + INLINE_TEMPLATE_ARGS Int_t UseCacheVectorPtrLoop(TBuffer &b, void *start, const void *end, const TConfiguration *conf) + { + TConfigurationUseCache *config = (TConfigurationUseCache*)conf; + Int_t bufpos = b.Length(); - TClass *newClass = config->fNewClass; - TClass *oldClass = config->fOldClass; + TVirtualArray *cached = b.PeekDataCache(); + if (cached==0) { + TStreamerElement *aElement = config->fCompInfo->fElem; + TStreamerInfo *info = (TStreamerInfo*)config->fInfo; + Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); + char *ptr = (char*)start; + UInt_t n = (((void**)end)-((void**)start)); + info->ReadBufferSkip(b,&ptr,config->fCompInfo,conf->fCompInfo->fType+TStreamerInfo::kSkip,aElement,n,0); + } else { + TVectorLoopConfig cached_config( nullptr, cached->fClass->Size(), /* read */ kTRUE ); + void *cached_start = (*cached)[0]; + void *cached_end = ((char*)cached_start) + cached->fSize * cached_config.fIncrement; + config->fAction(b,cached_start,cached_end,&cached_config); + } + // Idea: Factor out this 'if' to a UseCacheRepeat function + if (config->fNeedRepeat) { + b.SetBufferOffset(bufpos); + } + return 0; + } - if( vers < 8 ) { - Error( "ReadSTLMemberWiseChangedClass", "Unfortunately, version %d of TStreamerInfo (used in %s) did not record enough information to convert a %s into a %s.", - vers, buf.GetParent() ? buf.GetParent()->GetName() : "memory/socket", oldClass ? oldClass->GetName() : "(could not find the origin TClass)", newClass ? newClass->GetName() : "(could not find the destination TClass)" ); - } else if (newClass && oldClass) { + INLINE_TEMPLATE_ARGS Int_t UseCacheVectorLoop(TBuffer &b, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *conf) + { + TConfigurationUseCache *config = (TConfigurationUseCache*)conf; - Version_t vClVersion = buf.ReadVersionForMemberWise( oldClass->GetCollectionProxy()->GetValueClass() ); + Int_t bufpos = b.Length(); + TVirtualArray *cached = b.PeekDataCache(); + if (cached==0) { + TStreamerElement *aElement = config->fCompInfo->fElem; + TStreamerInfo *info = (TStreamerInfo*)config->fInfo; + Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); + char *ptr = (char*)start; + UInt_t n = (((char*)end)-((char*)start))/((TVectorLoopConfig*)loopconf)->fIncrement; + info->ReadBufferSkip(b,&ptr,config->fCompInfo,config->fCompInfo->fType+TStreamerInfo::kSkip,aElement,n,0); + } else { + TVectorLoopConfig cached_config( nullptr, cached->fClass->Size(), /* read */ kTRUE ); + void *cached_start = (*cached)[0]; + void *cached_end = ((char*)cached_start) + cached->fSize * cached_config.fIncrement; + config->fAction(b,cached_start,cached_end,&cached_config); + } + // Idea: Factor out this 'if' to a UseCacheRepeat function + if (config->fNeedRepeat) { + b.SetBufferOffset(bufpos); + } + return 0; + } - TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); - TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy(); + INLINE_TEMPLATE_ARGS Int_t UseCacheGenericCollection(TBuffer &b, void *, const void *, const TLoopConfiguration *loopconfig, const TConfiguration *conf) + { + TConfigurationUseCache *config = (TConfigurationUseCache*)conf; - int objectSize = newClass->Size(); - char *obj = (char*)addr; - char *endobj = obj + conf->fLength*objectSize; + Int_t bufpos = b.Length(); + TVirtualArray *cached = b.PeekDataCache(); + if (cached==0) { + TStreamerElement *aElement = config->fCompInfo->fElem; + TStreamerInfo *info = (TStreamerInfo*)config->fInfo; - for(; objAllocate(nobjects,true); - if (nobjects) { - TActionSequence *actions = newProxy->GetConversionReadMemberWiseActions( oldProxy->GetValueClass(), vClVersion ); - char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; - char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; - void *begin = &(startbuf[0]); - void *end = &(endbuf[0]); - config->fCreateIterators( alternative, &begin, &end, newProxy); - // We can not get here with a split vector of pointer, so we can indeed assume - // that actions->fConfiguration != null. - buf.ApplySequence(*actions, begin, end); - if (begin != &(startbuf[0])) { - // assert(end != endbuf); - config->fDeleteTwoIterators(begin,end); - } - } - newProxy->Commit(alternative); - } + TVirtualCollectionProxy *proxy = ((TGenericLoopConfig*)loopconfig)->fProxy; + Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); + UInt_t n = proxy->Size(); + info->ReadBufferSkip(b, *proxy,config->fCompInfo,config->fCompInfo->fType+TStreamerInfo::kSkip,aElement,n,0); + } else { + TVectorLoopConfig cached_config( nullptr, cached->fClass->Size(), /* read */ kTRUE ); + void *cached_start = (*cached)[0]; + void *cached_end = ((char*)cached_start) + cached->fSize * cached_config.fIncrement; + config->fAction(b,cached_start,cached_end,&cached_config); + } + // Idea: Factor out this 'if' to a UseCacheRepeat function + if (config->fNeedRepeat) { + b.SetBufferOffset(bufpos); } + return 0; } - INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseFastArray(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t /* vers */, UInt_t /* start */) + // Support for collections. + + Int_t ReadLoopInvalid(TBuffer &, void *, const void *, const TConfiguration *config) { - TConfigSTL *config = (TConfigSTL*)conf; - // Idea: This needs to be unrolled, it currently calls the TGenCollectionStreamer .... - buf.ReadFastArray(addr,config->fNewClass,conf->fLength,(TMemberStreamer*)0,config->fOldClass); + Fatal("ApplySequence","The sequence of actions to read %s:%d member-wise was not initialized.",config->fInfo->GetName(),config->fInfo->GetClassVersion()); + return 0; } - INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseStreamer(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t /* vers */, UInt_t /* start */) + + Int_t WriteLoopInvalid(TBuffer &, void *, const void *, const TConfiguration *config) { - TConfigSTL *config = (TConfigSTL*)conf; - (*config->fStreamer)(buf,addr,conf->fLength); + Fatal("ApplySequence","The sequence of actions to write %s:%d member-wise was not initialized.",config->fInfo->GetName(),config->fInfo->GetClassVersion()); + return 0; } - INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseFastArrayV2(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers, UInt_t start) - { - // case of old TStreamerInfo - TConfigSTL *config = (TConfigSTL*)conf; - // Backward compatibility. Some TStreamerElement's where without - // Streamer but were not removed from element list - if (config->fIsSTLBase || vers == 0) { - buf.SetBufferOffset(start); //there is no byte count - } - // Idea: This needs to be unrolled, it currently calls the TGenCollectionStreamer .... - buf.ReadFastArray(addr,config->fNewClass,conf->fLength,(TMemberStreamer*)0,config->fOldClass); - } - INLINE_TEMPLATE_ARGS void ReadSTLObjectWiseStreamerV2(TBuffer &buf, void *addr, const TConfiguration *conf, Version_t vers, UInt_t start) - { - // case of old TStreamerInfo + enum ESelectLooper { kVectorLooper, kVectorPtrLooper, kAssociativeLooper, kGenericLooper }; - TConfigSTL *config = (TConfigSTL*)conf; - // Backward compatibility. Some TStreamerElement's where without - // Streamer but were not removed from element list - if (config->fIsSTLBase || vers == 0) { - buf.SetBufferOffset(start); //there is no byte count + ESelectLooper SelectLooper(TVirtualCollectionProxy &proxy) + { + if ( (proxy.GetProperties() & TVirtualCollectionProxy::kIsEmulated) ) { + return kVectorLooper; + } else if ( (proxy.GetCollectionType() == ROOT::kSTLvector)) { + if (proxy.GetProperties() & TVirtualCollectionProxy::kCustomAlloc) + return kGenericLooper; + else + return kVectorLooper; + } else if (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLunorderedset + || proxy.GetCollectionType() == ROOT::kSTLmultiset || proxy.GetCollectionType() == ROOT::kSTLunorderedmultiset + || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap + || proxy.GetCollectionType() == ROOT::kSTLunorderedmap || proxy.GetCollectionType() == ROOT::kSTLunorderedmultimap + || proxy.GetCollectionType() == ROOT::kSTLbitset) { + return kAssociativeLooper; + } else { + return kGenericLooper; } - (*config->fStreamer)(buf,addr,conf->fLength); } - template - INLINE_TEMPLATE_ARGS Int_t ReadSTL(TBuffer &buf, void *addr, const TConfiguration *conf) + ESelectLooper SelectLooper(TVirtualCollectionProxy *proxy) { - TConfigSTL *config = (TConfigSTL*)conf; - UInt_t start, count; - Version_t vers = buf.ReadVersion(&start, &count, config->fOldClass); - if ( vers & TBufferFile::kStreamedMemberWise ) { - memberwise(buf,((char*)addr)+config->fOffset,config, vers); - } else { - objectwise(buf,((char*)addr)+config->fOffset,config, vers, start); - } - buf.CheckByteCount(start,count,config->fTypeName); - return 0; + if (proxy) + return SelectLooper(*proxy); + else + return kVectorPtrLooper; } - template - struct ConvertBasicType { - static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + + template + struct CollectionLooper { + + static std::unique_ptr + CreateReadActionSquence(TStreamerInfo &info, TLoopConfiguration *loopConfig) { - // Simple conversion from a 'From' on disk to a 'To' in memory. - From temp; - buf >> temp; - *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; - return 0; + TLoopConfiguration *localLoopConfig = loopConfig ? loopConfig->Copy() : nullptr; + std::unique_ptr actions( + TActionSequence::CreateReadMemberWiseActions(info, localLoopConfig)); + return actions; } - }; - template - struct ConvertBasicType { - static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + static std::unique_ptr + CreateWriteActionSquence(TStreamerInfo &info, TLoopConfiguration *loopConfig) { - // Simple conversion from a 'From' on disk to a 'To' in memory - UInt_t temp; - buf >> temp; - - if ((temp & kIsReferenced) != 0) { - HandleReferencedTObject(buf,addr,config); - } + TLoopConfiguration *localLoopConfig = loopConfig ? loopConfig->Copy() : nullptr; + std::unique_ptr actions( + TActionSequence::CreateWriteMemberWiseActions(info, localLoopConfig)); + return actions; + } - *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; + static inline Int_t SubSequenceAction(TBuffer &buf, void *start, const void *end, const TLoopConfiguration * /* loopconfig */, const TConfiguration *config) + { + auto conf = (TConfSubSequence*)config; + auto actions = conf->fActions.get(); + // FIXME: need to update the signature of ApplySequence. + buf.ApplySequence(*actions, start, const_cast(end)); return 0; } - }; - template - struct ConvertBasicType,To> { - static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + static inline Int_t ReadStreamerCase(TBuffer &buf, void *start, const void *end, const TLoopConfiguration * loopconfig, const TConfiguration *config) { - // Simple conversion from a 'From' on disk to a 'To' in memory. - TConfWithFactor *conf = (TConfWithFactor *)config; - From temp; - buf.ReadWithFactor(&temp, conf->fFactor, conf->fXmin); - *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; + UInt_t pos, count; + /* Version_t v = */ buf.ReadVersion(&pos, &count, config->fInfo->IsA()); + + Looper::template LoopOverCollection< ReadViaExtStreamer >(buf, start, end, loopconfig, config); + + buf.CheckByteCount(pos, count, config->fCompInfo->fElem->GetFullName()); return 0; } - }; - template - struct ConvertBasicType,To> { - static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *config) + static inline Int_t WriteStreamerCase(TBuffer &buf, void *start, const void *end, const TLoopConfiguration * loopconfig, const TConfiguration *config) { - // Simple conversion from a 'From' on disk to a 'To' in memory. - TConfNoFactor *conf = (TConfNoFactor *)config; - From temp; - buf.ReadWithNbits(&temp, conf->fNbits); - *(To*)( ((char*)addr) + config->fOffset ) = (To)temp; + UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + Looper::template LoopOverCollection< WriteViaExtStreamer >(buf, start, end, loopconfig, config); + + buf.SetByteCount(pos, kTRUE); return 0; } - }; - class TConfigurationPushDataCache : public TConfiguration { - // Configuration object for the PushDataCache case. - public: - TVirtualArray *fOnfileObject; + static inline Int_t StreamerLoopExternal(TBuffer &buf, void *addr, const TConfiguration *actionConfig) { + UInt_t ioffset = actionConfig->fOffset; + // Get any private streamer which was set for the data member. + TMemberStreamer* pstreamer = actionConfig->fCompInfo->fStreamer; + Int_t* counter = (Int_t*) ((char *) addr /*entry pointer*/ + actionConfig->fCompInfo->fMethod /*counter offset*/); + // And call the private streamer, passing it the buffer, the object, and the counter. + (*pstreamer)(buf, (char *) addr /*entry pointer*/ + ioffset /*object offset*/, *counter); + return 0; + }; - TConfigurationPushDataCache(TVirtualStreamerInfo *info, TVirtualArray *onfileObject, Int_t offset) : - TConfiguration(info, -1, nullptr, offset), fOnfileObject(onfileObject) - {} + template + static Int_t WriteStreamerLoopPoly(TBuffer &buf, void *addr, const TConfiguration *config) { + // Get the class of the data member. + TClass* cl = config->fCompInfo->fClass; + UInt_t ioffset = config->fOffset; + bool isPtrPtr = ((TConfStreamerLoop*)config)->fIsPtrPtr; - void Print() const override - { - TStreamerInfo *info = (TStreamerInfo*)fInfo; - if (fOnfileObject) - printf("StreamerInfoAction, class:%s, PushDataCache offset=%d\n", - info->GetClass()->GetName(), fOffset); - else - printf("StreamerInfoAction, class:%s, PopDataCache offset=%d\n", - info->GetClass()->GetName(), fOffset); - } - void PrintDebug(TBuffer &buffer, void *object) const override - { - if (gDebug > 1) { - TStreamerInfo *info = (TStreamerInfo*)fInfo; - printf("StreamerInfoAction, class:%s, %sDataCache, bufpos=%d, arr=%p, offset=%d, onfileObject=%p\n", - info->GetClass()->GetName(), fOnfileObject ? "Push" : "Pop", buffer.Length(), object, fOffset, fOnfileObject); + // Get the counter for the varying length array. + Int_t vlen = *((Int_t*) ((char *) addr /*entry pointer*/ + config->fCompInfo->fMethod /*counter offset*/)); + //b << vlen; + if (vlen) { + // Get a pointer to the array of pointers. + char** pp = (char**) ((char *) addr /*entry pointer*/ + ioffset /*object offset*/); + // Loop over each element of the array of pointers to varying-length arrays. + for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { + if (!pp[ndx]) { + // -- We do not have a pointer to a varying-length array. + // Error("WriteBufferAux", "The pointer to element %s::%s type %d (%s) is null\n", GetName(), aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); + // ::ErrorHandler(kError, "::WriteStreamerLoop", Form("The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName())); + printf("WriteStreamerLoop - The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName()); + continue; + } + if (!isPtrPtr) { + // -- We are a varying-length array of objects. + // Write the entire array of objects to the buffer. + // Note: Polymorphism is not allowed here. + buf.WriteFastArray(pp[ndx], cl, vlen, nullptr); + } else { + // -- We are a varying-length array of pointers to objects. + // Write the entire array of object pointers to the buffer. + // Note: The object pointers are allowed to be polymorphic. + buf.WriteFastArray((void **)pp[ndx], cl, vlen, kFALSE, nullptr); + } // isPtrPtr + } // ndx + } else // vlen + if (kIsTextT) { + // special handling for the text-based streamers + for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) + buf.WriteFastArray((void *)nullptr, cl, -1, nullptr); } + return 0; } - }; - Int_t PushDataCache(TBuffer &b, void *, const TConfiguration *conf) - { - TConfigurationPushDataCache *config = (TConfigurationPushDataCache*)conf; - auto onfileObject = config->fOnfileObject; + static Int_t WriteStreamerLoopStatic(TBuffer &buf, void *addr, const TConfiguration *config) { + // Get the class of the data member. + TClass* cl = config->fCompInfo->fClass; + UInt_t ioffset = config->fOffset; + bool isPtrPtr = ((TConfStreamerLoop*)config)->fIsPtrPtr; - // onfileObject->SetSize(1); - b.PushDataCache( onfileObject ); + // Get the counter for the varying length array. + Int_t vlen = *((Int_t*) ((char *) addr /*entry pointer*/ + config->fCompInfo->fMethod /*counter offset*/)); + //b << vlen; + if (vlen) { + // Get a pointer to the array of pointers. + char** pp = (char**) ((char *) addr /*entry pointer*/ + ioffset /*object offset*/); + // -- Older versions do *not* allow polymorphic pointers to objects. + // Loop over each element of the array of pointers to varying-length arrays. + for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { + if (!pp[ndx]) { + // -- We do not have a pointer to a varying-length array. + //Error("WriteBufferAux", "The pointer to element %s::%s type %d (%s) is null\n", GetName(), aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); + // ::ErrorHandler(kError, "::WriteTextStreamerLoop", Form("The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName())); + printf("WriteStreamerLoop - The pointer to element %s::%s type %d (%s) is null\n", config->fInfo->GetName(), config->fCompInfo->fElem->GetFullName(), config->fCompInfo->fType, config->fCompInfo->fElem->GetTypeName()); + continue; + } + if (!isPtrPtr) { + // -- We are a varying-length array of objects. + // Loop over the elements of the varying length array. + for (Int_t v = 0; v < vlen; ++v) { + // Write the object to the buffer. + cl->Streamer(pp[ndx] + (v * cl->Size()), buf); + } // v + } + else { + // -- We are a varying-length array of pointers to objects. + // Loop over the elements of the varying length array. + for (Int_t v = 0; v < vlen; ++v) { + // Get a pointer to the object pointer. + char** r = (char**) pp[ndx]; + // Write the object to the buffer. + cl->Streamer(r[v], buf); + } // v + } // isPtrPtr + } // ndx + } // vlen + return 0; + } - return 0; - } + template + struct WriteStreamerLoop { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *start, Ts... args, const TConfiguration *config) + { + if (!kIsTextT && config->fCompInfo->fStreamer) { + // -- We have a private streamer. + UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); - Int_t PushDataCacheVectorPtr(TBuffer &b, void *, const void *, const TConfiguration *conf) - { - TConfigurationPushDataCache *config = (TConfigurationPushDataCache*)conf; - auto onfileObject = config->fOnfileObject; + // Loop over the entries in the clones array or the STL container. + Looper::template LoopOverCollection< StreamerLoopExternal > (buf, start, args..., config); - // onfileObject->SetSize(n); - b.PushDataCache( onfileObject ); + buf.SetByteCount(pos, kTRUE); + // We are done, next streamer element. + return 0; + } - return 0; - } + // By default assume the file version is the newest. + Int_t fileVersion = kMaxInt; - Int_t PushDataCacheGenericCollection(TBuffer &b, void *, const void *, const TLoopConfiguration *loopconfig, const TConfiguration *conf) - { - TConfigurationPushDataCache *config = (TConfigurationPushDataCache*)conf; - auto onfileObject = config->fOnfileObject; + if (!kIsTextT) { + // At this point we do *not* have a private streamer. + // Get the version of the file we are writing to. + TFile* file = (TFile*) buf.GetParent(); + if (file) { + fileVersion = file->GetVersion(); + } + } + // Write the class version to the buffer. + UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + if (fileVersion > 51508) { + // -- Newer versions allow polymorphic pointers to objects. + // Loop over the entries in the clones array or the STL container. + Looper::template LoopOverCollection< WriteStreamerLoopPoly > (buf, start, args..., config); + } + else { + // -- Older versions do *not* allow polymorphic pointers to objects. + // Loop over the entries in the clones array or the STL container. + Looper::template LoopOverCollection< ReadStreamerLoopStatic > (buf, start, args..., config); + } // fileVersion + // Backpatch the byte count into the buffer. + buf.SetByteCount(pos, kTRUE); - TVirtualCollectionProxy *proxy = ((TGenericLoopConfig*)loopconfig)->fProxy; - UInt_t n = proxy->Size(); + return 0; + } + }; - onfileObject->SetSize(n); - b.PushDataCache( onfileObject ); + template + static Int_t ReadStreamerLoopPoly(TBuffer &buf, void *addr, const TConfiguration *config) { + // Get the class of the data member. + TClass* cl = config->fCompInfo->fClass; + UInt_t ioffset = config->fOffset; + // Which are we, an array of objects or an array of pointers to objects? + bool isPtrPtr = ((TConfStreamerLoop*)config)->fIsPtrPtr; - return 0; - } + // Get the counter for the varying length array. + Int_t vlen = *((Int_t *)((char *)addr /*entry pointer*/ + + config->fCompInfo->fMethod /*counter offset*/)); + // Int_t realLen; + // b >> realLen; + // if (realLen != vlen) { + // fprintf(stderr, "read vlen: %d realLen: %s\n", vlen, realLen); + //} + // Get a pointer to the array of pointers. + char **pp = (char **)((char *)addr /*entry pointer*/ + ioffset /*object offset*/); + // Loop over each element of the array of pointers to varying-length arrays. + // if (!pp) { + // continue; + // } - Int_t PopDataCache(TBuffer &b, void *, const TConfiguration *) - { - b.PopDataCache(); - return 0; - } + if (pp) // SL: place it here instead of continue, which is related to for(k) loop + for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { + // if (!pp[ndx]) { + // -- We do not have a pointer to a varying-length array. + // Error("ReadBuffer", "The pointer to element %s::%s type %d (%s) is null\n", thisVar->GetName(), + // aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); + // continue; + //} + // Delete any memory at pp[ndx]. + if (!isPtrPtr) { + cl->DeleteArray(pp[ndx]); + pp[ndx] = 0; + } else { + // Using vlen is wrong here because it has already + // been overwritten with the value needed to read + // the current record. Fixing this will require + // doing a pass over the object at the beginning + // of the I/O and releasing all the buffer memory + // for varying length arrays before we overwrite + // the counter values. + // + // For now we will just leak memory, just as we + // have always done in the past. Fix this. + // + // char** r = (char**) pp[ndx]; + // if (r) { + // for (Int_t v = 0; v < vlen; ++v) { + // cl->Destructor(r[v]); + // r[v] = 0; + // } + //} + delete[] pp[ndx]; + pp[ndx] = 0; + } + if (!vlen) { + if (kIsTextT) { + // special handling for the text-based streamers - keep calling to shift array index + buf.ReadFastArray((void *)nullptr, cl, -1, nullptr); + } + continue; + } + // Note: We now have pp[ndx] is null. + // Allocate memory to read into. + if (!isPtrPtr) { + // -- We are a varying-length array of objects. + // Note: Polymorphism is not allowed here. + // Allocate a new array of objects to read into. + pp[ndx] = (char *)cl->NewArray(vlen); + if (!pp[ndx]) { + Error("ReadBuffer", "Memory allocation failed!\n"); + continue; + } + } else { + // -- We are a varying-length array of pointers to objects. + // Note: The object pointers are allowed to be polymorphic. + // Allocate a new array of pointers to objects to read into. + pp[ndx] = (char *)new char *[vlen]; + if (!pp[ndx]) { + Error("ReadBuffer", "Memory allocation failed!\n"); + continue; + } + // And set each pointer to null. + memset(pp[ndx], 0, vlen * sizeof(char *)); // This is the right size we really have a char**: pp[ndx] + // = (char*) new char*[vlen]; + } + if (!isPtrPtr) { + // -- We are a varying-length array of objects. + buf.ReadFastArray(pp[ndx], cl, vlen, nullptr); + } else { + // -- We are a varying-length array of object pointers. + buf.ReadFastArray((void **)pp[ndx], cl, vlen, kFALSE, nullptr); + } // isPtrPtr + } // ndx + return 0; + }; // StreamerLoopPoly - Int_t PopDataCacheVectorPtr(TBuffer &b, void *, const void *, const TConfiguration *) - { - b.PopDataCache(); - return 0; - } + static Int_t ReadStreamerLoopStatic(TBuffer &buf, void *addr, const TConfiguration *config) { + // Get the class of the data member. + TClass* cl = config->fCompInfo->fClass; + UInt_t ioffset = config->fOffset; + // Which are we, an array of objects or an array of pointers to objects? + bool isPtrPtr = ((TConfStreamerLoop*)config)->fIsPtrPtr; + + // Get the counter for the varying length array. + Int_t vlen = *((Int_t *)((char *)addr /*entry pointer*/ + + config->fCompInfo->fMethod /*counter offset*/)); + // Int_t realLen; + // b >> realLen; + // if (realLen != vlen) { + // fprintf(stderr, "read vlen: %d realLen: %s\n", vlen, realLen); + //} + // Get a pointer to the array of pointers. + char **pp = (char **)((char *)addr /*entry pointer*/ + ioffset /*object offset*/); + // if (!pp) { + // continue; + //} + + if (pp) // SL: place it here instead of continue, which is related to for(k) loop + + // Loop over each element of the array of pointers to varying-length arrays. + for (Int_t ndx = 0; ndx < config->fCompInfo->fLength; ++ndx) { + // if (!pp[ndx]) { + // -- We do not have a pointer to a varying-length array. + // Error("ReadBuffer", "The pointer to element %s::%s type %d (%s) is null\n", thisVar->GetName(), + // aElement->GetFullName(), compinfo[i]->fType, aElement->GetTypeName()); + // continue; + //} + // Delete any memory at pp[ndx]. + if (!isPtrPtr) { + cl->DeleteArray(pp[ndx]); + pp[ndx] = 0; + } else { + // Using vlen is wrong here because it has already + // been overwritten with the value needed to read + // the current record. Fixing this will require + // doing a pass over the object at the beginning + // of the I/O and releasing all the buffer memory + // for varying length arrays before we overwrite + // the counter values. + // + // For now we will just leak memory, just as we + // have always done in the past. Fix this. + // + // char** r = (char**) pp[ndx]; + // if (r) { + // for (Int_t v = 0; v < vlen; ++v) { + // cl->Destructor(r[v]); + // r[v] = 0; + // } + //} + delete[] pp[ndx]; + pp[ndx] = 0; + } + if (!vlen) { + continue; + } + // Note: We now have pp[ndx] is null. + // Allocate memory to read into. + if (!isPtrPtr) { + // -- We are a varying-length array of objects. + // Note: Polymorphism is not allowed here. + // Allocate a new array of objects to read into. + pp[ndx] = (char *)cl->NewArray(vlen); + if (!pp[ndx]) { + Error("ReadBuffer", "Memory allocation failed!\n"); + continue; + } + } else { + // -- We are a varying-length array of pointers to objects. + // Note: The object pointers are allowed to be polymorphic. + // Allocate a new array of pointers to objects to read into. + pp[ndx] = (char *)new char *[vlen]; + if (!pp[ndx]) { + Error("ReadBuffer", "Memory allocation failed!\n"); + continue; + } + // And set each pointer to null. + memset(pp[ndx], 0, vlen * sizeof(char *)); // This is the right size we really have a char**: pp[ndx] + // = (char*) new char*[vlen]; + } + if (!isPtrPtr) { + // -- We are a varying-length array of objects. + // Loop over the elements of the varying length array. + for (Int_t v = 0; v < vlen; ++v) { + // Read the object from the buffer. + cl->Streamer(pp[ndx] + (v * cl->Size()), buf); + } // v + } else { + // -- We are a varying-length array of object pointers. + // Get a pointer to the object pointer array. + char **r = (char **)pp[ndx]; + // Loop over the elements of the varying length array. + for (Int_t v = 0; v < vlen; ++v) { + // Allocate an object to read into. + r[v] = (char *)cl->New(); + if (!r[v]) { + // Do not print a second error message here. + // Error("ReadBuffer", "Memory allocation failed!\n"); + continue; + } + // Read the object from the buffer. + cl->Streamer(r[v], buf); + } // v + } // isPtrPtr + } // ndx + return 0; + }; // action - Int_t PopDataCacheGenericCollection(TBuffer &b, void *, const void *, const TLoopConfiguration *, const TConfiguration *) - { - b.PopDataCache(); - return 0; - } + template + struct ReadStreamerLoop { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *start, Ts... args, const TConfiguration *config) + { + // Check for a private streamer. + if (!kIsTextT && config->fCompInfo->fStreamer) { + // -- We have a private streamer. + // Read the class version and byte count from the buffer. + UInt_t pos = 0; + UInt_t count = 0; + buf.ReadVersion(&pos, &count, config->fInfo->IsA()); + + // Loop over the entries in the clones array or the STL container. + Looper::template LoopOverCollection< StreamerLoopExternal > (buf, start, args..., config); + + buf.CheckByteCount(pos, count, config->fCompInfo->fElem->GetFullName()); + // We are done, next streamer element. + return 0; + } - class TConfigurationUseCache : public TConfiguration { - // Configuration object for the UseCache case. - public: - TConfiguredAction fAction; - Bool_t fNeedRepeat; + // By default assume the file version is the newest. + Int_t fileVersion = kMaxInt; + if (!kIsTextT) { + // At this point we do *not* have a private streamer. + // Get the version of the file we are reading from. + TFile* file = (TFile*) buf.GetParent(); + if (file) { + fileVersion = file->GetVersion(); + } + } + // Read the class version and byte count from the buffer. + UInt_t pos = 0; + UInt_t count = 0; + buf.ReadVersion(&pos, &count, config->fInfo->IsA()); + if (fileVersion > 51508) { + // -- Newer versions allow polymorphic pointers. + + // Loop over the entries in the clones array or the STL container. + Looper::template LoopOverCollection< ReadStreamerLoopPoly > (buf, start, args..., config); + } else { + // -- Older versions do *not* allow polymorphic pointers. - TConfigurationUseCache(TVirtualStreamerInfo *info, TConfiguredAction &action, Bool_t repeat) : - TConfiguration(info,action.fConfiguration->fElemId,action.fConfiguration->fCompInfo,action.fConfiguration->fOffset),fAction(action),fNeedRepeat(repeat) {}; - void PrintDebug(TBuffer &b, void *addr) const override - { - if (gDebug > 1) { - // Idea: We should print the name of the action function. - TStreamerInfo *info = (TStreamerInfo*)fInfo; - TStreamerElement *aElement = fCompInfo->fElem; - fprintf(stdout,"StreamerInfoAction, class:%s, name=%s, fType[%d]=%d," - " %s, bufpos=%d, arr=%p, eoffset=%d, Redirect=%p\n", - info->GetClass()->GetName(),aElement->GetName(),fElemId,fCompInfo->fType, - aElement->ClassName(),b.Length(),addr, 0,b.PeekDataCache() ? b.PeekDataCache()->GetObjectAt(0) : 0); + // Loop over the entries in the clones array or the STL container. + Looper::template LoopOverCollection< ReadStreamerLoopStatic > (buf, start, args..., config); + } // fileVersion + buf.CheckByteCount(pos, count, config->fCompInfo->fElem->GetFullName()); + return 0; } + }; - } - ~TConfigurationUseCache() override {} - TConfiguration *Copy() override - { - TConfigurationUseCache *copy = new TConfigurationUseCache(*this); - fAction.fConfiguration = copy->fAction.fConfiguration->Copy(); // since the previous allocation did a 'move' of fAction we need to fix it. - return copy; - } }; - INLINE_TEMPLATE_ARGS Int_t UseCache(TBuffer &b, void *addr, const TConfiguration *conf) - { - TConfigurationUseCache *config = (TConfigurationUseCache*)conf; - - Int_t bufpos = b.Length(); - TVirtualArray *cached = b.PeekDataCache(); - if (cached==0) { - TStreamerElement *aElement = conf->fCompInfo->fElem; - TStreamerInfo *info = (TStreamerInfo*)conf->fInfo; - Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); - char *ptr = (char*)addr; - info->ReadBufferSkip(b,&ptr,config->fCompInfo,config->fCompInfo->fType+TStreamerInfo::kSkip,aElement,1,0); - } else { - config->fAction(b, (*cached)[0]); - } - // Idea: Factor out this 'if' to a UseCacheRepeat function - if (config->fNeedRepeat) { - b.SetBufferOffset(bufpos); - } - return 0; - } - - INLINE_TEMPLATE_ARGS Int_t UseCacheVectorPtrLoop(TBuffer &b, void *start, const void *end, const TConfiguration *conf) - { - TConfigurationUseCache *config = (TConfigurationUseCache*)conf; - Int_t bufpos = b.Length(); - - TVirtualArray *cached = b.PeekDataCache(); - if (cached==0) { - TStreamerElement *aElement = config->fCompInfo->fElem; - TStreamerInfo *info = (TStreamerInfo*)config->fInfo; - Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); - char *ptr = (char*)start; - UInt_t n = (((void**)end)-((void**)start)); - info->ReadBufferSkip(b,&ptr,config->fCompInfo,conf->fCompInfo->fType+TStreamerInfo::kSkip,aElement,n,0); - } else { - TVectorLoopConfig cached_config( nullptr, cached->fClass->Size(), /* read */ kTRUE ); - void *cached_start = (*cached)[0]; - void *cached_end = ((char*)cached_start) + cached->fSize * cached_config.fIncrement; - config->fAction(b,cached_start,cached_end,&cached_config); - } - // Idea: Factor out this 'if' to a UseCacheRepeat function - if (config->fNeedRepeat) { - b.SetBufferOffset(bufpos); - } - return 0; - } - - INLINE_TEMPLATE_ARGS Int_t UseCacheVectorLoop(TBuffer &b, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *conf) - { - TConfigurationUseCache *config = (TConfigurationUseCache*)conf; - - Int_t bufpos = b.Length(); - TVirtualArray *cached = b.PeekDataCache(); - if (cached==0) { - TStreamerElement *aElement = config->fCompInfo->fElem; - TStreamerInfo *info = (TStreamerInfo*)config->fInfo; - Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); - char *ptr = (char*)start; - UInt_t n = (((char*)end)-((char*)start))/((TVectorLoopConfig*)loopconf)->fIncrement; - info->ReadBufferSkip(b,&ptr,config->fCompInfo,config->fCompInfo->fType+TStreamerInfo::kSkip,aElement,n,0); - } else { - TVectorLoopConfig cached_config( nullptr, cached->fClass->Size(), /* read */ kTRUE ); - void *cached_start = (*cached)[0]; - void *cached_end = ((char*)cached_start) + cached->fSize * cached_config.fIncrement; - config->fAction(b,cached_start,cached_end,&cached_config); - } - // Idea: Factor out this 'if' to a UseCacheRepeat function - if (config->fNeedRepeat) { - b.SetBufferOffset(bufpos); - } - return 0; - } - - INLINE_TEMPLATE_ARGS Int_t UseCacheGenericCollection(TBuffer &b, void *, const void *, const TLoopConfiguration *loopconfig, const TConfiguration *conf) + // The Scalar 'looper' only process one element. + struct ScalarLooper : public CollectionLooper { - TConfigurationUseCache *config = (TConfigurationUseCache*)conf; + using LoopAction_t = Int_t (*)(TBuffer &, void*, const TConfiguration*); - Int_t bufpos = b.Length(); - TVirtualArray *cached = b.PeekDataCache(); - if (cached==0) { - TStreamerElement *aElement = config->fCompInfo->fElem; - TStreamerInfo *info = (TStreamerInfo*)config->fInfo; - - TVirtualCollectionProxy *proxy = ((TGenericLoopConfig*)loopconfig)->fProxy; - Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",info->GetName(),aElement->GetName()); - UInt_t n = proxy->Size(); - info->ReadBufferSkip(b, *proxy,config->fCompInfo,config->fCompInfo->fType+TStreamerInfo::kSkip,aElement,n,0); - } else { - TVectorLoopConfig cached_config( nullptr, cached->fClass->Size(), /* read */ kTRUE ); - void *cached_start = (*cached)[0]; - void *cached_end = ((char*)cached_start) + cached->fSize * cached_config.fIncrement; - config->fAction(b,cached_start,cached_end,&cached_config); - } - // Idea: Factor out this 'if' to a UseCacheRepeat function - if (config->fNeedRepeat) { - b.SetBufferOffset(bufpos); + template + static INLINE_TEMPLATE_ARGS Int_t LoopOverCollection(TBuffer &buf, void *start, const TConfiguration *config) + { + iter_action(buf, start, config); + return 0; } - return 0; - } - - // Support for collections. - - Int_t ReadLoopInvalid(TBuffer &, void *, const void *, const TConfiguration *config) - { - Fatal("ApplySequence","The sequence of actions to read %s:%d member-wise was not initialized.",config->fInfo->GetName(),config->fInfo->GetClassVersion()); - return 0; - } + }; - Int_t WriteLoopInvalid(TBuffer &, void *, const void *, const TConfiguration *config) + struct VectorLooper : public CollectionLooper { - Fatal("ApplySequence","The sequence of actions to write %s:%d member-wise was not initialized.",config->fInfo->GetName(),config->fInfo->GetClassVersion()); - return 0; - } + using LoopAction_t = Int_t (*)(TBuffer &, void*, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration*); - enum ESelectLooper { kVectorLooper, kVectorPtrLooper, kAssociativeLooper, kGenericLooper }; + template + using ReadStreamerLoop = CollectionLooper::ReadStreamerLoop; + template + using WriteStreamerLoop = CollectionLooper::WriteStreamerLoop; - ESelectLooper SelectLooper(TVirtualCollectionProxy &proxy) - { - if ( (proxy.GetProperties() & TVirtualCollectionProxy::kIsEmulated) ) { - return kVectorLooper; - } else if ( (proxy.GetCollectionType() == ROOT::kSTLvector)) { - if (proxy.GetProperties() & TVirtualCollectionProxy::kCustomAlloc) - return kGenericLooper; - else - return kVectorLooper; - } else if (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLunorderedset - || proxy.GetCollectionType() == ROOT::kSTLmultiset || proxy.GetCollectionType() == ROOT::kSTLunorderedmultiset - || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap - || proxy.GetCollectionType() == ROOT::kSTLunorderedmap || proxy.GetCollectionType() == ROOT::kSTLunorderedmultimap - || proxy.GetCollectionType() == ROOT::kSTLbitset) { - return kAssociativeLooper; - } else { - return kGenericLooper; + template + static INLINE_TEMPLATE_ARGS Int_t LoopOverCollection(TBuffer &buf, void *start, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration *config) + { + const Int_t incr = ((TVectorLoopConfig*)loopconfig)->fIncrement; + //Idea: can we factor out the addition of fOffset + // iter = (char*)iter + config->fOffset; + for(void *iter = start; iter != end; iter = (char*)iter + incr ) { + iter_action(buf, iter, config); + } + return 0; } - } - - struct VectorLooper { template static INLINE_TEMPLATE_ARGS Int_t ReadBasicType(TBuffer &buf, void *iter, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration *config) @@ -1819,17 +2105,55 @@ namespace TStreamerInfoActions return 0; } - template - static INLINE_TEMPLATE_ARGS Int_t ReadAction(TBuffer &buf, void *start, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration *config) - { - const Int_t incr = ((TVectorLoopConfig*)loopconfig)->fIncrement; - //Idea: can we factor out the addition of fOffset - // iter = (char*)iter + config->fOffset; - for(void *iter = start; iter != end; iter = (char*)iter + incr ) { - iter_action(buf, iter, config); + template + struct WriteConvertBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *iter, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration *config) + { + const Int_t incr = ((TVectorLoopConfig*)loopconfig)->fIncrement; + iter = (char*)iter + config->fOffset; + end = (char*)end + config->fOffset; + for(; iter != end; iter = (char*)iter + incr ) { + // Simple conversion from a 'From' on disk to a 'To' in memory. + Onfile temp = (Onfile)(*(Memory*)((char*) iter)); + buf << temp; + } + return 0; } - return 0; - } + }; + + template + struct WriteConvertBasicType, Memory> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *iter, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration *config) + { + const TStreamerElement *elem = config->fCompInfo->fElem; + const Int_t incr = ((TVectorLoopConfig*)loopconfig)->fIncrement; + iter = (char*)iter + config->fOffset; + end = (char*)end + config->fOffset; + for(; iter != end; iter = (char*)iter + incr ) { + // Simple conversion from a 'From' on disk to a 'To' in memory. + Onfile temp = (Onfile)(*(Memory*)((char*) iter)); + WriteCompressed(buf, &temp, elem); + } + return 0; + } + }; + + template + struct WriteConvertBasicType, Memory> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *iter, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration *config) + { + const TStreamerElement *elem = config->fCompInfo->fElem; + const Int_t incr = ((TVectorLoopConfig*)loopconfig)->fIncrement; + iter = (char*)iter + config->fOffset; + end = (char*)end + config->fOffset; + for(; iter != end; iter = (char*)iter + incr ) { + // Simple conversion from a 'From' on disk to a 'To' in memory. + Onfile temp = (Onfile)(*(Memory*)((char*) iter)); + WriteCompressed(buf, &temp, elem); + } + return 0; + } + }; static INLINE_TEMPLATE_ARGS Int_t ReadBase(TBuffer &buf, void *start, const void *end, const TLoopConfiguration * loopconfig, const TConfiguration *config) { @@ -1845,27 +2169,23 @@ namespace TStreamerInfoActions } ((TStreamerInfo*)config->fInfo)->ReadBuffer(buf, arrptr, &(config->fCompInfo), /*first*/ 0, /*last*/ 1, /*narr*/ n, config->fOffset, 1|2 ); delete [] arrptr; + return 0; + } + + static INLINE_TEMPLATE_ARGS Int_t WriteBase(TBuffer &buf, void *start, const void *end, const TLoopConfiguration * loopconfig, const TConfiguration *config) + { + // Well the implementation is non trivial since we do not have a proxy for the container of _only_ the base class. For now + // punt. - // // Idea: need to cache this result! - // TStreamerInfo *info = (TStreamerInfo*)config->fInfo; - // TStreamerElement *aElement = (TStreamerElement*)info->GetElem(config->fElemId); - // - // *Int_t clversion = ((TStreamerBase*)aElement)->Get BaseVersion(); - // *TClass *cle = aElement->GetNewBaseClass(); - // *(TSequence *actions = CreateReadMemberWiseActions( cle->GetStreamerInfo(clversion), ???? ); - // - // TSequence *actions = CreateReadMemberWiseActions( ((TStreamerBase*)aElement)->GetBaseStreamerInfo(), ???? ); - // - // actions->ReadBuffer(b,start,end); - // delete actions; - - // const Int_t incr = ((TVectorLoopConfig*)loopconfig)->fIncrement; - // for(void *iter = start; iter != end; iter = (char*)iter + incr ) - // { - // ((TStreamerInfo*)(((TStreamerBase*)aElement)->GetBaseStreamerInfo())->ReadBuffer(b,arr,-1,narr,ioffset,arrayMode); - // - // ((TStreamerInfo*)config->fInfo)->ReadBuffer(buf, (char**)&iter, config->fElemId, 1, config->fOffset, 1|2 ); - // } + UInt_t incr = ((TVectorLoopConfig*)loopconfig)->fIncrement; + UInt_t n = (((char*)end)-((char*)start))/incr; + char **arrptr = new char*[n]; + UInt_t i = 0; + for(void *iter = start; iter != end; iter = (char*)iter + incr, ++i ) { + arrptr[i] = (char*)iter; + } + ((TStreamerInfo*)config->fInfo)->WriteBufferAux(buf, arrptr, &(config->fCompInfo), /*first*/ 0, /*last*/ 1, /*narr*/ n, config->fOffset, 1|2 ); + delete [] arrptr; return 0; } @@ -1928,6 +2248,24 @@ namespace TStreamerInfoActions return 0; } + template + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionBasicType(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // Collection of numbers. Memberwise or not, it is all the same. + + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + std::vector *const vec = (std::vector*)(((char*)addr)+config->fOffset); + Int_t nvalues = vec->size(); + buf.WriteInt(nvalues); + + T *begin = &(*vec->begin()); + buf.WriteFastArray(begin, nvalues); + + buf.SetByteCount(start); + return 0; + } static INLINE_TEMPLATE_ARGS Int_t ReadCollectionFloat16(TBuffer &buf, void *addr, const TConfiguration *conf) { @@ -1955,6 +2293,24 @@ namespace TStreamerInfoActions return 0; } + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionFloat16(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // Collection of numbers. Memberwise or not, it is all the same. + + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + std::vector *const vec = (std::vector*)(((char*)addr)+config->fOffset); + Int_t nvalues = vec->size(); + buf.WriteInt(nvalues); + + float *begin = &(*vec->begin()); + buf.WriteFastArrayFloat16(begin, nvalues); + + buf.SetByteCount(start); + return 0; + } + static INLINE_TEMPLATE_ARGS Int_t ReadCollectionDouble32(TBuffer &buf, void *addr, const TConfiguration *conf) { // Collection of numbers. Memberwise or not, it is all the same. @@ -1981,6 +2337,24 @@ namespace TStreamerInfoActions return 0; } + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionDouble32(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // Collection of numbers. Memberwise or not, it is all the same. + + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + std::vector *const vec = (std::vector*)(((char*)addr)+config->fOffset); + Int_t nvalues = vec->size(); + buf.WriteInt(nvalues); + + double *begin = &(*vec->begin()); + buf.WriteFastArrayDouble32(begin, nvalues); + + buf.SetByteCount(start); + return 0; + } + template struct ConvertCollectionBasicType { static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) @@ -2060,9 +2434,76 @@ namespace TStreamerInfoActions return 0; } + template + struct WriteConvertCollectionBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // Collection of numbers. Memberwise or not, it is all the same. + + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start = buf.WriteVersion( config->fInfo->IsA(), kTRUE ); + + std::vector *const vec = (std::vector*)(((char*)addr)+config->fOffset); + Int_t nvalues = vec->size(); + buf.WriteInt(nvalues); + + // We have to call WriteFastArray for proper JSON writing + // So we need to convert before hand :( + Onfile *temp = new Onfile[nvalues]; + for(Int_t ind = 0; ind < nvalues; ++ind) { + temp[ind] = (Onfile)((*vec)[ind]); + } + buf.WriteFastArray(temp, nvalues); + delete [] temp; + + buf.SetByteCount(start, kTRUE); + return 0; + } + }; }; - struct VectorPtrLooper { + struct VectorPtrLooper : public CollectionLooper { + // Can not inherit/use CollectionLooper, because this looper's + // function do not take a `TLoopConfiguration`. + + using LoopAction_t = Int_t (*)(TBuffer &, void *start, const void *end, const TConfiguration*); + + template + using ReadStreamerLoop = CollectionLooper::ReadStreamerLoop; + template + using WriteStreamerLoop = CollectionLooper::WriteStreamerLoop; + + static std::unique_ptr + CreateReadActionSquence(TStreamerInfo &info, TLoopConfiguration *) + { + using unique_ptr = std::unique_ptr; + return unique_ptr(info.GetReadMemberWiseActions(kTRUE)->CreateCopy()); + } + + static std::unique_ptr + CreateWriteActionSquence(TStreamerInfo &info, TLoopConfiguration *) + { + using unique_ptr = std::unique_ptr; + return unique_ptr(info.GetWriteMemberWiseActions(kTRUE)->CreateCopy()); + } + + template + static INLINE_TEMPLATE_ARGS Int_t LoopOverCollection(TBuffer &buf, void *start, const void *end, const TConfiguration *config) + { + for(void *iter = start; iter != end; iter = (char*)iter + sizeof(void*) ) { + action(buf, *(void**)iter, config); + } + return 0; + } + + static inline Int_t SubSequenceAction(TBuffer &buf, void *start, const void *end, const TConfiguration *config) + { + auto conf = (TConfSubSequence*)config; + auto actions = conf->fActions.get(); + // FIXME: need to update the signature of ApplySequence. + buf.ApplySequenceVecPtr(*actions, start, const_cast(end)); + return 0; + } template static INLINE_TEMPLATE_ARGS Int_t ReadBasicType(TBuffer &buf, void *iter, const void *end, const TConfiguration *config) @@ -2159,14 +2600,51 @@ namespace TStreamerInfoActions return 0; } - template - static INLINE_TEMPLATE_ARGS Int_t ReadAction(TBuffer &buf, void *start, const void *end, const TConfiguration *config) - { - for(void *iter = start; iter != end; iter = (char*)iter + sizeof(void*) ) { - action(buf, *(void**)iter, config); + template + struct WriteConvertBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *iter, const void *end, const TConfiguration *config) + { + const Int_t offset = config->fOffset; + + for(; iter != end; iter = (char*)iter + sizeof(void*) ) { + From *from = (From*)( ((char*) (*(void**)iter) ) + offset ); + To to = (To)(*from); + buf << to; + } + return 0; } - return 0; - } + }; + + template + struct WriteConvertBasicType, From> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *iter, const void *end, const TConfiguration *config) + { + const Int_t offset = config->fOffset; + const TStreamerElement *elem = config->fCompInfo->fElem; + + for(; iter != end; iter = (char*)iter + sizeof(void*) ) { + From *from = (From*)( ((char*) (*(void**)iter) ) + offset ); + To to = (To)(*from); + WriteCompressed(buf, &to, elem); + } + return 0; + } + }; + template + struct WriteConvertBasicType, From> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *iter, const void *end, const TConfiguration *config) + { + const Int_t offset = config->fOffset; + const TStreamerElement *elem = config->fCompInfo->fElem; + + for(; iter != end; iter = (char*)iter + sizeof(void*) ) { + From *from = (From*)( ((char*) (*(void**)iter) ) + offset ); + To to = (To)(*from); + WriteCompressed(buf, &to, elem); + } + return 0; + } + }; static INLINE_TEMPLATE_ARGS Int_t ReadBase(TBuffer &buf, void *start, const void *end, const TConfiguration *config) { @@ -2176,6 +2654,35 @@ namespace TStreamerInfoActions return GenericRead(buf,start,end,config); } + static INLINE_TEMPLATE_ARGS Int_t WriteBase(TBuffer &buf, void *start, const void *end, const TConfiguration *config) + { + // Well the implementation is non trivial since we do not have a proxy for the container of _only_ the base class. For now + // punt. + + return GenericWrite(buf,start,end,config); + } + + static inline Int_t ReadStreamerCase(TBuffer &buf, void *start, const void *end, const TConfiguration *config) + { + UInt_t pos, count; + /* Version_t v = */ buf.ReadVersion(&pos, &count, config->fInfo->IsA()); + + LoopOverCollection< ReadViaExtStreamer >(buf, start, end, config); + + buf.CheckByteCount(pos, count, config->fCompInfo->fElem->GetFullName()); + return 0; + } + + static inline Int_t WriteStreamerCase(TBuffer &buf, void *start, const void *end, const TConfiguration *config) + { + UInt_t pos = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + LoopOverCollection< WriteViaExtStreamer >(buf, start, end, config); + + buf.SetByteCount(pos, kTRUE); + return 0; + } + static INLINE_TEMPLATE_ARGS Int_t GenericRead(TBuffer &buf, void *iter, const void *end, const TConfiguration *config) { Int_t n = ( ((void**)end) - ((void**)iter) ); @@ -2193,6 +2700,9 @@ namespace TStreamerInfoActions }; struct AssociativeLooper { + using LoopAction_t = void (*)(TBuffer&, void *, const void *, Next_t, Int_t, const TStreamerElement *elem); + +protected: template static INLINE_TEMPLATE_ARGS void SimpleRead(TBuffer &buf, void *addr, Int_t nvalues) @@ -2210,6 +2720,38 @@ namespace TStreamerInfoActions buf.ReadFastArrayDouble32((double*)addr, nvalues); } + template + static INLINE_TEMPLATE_ARGS void SimpleWrite(TBuffer &buf, void *addr, const TStreamerElement *) + { + buf << *(T*)addr; + } + + static INLINE_TEMPLATE_ARGS void SimpleWriteFloat16(TBuffer &buf, void *addr, const TStreamerElement *elem) + { + buf.WriteFloat16((float*)addr, const_cast(elem)); + } + + static INLINE_TEMPLATE_ARGS void SimpleWriteDouble32(TBuffer &buf, void *addr, const TStreamerElement *elem) + { + buf.WriteDouble32((double*)addr, const_cast(elem)); + } + + template + static INLINE_TEMPLATE_ARGS void ArrayWrite(TBuffer &buf, void *addr, Int_t nvalues, const TStreamerElement *) + { + buf.WriteFastArray((T*)addr, nvalues); + } + + static INLINE_TEMPLATE_ARGS void ArrayWriteCompressed(TBuffer &buf, float *addr, Int_t nvalues, const TStreamerElement *elem) + { + buf.WriteFastArrayFloat16((float*)addr, nvalues, const_cast(elem)); + } + + static INLINE_TEMPLATE_ARGS void ArrayWriteCompressed(TBuffer &buf, double *addr, Int_t nvalues, const TStreamerElement *elem) + { + buf.WriteFastArrayDouble32((double*)addr, nvalues, const_cast(elem)); + } + template static INLINE_TEMPLATE_ARGS Int_t ReadNumericalCollection(TBuffer &buf, void *addr, const TConfiguration *conf) { @@ -2248,22 +2790,97 @@ namespace TStreamerInfoActions return 0; } + template + static INLINE_TEMPLATE_ARGS void LoopOverCollection(TBuffer &buf, void *iter, const void *end, Next_t next, Int_t /* nvalues */, const TStreamerElement *elem) + { + void *addr; + while( (addr = next(iter, end)) ) + { + action(buf, addr, elem); + } + } + + template + static INLINE_TEMPLATE_ARGS void ConvertLoopOverCollection(TBuffer &buf, void *iter, const void *end, Next_t next, Int_t nvalues, const TStreamerElement *elem) + { + Onfile *temp = new Onfile[nvalues]; + Int_t ind = 0; + void *addr; + while( (addr = next(iter, end)) ) + { + temp[ind] = (Onfile)*(Memory*)addr; + ++ind; + } + action(buf, temp, nvalues, elem); + delete [] temp; + } + + template + static INLINE_TEMPLATE_ARGS Int_t WriteNumericalCollection(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // Collection of numbers. Memberwise or not, it is all the same. + + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + TClass *newClass = config->fNewClass; + TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); + void *collection = ((char*)addr)+config->fOffset; + TVirtualCollectionProxy::TPushPop helper( newProxy, collection); + + Int_t nvalues = newProxy->Size(); + buf.WriteInt(nvalues); + if (nvalues) { + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin = &(startbuf[0]); + void *end = &(endbuf[0]); + config->fCreateIterators(collection, &begin, &end, newProxy); + + action(buf, begin, end, config->fNext, nvalues, config->fCompInfo->fElem); + + if (begin != &(startbuf[0])) { + // assert(end != endbuf); + config->fDeleteTwoIterators(begin, end); + } + } + buf.SetByteCount(start); + return 0; + } + +public: static INLINE_TEMPLATE_ARGS Int_t ReadCollectionFloat16(TBuffer &buf, void *addr, const TConfiguration *conf) { return ReadNumericalCollection(buf,addr,conf); } + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionFloat16(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection >(buf,addr,conf); + } + static INLINE_TEMPLATE_ARGS Int_t ReadCollectionDouble32(TBuffer &buf, void *addr, const TConfiguration *conf) { return ReadNumericalCollection(buf,addr,conf); } + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionDouble32(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection >(buf,addr,conf); + } + template static INLINE_TEMPLATE_ARGS Int_t ReadCollectionBasicType(TBuffer &buf, void *addr, const TConfiguration *conf) { return ReadNumericalCollection >(buf,addr,conf); } + template + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionBasicType(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection> >(buf,addr,conf); + } + template struct ConvertRead { static INLINE_TEMPLATE_ARGS void Action(TBuffer &buf, void *addr, Int_t nvalues) @@ -2306,7 +2923,6 @@ namespace TStreamerInfoActions delete [] temp; } }; - template struct ConvertCollectionBasicType { static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) @@ -2315,9 +2931,38 @@ namespace TStreamerInfoActions } }; + template + struct WriteConvertCollectionBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection> >(buf,addr,conf); + } + }; + template + struct WriteConvertCollectionBasicType> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection >(buf,addr,conf); + } + }; + + template + struct WriteConvertCollectionBasicType> { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection >(buf,addr,conf); + } + }; + }; - struct GenericLooper { + struct GenericLooper : public CollectionLooper { + using LoopAction_t = Int_t (*)(TBuffer &, void*, const void *end, const TLoopConfiguration *loopconfig, const TConfiguration*); + + template + using ReadStreamerLoop = CollectionLooper::ReadStreamerLoop; + template + using WriteStreamerLoop = CollectionLooper::WriteStreamerLoop; template static INLINE_TEMPLATE_ARGS Int_t ReadBasicType(TBuffer &buf, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config) @@ -2361,8 +3006,32 @@ namespace TStreamerInfoActions return 0; } + template + struct Write_WithoutFastArray_ConvertBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config) + { + TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; + + Next_t next = loopconfig->fNext; + const Int_t offset = config->fOffset; + + char iterator[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *iter = loopconfig->fCopyIterator(iterator,start); + void *addr; + while( (addr = next(iter,end)) ) { + From *x = (From*)( ((char*)addr) + offset ); + To t = (To)(*x); + buf << t; + } + if (iter != &iterator[0]) { + loopconfig->fDeleteIterator(iter); + } + return 0; + } + }; + template - static INLINE_TEMPLATE_ARGS Int_t ReadAction(TBuffer &buf, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config) + static INLINE_TEMPLATE_ARGS Int_t LoopOverCollection(TBuffer &buf, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config) { TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; @@ -2401,11 +3070,33 @@ namespace TStreamerInfoActions if (iter != &iterator[0]) { loopconfig->fDeleteIterator(iter); } + } + // ConvertAction used for writing. + static void WriteConvertAction(void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config, To *items) + { + TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; + + const Int_t offset = config->fOffset; + Next_t next = loopconfig->fNext; + + char iterator[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *iter = loopconfig->fCopyIterator(&iterator,start); + void *addr; + + while( (addr = next(iter,end)) ) { + From *x = (From*)( ((char*)addr) + offset ); + *items = (To)*x; + ++items; + } + if (iter != &iterator[0]) { + loopconfig->fDeleteIterator(iter); + } } }; template struct Numeric { + // ConvertAction used for reading. static void ConvertAction(From *items, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration * /* config */) { // The difference with ConvertAction is that we can modify the start @@ -2422,6 +3113,23 @@ namespace TStreamerInfoActions ++items; } } + // ConvertAction used for writing. + static void WriteConvertAction(void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration * /* config */, To *items) + { + // The difference with ConvertAction is that we can modify the start + // iterator and skip the copy. We also never have an offset. + + TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; + Next_t next = loopconfig->fNext; + + void *iter = start; + void *addr; + while( (addr = next(iter,end)) ) { + From *x = (From*)(addr); + *items = (To)*x; + ++items; + } + } }; template class Converter = Generic > @@ -2519,6 +3227,68 @@ namespace TStreamerInfoActions } }; + template class Converter = Generic > + struct WriteConvertBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory. + + TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; + TVirtualCollectionProxy *proxy = loopconfig->fProxy; + Int_t nvalues = proxy->Size(); + + Onfile *items = new Onfile[nvalues]; + Converter::WriteConvertAction(start, end, loopconfig, config, items); + buf.WriteFastArray(items, nvalues); + delete [] items; + return 0; + } + }; + + template class Converter > + struct WriteConvertBasicType, Memory, Converter > { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer & /* buf */, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory. + + TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; + TVirtualCollectionProxy *proxy = loopconfig->fProxy; + Int_t nvalues = proxy->Size(); + + TConfSTLNoFactor *conf = (TConfSTLNoFactor *)config; + + Onfile *items = new Onfile[nvalues]; + Converter::WriteConvertAction(start, end, loopconfig, config, items); + R__ASSERT(false && "Not yet implemented"); + (void)conf; + // buf.WriteFastArrayWithNbits(items, nvalues, conf->fNbits); + delete [] items; + return 0; + } + }; + + template class Converter > + struct WriteConvertBasicType, Memory, Converter > { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer & /* buf */, void *start, const void *end, const TLoopConfiguration *loopconf, const TConfiguration *config) + { + // Simple conversion from a 'From' on disk to a 'To' in memory. + + TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; + TVirtualCollectionProxy *proxy = loopconfig->fProxy; + Int_t nvalues = proxy->Size(); + + TConfSTLNoFactor *conf = (TConfSTLNoFactor *)config; + + Onfile *items = new Onfile[nvalues]; + Converter::WriteConvertAction(start, end, loopconfig, config, items); + R__ASSERT(false && "Not yet implemented"); + (void)conf; + // buf.WriteFastArrayWithNbits(items, nvalues, conf->fNbits); + delete [] items; + return 0; + } + }; + static INLINE_TEMPLATE_ARGS Int_t ReadBase(TBuffer &buf, void *start, const void *end, const TLoopConfiguration * loopconfig, const TConfiguration *config) { // Well the implementation is non trivial since we do not have a proxy for the container of _only_ the base class. For now @@ -2527,6 +3297,14 @@ namespace TStreamerInfoActions return GenericRead(buf,start,end,loopconfig, config); } + static INLINE_TEMPLATE_ARGS Int_t WriteBase(TBuffer &buf, void *start, const void *end, const TLoopConfiguration * loopconfig, const TConfiguration *config) + { + // Well the implementation is non trivial since we do not have a proxy for the container of _only_ the base class. For now + // punt. + + return GenericWrite(buf,start,end,loopconfig, config); + } + static INLINE_TEMPLATE_ARGS Int_t GenericRead(TBuffer &buf, void *, const void *, const TLoopConfiguration * loopconf, const TConfiguration *config) { TGenericLoopConfig *loopconfig = (TGenericLoopConfig*)loopconf; @@ -2552,6 +3330,11 @@ namespace TStreamerInfoActions buf.ReadWithNbits((float*)addr,12); } + static INLINE_TEMPLATE_ARGS void SimpleWriteFloat16(TBuffer &buf, void *addr) + { + buf.WriteFloat16((float*)addr, nullptr); + } + static INLINE_TEMPLATE_ARGS void SimpleReadDouble32(TBuffer &buf, void *addr) { //we read a float and convert it to double @@ -2560,6 +3343,13 @@ namespace TStreamerInfoActions *(double*)addr = (Double_t)afloat; } + static INLINE_TEMPLATE_ARGS void SimpleWriteDouble32(TBuffer &buf, void *addr) + { + //we read a float and convert it to double + Float_t afloat = (Float_t)*(double*)addr; + buf << afloat; + } + template static INLINE_TEMPLATE_ARGS Int_t ReadNumericalCollection(TBuffer &buf, void *addr, const TConfiguration *conf) { @@ -2599,9 +3389,51 @@ namespace TStreamerInfoActions return 0; } + template + static INLINE_TEMPLATE_ARGS Int_t WriteNumericalCollection(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // Collection of numbers. Memberwise or not, it is all the same. + + TConfigSTL *config = (TConfigSTL*)conf; + UInt_t start = buf.WriteVersion(config->fInfo->IsA(), kTRUE); + + TClass *newClass = config->fNewClass; + TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy(); + void *collection = ((char*)addr)+config->fOffset; + TVirtualCollectionProxy::TPushPop helper( newProxy, collection ); + + Int_t nvalues = newProxy->Size(); + buf.WriteInt(nvalues); + if (nvalues) { + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin = &(startbuf[0]); + void *end = &(endbuf[0]); + config->fCreateIterators(collection, &begin, &end, newProxy); + // We can not get here with a split vector of pointer, so we can indeed assume + // that actions->fConfiguration != null. + + TGenericLoopConfig loopconf(newProxy, /* read */ kTRUE); + ActionHolder::Action(buf,begin,end,&loopconf,config); + + if (begin != &(startbuf[0])) { + // assert(end != endbuf); + config->fDeleteTwoIterators(begin,end); + } + } + + buf.SetByteCount(start); + return 0; + } + static INLINE_TEMPLATE_ARGS Int_t ReadCollectionFloat16(TBuffer &buf, void *addr, const TConfiguration *conf) { - return ReadNumericalCollection,float,Numeric > >(buf,addr,conf); + return ReadNumericalCollection, float, Numeric > >(buf,addr,conf); + } + + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionFloat16(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection, float, Numeric > >(buf,addr,conf); } static INLINE_TEMPLATE_ARGS Int_t ReadCollectionDouble32(TBuffer &buf, void *addr, const TConfiguration *conf) @@ -2611,6 +3443,13 @@ namespace TStreamerInfoActions // return ReadNumericalCollection,double,Numeric > >(buf,addr,conf); } + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionDouble32(TBuffer &buf, void *addr, const TConfiguration *conf) + { + return WriteNumericalCollection >(buf,addr,conf); + // Could also use: + // return ReadNumericalCollection, double, Numeric > >(buf,addr,conf); + } + template static INLINE_TEMPLATE_ARGS Int_t ReadCollectionBasicType(TBuffer &buf, void *addr, const TConfiguration *conf) { @@ -2619,6 +3458,14 @@ namespace TStreamerInfoActions return ReadNumericalCollection >(buf,addr,conf); } + template + static INLINE_TEMPLATE_ARGS Int_t WriteCollectionBasicType(TBuffer &buf, void *addr, const TConfiguration *conf) + { + //TODO: Check whether we can implement this without loading the data in + // a temporary variable and whether this is noticeably faster. + return WriteNumericalCollection >(buf,addr,conf); + } + template struct ConvertCollectionBasicType { static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) @@ -2628,6 +3475,14 @@ namespace TStreamerInfoActions } }; + template + struct WriteConvertCollectionBasicType { + static INLINE_TEMPLATE_ARGS Int_t Action(TBuffer &buf, void *addr, const TConfiguration *conf) + { + // return ReadNumericalCollection::Action >(buf,addr,conf); + return WriteNumericalCollection >(buf,addr,conf); + } + }; }; } @@ -2637,25 +3492,24 @@ template static TConfiguredAction GetCollectionReadConvertAction(Int_t newtype, TConfiguration *conf) { switch (newtype) { - case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kFloat16: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kDouble32:return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kBits: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); break; + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kFloat16: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kDouble32:return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); + case TStreamerInfo::kBits: return TConfiguredAction( Looper::template ConvertBasicType::Action, conf ); default: return TConfiguredAction( Looper::GenericRead, conf ); - break; } R__ASSERT(0); // We should never be here return TConfiguredAction(); @@ -2670,48 +3524,46 @@ static TConfiguredAction GetNumericCollectionReadAction(Int_t type, TConfigSTL * // Read basic types. // Because of std::vector of bool is not backed up by an array of bool we have to converted it first. - case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ReadCollectionBasicType,conf ); break; - case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; - case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); break; + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ReadCollectionBasicType,conf ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ReadCollectionBasicType, conf ); case TStreamerInfo::kBits: Error("GetNumericCollectionReadAction","There is no support for kBits outside of a TObject."); break; case TStreamerInfo::kFloat16: { TConfigSTL *alternate = new TConfSTLNoFactor(conf,12); delete conf; return TConfiguredAction( Looper::ReadCollectionFloat16, alternate ); // if (element->GetFactor() != 0) { - // return TConfiguredAction( Looper::template ReadAction >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); // } else { // Int_t nbits = (Int_t)element->GetXmin(); // if (!nbits) nbits = 12; - // return TConfiguredAction( Looper::template ReadAction >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); // } - break; } case TStreamerInfo::kDouble32: { TConfigSTL *alternate = new TConfSTLNoFactor(conf,0); delete conf; return TConfiguredAction( Looper::ReadCollectionDouble32, alternate ); // if (element->GetFactor() != 0) { - // return TConfiguredAction( Looper::template ReadAction >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); // } else { // Int_t nbits = (Int_t)element->GetXmin(); // if (!nbits) { - // return TConfiguredAction( Looper::template ReadAction >, new TConfiguration(info,i,compinfo,offset) ); + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfiguration(info,i,compinfo,offset) ); // } else { - // return TConfiguredAction( Looper::template ReadAction >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); // } // } - break; } } Fatal("GetNumericCollectionReadAction","Is confused about %d",type); @@ -2723,22 +3575,22 @@ template static TConfiguredAction GetConvertCollectionReadActionFrom(Int_t newtype, TConfiguration *conf) { switch (newtype) { - case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kFloat16: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kDouble32:return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; - case TStreamerInfo::kBits: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); break; + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kFloat16: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kDouble32:return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kBits: return TConfiguredAction( Looper::template ConvertCollectionBasicType::Action, conf ); default: break; } @@ -2756,49 +3608,34 @@ static TConfiguredAction GetConvertCollectionReadAction(Int_t oldtype, Int_t new switch (oldtype) { case TStreamerInfo::kBool: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kChar: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kShort: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kInt: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kLong: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kLong64: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kFloat: return GetConvertCollectionReadActionFrom( newtype, conf ); - break; case TStreamerInfo::kDouble: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kUChar: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kUShort: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kUInt: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kULong: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kULong64: return GetConvertCollectionReadActionFrom(newtype, conf ); - break; case TStreamerInfo::kFloat16: return GetConvertCollectionReadActionFrom >( newtype, conf ); - break; case TStreamerInfo::kDouble32: return GetConvertCollectionReadActionFrom >( newtype, conf ); - break; case TStreamerInfo::kBits: Error("GetConvertCollectionReadAction","There is no support for kBits outside of a TObject."); break; @@ -2811,101 +3648,126 @@ static TConfiguredAction GetConvertCollectionReadAction(Int_t oldtype, Int_t new } template -static TConfiguredAction GetCollectionReadAction(TVirtualStreamerInfo *info, TStreamerElement *element, Int_t type, UInt_t i, TStreamerInfo::TCompInfo_t *compinfo, Int_t offset) +static TConfiguredAction +GetCollectionReadAction(TVirtualStreamerInfo *info, TLoopConfiguration *loopConfig, TStreamerElement *element, + Int_t type, UInt_t i, TStreamerInfo::TCompInfo_t *compinfo, Int_t offset) { switch (type) { // Read basic types. - case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ReadBasicType,new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kBits: return TConfiguredAction( Looper::template ReadAction > , new TBitsConfiguration(info,i,compinfo,offset) ); break; + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template ReadBasicType,new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template ReadBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kBits: return TConfiguredAction( Looper::template LoopOverCollection > , new TBitsConfiguration(info,i,compinfo,offset) ); case TStreamerInfo::kFloat16: { if (element->GetFactor() != 0) { - return TConfiguredAction( Looper::template ReadAction >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + return TConfiguredAction( Looper::template LoopOverCollection >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); } else { Int_t nbits = (Int_t)element->GetXmin(); if (!nbits) nbits = 12; - return TConfiguredAction( Looper::template ReadAction >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); + return TConfiguredAction( Looper::template LoopOverCollection >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); } - break; } case TStreamerInfo::kDouble32: { if (element->GetFactor() != 0) { - return TConfiguredAction( Looper::template ReadAction >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + return TConfiguredAction( Looper::template LoopOverCollection >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); } else { Int_t nbits = (Int_t)element->GetXmin(); if (!nbits) { - return TConfiguredAction( Looper::template ReadAction::Action >, new TConfiguration(info,i,compinfo,offset) ); + return TConfiguredAction( Looper::template LoopOverCollection::Action >, new TConfiguration(info,i,compinfo,offset) ); } else { - return TConfiguredAction( Looper::template ReadAction >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); + return TConfiguredAction( Looper::template LoopOverCollection >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); } } - break; } - case TStreamerInfo::kTNamed: return TConfiguredAction( Looper::template ReadAction, new TConfiguration(info,i,compinfo,offset) ); break; + case TStreamerInfo::kTNamed: + return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info, i, compinfo, offset) ); // Idea: We should calculate the CanIgnoreTObjectStreamer here and avoid calling the // Streamer alltogether. - case TStreamerInfo::kTObject: return TConfiguredAction( Looper::template ReadAction, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kTString: return TConfiguredAction( Looper::template ReadAction, new TConfiguration(info,i,compinfo,offset) ); break; + case TStreamerInfo::kTObject: + return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info, i, compinfo,offset) ); + case TStreamerInfo::kTString: + return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info, i, compinfo,offset) ); case TStreamerInfo::kArtificial: case TStreamerInfo::kCacheNew: case TStreamerInfo::kCacheDelete: - case TStreamerInfo::kSTL: return TConfiguredAction( Looper::GenericRead, new TGenericConfiguration(info,i,compinfo) ); break; - case TStreamerInfo::kBase: return TConfiguredAction( Looper::ReadBase, new TGenericConfiguration(info,i,compinfo) ); break; + case TStreamerInfo::kSTL: return TConfiguredAction( Looper::GenericRead, new TGenericConfiguration(info, i, compinfo) ); + case TStreamerInfo::kBase: { + TStreamerBase *baseEl = dynamic_cast(element); + if (baseEl) { + auto baseinfo = (TStreamerInfo *)baseEl->GetBaseStreamerInfo(); + assert(baseinfo); + TLoopConfiguration *baseLoopConfig = loopConfig ? loopConfig->Copy() : nullptr; + auto baseActions = Looper::CreateReadActionSquence(*baseinfo, baseLoopConfig); + baseActions->AddToOffset(baseEl->GetOffset()); + return TConfiguredAction( Looper::SubSequenceAction, new TConfSubSequence(info, i, compinfo, 0, std::move(baseActions))); + + } else + return TConfiguredAction( Looper::ReadBase, new TGenericConfiguration(info, i, compinfo) ); + } + case TStreamerInfo::kStreamLoop: + case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: { + bool isPtrPtr = (strstr(compinfo->fElem->GetTypeName(), "**") != 0); + return TConfiguredAction(Looper::template ReadStreamerLoop::Action, + new TConfStreamerLoop(info, i, compinfo, offset, isPtrPtr)); + } + case TStreamerInfo::kStreamer: + if (info->GetOldVersion() >= 3) + return TConfiguredAction( Looper::ReadStreamerCase, new TGenericConfiguration(info, i, compinfo) ); + else + return TConfiguredAction( Looper::GenericRead, new TGenericConfiguration(info, i, compinfo) ); + case TStreamerInfo::kAny: + if (compinfo->fStreamer) + return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info, i, compinfo,offset) ); + else { + if (compinfo->fNewClass && compinfo->fNewClass->HasDirectStreamerInfoUse()) + return TConfiguredAction( Looper::template LoopOverCollection, + new TConfObject(info, i, compinfo, compinfo->fOffset, compinfo->fClass, compinfo->fNewClass) ); + else if (compinfo->fClass && compinfo->fClass->HasDirectStreamerInfoUse()) + return TConfiguredAction( Looper::template LoopOverCollection, + new TConfObject(info, i, compinfo, compinfo->fOffset, compinfo->fClass, nullptr) ); + else // Use the slower path for unusual cases + return TConfiguredAction( Looper::GenericRead, new TGenericConfiguration(info, i, compinfo) ); + } // Conversions. case TStreamerInfo::kConv + TStreamerInfo::kBool: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kChar: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kShort: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kInt: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kLong: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kLong64: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kFloat: return GetCollectionReadConvertAction( element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kDouble: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kUChar: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kUShort: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kUInt: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kULong: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kULong64: return GetCollectionReadConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kBits: return GetCollectionReadConvertAction(element->GetNewType(), new TBitsConfiguration(info,i,compinfo,offset) ); - break; case TStreamerInfo::kConv + TStreamerInfo::kFloat16: { if (element->GetFactor() != 0) { return GetCollectionReadConvertAction >(element->GetNewType(), new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); @@ -2914,7 +3776,6 @@ static TConfiguredAction GetCollectionReadAction(TVirtualStreamerInfo *info, TSt if (!nbits) nbits = 12; return GetCollectionReadConvertAction >(element->GetNewType(), new TConfNoFactor(info,i,compinfo,offset,nbits) ); } - break; } case TStreamerInfo::kConv + TStreamerInfo::kDouble32: { if (element->GetFactor() != 0) { @@ -2927,42 +3788,338 @@ static TConfiguredAction GetCollectionReadAction(TVirtualStreamerInfo *info, TSt return GetCollectionReadConvertAction >(element->GetNewType(), new TConfNoFactor(info,i,compinfo,offset,nbits) ); } } - break; } default: return TConfiguredAction( Looper::GenericRead, new TGenericConfiguration(info,i,compinfo) ); + } + R__ASSERT(0); // We should never be here + return TConfiguredAction(); +} + +template +static TConfiguredAction GetConvertCollectionWriteActionFrom(Int_t onfileType, TConfiguration *conf) +{ + switch (onfileType) { + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kBits: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + + // Supporting this requires adding TBuffer::WiteFastArrayWithNbits + // and the proper struct WriteConvertCollectionBasicType> here + case TStreamerInfo::kFloat16: + Error("GetConvertCollectionWriteActionFrom", "Write Conversion to Float16_t not yet supported"); + return TConfiguredAction(); + // return TConfiguredAction( Looper::template WriteConvertCollectionBasicType>::Action, conf ); + case TStreamerInfo::kDouble32: + Error("GetConvertCollectionWriteActionFrom", "Write Conversion to Double32_t not yet supported"); + return TConfiguredAction(); + // return TConfiguredAction( Looper::template WriteConvertCollectionBasicType>::Action, conf ); + default: + break; + } + Error("GetConvertCollectionWriteActionFrom", "UNEXPECTED: onfileType/oldtype == %d", onfileType); + R__ASSERT(0); // We should never be here + return TConfiguredAction(); +} + +// Used in to implement the kSTL case +// Not to be confused with GetCollectionWriteConvertAction +// nor with GetConvertCollectionWriteActionFrom (used to implement this function) +template +static TConfiguredAction GetConvertCollectionWriteAction(Int_t onfileType, Int_t memoryType, TConfiguration *conf) +{ + switch (memoryType) { + case TStreamerInfo::kBool: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kChar: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kShort: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kInt: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kLong: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kLong64: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kFloat: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kDouble: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kUChar: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kUShort: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kUInt: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kULong: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kULong64: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kFloat16: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kDouble32: + return GetConvertCollectionWriteActionFrom(onfileType, conf ); + case TStreamerInfo::kBits: + Error("GetConvertCollectionWriteActionFrom","There is no support for kBits outside of a TObject."); + break; + default: + break; + } + Error("GetConvertCollectionWriteActionFrom", "UNEXPECTED: memoryType/newype == %d", memoryType); + R__ASSERT(0); // We should never be here + return TConfiguredAction(); +} + +// Used in GetCollectionWriteAction for the kConv cases within a collection +// Not to be confused with GetConvertCollectionWriteAction +// Note the read and write version could be merged (using yet another template parameter) +template +static TConfiguredAction GetCollectionWriteConvertAction(Int_t newtype, TConfiguration *conf) +{ + switch (newtype) { + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kFloat16: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kDouble32:return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + case TStreamerInfo::kBits: return TConfiguredAction( Looper::template WriteConvertBasicType::Action, conf ); + default: + return TConfiguredAction( Looper::GenericRead, conf ); + } + R__ASSERT(0); // We should never be here + return TConfiguredAction(); +} + +template +static TConfiguredAction +GetCollectionWriteAction(TVirtualStreamerInfo *info, TLoopConfiguration *loopConfig, TStreamerElement *element, + Int_t type, UInt_t i, TStreamerInfo::TCompInfo_t *compinfo, Int_t offset) +{ + switch (type) { + // write basic types + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template WriteBasicType,new TConfiguration(info,i,compinfo,offset) ); + // the simple type missing are kBits and kCounter. + + // Handling of the error case where we are asked to write a missing data member. + case TStreamerInfo::kBool + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kChar + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kShort + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kInt + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kLong + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kLong64 + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kFloat + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kDouble + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUChar + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUShort + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kUInt + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kULong + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kULong64 + TStreamerInfo::kSkip: + return TConfiguredAction( Looper::template LoopOverCollection>, new TConfiguration(info,i,compinfo,offset) ); + + // Conversions. + case TStreamerInfo::kConv + TStreamerInfo::kBool: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kChar: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kShort: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kInt: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kLong: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kLong64: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kFloat: + return GetCollectionWriteConvertAction( element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kDouble: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kUChar: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kUShort: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kUInt: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kULong: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kConv + TStreamerInfo::kULong64: + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); +#ifdef NOT_YET + /* The conversion writing acceleration was not yet written for kBits */ + case TStreamerInfo::kConv + TStreamerInfo::kBits: + return GetCollectionWriteConvertAction(element->GetNewType(), new TBitsConfiguration(info,i,compinfo,offset) ); +#endif + case TStreamerInfo::kConv + TStreamerInfo::kFloat16: { + if (element->GetFactor() != 0) { + return GetCollectionWriteConvertAction >(element->GetNewType(), new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + } else { + Int_t nbits = (Int_t)element->GetXmin(); + if (!nbits) nbits = 12; + return GetCollectionWriteConvertAction >(element->GetNewType(), new TConfNoFactor(info,i,compinfo,offset,nbits) ); + } + break; + } + case TStreamerInfo::kConv + TStreamerInfo::kDouble32: { + if (element->GetFactor() != 0) { + return GetCollectionWriteConvertAction >(element->GetNewType(), new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + } else { + Int_t nbits = (Int_t)element->GetXmin(); + if (!nbits) { + return GetCollectionWriteConvertAction(element->GetNewType(), new TConfiguration(info,i,compinfo,offset) ); + } else { + return GetCollectionWriteConvertAction >(element->GetNewType(), new TConfNoFactor(info,i,compinfo,offset,nbits) ); + } + } break; + } + case TStreamerInfo::kTNamed: return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info,i,compinfo,offset) ); + // Idea: We should calculate the CanIgnoreTObjectStreamer here and avoid calling the + // Streamer alltogether. + case TStreamerInfo::kTObject: return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kTString: return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info,i,compinfo,offset) ); + case TStreamerInfo::kBase: { + TStreamerBase *baseEl = dynamic_cast(element); + if (baseEl) { + auto baseinfo = (TStreamerInfo *)baseEl->GetBaseStreamerInfo(); + assert(baseinfo); + TLoopConfiguration *baseLoopConfig = loopConfig ? loopConfig->Copy() : nullptr; + auto baseActions = Looper::CreateWriteActionSquence(*baseinfo, baseLoopConfig); + baseActions->AddToOffset(baseEl->GetOffset()); + return TConfiguredAction( Looper::SubSequenceAction, new TConfSubSequence(info, i, compinfo, 0, std::move(baseActions))); + + } else + return TConfiguredAction( Looper::WriteBase, new TGenericConfiguration(info, i, compinfo) ); + } + case TStreamerInfo::kStreamLoop: + case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: { + bool isPtrPtr = (strstr(compinfo->fElem->GetTypeName(), "**") != 0); + return TConfiguredAction(Looper::template WriteStreamerLoop::Action, + new TConfStreamerLoop(info, i, compinfo, offset, isPtrPtr)); + } + case TStreamerInfo::kStreamer: + if (info->GetOldVersion() >= 3) + return TConfiguredAction( Looper::WriteStreamerCase, new TGenericConfiguration(info, i, compinfo) ); + else + return TConfiguredAction( Looper::GenericWrite, new TGenericConfiguration(info, i, compinfo) ); + case TStreamerInfo::kAny: + if (compinfo->fStreamer) + return TConfiguredAction( Looper::template LoopOverCollection, new TConfiguration(info, i, compinfo,offset) ); + else { + if (compinfo->fNewClass && compinfo->fNewClass->HasDirectStreamerInfoUse()) + return TConfiguredAction( Looper::template LoopOverCollection, + new TConfObject(info, i, compinfo, compinfo->fOffset, compinfo->fClass, compinfo->fNewClass) ); + else if (compinfo->fClass && compinfo->fClass->HasDirectStreamerInfoUse()) + return TConfiguredAction( Looper::template LoopOverCollection, + new TConfObject(info, i, compinfo, compinfo->fOffset, compinfo->fClass, nullptr) ); + else // Use the slower path for unusual cases + return TConfiguredAction( Looper::GenericWrite, new TGenericConfiguration(info, i, compinfo) ); + } + default: + return TConfiguredAction( Looper::GenericWrite, new TConfiguration(info,i,compinfo,0 /* 0 because we call the legacy code */) ); } R__ASSERT(0); // We should never be here return TConfiguredAction(); } template -static TConfiguredAction GetCollectionWriteAction(TVirtualStreamerInfo *info, TStreamerElement * /*element*/, Int_t type, UInt_t i, TStreamerInfo::TCompInfo_t *compinfo, Int_t offset) { +static TConfiguredAction GetNumericCollectionWriteAction(Int_t type, TConfigSTL *conf) +{ + // If we ever support std::vector fValues; //[...] we would get the info from the StreamerElement for fValues. + switch (type) { - // read basic types - case TStreamerInfo::kBool: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kChar: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kShort: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kInt: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kLong: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kULong: return TConfiguredAction( Looper::template WriteBasicType, new TConfiguration(info,i,compinfo,offset) ); break; - case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template WriteBasicType,new TConfiguration(info,i,compinfo,offset) ); break; - // the simple type missing are kBits and kCounter. - default: - return TConfiguredAction( Looper::GenericWrite, new TConfiguration(info,i,compinfo,0 /* 0 because we call the legacy code */) ); + // Write basic types. + // Because of std::vector of bool is not backed up by an array of bool we have to converted it first. + case TStreamerInfo::kBool: return TConfiguredAction( Looper::template WriteConvertCollectionBasicType::Action, conf ); + case TStreamerInfo::kChar: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kShort: return TConfiguredAction( Looper::template WriteCollectionBasicType,conf ); + case TStreamerInfo::kInt: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kLong: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kLong64: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kFloat: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kDouble: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kUChar: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kUShort: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kUInt: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kULong: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kULong64: return TConfiguredAction( Looper::template WriteCollectionBasicType, conf ); + case TStreamerInfo::kBits: Error("GetNumericCollectionWriteAction","There is no support for kBits outside of a TObject."); break; + case TStreamerInfo::kFloat16: { + TConfigSTL *alternate = new TConfSTLNoFactor(conf,12); + delete conf; + return TConfiguredAction( Looper::WriteCollectionFloat16, alternate ); + // if (element->GetFactor() != 0) { + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + // } else { + // Int_t nbits = (Int_t)element->GetXmin(); + // if (!nbits) nbits = 12; + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); + // } + } + case TStreamerInfo::kDouble32: { + TConfigSTL *alternate = new TConfSTLNoFactor(conf,0); + delete conf; + return TConfiguredAction( Looper::WriteCollectionDouble32, alternate ); + // if (element->GetFactor() != 0) { + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); + // } else { + // Int_t nbits = (Int_t)element->GetXmin(); + // if (!nbits) { + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfiguration(info,i,compinfo,offset) ); + // } else { + // return TConfiguredAction( Looper::template LoopOverCollection >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); + // } + // } + } } + Fatal("GetNumericCollectionWriteAction","Is confused about %d",type); R__ASSERT(0); // We should never be here return TConfiguredAction(); } - //////////////////////////////////////////////////////////////////////////////// /// loop on the TStreamerElement list /// regroup members with same type @@ -3216,7 +4373,7 @@ static void AddReadConvertAction(TStreamerInfoActions::TActionSequence *sequence switch (newtype) { case TStreamerInfo::kBool: sequence->AddAction( ConvertBasicType::Action, conf ); break; case TStreamerInfo::kChar: sequence->AddAction( ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kShort: sequence->AddAction( ConvertBasicType::Action, conf ); break; + case TStreamerInfo::kShort: sequence->AddAction( ConvertBasicType::Action, conf ); break; case TStreamerInfo::kInt: sequence->AddAction( ConvertBasicType::Action, conf ); break; case TStreamerInfo::kLong: sequence->AddAction( ConvertBasicType::Action,conf ); break; case TStreamerInfo::kLong64: sequence->AddAction( ConvertBasicType::Action, conf ); break; @@ -3228,11 +4385,36 @@ static void AddReadConvertAction(TStreamerInfoActions::TActionSequence *sequence case TStreamerInfo::kUShort: sequence->AddAction( ConvertBasicType::Action, conf ); break; case TStreamerInfo::kUInt: sequence->AddAction( ConvertBasicType::Action, conf ); break; case TStreamerInfo::kULong: sequence->AddAction( ConvertBasicType::Action, conf ); break; - case TStreamerInfo::kULong64: sequence->AddAction( ConvertBasicType::Action,conf ); break; + case TStreamerInfo::kULong64: sequence->AddAction( ConvertBasicType::Action,conf ); break; case TStreamerInfo::kBits: sequence->AddAction( ConvertBasicType::Action, conf ); break; } } +template +static void AddWriteConvertAction(TStreamerInfoActions::TActionSequence *sequence, Int_t newtype, TConfiguration *conf) +{ + // When writing 'newtype' is the origin of the information (i.e. the in memory representation) + // and the template parameter `To` is the representation on disk + switch (newtype) { + case TStreamerInfo::kBool: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kChar: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kShort: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kInt: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kLong: sequence->AddAction( WriteConvertBasicType::Action,conf ); break; + case TStreamerInfo::kLong64: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kFloat: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kFloat16: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kDouble: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kDouble32:sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kUChar: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kUShort: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kUInt: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kULong: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + case TStreamerInfo::kULong64: sequence->AddAction( WriteConvertBasicType::Action,conf ); break; + case TStreamerInfo::kBits: sequence->AddAction( WriteConvertBasicType::Action, conf ); break; + } +} + //////////////////////////////////////////////////////////////////////////////// /// Add a read action for the given element. @@ -3295,60 +4477,60 @@ void TStreamerInfo::AddReadAction(TStreamerInfoActions::TActionSequence *readSeq if (fOldVersion<3){ // case of old TStreamerInfo if (newClass && newClass != oldClass) { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase)); } } else { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase)); } } } else { if (newClass && newClass != oldClass) { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else if (oldClass) { if (oldClass->GetCollectionProxy() == 0 || oldClass->GetCollectionProxy()->GetValueClass() || oldClass->GetCollectionProxy()->HasPointers() ) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase)); } else { switch (SelectLooper(*newClass->GetCollectionProxy())) { case kVectorLooper: - readSequence->AddAction(GetConvertCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase))); + readSequence->AddAction(GetConvertCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase))); break; case kAssociativeLooper: - readSequence->AddAction(GetConvertCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase))); + readSequence->AddAction(GetConvertCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase))); break; case kVectorPtrLooper: case kGenericLooper: default: // For now TBufferXML would force use to allocate the data buffer each time and copy into the real thing. - readSequence->AddAction(GetConvertCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase))); + readSequence->AddAction(GetConvertCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase))); break; } } } } else { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else if (oldClass) { if (oldClass->GetCollectionProxy() == 0 || oldClass->GetCollectionProxy()->GetValueClass() || oldClass->GetCollectionProxy()->HasPointers() ) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase)); } else { switch (SelectLooper(*oldClass->GetCollectionProxy())) { case kVectorLooper: - readSequence->AddAction(GetNumericCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase))); + readSequence->AddAction(GetNumericCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase))); break; case kAssociativeLooper: - readSequence->AddAction(GetNumericCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase))); + readSequence->AddAction(GetNumericCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase))); break; case kVectorPtrLooper: case kGenericLooper: default: // For now TBufferXML would force use to allocate the data buffer each time and copy into the real thing. - readSequence->AddAction(GetNumericCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase))); + readSequence->AddAction(GetNumericCollectionReadAction(oldClass->GetCollectionProxy()->GetType(), new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase))); break; } } @@ -3359,29 +4541,29 @@ void TStreamerInfo::AddReadAction(TStreamerInfoActions::TActionSequence *readSeq if (fOldVersion<3){ // case of old TStreamerInfo if (newClass && newClass != oldClass) { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetTypeName(),isSTLbase)); } } else { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetTypeName(),isSTLbase)); } } } else { if (newClass && newClass != oldClass) { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetTypeName(),isSTLbase)); } } else { if (element->GetStreamer()) { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); } else { - readSequence->AddAction(ReadSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetTypeName(),isSTLbase)); + readSequence->AddAction(ReadSTL, new TConfigSTL(/*read = */ true, this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetTypeName(),isSTLbase)); } } } @@ -3454,6 +4636,54 @@ void TStreamerInfo::AddReadAction(TStreamerInfoActions::TActionSequence *readSeq } break; } + case TStreamerInfo::kStreamLoop: + case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: { + bool isPtrPtr = (strstr(compinfo->fElem->GetTypeName(), "**") != 0); + readSequence->AddAction(ScalarLooper::ReadStreamerLoop::Action, + new TConfStreamerLoop(this, i, compinfo, compinfo->fOffset, isPtrPtr)); + break; + } + case TStreamerInfo::kBase: + if (compinfo->fStreamer) + readSequence->AddAction( ReadStreamerCase, new TGenericConfiguration(this,i,compinfo, compinfo->fOffset) ); + else { + auto base = dynamic_cast(element); + auto onfileBaseCl = base ? base->GetClassPointer() : nullptr; + auto memoryBaseCl = base && base->GetNewBaseClass() ? base->GetNewBaseClass() : onfileBaseCl; + + if(!base || !memoryBaseCl || + memoryBaseCl->GetStreamer() || + memoryBaseCl->GetStreamerFunc() || memoryBaseCl->GetConvStreamerFunc()) + { + // Unusual Case. + readSequence->AddAction( GenericReadAction, new TGenericConfiguration(this,i,compinfo) ); + } + else + readSequence->AddAction( ReadViaClassBuffer, + new TConfObject(this, i, compinfo, compinfo->fOffset, onfileBaseCl, memoryBaseCl) ); + } + break; + case TStreamerInfo::kStreamer: + if (fOldVersion >= 3) + readSequence->AddAction( ReadStreamerCase, new TGenericConfiguration(this,i,compinfo, compinfo->fOffset) ); + else + // Use the slower path for legacy files + readSequence->AddAction( GenericReadAction, new TGenericConfiguration(this,i,compinfo) ); + break; + case TStreamerInfo::kAny: + if (compinfo->fStreamer) + readSequence->AddAction( ReadViaExtStreamer, new TGenericConfiguration(this, i, compinfo, compinfo->fOffset) ); + else { + if (compinfo->fNewClass && compinfo->fNewClass->HasDirectStreamerInfoUse()) + readSequence->AddAction( ReadViaClassBuffer, + new TConfObject(this, i, compinfo, compinfo->fOffset, compinfo->fClass, compinfo->fNewClass) ); + else if (compinfo->fClass && compinfo->fClass->HasDirectStreamerInfoUse()) + readSequence->AddAction( ReadViaClassBuffer, + new TConfObject(this, i, compinfo, compinfo->fOffset, compinfo->fClass, nullptr) ); + else // Use the slower path for unusual cases + readSequence->AddAction( GenericReadAction, new TGenericConfiguration(this, i, compinfo) ); + } + break; default: readSequence->AddAction( GenericReadAction, new TGenericConfiguration(this,i,compinfo) ); break; @@ -3504,18 +4734,20 @@ void TStreamerInfo::AddReadTextAction(TStreamerInfoActions::TActionSequence *rea case TStreamerInfo::kSTLp: // Pointer to container with no virtual table (stl) and no comment case TStreamerInfo::kSTLp + TStreamerInfo::kOffsetL: // array of pointers to container with no virtual table (stl) and no comment - readSequence->AddAction(ReadSTLp, new TConfiguration(this, i, compinfo, compinfo->fOffset)); + readSequence->AddAction(TextReadSTLp, new TConfiguration(this, i, compinfo, compinfo->fOffset)); break; case TStreamerInfo::kStreamLoop: - case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: - readSequence->AddAction(ReadStreamerLoop, new TConfiguration(this, i, compinfo, compinfo->fOffset)); + case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: { + bool isPtrPtr = (strstr(compinfo->fElem->GetTypeName(), "**") != 0); + readSequence->AddAction(ScalarLooper::ReadStreamerLoop::Action, + new TConfStreamerLoop(this, i, compinfo, compinfo->fOffset, isPtrPtr)); break; - + } case TStreamerInfo::kBase: isBase = kTRUE; break; case TStreamerInfo::kStreamer: - readSequence->AddAction(ReadTextStreamer, new TGenericConfiguration(this, i, compinfo)); + readSequence->AddAction(ReadStreamerCase, new TGenericConfiguration(this, i, compinfo)); break; default: generic = kTRUE; break; @@ -3523,7 +4755,7 @@ void TStreamerInfo::AddReadTextAction(TStreamerInfoActions::TActionSequence *rea if (isBase) { if (compinfo->fStreamer) { - readSequence->AddAction(ReadTextStreamer, new TGenericConfiguration(this, i, compinfo)); + readSequence->AddAction(ReadStreamerCase, new TGenericConfiguration(this, i, compinfo)); } else { readSequence->AddAction(ReadTextBaseClass, new TGenericConfiguration(this, i, compinfo)); } @@ -3542,10 +4774,10 @@ void TStreamerInfo::AddReadMemberWiseVecPtrAction(TStreamerInfoActions::TActionS if (element->TestBit(TStreamerElement::kWrite)) return; if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action( GetCollectionReadAction(this,element,compinfo->fType,i,compinfo,compinfo->fOffset) ); + TConfiguredAction action( GetCollectionReadAction(this,nullptr,element,compinfo->fType,i,compinfo,compinfo->fOffset) ); readSequence->AddAction( UseCacheVectorPtrLoop, new TConfigurationUseCache(this,action,element->TestBit(TStreamerElement::kRepeat)) ); } else { - readSequence->AddAction( GetCollectionReadAction(this,element,compinfo->fType,i,compinfo,compinfo->fOffset) ); + readSequence->AddAction( GetCollectionReadAction(this,nullptr,element,compinfo->fType,i,compinfo,compinfo->fOffset) ); } } @@ -3577,6 +4809,212 @@ void TStreamerInfo::AddWriteAction(TStreamerInfoActions::TActionSequence *writeS case TStreamerInfo::kUInt: writeSequence->AddAction( WriteBasicType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; case TStreamerInfo::kULong: writeSequence->AddAction( WriteBasicType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; case TStreamerInfo::kULong64: writeSequence->AddAction( WriteBasicType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; + + case TStreamerInfo::kConv + TStreamerInfo::kChar: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kShort: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kInt: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kLong: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kLong64: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kUChar: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kUShort: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kUInt: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kULong: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + case TStreamerInfo::kConv + TStreamerInfo::kULong64: + AddWriteConvertAction(writeSequence, compinfo->fNewType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); + break; + + case kSTL: + { + TClass *newClass = element->GetNewClass(); + TClass *onfileClass = element->GetClassPointer(); + Bool_t isSTLbase = element->IsBase() && element->IsA()!=TStreamerBase::Class(); + + if (element->GetArrayLength() <= 1) { + if (newClass && newClass != onfileClass) { + if (element->GetStreamer()) { + writeSequence->AddAction(WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, + newClass, element->GetStreamer(), element->GetTypeName(), + isSTLbase)); + } else if (onfileClass) { + if (onfileClass->GetCollectionProxy() == 0 || onfileClass->GetCollectionProxy()->GetValueClass() || + onfileClass->GetCollectionProxy()->HasPointers()) { + writeSequence->AddAction( + WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, newClass, + element->GetTypeName(), isSTLbase)); + } else { + switch (SelectLooper(*newClass->GetCollectionProxy())) { + case kVectorLooper: + writeSequence->AddAction(GetConvertCollectionWriteAction( + onfileClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, newClass, + element->GetTypeName(), isSTLbase))); + break; + case kAssociativeLooper: + writeSequence->AddAction(GetConvertCollectionWriteAction( + onfileClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, newClass, + element->GetTypeName(), isSTLbase))); + break; + case kVectorPtrLooper: + case kGenericLooper: + default: + // For now TBufferXML would force use to allocate the data buffer each time and copy into the + // real thing. + writeSequence->AddAction(GetConvertCollectionWriteAction( + onfileClass->GetCollectionProxy()->GetType(), newClass->GetCollectionProxy()->GetType(), + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, newClass, + element->GetTypeName(), isSTLbase))); + break; + } + } + } + } else { + if (element->GetStreamer()) { + writeSequence->AddAction(WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, + element->GetStreamer(), element->GetTypeName(), + isSTLbase)); + } else if (onfileClass) { + if (onfileClass->GetCollectionProxy() == 0 || + onfileClass->GetCollectionProxy()->GetValueClass() || + onfileClass->GetCollectionProxy()->HasPointers()) { + writeSequence->AddAction(WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, + element->GetTypeName(), isSTLbase)); + } else { + switch (SelectLooper(*onfileClass->GetCollectionProxy())) { + case kVectorLooper: + writeSequence->AddAction(GetNumericCollectionWriteAction( + onfileClass->GetCollectionProxy()->GetType(), + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, element->GetTypeName(), + isSTLbase))); + break; + case kAssociativeLooper: + writeSequence->AddAction(GetNumericCollectionWriteAction( + onfileClass->GetCollectionProxy()->GetType(), + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, element->GetTypeName(), + isSTLbase))); + break; + case kVectorPtrLooper: + case kGenericLooper: + default: + // For now TBufferXML would force use to allocate the data buffer each time and copy into the + // real thing. + writeSequence->AddAction(GetNumericCollectionWriteAction( + onfileClass->GetCollectionProxy()->GetType(), + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, 1, onfileClass, element->GetTypeName(), + isSTLbase))); + break; + } + } + } + } + } else { + if (newClass && newClass != onfileClass) { + if (element->GetStreamer()) { + writeSequence->AddAction( + WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, element->GetArrayLength(), onfileClass, + newClass, element->GetStreamer(), element->GetTypeName(), isSTLbase)); + } else { + writeSequence->AddAction( + WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, element->GetArrayLength(), onfileClass, + newClass, element->GetTypeName(), isSTLbase)); + } + } else { + if (element->GetStreamer()) { + writeSequence->AddAction(WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, + element->GetArrayLength(), onfileClass, + element->GetStreamer(), element->GetTypeName(), isSTLbase)); + } else { + writeSequence->AddAction(WriteSTL, + new TConfigSTL(/*read = */ false, this, i, compinfo, compinfo->fOffset, + element->GetArrayLength(), onfileClass, + element->GetTypeName(), isSTLbase)); + } + } + } + break; + } + + case TStreamerInfo::kTNamed: writeSequence->AddAction( WriteTNamed, new TConfiguration(this, i, compinfo, compinfo->fOffset) ); break; + // Idea: We should calculate the CanIgnoreTObjectStreamer here and avoid calling the + // Streamer alltogether. + case TStreamerInfo::kTObject: writeSequence->AddAction( WriteTObject, new TConfiguration(this, i, compinfo, compinfo->fOffset) ); break; + case TStreamerInfo::kTString: writeSequence->AddAction( WriteTString, new TConfiguration(this, i, compinfo, compinfo->fOffset) ); break; + + case TStreamerInfo::kStreamLoop: + case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: { + bool isPtrPtr = (strstr(compinfo->fElem->GetTypeName(), "**") != 0); + writeSequence->AddAction(ScalarLooper::WriteStreamerLoop::Action, + new TConfStreamerLoop(this, i, compinfo, compinfo->fOffset, isPtrPtr)); + break; + } + case TStreamerInfo::kBase: + if (compinfo->fStreamer) + writeSequence->AddAction( WriteStreamerCase, new TGenericConfiguration(this,i,compinfo, compinfo->fOffset) ); + else { + auto base = dynamic_cast(element); + auto onfileBaseCl = base ? base->GetClassPointer() : nullptr; + auto memoryBaseCl = base && base->GetNewBaseClass() ? base->GetNewBaseClass() : onfileBaseCl; + + if(!base || !memoryBaseCl || + memoryBaseCl->GetStreamer() || + memoryBaseCl->GetStreamerFunc() || memoryBaseCl->GetConvStreamerFunc()) + { + // Unusual Case. + writeSequence->AddAction( GenericWriteAction, new TGenericConfiguration(this,i,compinfo) ); + } + else { + writeSequence->AddAction( WriteViaClassBuffer, + new TConfObject(this, i, compinfo, compinfo->fOffset, onfileBaseCl, memoryBaseCl) ); + } + } + break; + case TStreamerInfo::kStreamer: + if (fOldVersion >= 3) + writeSequence->AddAction( WriteStreamerCase, new TGenericConfiguration(this,i,compinfo, compinfo->fOffset) ); + else + // Use the slower path for legacy files + writeSequence->AddAction( GenericWriteAction, new TGenericConfiguration(this,i,compinfo) ); + break; + case TStreamerInfo::kAny: + if (compinfo->fStreamer) + writeSequence->AddAction( WriteViaExtStreamer, new TGenericConfiguration(this, i, compinfo, compinfo->fOffset) ); + else { + if (compinfo->fNewClass && compinfo->fNewClass->fStreamerImpl == &TClass::StreamerStreamerInfo) + writeSequence->AddAction( WriteViaClassBuffer, + new TConfObject(this, i, compinfo, compinfo->fOffset, compinfo->fClass, compinfo->fNewClass) ); + else if (compinfo->fClass && compinfo->fClass->fStreamerImpl == &TClass::StreamerStreamerInfo) + writeSequence->AddAction( WriteViaClassBuffer, + new TConfObject(this, i, compinfo, compinfo->fOffset, compinfo->fClass, nullptr) ); + else // Use the slower path for unusual cases + writeSequence->AddAction( GenericWriteAction, new TGenericConfiguration(this, i, compinfo) ); + } + break; + // case TStreamerInfo::kBits: writeSequence->AddAction( WriteBasicType, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; /*case TStreamerInfo::kFloat16: { if (element->GetFactor() != 0) { @@ -3600,59 +5038,11 @@ void TStreamerInfo::AddWriteAction(TStreamerInfoActions::TActionSequence *writeS } } break; - } */ - //case TStreamerInfo::kTNamed: writeSequence->AddAction( WriteTNamed, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; - // Idea: We should calculate the CanIgnoreTObjectStreamer here and avoid calling the - // Streamer alltogether. - //case TStreamerInfo::kTObject: writeSequence->AddAction( WriteTObject, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; - //case TStreamerInfo::kTString: writeSequence->AddAction( WriteTString, new TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; - /*case TStreamerInfo::kSTL: { - TClass *newClass = element->GetNewClass(); - TClass *oldClass = element->GetClassPointer(); - Bool_t isSTLbase = element->IsBase() && element->IsA()!=TStreamerBase::Class(); - - if (element->GetArrayLength() <= 1) { - if (newClass && newClass != oldClass) { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase)); - } - } else { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase)); - } - } - } else { - if (newClass && newClass != oldClass) { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetTypeName(),isSTLbase)); - } - } else { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetTypeName(),isSTLbase)); - } - } - } - break; } */ default: writeSequence->AddAction( GenericWriteAction, new TGenericConfiguration(this,i,compinfo) ); break; } -#if defined(CDJ_NO_COMPILE) - if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action( writeSequence->fActions.back() ); // Action is moved, we must pop it next. - writeSequence->fActions.pop_back(); - writeSequence->AddAction( UseCache, new TConfigurationUseCache(this,action,element->TestBit(TStreamerElement::kRepeat)) ); - } -#endif } //////////////////////////////////////////////////////////////////////////////// @@ -3674,43 +5064,32 @@ void TStreamerInfo::AddWriteTextAction(TStreamerInfoActions::TActionSequence *wr switch (compinfo->fType) { // write basic types case TStreamerInfo::kBool: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kChar: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kShort: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kInt: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kLong: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kLong64: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kFloat: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kDouble: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kUChar: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kUShort: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kUInt: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kULong: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); - break; case TStreamerInfo::kULong64: - writeSequence->AddAction(WriteBasicType, new TConfiguration(this, i, compinfo, compinfo->fOffset)); + case TStreamerInfo::kFloat16: + case TStreamerInfo::kDouble32: + case TStreamerInfo::kConv + TStreamerInfo::kChar: + case TStreamerInfo::kConv + TStreamerInfo::kShort: + case TStreamerInfo::kConv + TStreamerInfo::kInt: + case TStreamerInfo::kConv + TStreamerInfo::kLong: + case TStreamerInfo::kConv + TStreamerInfo::kLong64: + case TStreamerInfo::kConv + TStreamerInfo::kUChar: + case TStreamerInfo::kConv + TStreamerInfo::kUShort: + case TStreamerInfo::kConv + TStreamerInfo::kUInt: + case TStreamerInfo::kConv + TStreamerInfo::kULong: + case TStreamerInfo::kConv + TStreamerInfo::kULong64: + // Actually same action for this level + AddWriteAction(writeSequence, i, compinfo); break; case TStreamerInfo::kTObject: @@ -3730,108 +5109,27 @@ void TStreamerInfo::AddWriteTextAction(TStreamerInfoActions::TActionSequence *wr case TStreamerInfo::kSTLp: // Pointer to container with no virtual table (stl) and no comment case TStreamerInfo::kSTLp + TStreamerInfo::kOffsetL: // array of pointers to container with no virtual table (stl) and no comment - writeSequence->AddAction(WriteSTLp, new TConfiguration(this, i, compinfo, compinfo->fOffset)); + writeSequence->AddAction(TextWriteSTLp, new TConfiguration(this, i, compinfo, compinfo->fOffset)); break; case TStreamerInfo::kStreamLoop: - case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: - writeSequence->AddAction(WriteStreamerLoop, new TConfiguration(this, i, compinfo, compinfo->fOffset)); + case TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop: { + bool isPtrPtr = (strstr(compinfo->fElem->GetTypeName(), "**") != 0); + writeSequence->AddAction(ScalarLooper::WriteStreamerLoop::Action, new TConfStreamerLoop(this, i, compinfo, compinfo->fOffset, isPtrPtr)); break; - + } case TStreamerInfo::kBase: isBase = kTRUE; break; case TStreamerInfo::kStreamer: - writeSequence->AddAction(WriteTextStreamer, new TGenericConfiguration(this, i, compinfo)); + writeSequence->AddAction(WriteStreamerCase, new TGenericConfiguration(this, i, compinfo)); break; - // case TStreamerInfo::kBits: writeSequence->AddAction( WriteBasicType, new - // TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; - /*case TStreamerInfo::kFloat16: { - if (element->GetFactor() != 0) { - writeSequence->AddAction( WriteBasicType_WithFactor, new - TConfWithFactor(this,i,compinfo,compinfo->fOffset,element->GetFactor(),element->GetXmin()) ); - } else { - Int_t nbits = (Int_t)element->GetXmin(); - if (!nbits) nbits = 12; - writeSequence->AddAction( WriteBasicType_NoFactor, new - TConfNoFactor(this,i,compinfo,compinfo->fOffset,nbits) ); - } - break; - } */ - /*case TStreamerInfo::kDouble32: { - if (element->GetFactor() != 0) { - writeSequence->AddAction( WriteBasicType_WithFactor, new - TConfWithFactor(this,i,compinfo,compinfo->fOffset,element->GetFactor(),element->GetXmin()) ); - } else { - Int_t nbits = (Int_t)element->GetXmin(); - if (!nbits) { - writeSequence->AddAction( ConvertBasicType, new - TConfiguration(this,i,compinfo,compinfo->fOffset) ); - } else { - writeSequence->AddAction( WriteBasicType_NoFactor, new - TConfNoFactor(this,i,compinfo,compinfo->fOffset,nbits) ); - } - } - break; - } */ - // case TStreamerInfo::kTNamed: writeSequence->AddAction( WriteTNamed, new - // TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; - // Idea: We should calculate the CanIgnoreTObjectStreamer here and avoid calling the - // Streamer alltogether. - // case TStreamerInfo::kTObject: writeSequence->AddAction( WriteTObject, new - // TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; - // case TStreamerInfo::kTString: writeSequence->AddAction( WriteTString, new - // TConfiguration(this,i,compinfo,compinfo->fOffset) ); break; - /*case TStreamerInfo::kSTL: { - TClass *newClass = element->GetNewClass(); - TClass *oldClass = element->GetClassPointer(); - Bool_t isSTLbase = element->IsBase() && element->IsA()!=TStreamerBase::Class(); - - if (element->GetArrayLength() <= 1) { - if (newClass && newClass != oldClass) { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,newClass,element->GetTypeName(),isSTLbase)); - } - } else { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,1,oldClass,element->GetTypeName(),isSTLbase)); - } - } - } else { - if (newClass && newClass != oldClass) { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,newClass,element->GetTypeName(),isSTLbase)); - } - } else { - if (element->GetStreamer()) { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetStreamer(),element->GetTypeName(),isSTLbase)); - } else { - writeSequence->AddAction(WriteSTL, new - TConfigSTL(this,i,compinfo,compinfo->fOffset,element->GetArrayLength(),oldClass,element->GetTypeName(),isSTLbase)); - } - } - } - break; - } */ default: generic = kTRUE; break; } if (isBase) { if (compinfo->fStreamer) { - writeSequence->AddAction(WriteTextStreamer, new TGenericConfiguration(this, i, compinfo)); + writeSequence->AddAction(WriteStreamerCase, new TGenericConfiguration(this, i, compinfo)); } else { writeSequence->AddAction(WriteTextBaseClass, new TGenericConfiguration(this, i, compinfo)); } @@ -3840,16 +5138,7 @@ void TStreamerInfo::AddWriteTextAction(TStreamerInfoActions::TActionSequence *wr // use generic write action when special handling is not provided if (generic) - writeSequence->AddAction(GenericWriteAction, new TGenericConfiguration(this, i, compinfo)); - -#if defined(CDJ_NO_COMPILE) - if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action(writeSequence->fActions.back()); // Action is moved, we must pop it next. - writeSequence->fActions.pop_back(); - writeSequence->AddAction(UseCache, - new TConfigurationUseCache(this, action, element->TestBit(TStreamerElement::kRepeat))); - } -#endif + writeSequence->AddAction(GenericWriteAction, new TGenericConfiguration(this, i, compinfo)); } //////////////////////////////////////////////////////////////////////////////// @@ -3866,18 +5155,7 @@ void TStreamerInfo::AddWriteMemberWiseVecPtrAction(TStreamerInfoActions::TAction // Skip artificial element used for reading purposes. return; } - -#if defined(CDJ_NO_COMPILE) - if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action( GetCollectionWriteAction(this,element,compinfo->fType,i,compinfo,compinfo->fOffset) ); - writeSequence->AddAction( UseCacheVectorPtrLoop, new TConfigurationUseCache(this,action,element->TestBit(TStreamerElement::kRepeat)) ); - } else { - writeSequence->Addaction( GetCollectionWriteAction(this,element,compinfo->fType,i,compinfo,compinfo->fOffset) ); - } -#else - writeSequence->AddAction( VectorPtrLooper::GenericWrite, new TGenericConfiguration(this,i,compinfo) ); -#endif - + writeSequence->AddAction( GetCollectionWriteAction(this,nullptr,element,compinfo->fType,i,compinfo,compinfo->fOffset) ); } //////////////////////////////////////////////////////////////////////////////// @@ -3889,37 +5167,44 @@ TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::Cr return new TStreamerInfoActions::TActionSequence(0,0); } - TStreamerInfo *sinfo = static_cast(info); - - UInt_t ndata = info->GetElements()->GetEntriesFast(); - TStreamerInfoActions::TActionSequence *sequence = new TStreamerInfoActions::TActionSequence(info,ndata); + TLoopConfiguration *loopConfig = nullptr; if (IsDefaultVector(proxy)) { if (proxy.HasPointers()) { + TStreamerInfo *sinfo = static_cast(info); // Instead of the creating a new one let's copy the one from the StreamerInfo. - delete sequence; - - sequence = sinfo->GetReadMemberWiseActions(kTRUE)->CreateCopy(); - - return sequence; + return sinfo->GetReadMemberWiseActions(kTRUE)->CreateCopy(); } // We can speed up the iteration in case of vector. We also know that all emulated collection are stored internally as a vector. Long_t increment = proxy.GetIncrement(); - sequence->fLoopConfig = new TVectorLoopConfig(&proxy, increment, /* read */ kTRUE); + loopConfig = new TVectorLoopConfig(&proxy, increment, /* read */ kTRUE); } else if (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLunorderedset || proxy.GetCollectionType() == ROOT::kSTLmultiset || proxy.GetCollectionType() == ROOT::kSTLunorderedmultiset || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap || proxy.GetCollectionType() == ROOT::kSTLunorderedmap || proxy.GetCollectionType() == ROOT::kSTLunorderedmultimap) { Long_t increment = proxy.GetIncrement(); - sequence->fLoopConfig = new TVectorLoopConfig(&proxy, increment, /* read */ kTRUE); + loopConfig = new TVectorLoopConfig(&proxy, increment, /* read */ kTRUE); // sequence->fLoopConfig = new TAssocLoopConfig(proxy); } else { - sequence->fLoopConfig = new TGenericLoopConfig(&proxy, /* read */ kTRUE); + loopConfig = new TGenericLoopConfig(&proxy, /* read */ kTRUE); } + + return CreateReadMemberWiseActions(*info, loopConfig); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create the bundle of the actions necessary for the streaming memberwise of the content described by 'info' into the collection described by 'proxy' + +TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::CreateReadMemberWiseActions(TVirtualStreamerInfo &info, TLoopConfiguration *loopConfig) +{ + UInt_t ndata = info.GetElements()->GetEntriesFast(); + TStreamerInfoActions::TActionSequence *sequence = new TStreamerInfoActions::TActionSequence(&info, ndata); + sequence->fLoopConfig = loopConfig; + for (UInt_t i = 0; i < ndata; ++i) { - TStreamerElement *element = (TStreamerElement*) info->GetElements()->At(i); + TStreamerElement *element = (TStreamerElement*) info.GetElements()->At(i); if (!element) { break; } @@ -3948,6 +5233,7 @@ TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::Cr } } + TStreamerInfo *sinfo = static_cast(&info); TStreamerInfo::TCompInfo_t *compinfo = sinfo->fCompFull[i]; Int_t oldType = element->GetType(); @@ -3955,6 +5241,8 @@ TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::Cr Int_t offset = element->GetOffset(); if (newType != oldType) { + // This 'prevents' the switch from base class (kBase (0)) to + // anything else. if (newType > 0) { if (oldType != TVirtualStreamerInfo::kCounter) { oldType += TVirtualStreamerInfo::kConv; @@ -3963,29 +5251,29 @@ TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::Cr oldType += TVirtualStreamerInfo::kSkip; } } - switch (SelectLooper(proxy)) { + switch (SelectLooper(loopConfig ? loopConfig->fProxy : nullptr)) { case kAssociativeLooper: // } else if (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLmultiset // || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap) { -// sequence->AddAction( GenericAssocCollectionAction, new TConfigSTL(info,i,compinfo,offset,0,proxy.GetCollectionClass(),0,0) ); +// sequence->AddAction( GenericAssocCollectionAction, new TConfigSTL(true, info,i,compinfo,offset,0,proxy.GetCollectionClass(),0,0) ); case kVectorLooper: case kVectorPtrLooper: // We can speed up the iteration in case of vector. We also know that all emulated collection are stored internally as a vector. if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action( GetCollectionReadAction(info,element,oldType,i,compinfo,offset) ); - sequence->AddAction( UseCacheVectorLoop, new TConfigurationUseCache(info,action,element->TestBit(TStreamerElement::kRepeat)) ); + TConfiguredAction action( GetCollectionReadAction(&info,loopConfig,element,oldType,i,compinfo,offset) ); + sequence->AddAction( UseCacheVectorLoop, new TConfigurationUseCache(&info,action,element->TestBit(TStreamerElement::kRepeat)) ); } else { - sequence->AddAction( GetCollectionReadAction(info,element,oldType,i,compinfo,offset)); + sequence->AddAction( GetCollectionReadAction(&info,loopConfig,element,oldType,i,compinfo,offset)); } break; case kGenericLooper: default: // The usual collection case. if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action( GetCollectionReadAction(info,element,oldType,i,compinfo,offset) ); - sequence->AddAction( UseCacheGenericCollection, new TConfigurationUseCache(info,action,element->TestBit(TStreamerElement::kRepeat)) ); + TConfiguredAction action( GetCollectionReadAction(&info,loopConfig,element,oldType,i,compinfo,offset) ); + sequence->AddAction( UseCacheGenericCollection, new TConfigurationUseCache(&info,action,element->TestBit(TStreamerElement::kRepeat)) ); } else { - sequence->AddAction( GetCollectionReadAction(info,element,oldType,i,compinfo,offset) ); + sequence->AddAction( GetCollectionReadAction(&info,loopConfig,element,oldType,i,compinfo,offset) ); } break; } @@ -3998,161 +5286,107 @@ TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::Cr TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::CreateWriteMemberWiseActions(TVirtualStreamerInfo *info, TVirtualCollectionProxy &proxy) { - if (info == 0) { - return new TStreamerInfoActions::TActionSequence(0,0); - } + if (info == 0) { + return new TStreamerInfoActions::TActionSequence(0,0); + } - UInt_t ndata = info->GetElements()->GetEntriesFast(); - TStreamerInfo *sinfo = static_cast(info); - TStreamerInfoActions::TActionSequence *sequence = new TStreamerInfoActions::TActionSequence(info,ndata); + TLoopConfiguration *loopConfig = nullptr; + if (IsDefaultVector(proxy)) + { + if (proxy.HasPointers()) { + TStreamerInfo *sinfo = static_cast(info); + // Instead of the creating a new one let's copy the one from the StreamerInfo. + return sinfo->GetWriteMemberWiseActions(kTRUE)->CreateCopy(); + } - if (IsDefaultVector(proxy)) - { - if (proxy.HasPointers()) { - // Instead of the creating a new one let's copy the one from the StreamerInfo. - delete sequence; + // We can speed up the iteration in case of vector. We also know that all emulated collection are stored internally as a vector. + Long_t increment = proxy.GetIncrement(); + loopConfig = new TVectorLoopConfig(&proxy, increment, /* read */ kFALSE); + } else { + loopConfig = new TGenericLoopConfig(&proxy, /* read */ kFALSE); + } + return CreateWriteMemberWiseActions(*info, loopConfig); +} - sequence = sinfo->GetWriteMemberWiseActions(kTRUE)->CreateCopy(); +//////////////////////////////////////////////////////////////////////////////// +/// Create the bundle of the actions necessary for the streaming memberwise of the content described by 'info' into the collection described by 'proxy' - return sequence; - } +TStreamerInfoActions::TActionSequence *TStreamerInfoActions::TActionSequence::CreateWriteMemberWiseActions(TVirtualStreamerInfo &info, TLoopConfiguration *loopConfig) +{ + UInt_t ndata = info.GetElements()->GetEntriesFast(); + TStreamerInfoActions::TActionSequence *sequence = new TStreamerInfoActions::TActionSequence(&info, ndata); + sequence->fLoopConfig = loopConfig; - // We can speed up the iteration in case of vector. We also know that all emulated collection are stored internally as a vector. - Long_t increment = proxy.GetIncrement(); - sequence->fLoopConfig = new TVectorLoopConfig(&proxy, increment, /* read */ kFALSE); - /*} else if (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLmultiset - || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap) - { - Long_t increment = proxy.GetIncrement(); - sequence->fLoopConfig = new TVectorLoopConfig(increment); - // sequence->fLoopConfig = new TAssocLoopConfig(proxy); */ - } else { - sequence->fLoopConfig = new TGenericLoopConfig(&proxy, /* read */ kFALSE); + for (UInt_t i = 0; i < ndata; ++i) { + TStreamerElement *element = (TStreamerElement*) info.GetElements()->At(i); + if (!element) { + break; } - for (UInt_t i = 0; i < ndata; ++i) { - TStreamerElement *element = (TStreamerElement*) info->GetElements()->At(i); - if (!element) { - break; - } - if (element->GetType() < 0) { - // -- Skip an ignored TObject base class. - // Note: The only allowed negative value here is -1, and signifies that Build() has found a TObject - // base class and TClass::IgnoreTObjectStreamer() was called. In this case the compiled version of the - // elements omits the TObject base class element, which has to be compensated for by TTree::Bronch() - // when it is making branches for a split object. - continue; - } - if (element->TestBit(TStreamerElement::kCache) && !element->TestBit(TStreamerElement::kWrite)) { - // Skip element cached for reading purposes. - continue; - } - if (element->GetType() >= TVirtualStreamerInfo::kArtificial && !element->TestBit(TStreamerElement::kWrite)) { - // Skip artificial element used for reading purposes. - continue; - } - TStreamerInfo::TCompInfo *compinfo = sinfo->fCompFull[i]; - Int_t oldType = element->GetType(); - Int_t offset = element->GetOffset(); -#if defined(CDJ_NO_COMPILE) - Int_t newType = element->GetNewType(); - - if (newType != oldType) { - if (newType > 0) { - if (oldType != TVirtualStreamerInfo::kCounter) { - oldType += TVirtualStreamerInfo::kConv; - } - } else { - oldType += TVirtualStreamerInfo::kSkip; - } - } - if ( IsDefaultVector(proxy) - /*|| (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLmultiset - || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap) */ ) - { - - // We can speed up the iteration in case of vector. We also know that all emulated collection are stored internally as a vector. - if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action( GetCollectionWriteAction(info,element,oldType,i,compinfo,offset) ); - sequence->AddAction( UseCacheVectorLoop, new TConfigurationUseCache(info,action,element->TestBit(TStreamerElement::kRepeat)) ); - } else { - sequence->AddAction(GetCollectionWriteAction(info,element,oldType,i,compinfo,offset)); - } - - // } else if (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLmultiset - // || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap) { - // sequence->AddAction( GenericAssocCollectionAction, new TConfigSTL(info,i,compinfo,offset,0,proxy.GetCollectionClass(),0,0) ); - } else { - // The usual collection case. - if (element->TestBit(TStreamerElement::kCache)) { - TConfiguredAction action( GetWriteAction(info,element,oldType,i,compinfo,offset) ); - sequence->AddAction( UseCacheGenericCollection, new TConfigurationUseCache(info,action,element->TestBit(TStreamerElement::kRepeat)) ); - } else { - switch (oldType) { - // read basic types - case TVirtualStreamerInfo::kBool: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kChar: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kShort: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kInt: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kLong: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kLong64: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kFloat: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kDouble: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kUChar: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kUShort: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kUInt: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kULong: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kULong64: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - // case TVirtualStreamerInfo::kBits: sequence->AddAction( WriteBasicTypeGenericLoop, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kFloat16: { - if (element->GetFactor() != 0) { - sequence->AddAction( GenericLooper >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); - } else { - Int_t nbits = (Int_t)element->GetXmin(); - if (!nbits) nbits = 12; - sequence->AddAction( GenericLooper >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); - } - break; - } - case TVirtualStreamerInfo::kDouble32: { - if (element->GetFactor() != 0) { - sequence->AddAction( GenericLooper >, new TConfWithFactor(info,i,compinfo,offset,element->GetFactor(),element->GetXmin()) ); - } else { - Int_t nbits = (Int_t)element->GetXmin(); - if (!nbits) { - sequence->AddAction( GenericLooper >, new TConfiguration(info,i,compinfo,offset) ); - } else { - sequence->AddAction( GenericLooper >, new TConfNoFactor(info,i,compinfo,offset,nbits) ); - } - } - break; - } - case TVirtualStreamerInfo::kTNamed: sequence->AddAction( GenericLooper, new TConfiguration(info,i,compinfo,offset) ); break; - // Idea: We should calculate the CanIgnoreTObjectStreamer here and avoid calling the - // Streamer alltogether. - case TVirtualStreamerInfo::kTObject: sequence->AddAction( GenericLooper, new TConfiguration(info,i,compinfo,offset) ); break; - case TVirtualStreamerInfo::kTString: sequence->AddAction( GenericLooper, new TConfiguration(info,i,compinfo,offset) ); break; - default: - sequence->AddAction( GenericCollectionWriteAction, new TConfigSTL(info,i,0 /* the offset will be used from TStreamerInfo */,0,proxy.GetCollectionClass(),0,0) ); - break; - } + if (element->GetType() < 0) { + // -- Skip an ignored TObject base class. + // Note: The only allowed negative value here is -1, and signifies that Build() has found a TObject + // base class and TClass::IgnoreTObjectStreamer() was called. In this case the compiled version of the + // elements omits the TObject base class element, which has to be compensated for by TTree::Bronch() + // when it is making branches for a split object. + continue; + } + if (element->TestBit(TStreamerElement::kCache) && !element->TestBit(TStreamerElement::kWrite)) { + // Skip element cached for reading purposes. + continue; + } + if (element->GetType() >= TVirtualStreamerInfo::kArtificial && !element->TestBit(TStreamerElement::kWrite)) { + // Skip artificial element used for reading purposes. + continue; + } + TStreamerInfo *sinfo = static_cast(&info); + TStreamerInfo::TCompInfo *compinfo = sinfo->fCompFull[i]; + Int_t onfileType = element->GetType(); + Int_t memoryType = element->GetNewType(); + if (memoryType != onfileType) { + // This 'prevents' the switch from base class (kBase (0)) to + // anything else. + if (memoryType > 0) { + if (onfileType != TVirtualStreamerInfo::kCounter) { + onfileType += TVirtualStreamerInfo::kConv; } - } -#else - if ( IsDefaultVector(proxy) - /*|| (proxy.GetCollectionType() == ROOT::kSTLset || proxy.GetCollectionType() == ROOT::kSTLmultiset - || proxy.GetCollectionType() == ROOT::kSTLmap || proxy.GetCollectionType() == ROOT::kSTLmultimap)*/ ) - { - sequence->AddAction( GetCollectionWriteAction(info,element,oldType,i,compinfo,offset) ); } else { - // NOTE: TBranch::FillLeavesCollection[Member] is not yet ready to handle the sequence - // as it does not create/use a TStaging as expected ... but then again it might - // not be the right things to expect ... - // sequence->AddAction( GetCollectionWriteAction(info,element,oldType,i,compinfo,offset) ); - sequence->AddAction( GenericLooper::GenericWrite, new TConfigSTL(info,i,compinfo,0 /* the offset will be used from TStreamerInfo */,0,proxy.GetCollectionClass(),0,0) ); + // When reading we need to consume the data in the onfile buffer, + // so we do create a 'Skip' action for the missing elements. + // When writing we should probably write a default value, for + // now let's just ignore the request. + // We could issue an error here but we might too often issue a + // 'false positive' if the user only reads. + // FIXME: The 'right' solution is to add a new action that first print + // once the following error message: + // + // info.Error("TActionSequence::CreateWriteMemberWiseActions", + // "Ignoring request to write the missing data member %s in %s version %d checksum 0x%x", + // element->GetName(), info.GetName(), info.GetClassVersion(), info.GetCheckSum()); + continue; + // + // Instead of skipping the field we could write a zero there with: + // onfileType += TVirtualStreamerInfo::kSkip; } -#endif } - return sequence; + + Int_t offset = element->GetOffset(); + switch (SelectLooper(loopConfig ? loopConfig->fProxy : nullptr)) { + case kAssociativeLooper: + sequence->AddAction( GetCollectionWriteAction(&info,loopConfig,element,onfileType,i,compinfo,offset) ); + break; + case kVectorLooper: + sequence->AddAction( GetCollectionWriteAction(&info,loopConfig,element,onfileType,i,compinfo,offset) ); + break; + case kVectorPtrLooper: + sequence->AddAction( GetCollectionWriteAction(&info,loopConfig,element,onfileType,i,compinfo,offset) ); + break; + case kGenericLooper: + default: + sequence->AddAction( GetCollectionWriteAction(&info,loopConfig,element,onfileType,i,compinfo,offset) ); + break; + } + } + return sequence; } void TStreamerInfoActions::TActionSequence::AddToOffset(Int_t delta) diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index 5b84e330f9b91..f5d42d34d8e35 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -1183,7 +1183,29 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, Int_t nobjects; b >> nobjects; env = newProxy->Allocate(nobjects,true); - subinfo->ReadBufferSTL(b,newProxy,nobjects,/* offset */ 0, vers>=7 ); + if (!nobjects && (vers>=7)) { + // Do nothing but in version 6 of TStreamerInfo and below, + // we were calling ReadBuffer for empty collection. + } else { + TStreamerInfoActions::TActionSequence *actions = nullptr; + if (newProxy != oldProxy) { + actions = newProxy->GetConversionReadMemberWiseActions( oldProxy->GetValueClass(), vClVersion ); + } else { + actions = oldProxy->GetReadMemberWiseActions( vClVersion ); + } + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin = &(startbuf[0]); + void *end = &(endbuf[0]); + newProxy->GetFunctionCreateIterators(/* read = */ kTRUE)(env, &begin, &end, newProxy); + // We can not get here with a split vector of pointer, so we can indeed assume + // that actions->fConfiguration != null. + b.ApplySequence(*actions, begin, end); + if (begin != &(startbuf[0])) { + // assert(end != endbuf); + newProxy->GetFunctionDeleteTwoIterators()(begin,end); + } + } newProxy->Commit(env); } } @@ -1271,7 +1293,30 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, Int_t nobjects; b >> nobjects; void* env = newProxy->Allocate(nobjects,true); - subinfo->ReadBufferSTL(b,newProxy,nobjects,/* offset */ 0, vers >= 7); + if (!nobjects && (vers>=7)) { + // Do nothing but in version 6 of TStreamerInfo and below, + // we were calling ReadBuffer for empty collection. + } else { + TStreamerInfoActions::TActionSequence *actions = nullptr; + if (newProxy != oldProxy) { + actions = newProxy->GetConversionReadMemberWiseActions( oldProxy->GetValueClass(), vClVersion ); + } else { + actions = oldProxy->GetReadMemberWiseActions( vClVersion ); + } + + char startbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + char endbuf[TVirtualCollectionProxy::fgIteratorArenaSize]; + void *begin_iter = &(startbuf[0]); + void *end_iter = &(endbuf[0]); + newProxy->GetFunctionCreateIterators(/* read = */ kTRUE)(env, &begin_iter, &end_iter, newProxy); + // We can not get here with a split vector of pointer, so we can indeed assume + // that actions->fConfiguration != null. + b.ApplySequence(*actions, begin_iter, end_iter); + if (begin_iter != &(startbuf[0])) { + // assert(end != endbuf); + newProxy->GetFunctionDeleteTwoIterators()(begin_iter,end_iter); + } + } newProxy->Commit(env); } } @@ -1381,7 +1426,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, // Backward compatibility. Some TStreamerElement's where without // Streamer but were not removed from element list UInt_t start,count; - Version_t v = b.ReadVersion(&start, &count, cle); + Version_t v = b.ReadVersion(&start, &count, this->IsA()); if (fOldVersion<3){ // case of old TStreamerInfo if (aElement->IsBase() && aElement->IsA()!=TStreamerBase::Class()) { b.SetBufferOffset(start); //it was no byte count