From 8682080d68023c9494e63953cf80708fbd51d976 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 5 Dec 2024 23:57:01 -0600 Subject: [PATCH 01/30] meta: Correct RemoveStreamerInfo for slot != current Previously the call to RemoveStreamerInfo was working only if the slot was the the slot of the current version. (the actually removed StreamerInfo was hard-coded to be the current version --- core/meta/src/TClass.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 80c8af356f5c2..db01168bd135a 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -7403,7 +7403,7 @@ void TClass::RemoveStreamerInfo(Int_t slot) if (fStreamerInfo->GetSize() >= slot) { R__LOCKGUARD(gInterpreterMutex); TVirtualStreamerInfo *info = (TVirtualStreamerInfo*)fStreamerInfo->At(slot); - fStreamerInfo->RemoveAt(fClassVersion); + fStreamerInfo->RemoveAt(slot); if (fLastReadInfo.load() == info) fLastReadInfo = nullptr; if (fCurrentInfo.load() == info) From 3ab3d2270469fe99b21f653ca7800d82f2ebf0fd Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 6 Dec 2024 00:05:02 -0600 Subject: [PATCH 02/30] io: Fix in-memory class of cached members. The data members are cached (because they are input of a rule) need to be read into the original type not the 'current in memory type' so that the rule can properly function if the type has changed. We should upgrade further for the in-memory type of the input of a rule to be the one specified in the rule independently of the onfile type and the final in-memory type. --- io/io/src/TStreamerInfo.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index de31d5ac7a9ee..d7da629ae3881 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -662,6 +662,7 @@ void TStreamerInfo::Build(Bool_t isTransient) } cached->SetBit(TStreamerElement::kCache); cached->SetNewType( cached->GetType() ); + cached->SetNewClass( nullptr ); } fElements->Add(element); @@ -2534,6 +2535,7 @@ void TStreamerInfo::BuildOld() } element->SetBit(TStreamerElement::kCache); element->SetNewType( element->GetType() ); + element->SetNewClass( nullptr ); // Technically we could also consider setting to the type specificed in the rule element->SetOffset(infoalloc ? infoalloc->GetOffset(element->GetName()) : 0); } else if (rules.HasRuleWithTarget( element->GetName(), kTRUE ) ) { // The data member exist in the onfile StreamerInfo and there is a rule From 25b7f4004518b30633175b76e4997efb14424c8d Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 09:20:58 -0600 Subject: [PATCH 03/30] meta: Add clarifying alias TSources::GetTypeName --- core/meta/inc/TSchemaRule.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/meta/inc/TSchemaRule.h b/core/meta/inc/TSchemaRule.h index cf9fdd461b7d6..2f15dcd8f3598 100644 --- a/core/meta/inc/TSchemaRule.h +++ b/core/meta/inc/TSchemaRule.h @@ -27,6 +27,8 @@ namespace ROOT { TSources(const char *name = nullptr, const char *title = nullptr, const char *dims = nullptr) : TNamed(name,title), fDimensions(dims) {} const char *GetDimensions() { return fDimensions; } + const char* GetTypeName() { return GetTitle(); } + ClassDefOverride(TSources,2); }; From 6c2a65397f1825d1df432cd59191cdfd39238b68 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 09:33:54 -0600 Subject: [PATCH 04/30] meta: TClass::GetRealData better search for array data member. In the "list of real data", the array members' name is "datamembername[arraysizes]" in most cases. GetRealData already has code to find the real data if * input has brackets and name has brackets * input has brackets and name does not have brackets * input has no brackets and name has no brackets This commit adds: * input has no brackets and name has brackets. This is required for simplicity of searching from the code generated for I/O customization rules --- core/meta/src/TClass.cxx | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index db01168bd135a..dcedde2327291 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -3593,23 +3593,19 @@ TRealData* TClass::GetRealData(const char* name) const // Try ignoring the array dimensions. std::string::size_type firstBracket = givenName.find_first_of("["); - if (firstBracket != std::string::npos) { - // -- We are looking for an array data member. - std::string nameNoDim(givenName.substr(0, firstBracket)); - TObjLink* lnk = fRealData->FirstLink(); - while (lnk) { - TObject* obj = lnk->GetObject(); - std::string objName(obj->GetName()); - std::string::size_type pos = objName.find_first_of("["); - // Only match arrays to arrays for now. - if (pos != std::string::npos) { - objName.erase(pos); - if (objName == nameNoDim) { - return static_cast(obj); - } - } - lnk = lnk->Next(); + std::string nameNoDim(givenName.substr(0, firstBracket)); + TObjLink* lnk = fRealData->FirstLink(); + while (lnk) { + TObject* obj = lnk->GetObject(); + std::string objName(obj->GetName()); + std::string::size_type pos = objName.find_first_of("["); + if (pos != std::string::npos) { + objName.erase(pos); } + if (objName == nameNoDim) { + return static_cast(obj); + } + lnk = lnk->Next(); } // Now try it as a pointer. From 29298f61d6918a8b35ce963fdb868ef875dc9ecd Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 09:34:37 -0600 Subject: [PATCH 05/30] meta: slight optimization in InsertArtificialElements In TStreamerInfo::InsertArtificialElements search for the offset of the data member based on the already calculated 'real data' spelling to speedup the lookup. --- io/io/src/TStreamerInfo.cxx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index d7da629ae3881..ee23ca09518df 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -4620,11 +4620,11 @@ void TStreamerInfo::InsertArtificialElements(std::vectorString(); TString realDataName; if ( TDataMember* dm = fClass->GetDataMember( newName ) ) { - TRealData::GetName(realDataName,dm); - newel = new TStreamerArtificial(realDataName,"", - fClass->GetDataMemberOffset(newName), + TRealData::GetName(realDataName, dm); + newel = new TStreamerArtificial(realDataName, "", + fClass->GetDataMemberOffset(realDataName), TStreamerInfo::kArtificial, - fClass->GetDataMember( newName )->GetTypeName()); + dm->GetTypeName()); newel->SetReadFunc( rule->GetReadFunctionPointer() ); newel->SetReadRawFunc( rule->GetReadRawFunctionPointer() ); toAdd.push_back(newel); @@ -4637,11 +4637,11 @@ void TStreamerInfo::InsertArtificialElements(std::vectorString(); if ( TDataMember* dm = fClass->GetDataMember( newName ) ) { - TRealData::GetName(realDataName,dm); - newel = new TStreamerArtificial(realDataName,"", - fClass->GetDataMemberOffset(newName), + TRealData::GetName(realDataName, dm); + newel = new TStreamerArtificial(realDataName, "", + fClass->GetDataMemberOffset(realDataName), TStreamerInfo::kArtificial, - fClass->GetDataMember( newName )->GetTypeName()); + dm->GetTypeName()); toAdd.push_back(newel); } } From 58cc31ba4136ea4a13bc7e520f7e0bbae7ff4de5 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 16 Dec 2024 14:48:48 -0600 Subject: [PATCH 06/30] TTreeReader: TBranchProxy handle cases of split objects. Previous, the inner code was looking an inexistent members (actually the name of the object in its parent rather than an inner data member) and was appearing to work because the offset return in case of error was '0' (which worked since indeed there was no offset to be added to the address we already calculated) --- tree/treeplayer/src/TBranchProxy.cxx | 24 ++++++++++++++++++++---- tree/treeplayer/test/leafs.cxx | 2 -- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tree/treeplayer/src/TBranchProxy.cxx b/tree/treeplayer/src/TBranchProxy.cxx index e0f08609663bc..671c27713c293 100644 --- a/tree/treeplayer/src/TBranchProxy.cxx +++ b/tree/treeplayer/src/TBranchProxy.cxx @@ -517,13 +517,19 @@ bool ROOT::Detail::TBranchProxy::Setup() fMemberOffset = fClass->GetDataMemberOffset(member); - if (fMemberOffset<0) { + if (fMemberOffset < 0) { Error("Setup","%s",Form("Negative offset %d for %s in %s", fMemberOffset,fBranch->GetName(), bcount?bcount->GetName():"unknown")); + fMemberOffset = 0; + } else if (fMemberOffset == TVirtualStreamerInfo::kMissing) { + Error("Setup","%s",Form("Missing data member in a TClonesArray, %s in %s and %s", + fDataMember.Data(), fBranch->GetName(), + bcount ? bcount->GetName() : "unknown")); + fMemberOffset = 0; } - } else if (fClass) { + } else if (fParent && fClass) { fElement = (TStreamerElement*) fClass->GetStreamerInfo()->GetElements()->FindObject(fDataMember); @@ -538,12 +544,22 @@ bool ROOT::Detail::TBranchProxy::Setup() fMemberOffset = fClass->GetDataMemberOffset(member); } + if (fMemberOffset < 0) { + Error("Setup","%s",Form("Negative offset %d for %s in %s, class: %s", + fMemberOffset, fDataMember.Data(), fBranch->GetName(), fClass->GetName())); + fMemberOffset = 0; + } else if (fMemberOffset == TVirtualStreamerInfo::kMissing) { + Error("Setup","%s",Form("Missing data member %s in %s, class: %s", + fDataMember.Data(), fBranch->GetName(), fClass->GetName())); + fMemberOffset = 0; + } + // The extra condition (fElement is not a TStreamerSTL) is to handle the case where fBranch is a // TBranchElement and fElement is a TStreamerSTL. Without the extra condition we get an error // message, although the vector (i.e. the TBranchElement) is accessible. - } else if (fBranch->IsA() != TBranch::Class() && fElement->IsA() != TStreamerBasicType::Class() + } else if (fParent && fBranch->IsA() != TBranch::Class() && fElement->IsA() != TStreamerBasicType::Class() && fElement->IsA() != TStreamerSTL::Class()) { - Error("Setup","%s",Form("Missing TClass object for %s\n",fClassName.Data())); + Error("Setup","%s",Form("Missing TClass object for %s",fClassName.Data())); } if ( fBranch->IsA()==TBranchElement::Class() diff --git a/tree/treeplayer/test/leafs.cxx b/tree/treeplayer/test/leafs.cxx index a219b13999524..4580829a3fe3f 100644 --- a/tree/treeplayer/test/leafs.cxx +++ b/tree/treeplayer/test/leafs.cxx @@ -127,8 +127,6 @@ TEST(TTreeReaderLeafs, LeafList) { EXPECT_EQ(6u, vec.GetSize()); { using namespace ROOT::TestSupport; - CheckDiagsRAII diagRAII; - diagRAII.requiredDiag(kError, "Setup", "Missing TClass object for", false); EXPECT_FLOAT_EQ(13., arr[1]); EXPECT_DOUBLE_EQ(43., arrU[1]); } From 8588ce8212260b526ee1964be61b0f499ba27d3a Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 10:17:42 -0600 Subject: [PATCH 07/30] meta: GetDataMemberOffset return kMissing for errors. Instead of returning 0 when not finding any information about the data member, `TClass::GetDataMemberOffset` now returns TVirtualStreamerInfo::kMissing. This allows the reading of the element to be "skipped" rather than having it over-write other members --- core/meta/src/TClass.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index dcedde2327291..d906002109c82 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -3557,7 +3557,7 @@ Longptr_t TClass::GetDataMemberOffset(const char *name) const return info->GetOffset(name); } } - return 0; + return TVirtualStreamerInfo::kMissing; } //////////////////////////////////////////////////////////////////////////////// From 3d3733231394dac350cf43581b194c09f0dd7d65 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 10:44:31 -0600 Subject: [PATCH 08/30] io: Enable implicit conversion from A* to B* --- io/io/src/TStreamerInfoReadBuffer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index f5d42d34d8e35..22d3b8fbfe3ad 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -1082,7 +1082,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, case TStreamerInfo::kAnyP: // Class* not derived from TObject with no comment field NOTE:: Re-added by Phil case TStreamerInfo::kAnyP+TStreamerInfo::kOffsetL: { DOLOOP { - b.ReadFastArray((void**)(arr[k]+ioffset),cle,compinfo[i]->fLength,isPreAlloc,pstreamer); + b.ReadFastArray((void**)(arr[k]+ioffset), newCle ? newCle : cle, compinfo[i]->fLength, isPreAlloc, pstreamer, cle); } } continue; From 75ace06b4ab3f648ba861ba23728cf6935d72928 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 10:49:07 -0600 Subject: [PATCH 09/30] io: Enable implicit conversion from A[] to B[] --- io/io/src/TStreamerInfoReadBuffer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index 22d3b8fbfe3ad..bda84dbe9e244 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -1380,7 +1380,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, case TStreamerInfo::kAny+TStreamerInfo::kOffsetL: { DOLOOP { - b.ReadFastArray((void*)(arr[k]+ioffset),cle,compinfo[i]->fLength,pstreamer); + b.ReadFastArray((void*)(arr[k]+ioffset), newCle ? newCle : cle, compinfo[i]->fLength, pstreamer, cle); } continue; } From aec62b1ca1e7f356d0e15b554786729d33877d1a Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 15:41:17 -0600 Subject: [PATCH 10/30] meta: Improve TSources interfaces. Return the underlying type and other piece and information separately --- core/meta/inc/TSchemaRule.h | 45 ++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/core/meta/inc/TSchemaRule.h b/core/meta/inc/TSchemaRule.h index 2f15dcd8f3598..a373028ff99fe 100644 --- a/core/meta/inc/TSchemaRule.h +++ b/core/meta/inc/TSchemaRule.h @@ -23,13 +23,46 @@ namespace ROOT { class TSources : public TNamed { private: TString fDimensions; - public: - TSources(const char *name = nullptr, const char *title = nullptr, const char *dims = nullptr) : TNamed(name,title), fDimensions(dims) {} - const char *GetDimensions() { return fDimensions; } - - const char* GetTypeName() { return GetTitle(); } + Int_t fPointerLevel = 0; - ClassDefOverride(TSources,2); + public: + TSources(const char *name = nullptr, const char *title = nullptr, const char *dims = nullptr) + : TNamed(name, title), fDimensions(dims) + { + if (fTitle.Length()) + while (fTitle[fTitle.Length() - fPointerLevel - 1] == '*') + ++fPointerLevel; + if (fPointerLevel) + fTitle.Remove(fTitle.Length() - fPointerLevel); + } + + const char *GetDimensions() const { return fDimensions; } + + bool IsPointer() const { return fPointerLevel > 0; } + + Int_t GetPointerLevel() const { return fPointerLevel; } + + const char* GetUnderlyingTypeName() const + { + return fTitle; + } + + // The source can be declared with: + // "%s %s%s;", GetTypeForDeclaration().Data(), GetName(), GetDimensions); + TString GetTypeForDeclaration() + { + TString type = fTitle; + for(Int_t s = 0; s < fPointerLevel; ++s) + type.Append('*'); + return type; + } + + void SetTitle(const char *) override + { + Fatal("SetTitle", "Changing the type represented by a TSources is not supported."); + } + + ClassDefOverride(TSources, 3); }; typedef enum From fe0e7747f89b4adce808c957783a751ea43aa451 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 15:58:16 -0600 Subject: [PATCH 11/30] Revert "Don't warn when we skip a rule due to missing source member." This reverts commit e900d325bfbb8d3ac6db8e58334e38b70740d5af. Bug fix: I/O customization rules which missing input are no longer (inappropriately) run. We now properly warn if a rule is requesting an input that is not present in the StreamerInfo that the rule is being applied to. To avoid the warning in some cases (in particular the cases used to support `HepMC` and are located in `$ROOTSYS/etc/class.rules`) we introduce a new "attribute" for I/O customization rules: `CanIgnore:` When using this attribute the rule will be ignored if the input is missing from the schema/class-layout they apply to instead of issue a `Warning` wip: Revert "Don't warn when ..." --- README/ReleaseNotes/v636/index.md | 2 ++ etc/class.rules | 18 +++++++++--------- io/io/src/TKey.cxx | 3 ++- io/io/src/TStreamerInfo.cxx | 26 +++++++++++++++----------- 4 files changed, 28 insertions(+), 21 deletions(-) diff --git a/README/ReleaseNotes/v636/index.md b/README/ReleaseNotes/v636/index.md index 0903b2413c304..37b078f0a95fb 100644 --- a/README/ReleaseNotes/v636/index.md +++ b/README/ReleaseNotes/v636/index.md @@ -64,6 +64,8 @@ The following people have contributed to this new version: [RNTupleMergeOptions](https://root.cern/doc/v634/structROOT_1_1Experimental_1_1Internal_1_1RNTupleMergeOptions.html)); * "rntuple.ErrBehavior=(Abort|Skip)": RNTuple-specific option that specifies the behavior of the RNTupleMerger on error (see link above); * "rntuple.ExtraVerbose": RNTuple-specific option that tells the RNTupleMerger to emit more information during the merge process. +* New attribute for I/O customization rules: `CanIgnore`. When using this attribute the rule will be ignored +if the input is missing from the schema/class-layout they apply to instead of issue a `Warning` ## RDataFrame diff --git a/etc/class.rules b/etc/class.rules index 780e50ab63c95..bce093a9618f4 100644 --- a/etc/class.rules +++ b/etc/class.rules @@ -8,19 +8,19 @@ # HepMC Rules -HepMC::GenVertex m_event attributes=NotOwner +HepMC::GenVertex m_event attributes=NotOwner,CanIgnore -HepMC::GenParticle m_production_vertex attributes=NotOwner -HepMC::GenParticle m_end_vertex attributes=NotOwner +HepMC::GenParticle m_production_vertex attributes=NotOwner,CanIgnore +HepMC::GenParticle m_end_vertex attributes=NotOwner,CanIgnore -HepMC::Flow m_particle_owner attributes=NotOwner +HepMC::Flow m_particle_owner attributes=NotOwner,CanIgnore -HepMC::GenEvent m_vertex_barcodes attributes=Owner -HepMC::GenEvent m_particle_barcodes attributes=Owner +HepMC::GenEvent m_vertex_barcodes attributes=Owner,CanIgnore +HepMC::GenEvent m_particle_barcodes attributes=Owner,CanIgnore -HepMC::GenEvent m_signal_process_vertex attributes=NotOwner -HepMC::GenEvent m_beam_particle_1 attributes=NotOwner -HepMC::GenEvent m_beam_particle_2 attributes=NotOwner +HepMC::GenEvent m_signal_process_vertex attributes=NotOwner,CanIgnore +HepMC::GenEvent m_beam_particle_1 attributes=NotOwner,CanIgnore +HepMC::GenEvent m_beam_particle_2 attributes=NotOwner,CanIgnore diff --git a/io/io/src/TKey.cxx b/io/io/src/TKey.cxx index 267d7655b4e63..7bc0c0e349571 100644 --- a/io/io/src/TKey.cxx +++ b/io/io/src/TKey.cxx @@ -1071,7 +1071,8 @@ void *TKey::ReadObjectAny(const TClass* expectedClass) baseOffset = 0; // For now we do not support requesting from a class that is the base of one of the class for which there is transformation to .... clOnfile = cl; cl = const_cast(expectedClass); - Info("ReadObjectAny","Using Converter StreamerInfo from %s to %s",clOnfile->GetName(),expectedClass->GetName()); + if (gDebug >0) + Info("ReadObjectAny","Using Converter StreamerInfo from %s to %s",clOnfile->GetName(),expectedClass->GetName()); } if (cl->GetState() > TClass::kEmulated && expectedClass->GetState() <= TClass::kEmulated) { //we cannot mix a compiled class with an emulated class in the inheritance diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index ee23ca09518df..297ff17dedd6a 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -4580,19 +4580,23 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetAttributes()[0] != 0) { + TString attr( r->GetAttributes() ); + attr.ToLower(); + return attr.Contains("canignore"); + } else + return false; + }; if ( !GetElements()->FindObject(src->GetName()) ) { // Missing source. -#if 0 // Don't warn about not activating the rule. If don't warn the user can - // have more flexibility in specifying when the rule applies and relying - // on both the version number *and* the presence of the source members. - // Activating this warning would for example mean that we need to carefully - // tweak $ROOTSYS/etc/class.rules. - TString ruleStr; - rule->AsString(ruleStr); - Warning("InsertArtificialElements","For class %s in StreamerInfo %d is missing the source data member %s when trying to apply the rule:\n %s", - GetName(),GetClassVersion(),src->GetName(),ruleStr.Data()); - rule = 0; -#endif + if (!canIgnore(rule)) { + TString ruleStr; + rule->AsString(ruleStr); + Warning("InsertArtificialElements","For class %s in StreamerInfo %d is missing the source data member %s when trying to apply the rule:\n %s", + GetName(),GetClassVersion(),src->GetName(),ruleStr.Data()); + } + rule = nullptr; break; } } From 9211b5b6f4ae6872d73eab7c28aec4475677b1fa Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Dec 2024 09:34:47 -0600 Subject: [PATCH 12/30] io: update element with rule input types. In TStreamerInfo's Build and BuildOld update the artificial StreamerElement's description of the in memory type to be used to store the data. This allows the rule to request an implicit transformation from onfile representation to in memory representation before running the explicit customization rules --- io/io/src/TStreamerInfo.cxx | 77 +++++++++++++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 297ff17dedd6a..38c0d132f41d1 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -253,6 +253,62 @@ namespace { TStreamerInfo* fInfo; }; + decltype(auto) GetSourceType(ROOT::TSchemaRule::TSources *s) + { + std::string localtypename(s->GetUnderlyingTypeName()); + Int_t ndim = 0; + Int_t memType = TVirtualStreamerInfo::kNoType; + auto memClass = TClass::GetClass(localtypename.c_str()); + bool isStdArray = memClass && TClassEdit::IsStdArray(memClass->GetName()); + if (isStdArray) { + std::array localMaxIndices; + TClassEdit::GetStdArrayProperties(memClass->GetName(), + localtypename, + localMaxIndices, + ndim); + memClass = TClass::GetClass(localtypename.c_str()); + } + if (memClass) { + //cached->SetNewType( cached->GetType() ); + if (s->GetPointerLevel()) { + if (memClass->IsTObject()) { + memType = TVirtualStreamerInfo::kObjectP; + } else if (memClass->GetCollectionProxy()) { + memType = TVirtualStreamerInfo::kSTLp; + } else { + memType = TVirtualStreamerInfo::kAnyP; + } + } else { + if (memClass->IsTObject()) { + memType = TVirtualStreamerInfo::kObject; + } else if (memClass->GetCollectionProxy()) { + memType = TVirtualStreamerInfo::kSTL; + } else { + memType = TVirtualStreamerInfo::kAny; + } + } + if ((!s->GetPointerLevel() || memType == TVirtualStreamerInfo::kSTLp) && + (isStdArray ? ndim >0 : s->GetDimensions()[0])) + { + memType += TVirtualStreamerInfo::kOffsetL; + } + } else { + auto d = gROOT->GetType(localtypename.c_str()); + memType = d->GetType(); + if (s->GetDimensions()[0]) + memType += TVirtualStreamerInfo::kOffsetL; + if (s->GetPointerLevel()) + memType += TVirtualStreamerInfo::kOffsetP; + } + return std::make_pair(memClass, memType); + } + + void UpdateFromRule(ROOT::TSchemaRule::TSources *s, TStreamerElement *element) + { + auto [memClass, memType] = GetSourceType(s); + element->SetNewType( memType ); + element->SetNewClass( memClass ); + } } //////////////////////////////////////////////////////////////////////////////// @@ -661,8 +717,13 @@ void TStreamerInfo::Build(Bool_t isTransient) element = writecopy; } cached->SetBit(TStreamerElement::kCache); - cached->SetNewType( cached->GetType() ); - cached->SetNewClass( nullptr ); + // Get one of the potentially many rules applicable + // We should check that we don't have a second rule + auto r = rules.GetRuleWithSource( element->GetName() ); + assert(r && r->GetSource()); + auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( element->GetName() )); + assert(s); + UpdateFromRule(s, cached); } fElements->Add(element); @@ -2534,8 +2595,16 @@ void TStreamerInfo::BuildOld() writecopy->SetOffset(element->GetOffset()); } element->SetBit(TStreamerElement::kCache); - element->SetNewType( element->GetType() ); - element->SetNewClass( nullptr ); // Technically we could also consider setting to the type specificed in the rule + + // Get one of the potentially many rules applicable + // We should check that we don't have a second rule + auto r = rules.GetRuleWithSource(element->GetName()); + assert(r && r->GetSource()); + auto s = (ROOT::TSchemaRule::TSources *)(r->GetSource()->FindObject(element->GetName())); + assert(s); + + UpdateFromRule(s, element); + element->SetOffset(infoalloc ? infoalloc->GetOffset(element->GetName()) : 0); } else if (rules.HasRuleWithTarget( element->GetName(), kTRUE ) ) { // The data member exist in the onfile StreamerInfo and there is a rule From 445b919615784a217c13e04b5ddc9b6479c7d84e Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 17 Dec 2024 14:06:04 -0600 Subject: [PATCH 13/30] io: Correct the code skipping array of objects. This allows to remove data member whose type is array of (sub)objects. --- io/io/src/TStreamerInfoReadBuffer.cxx | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index bda84dbe9e244..ad275dc158a7c 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -326,15 +326,21 @@ Int_t TStreamerInfo::ReadBufferSkip(TBuffer &b, const T &arr, const TCompInfo *c } // skip Class * not derived from TObject with comment field //-> - case TStreamerInfo::kSkip + TStreamerInfo::kAnyp: { + case TStreamerInfo::kSkip + TStreamerInfo::kAnyp: + case TStreamerInfo::kSkip + TStreamerInfo::kAnyp + TStreamerInfo::kOffsetL: + { DOLOOP { - b.SkipObjectAny(); + for (Int_t j=0;jfLength;j++) { + b.SkipObjectAny(); + } } break; } // skip Class* not derived from TObject - case TStreamerInfo::kSkip + TStreamerInfo::kAnyP: { + case TStreamerInfo::kSkip + TStreamerInfo::kAnyP: + case TStreamerInfo::kSkip + TStreamerInfo::kAnyP + TStreamerInfo::kOffsetL: + { DOLOOP { for (Int_t j=0;jfLength;j++) { b.SkipObjectAny(); @@ -344,9 +350,13 @@ Int_t TStreamerInfo::ReadBufferSkip(TBuffer &b, const T &arr, const TCompInfo *c } // skip Any Class not derived from TObject - case TStreamerInfo::kSkip + TStreamerInfo::kAny: { + case TStreamerInfo::kSkip + TStreamerInfo::kAny: + case TStreamerInfo::kSkip + TStreamerInfo::kAny + TStreamerInfo::kOffsetL: + { DOLOOP { - b.SkipObjectAny(); + for (Int_t j=0;jfLength;j++) { + b.SkipObjectAny(); + } } break; } @@ -369,10 +379,13 @@ Int_t TStreamerInfo::ReadBufferSkip(TBuffer &b, const T &arr, const TCompInfo *c } case TStreamerInfo::kSkip + TStreamerInfo::kStreamLoop: + case TStreamerInfo::kSkip + TStreamerInfo::kStreamLoop + TStreamerInfo::kOffsetL: case TStreamerInfo::kSkip + TStreamerInfo::kStreamer: { DOLOOP { - b.SkipObjectAny(); + for (Int_t j=0;jfLength;j++) { + b.SkipObjectAny(); } + } break; } default: From 57a90dbcd1a649bb6b071d5b0a87b56cfa2d73ae Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 2 Jan 2025 15:54:03 -0600 Subject: [PATCH 14/30] io: Update StreamerElement as requested by rule's input --- io/io/src/TStreamerInfo.cxx | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 38c0d132f41d1..263b912ee6fd7 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -738,6 +738,21 @@ void TStreamerInfo::Build(Bool_t isTransient) if (!isTransient) Error("Build","Could you create a TStreamerInfo for %s\n",TString::Format("%s@@%d",GetName(),GetClassVersion()).Data()); } else { + // Need to find the source of rules where an implicit conversion is requested. + if (rules) { + TIter next(infoalloc->fElements); + TStreamerElement *alloc_element; + while ((alloc_element = (TStreamerElement *)next())) { + if (rules.HasRuleWithSource(alloc_element->GetName(), kTRUE)) { + auto r = rules.GetRuleWithSource(alloc_element->GetName()); + assert(r && r->GetSource()); + auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( alloc_element->GetName() )); + assert(s); + UpdateFromRule(s, alloc_element); + } + } + } + // Tell clone we should rerun BuildOld infoalloc->SetBit(kBuildOldUsed,false); // Temporarily mark it as built to avoid the BuildCheck from removing @@ -2566,6 +2581,20 @@ void TStreamerInfo::BuildOld() if (!infoalloc) { Error("BuildOld","Unable to create the StreamerInfo for %s.",TString::Format("%s@@%d",GetName(),GetOnFileClassVersion()).Data()); } else { + // Need to find the source of rules where an implicit conversion is requested. + if (rules) { + TIter alloc_next(infoalloc->fElements); + TStreamerElement *alloc_element; + while ((alloc_element = (TStreamerElement *)alloc_next())) { + if (rules.HasRuleWithSource(alloc_element->GetName(), kTRUE)) { + auto r = rules.GetRuleWithSource(alloc_element->GetName()); + assert(r && r->GetSource()); + auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( alloc_element->GetName() )); + assert(s); + UpdateFromRule(s, alloc_element); + } + } + } infoalloc->SetBit(kBuildOldUsed,false); infoalloc->BuildCheck(); infoalloc->BuildOld(); From f844d925bbe5ff9746b69dedf86f88edbb7a0610 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 17 Dec 2024 16:54:18 -0600 Subject: [PATCH 15/30] io: error out for rule with conflicting type for sources --- io/io/src/TStreamerInfo.cxx | 59 +++++++++++++++++++++++++++---------- 1 file changed, 44 insertions(+), 15 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 263b912ee6fd7..796d372c6caff 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -4672,30 +4672,59 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetAttributes()[0] != 0) { + TString attr( r->GetAttributes() ); + attr.ToLower(); + return attr.Contains("canignore"); + } else + return false; + }; // NOTE: Before adding the rule we should check that the source do // existing in this StreamerInfo. const TObjArray *sources = rule->GetSource(); - TIter input(sources); - TObject *src; - while((src = input())) { - auto canIgnore = [](const ROOT::TSchemaRule *r) { - if (r->GetAttributes()[0] != 0) { - TString attr( r->GetAttributes() ); - attr.ToLower(); - return attr.Contains("canignore"); - } else - return false; - }; - if ( !GetElements()->FindObject(src->GetName()) ) { + if (sources) + for(auto src : TRangeDynCast( *sources )) + { + auto source_element = dynamic_cast(GetElements()->FindObject(src->GetName())); + if (!source_element) { // Missing source. if (!canIgnore(rule)) { TString ruleStr; rule->AsString(ruleStr); - Warning("InsertArtificialElements","For class %s in StreamerInfo %d is missing the source data member %s when trying to apply the rule:\n %s", - GetName(),GetClassVersion(),src->GetName(),ruleStr.Data()); + Warning("InsertArtificialElements", + "For class %s in StreamerInfo %d is missing the source data member %s when trying to apply the " + "rule:\n %s", + GetName(), GetClassVersion(), src->GetName(), ruleStr.Data()); } rule = nullptr; break; + } else { + // The source exists, let's check if it has the expected type. + auto [memClass, memType] = GetSourceType(src); + if ((memClass != source_element->GetNewClass() || memType != source_element->GetNewType()) + && (memType != TVirtualStreamerInfo::kNoContextMenu && memType != TVirtualStreamerInfo::kNoType)) + { + const char *dim = src->GetDimensions(); + TString ruleStr; + rule->AsString(ruleStr); + auto cl = source_element->GetNewClass(); + TString classmsg; + if (memClass != cl) { + classmsg = "and the memory TClass is \""; + classmsg += cl ? cl->GetName() : "nullptr"; + classmsg += "\" while the rule needed \""; + classmsg += memClass ? memClass->GetName() : "nullptr"; + classmsg += "\""; + } + Error("InsertArtificialElements", + "For class %s in StreamerInfo %d a rule has conflicting type for the source \"%s %s%s\",\n" + " The TStreamerElement has memory type %d (needed %d) %s:\n %s", + GetName(), GetClassVersion(), src->GetTypeForDeclaration().Data(), src->GetName(), + dim && dim[0] ? dim : "", source_element->GetNewType(), memType, classmsg.Data(), ruleStr.Data()); + rule = nullptr; + break; + } } } @@ -4750,7 +4779,7 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetSource()); Int_t loc = -1; while( TObjString *s = (TObjString*)s_iter() ) { From 09f6518713cc87ba5f85ce2eac29044acf62f1ca Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 2 Jan 2025 15:53:14 -0600 Subject: [PATCH 16/30] io: When rule change in-memory type, update size dim needs to be 0 for non-array --- io/io/src/TStreamerInfo.cxx | 53 +++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 796d372c6caff..9cfc2207f961c 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -257,15 +257,24 @@ namespace { { std::string localtypename(s->GetUnderlyingTypeName()); Int_t ndim = 0; + Int_t totaldim = 0; Int_t memType = TVirtualStreamerInfo::kNoType; + Int_t datasize = 1; auto memClass = TClass::GetClass(localtypename.c_str()); + std::vector dimensions; bool isStdArray = memClass && TClassEdit::IsStdArray(memClass->GetName()); if (isStdArray) { + totaldim = 1; std::array localMaxIndices; TClassEdit::GetStdArrayProperties(memClass->GetName(), localtypename, localMaxIndices, ndim); + for(Int_t i = 0; i < ndim; ++i) { + auto d = localMaxIndices[i]; + dimensions.push_back(d); + totaldim *= d; + } memClass = TClass::GetClass(localtypename.c_str()); } if (memClass) { @@ -292,22 +301,56 @@ namespace { { memType += TVirtualStreamerInfo::kOffsetL; } + datasize = memClass->GetClassSize(); } else { auto d = gROOT->GetType(localtypename.c_str()); - memType = d->GetType(); - if (s->GetDimensions()[0]) + if (d) { + memType = d->GetType(); + datasize = d->Size(); + } + if (s->GetDimensions()[0]) { memType += TVirtualStreamerInfo::kOffsetL; + } if (s->GetPointerLevel()) memType += TVirtualStreamerInfo::kOffsetP; } - return std::make_pair(memClass, memType); + if (s->GetDimensions()[0]) { + if (!totaldim) + totaldim = 1; + auto dims = s->GetDimensions(); + while(*dims == '[') { + ++dims; + uint32_t res = 0; + do { + if (!isdigit(*dims)) + break; + if (res * 10 < res) { + Error("GetSourceType", "Could not parse dimension string %s", s->GetDimensions()); + break; + } + res *= 10; + res += *dims - '0'; + } while (*++dims); + dimensions.push_back(res); + totaldim *= res; + } + } + return std::make_tuple(memClass, memType, datasize, dimensions, totaldim); } void UpdateFromRule(ROOT::TSchemaRule::TSources *s, TStreamerElement *element) { - auto [memClass, memType] = GetSourceType(s); + auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(s); element->SetNewType( memType ); element->SetNewClass( memClass ); + // We can not change the recorded dimensions. Let's check that + // the total number of elements is still the same. + if (totaldim != element->GetArrayLength()) { + Error("UpdateFromRule", + "The number of elements in the rule (%d) does not match the number of elements in the element (%d)", + totaldim, element->GetArrayLength()); + } + element->SetSize(totaldim ? totaldim * datasize : datasize); } } @@ -4701,7 +4744,7 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetNewClass() || memType != source_element->GetNewType()) && (memType != TVirtualStreamerInfo::kNoContextMenu && memType != TVirtualStreamerInfo::kNoType)) { From 37c860f9290c38a8ecaa0737b1d3c5d4dccbb980 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 3 Jan 2025 12:49:40 -0600 Subject: [PATCH 17/30] io-rule: don't complain if input is in base class --- io/io/src/TStreamerInfo.cxx | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 9cfc2207f961c..3298819c9fd02 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -4731,17 +4731,21 @@ void TStreamerInfo::InsertArtificialElements(std::vector(GetElements()->FindObject(src->GetName())); if (!source_element) { - // Missing source. - if (!canIgnore(rule)) { - TString ruleStr; - rule->AsString(ruleStr); - Warning("InsertArtificialElements", - "For class %s in StreamerInfo %d is missing the source data member %s when trying to apply the " - "rule:\n %s", - GetName(), GetClassVersion(), src->GetName(), ruleStr.Data()); + // It might still be in one the base classes. + if (fClass->GetListOfRealData() && !fClass->GetListOfRealData()->FindObject(src->GetName())) + { + // Missing source. + if (!canIgnore(rule)) { + TString ruleStr; + rule->AsString(ruleStr); + Warning("InsertArtificialElements", + "For class %s in StreamerInfo %d is missing the source data member `%s` when trying to apply the " + "rule:\n %s", + GetName(), GetClassVersion(), src->GetName(), ruleStr.Data()); + } + rule = nullptr; + break; } - rule = nullptr; - break; } else { // The source exists, let's check if it has the expected type. auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(src); From e54b0ea669d3f4eb231d6a6290035d24554e952d Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 3 Jan 2025 11:33:59 -0600 Subject: [PATCH 18/30] io: improve dim error message in UpdateFromRule --- io/io/src/TStreamerInfo.cxx | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 3298819c9fd02..46cd237b54a60 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -253,7 +253,7 @@ namespace { TStreamerInfo* fInfo; }; - decltype(auto) GetSourceType(ROOT::TSchemaRule::TSources *s) + decltype(auto) GetSourceType(const ROOT::TSchemaRule::TSources *s, const TStreamerElement *element) { std::string localtypename(s->GetUnderlyingTypeName()); Int_t ndim = 0; @@ -335,20 +335,28 @@ namespace { totaldim *= res; } } + if (element->GetType() == TStreamerInfo::kStreamLoop && + (memType == TStreamerInfo::kAnyp || memType == TStreamerInfo::kAnyP + || memType == TStreamerInfo::kObjectp || memType == TStreamerInfo::kObjectP)) + { + memType = TStreamerInfo::kStreamLoop; + } + if (element->GetType() == TStreamerInfo::kStreamer) + memType = element->GetType(); return std::make_tuple(memClass, memType, datasize, dimensions, totaldim); } - void UpdateFromRule(ROOT::TSchemaRule::TSources *s, TStreamerElement *element) + void UpdateFromRule(const TStreamerInfo *info, const ROOT::TSchemaRule::TSources *s, TStreamerElement *element) { - auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(s); + auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(s, element); element->SetNewType( memType ); element->SetNewClass( memClass ); // We can not change the recorded dimensions. Let's check that // the total number of elements is still the same. if (totaldim != element->GetArrayLength()) { Error("UpdateFromRule", - "The number of elements in the rule (%d) does not match the number of elements in the element (%d)", - totaldim, element->GetArrayLength()); + "For %s::%s the number of array elements in the rule (%d) does not match the number in the StreamerElement (%d)", + info->GetName(), element->GetFullName(), totaldim, element->GetArrayLength()); } element->SetSize(totaldim ? totaldim * datasize : datasize); } @@ -766,7 +774,7 @@ void TStreamerInfo::Build(Bool_t isTransient) assert(r && r->GetSource()); auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( element->GetName() )); assert(s); - UpdateFromRule(s, cached); + UpdateFromRule(this, s, cached); } fElements->Add(element); @@ -791,7 +799,7 @@ void TStreamerInfo::Build(Bool_t isTransient) assert(r && r->GetSource()); auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( alloc_element->GetName() )); assert(s); - UpdateFromRule(s, alloc_element); + UpdateFromRule(this, s, alloc_element); } } } @@ -2634,7 +2642,7 @@ void TStreamerInfo::BuildOld() assert(r && r->GetSource()); auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( alloc_element->GetName() )); assert(s); - UpdateFromRule(s, alloc_element); + UpdateFromRule(this, s, alloc_element); } } } @@ -2675,7 +2683,7 @@ void TStreamerInfo::BuildOld() auto s = (ROOT::TSchemaRule::TSources *)(r->GetSource()->FindObject(element->GetName())); assert(s); - UpdateFromRule(s, element); + UpdateFromRule(this, s, element); element->SetOffset(infoalloc ? infoalloc->GetOffset(element->GetName()) : 0); } else if (rules.HasRuleWithTarget( element->GetName(), kTRUE ) ) { @@ -4748,7 +4756,7 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetNewClass() || memType != source_element->GetNewType()) && (memType != TVirtualStreamerInfo::kNoContextMenu && memType != TVirtualStreamerInfo::kNoType)) { From bcd92303badb35291c8c768443296fd6e75b73af Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sun, 19 Jan 2025 09:59:46 -0600 Subject: [PATCH 19/30] [NFC] Fix indentation --- io/io/src/TStreamerInfo.cxx | 83 ++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 46cd237b54a60..450dd960f52ab 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -4735,53 +4735,52 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetSource(); if (sources) - for(auto src : TRangeDynCast( *sources )) - { - auto source_element = dynamic_cast(GetElements()->FindObject(src->GetName())); - if (!source_element) { - // It might still be in one the base classes. - if (fClass->GetListOfRealData() && !fClass->GetListOfRealData()->FindObject(src->GetName())) - { - // Missing source. - if (!canIgnore(rule)) { + for (auto src : TRangeDynCast(*sources)) { + auto source_element = dynamic_cast(GetElements()->FindObject(src->GetName())); + if (!source_element) { + // It might still be in one the base classes. + if (fClass->GetListOfRealData() && !fClass->GetListOfRealData()->FindObject(src->GetName())) { + // Missing source. + if (!canIgnore(rule)) { + TString ruleStr; + rule->AsString(ruleStr); + Warning("InsertArtificialElements", + "For class %s in StreamerInfo %d is missing the source data member `%s` when trying to " + "apply the " + "rule:\n %s", + GetName(), GetClassVersion(), src->GetName(), ruleStr.Data()); + } + rule = nullptr; + break; + } + } else { + // The source exists, let's check if it has the expected type. + auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(src, source_element); + if ((memClass != source_element->GetNewClass() || memType != source_element->GetNewType()) && + (memType != TVirtualStreamerInfo::kNoContextMenu && memType != TVirtualStreamerInfo::kNoType)) { + const char *dim = src->GetDimensions(); TString ruleStr; rule->AsString(ruleStr); - Warning("InsertArtificialElements", - "For class %s in StreamerInfo %d is missing the source data member `%s` when trying to apply the " - "rule:\n %s", - GetName(), GetClassVersion(), src->GetName(), ruleStr.Data()); - } - rule = nullptr; - break; - } - } else { - // The source exists, let's check if it has the expected type. - auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(src, source_element); - if ((memClass != source_element->GetNewClass() || memType != source_element->GetNewType()) - && (memType != TVirtualStreamerInfo::kNoContextMenu && memType != TVirtualStreamerInfo::kNoType)) - { - const char *dim = src->GetDimensions(); - TString ruleStr; - rule->AsString(ruleStr); - auto cl = source_element->GetNewClass(); - TString classmsg; - if (memClass != cl) { - classmsg = "and the memory TClass is \""; - classmsg += cl ? cl->GetName() : "nullptr"; - classmsg += "\" while the rule needed \""; - classmsg += memClass ? memClass->GetName() : "nullptr"; - classmsg += "\""; + auto cl = source_element->GetNewClass(); + TString classmsg; + if (memClass != cl) { + classmsg = "and the memory TClass is \""; + classmsg += cl ? cl->GetName() : "nullptr"; + classmsg += "\" while the rule needed \""; + classmsg += memClass ? memClass->GetName() : "nullptr"; + classmsg += "\""; + } + Error("InsertArtificialElements", + "For class %s in StreamerInfo %d a rule has conflicting type for the source \"%s %s%s\",\n" + " The TStreamerElement has memory type %d (needed %d) %s:\n %s", + GetName(), GetClassVersion(), src->GetTypeForDeclaration().Data(), src->GetName(), + dim && dim[0] ? dim : "", source_element->GetNewType(), memType, classmsg.Data(), + ruleStr.Data()); + rule = nullptr; + break; } - Error("InsertArtificialElements", - "For class %s in StreamerInfo %d a rule has conflicting type for the source \"%s %s%s\",\n" - " The TStreamerElement has memory type %d (needed %d) %s:\n %s", - GetName(), GetClassVersion(), src->GetTypeForDeclaration().Data(), src->GetName(), - dim && dim[0] ? dim : "", source_element->GetNewType(), memType, classmsg.Data(), ruleStr.Data()); - rule = nullptr; - break; } } - } if (!rule) continue; From 982dada6d0ef003f3f8699f7f5b4b1b5768d09bf Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 2 Jan 2025 15:50:21 -0600 Subject: [PATCH 20/30] io: Base sizeof TStreamerElement on the in-memory type. Previously the code in TStreamerElement assumed that GetClassPointer return the underlying type of the in memory representation. This was true until recently as you could not change the representation in the 'current' StreamerInfo ( GetClassPointer was matching the layout as decribed by the Clang AST ) and if the class was not loaded, the 'old/onfile' representation was the only possible choice. However we now can change the representation on file (i.e. GetClassPointer) for the current StreamerInfo of a loaded class (i.e. later using Write rule, now in the case of enum class and collection thereof) and in the case of the synthetic/artifical class created to cache inputs of rules (they are named `classname@@versionNumber`), the rule now dictates the representation in memory --- core/meta/src/TStreamerElement.cxx | 31 +++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/core/meta/src/TStreamerElement.cxx b/core/meta/src/TStreamerElement.cxx index 339811a72c226..4ac7da0d0d94d 100644 --- a/core/meta/src/TStreamerElement.cxx +++ b/core/meta/src/TStreamerElement.cxx @@ -676,8 +676,11 @@ TClass *TStreamerBase::GetClassPointer() const Int_t TStreamerBase::GetSize() const { - TClass *cl = GetClassPointer(); - if (cl) return cl->Size(); + TClass *cl = GetNewClass(); + if (!cl) + cl = GetClassPointer(); + if (cl) + return cl->Size(); return 0; } @@ -1281,10 +1284,14 @@ const char *TStreamerObject::GetInclude() const Int_t TStreamerObject::GetSize() const { - TClass *cl = GetClassPointer(); + TClass *cl = GetNewClass(); + if (!cl) + cl = GetClassPointer(); Int_t classSize = 8; - if (cl) classSize = cl->Size(); - if (fArrayLength) return fArrayLength*classSize; + if (cl) + classSize = cl->Size(); + if (fArrayLength) + return fArrayLength * classSize; return classSize; } @@ -1374,10 +1381,14 @@ const char *TStreamerObjectAny::GetInclude() const Int_t TStreamerObjectAny::GetSize() const { - TClass *cl = GetClassPointer(); + TClass *cl = GetNewClass(); + if (!cl) + cl = GetClassPointer(); Int_t classSize = 8; - if (cl) classSize = cl->Size(); - if (fArrayLength) return fArrayLength*classSize; + if (cl) + classSize = cl->Size(); + if (fArrayLength) + return fArrayLength * classSize; return classSize; } @@ -1906,7 +1917,9 @@ Int_t TStreamerSTL::GetSize() const // Since the STL collection might or might not be emulated and that the // sizeof the object depends on this, let's just always retrieve the // current size! - TClass *cl = GetClassPointer(); + TClass *cl = GetNewClass(); + if (!cl) + cl = GetClassPointer(); UInt_t size = 0; if (cl==nullptr) { if (!TestBit(kWarned)) { From efc25b379838a9e29c9cbc3841ac6df07b321bbd Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 2 Jan 2025 15:54:52 -0600 Subject: [PATCH 21/30] io: Allocated/Destroy in StreamerInfo give the in-memory type --- io/io/src/TStreamerInfo.cxx | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 450dd960f52ab..b28d57d9bf38d 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -4981,13 +4981,16 @@ void* TStreamerInfo::New(void *obj) // Skip elements for which we do not have any class // information. FIXME: Document how this could happen. - TClass* cle = element->GetClassPointer(); - if (!cle) { + TClass* cle = element->GetNewClass(); + if (!cle) + cle = element->GetClassPointer(); + if (!cle) continue; - } char* eaddr = p + element->GetOffset(); - Int_t etype = element->GetType(); + Int_t etype = element->GetNewType(); + if (etype == TStreamerInfo::kNoType) + etype = element->GetType(); //cle->GetStreamerInfo(); //necessary in case "->" is not specified @@ -5167,7 +5170,9 @@ void TStreamerInfo::DestructorImpl(void* obj, Bool_t dtorOnly) char* eaddr = p + ele->GetOffset(); - Int_t etype = ele->GetType(); + Int_t etype = ele->GetNewType(); // in memory type + if (etype == TStreamerInfo::kNoType) + etype = ele->GetType(); switch(etype) { case TStreamerInfo::kOffsetP + TStreamerInfo::kBool: DeleteBasicPointer(eaddr,ele,Bool_t); continue; @@ -5190,8 +5195,11 @@ void TStreamerInfo::DestructorImpl(void* obj, Bool_t dtorOnly) - TClass* cle = ele->GetClassPointer(); - if (!cle) continue; + TClass* cle = ele->GetNewClass(); // in memory type + if (!cle) + cle = ele->GetClassPointer(); + if (!cle) + continue; if (etype == kObjectp || etype == kAnyp) { From 60a2a1ab7bb987c09911d27e492b88451663031a Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 27 Dec 2024 14:56:24 -0600 Subject: [PATCH 22/30] io: Announce attribute for I/O customization rules: `CanIgnore` --- README/ReleaseNotes/v636/index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README/ReleaseNotes/v636/index.md b/README/ReleaseNotes/v636/index.md index 37b078f0a95fb..b01c9f363012d 100644 --- a/README/ReleaseNotes/v636/index.md +++ b/README/ReleaseNotes/v636/index.md @@ -64,8 +64,7 @@ The following people have contributed to this new version: [RNTupleMergeOptions](https://root.cern/doc/v634/structROOT_1_1Experimental_1_1Internal_1_1RNTupleMergeOptions.html)); * "rntuple.ErrBehavior=(Abort|Skip)": RNTuple-specific option that specifies the behavior of the RNTupleMerger on error (see link above); * "rntuple.ExtraVerbose": RNTuple-specific option that tells the RNTupleMerger to emit more information during the merge process. -* New attribute for I/O customization rules: `CanIgnore`. When using this attribute the rule will be ignored -if the input is missing from the schema/class-layout they apply to instead of issue a `Warning` +* New attribute for I/O customization rules: `CanIgnore`. When using this attribute the rule will be ignored if the input is missing from the schema/class-layout they apply to instead of issue a `Warning` ## RDataFrame From f9cc521956e3f94ab7c1a730023eb870893e7126 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 27 Dec 2024 15:32:17 -0600 Subject: [PATCH 23/30] [NFC] io: remove spurrious cast --- io/io/src/TStreamerInfoReadBuffer.cxx | 12 +++++------ io/io/src/TStreamerInfoWriteBuffer.cxx | 10 ++++----- tree/tree/src/TBranchElement.cxx | 28 +++++++++++++------------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index ad275dc158a7c..308dae0298718 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -554,7 +554,7 @@ Int_t TStreamerInfo::ReadBufferArtificial(TBuffer &b, const T &arr, // Process the result if (readfunc) { TVirtualObject obj(0); - TVirtualArray *objarr = ((TBufferFile&)b).PeekDataCache(); + TVirtualArray *objarr = b.PeekDataCache(); if (objarr) { obj.fClass = objarr->fClass; @@ -795,7 +795,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, if (R__TestUseCache(aElement)) { Int_t bufpos = b.Length(); - if (((TBufferFile&)b).PeekDataCache()==0) { + if (b.PeekDataCache()==0) { Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",thisVar->GetName(),aElement->GetName()); thisVar->ReadBufferSkip(b,arr,compinfo[i],compinfo[i]->fType+TStreamerInfo::kSkip,aElement,narr,eoffset); } else { @@ -803,9 +803,9 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, printf("ReadBuffer, class:%s, name=%s, fType[%d]=%d," " %s, bufpos=%d, arr=%p, eoffset=%d, Redirect=%p\n", fClass->GetName(),aElement->GetName(),i,compinfo[i]->fType, - aElement->ClassName(),b.Length(),arr[0], eoffset,((TBufferFile&)b).PeekDataCache()->GetObjectAt(0)); + aElement->ClassName(),b.Length(),arr[0], eoffset,b.PeekDataCache()->GetObjectAt(0)); } - thisVar->ReadBuffer(b,*((TBufferFile&)b).PeekDataCache(),compinfo,i,i+1,narr,eoffset, arrayMode); + thisVar->ReadBuffer(b,*b.PeekDataCache(),compinfo,i,i+1,narr,eoffset, arrayMode); } if (aElement->TestBit(TStreamerElement::kRepeat)) { b.SetBufferOffset(bufpos); } continue; @@ -1699,10 +1699,10 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, } case TStreamerInfo::kCacheNew: - ((TBufferFile&)b).PushDataCache( new TVirtualArray( aElement->GetClassPointer(), narr ) ); + b.PushDataCache( new TVirtualArray( aElement->GetClassPointer(), narr ) ); continue; case TStreamerInfo::kCacheDelete: - delete ((TBufferFile&)b).PopDataCache(); + delete b.PopDataCache(); continue; case -1: diff --git a/io/io/src/TStreamerInfoWriteBuffer.cxx b/io/io/src/TStreamerInfoWriteBuffer.cxx index 93ec92274a752..ee5ff66cc08a1 100644 --- a/io/io/src/TStreamerInfoWriteBuffer.cxx +++ b/io/io/src/TStreamerInfoWriteBuffer.cxx @@ -146,16 +146,16 @@ Int_t TStreamerInfo::WriteBufferAux(TBuffer &b, const T &arr, if (R__TestUseCache(aElement)) { if (aElement->TestBit(TStreamerElement::kWrite)) { - if (((TBufferFile&)b).PeekDataCache()==0) { + if (b.PeekDataCache()==0) { Warning("WriteBuffer","Skipping %s::%s because the cache is missing.",GetName(),aElement->GetName()); } else { if (gDebug > 1) { printf("WriteBuffer, class:%s, name=%s, fType[%d]=%d," " %s, bufpos=%d, arr=%p, eoffset=%d, Redirect=%p\n", fClass->GetName(),aElement->GetName(),i,compinfo[i]->fType, - aElement->ClassName(),b.Length(),arr[0], eoffset,((TBufferFile&)b).PeekDataCache()->GetObjectAt(0)); + aElement->ClassName(),b.Length(),arr[0], eoffset,b.PeekDataCache()->GetObjectAt(0)); } - WriteBufferAux(b,*((TBufferFile&)b).PeekDataCache(),compinfo,i,i+1,narr,eoffset, arrayMode); + WriteBufferAux(b,*b.PeekDataCache(),compinfo,i,i+1,narr,eoffset, arrayMode); } continue; } else { @@ -801,10 +801,10 @@ Int_t TStreamerInfo::WriteBufferAux(TBuffer &b, const T &arr, } case TStreamerInfo::kCacheNew: - ((TBufferFile&)b).PushDataCache( new TVirtualArray( aElement->GetClassPointer(), narr ) ); + b.PushDataCache( new TVirtualArray( aElement->GetClassPointer(), narr ) ); continue; case TStreamerInfo::kCacheDelete: - delete ((TBufferFile&)b).PopDataCache(); + delete b.PopDataCache(); continue; case TStreamerInfo::kArtificial: #if 0 diff --git a/tree/tree/src/TBranchElement.cxx b/tree/tree/src/TBranchElement.cxx index a55bf4c439fbf..6ff6a5b34eb93 100644 --- a/tree/tree/src/TBranchElement.cxx +++ b/tree/tree/src/TBranchElement.cxx @@ -64,10 +64,10 @@ namespace { } } struct R__PushCache { - TBufferFile &fBuffer; + TBuffer &fBuffer; TVirtualArray *fOnfileObject; - R__PushCache(TBufferFile &b, TVirtualArray *in, UInt_t size) : fBuffer(b), fOnfileObject(in) { + R__PushCache(TBuffer &b, TVirtualArray *in, UInt_t size) : fBuffer(b), fOnfileObject(in) { if (fOnfileObject) { fOnfileObject->SetSize(size); fBuffer.PushDataCache( fOnfileObject ); @@ -2744,7 +2744,7 @@ Int_t TBranchElement::GetEntry(Long64_t entry, Int_t getall) if (clones->IsZombie()) { return -1; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,ndata); + R__PushCache onfileObject(b,fOnfileObject,ndata); char **arr = (char **)clones->GetObjectRef(); char **end = arr + fNdata; @@ -2757,7 +2757,7 @@ Int_t TBranchElement::GetEntry(Long64_t entry, Int_t getall) auto ndata = GetNdata(); - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,ndata); + R__PushCache onfileObject(b,fOnfileObject,ndata); TVirtualCollectionProxy *proxy = GetCollectionProxy(); TVirtualCollectionProxy::TPushPop helper(proxy, fObject); @@ -2767,7 +2767,7 @@ Int_t TBranchElement::GetEntry(Long64_t entry, Int_t getall) // Apply the unattached rules; by definition they do not need any // input from a buffer. TBufferFile b(TBufferFile::kRead, 1); - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,fNdata); + R__PushCache onfileObject(b,fOnfileObject,fNdata); b.ApplySequence(*fReadActionSequence, fObject); } } @@ -4289,7 +4289,7 @@ void TBranchElement::ReadLeavesCollection(TBuffer& b) } fNdata = n; - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,1); + R__PushCache onfileObject(b,fOnfileObject,1); // Note: Proxy-helper needs to "embrace" the entire // streaming of this STL container if the container @@ -4379,7 +4379,7 @@ void TBranchElement::ReadLeavesCollectionSplitPtrMember(TBuffer& b) return; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,fNdata); + R__PushCache onfileObject(b,fOnfileObject,fNdata); TStreamerInfo *info = GetInfoImp(); if (info == nullptr) return; @@ -4411,7 +4411,7 @@ void TBranchElement::ReadLeavesCollectionSplitVectorPtrMember(TBuffer& b) if (!fNdata) { return; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,fNdata); + R__PushCache onfileObject(b,fOnfileObject,fNdata); TStreamerInfo *info = GetInfoImp(); if (info == nullptr) return; @@ -4442,7 +4442,7 @@ void TBranchElement::ReadLeavesCollectionMember(TBuffer& b) if (!fNdata) { return; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,fNdata); + R__PushCache onfileObject(b,fOnfileObject,fNdata); TStreamerInfo *info = GetInfoImp(); if (info == nullptr) return; @@ -4522,7 +4522,7 @@ void TBranchElement::ReadLeavesClonesMember(TBuffer& b) // Note, we could (possibly) save some more, by configuring the action // based on the value of fOnfileObject rather than pushing in on a stack. - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,fNdata); + R__PushCache onfileObject(b,fOnfileObject,fNdata); char **arr = (char **)clones->GetObjectRef(); char **end = arr + fNdata; @@ -4547,7 +4547,7 @@ void TBranchElement::ReadLeavesMember(TBuffer& b) return; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,1); + R__PushCache onfileObject(b,fOnfileObject,1); // If not a TClonesArray or STL container master branch // or sub-branch and branch inherits from tobject, // then register with the buffer so that pointers are @@ -4599,7 +4599,7 @@ void TBranchElement::ReadLeavesMemberBranchCount(TBuffer& b) if (!info) { return; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,1); // Here we have a single object that contains a variable size C-style array. + R__PushCache onfileObject(b,fOnfileObject,1); // Here we have a single object that contains a variable size C-style array. // Since info is not null, fReadActionSequence is not null either. b.ApplySequence(*fReadActionSequence, fObject); } @@ -4634,7 +4634,7 @@ void TBranchElement::ReadLeavesMemberCounter(TBuffer& b) return; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,1); + R__PushCache onfileObject(b,fOnfileObject,1); // Since info is not null, fReadActionSequence is not null either. b.ApplySequence(*fReadActionSequence, fObject); @@ -4655,7 +4655,7 @@ void TBranchElement::ReadLeavesCustomStreamer(TBuffer& b) return; } - R__PushCache onfileObject(((TBufferFile&)b),fOnfileObject,1); + R__PushCache onfileObject(b,fOnfileObject,1); fBranchClass->Streamer(fObject,b); } From 56fdd1340bd8e28d29613b0c72ab83f34fad20e2 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 9 Jan 2025 11:41:07 -0600 Subject: [PATCH 24/30] io: Correct validation test in add rule to set --- core/meta/src/TSchemaRuleSet.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/meta/src/TSchemaRuleSet.cxx b/core/meta/src/TSchemaRuleSet.cxx index 3e41becb99906..7e67f87f23c7a 100644 --- a/core/meta/src/TSchemaRuleSet.cxx +++ b/core/meta/src/TSchemaRuleSet.cxx @@ -113,7 +113,7 @@ Bool_t TSchemaRuleSet::AddRule( TSchemaRule* rule, EConsistencyCheck checkConsis bool streamerInfosTest; { R__LOCKGUARD(gInterpreterMutex); - streamerInfosTest = (fClass->GetStreamerInfos()==nullptr || fClass->GetStreamerInfos()->IsEmpty()); + streamerInfosTest = !(fClass->GetStreamerInfos()==nullptr || fClass->GetStreamerInfos()->IsEmpty()); } if (rule->GetTarget() && fClass->GetState() > TClass::kEmulated && streamerInfosTest) { From 963e17b6a2511d851a017aeb0e6ad6198b5a4650 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 9 Jan 2025 12:26:05 -0600 Subject: [PATCH 25/30] io: Actual check in StreamerInfo for rule validation --- core/meta/src/TSchemaRuleSet.cxx | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/core/meta/src/TSchemaRuleSet.cxx b/core/meta/src/TSchemaRuleSet.cxx index 7e67f87f23c7a..ca72b79adc060 100644 --- a/core/meta/src/TSchemaRuleSet.cxx +++ b/core/meta/src/TSchemaRuleSet.cxx @@ -110,18 +110,26 @@ Bool_t TSchemaRuleSet::AddRule( TSchemaRule* rule, EConsistencyCheck checkConsis // Check only if we have some information about the class, otherwise we have // nothing to check against - bool streamerInfosTest; - { - R__LOCKGUARD(gInterpreterMutex); - streamerInfosTest = !(fClass->GetStreamerInfos()==nullptr || fClass->GetStreamerInfos()->IsEmpty()); - } - if (rule->GetTarget() && fClass->GetState() > TClass::kEmulated && streamerInfosTest) + + // Do not generate/build the StreamerInfo if it is not already there. + TVirtualStreamerInfo *info = fClass->GetCurrentStreamerInfo(); + if (rule->GetTarget() && (fClass->GetState() > TClass::kEmulated || info)) { TObjArrayIter titer( rule->GetTarget() ); TObject* obj; while( (obj = titer.Next()) ) { TObjString* str = (TObjString*)obj; - if( !fClass->GetDataMember( str->GetString() ) && !fClass->GetBaseClass( str->GetString() ) ) { + bool found = false; + // Note: an alternative would be to use the ListOfRealData which + // is populated in both cases but it might or might not have already + // been set and would make detecting if the data member is local to + // the current class (as opposed to a base class) more difficult. + if (fClass->GetState() > TClass::kEmulated) { + found = fClass->GetDataMember( str->GetString() ) || fClass->GetBaseClass( str->GetString() ); + } else if (info) { + found = info->GetElements()->FindObject(str->GetString()); + } + if (!found) { if (checkConsistency == kCheckAll) { if (errmsg) { errmsg->Form("the target member (%s) is unknown",str->GetString().Data()); From 8fae3ced8bef87071ec18f16c647cc419ef1c0fb Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 10 Jan 2025 19:10:20 -0600 Subject: [PATCH 26/30] io-meta: allow registration of rules to classes from another dict. This patch allows to register rules on a class which dictionary is in a different dictionary source file. Introduce the internal functions: ROOT::TMetaUtils::WriteStandaloneReadRules ROOT::TMetaUtils::WriteRulesRegistration TClass::GetReadRulesRegistry TClass::RegisterReadRules --- core/clingutils/res/TClingUtils.h | 10 ++ core/clingutils/src/TClingUtils.cxx | 126 ++++++++++++++++-- core/dictgen/src/rootcling_impl.cxx | 7 + core/foundation/res/RConversionRuleParser.h | 13 +- core/foundation/src/RConversionRuleParser.cxx | 12 +- core/meta/inc/TClass.h | 7 + core/meta/inc/TSchemaRule.h | 6 + core/meta/src/TClass.cxx | 68 +++++++++- core/meta/src/TGenericClassInfo.cxx | 25 +--- core/meta/src/TSchemaRule.cxx | 30 +++++ 10 files changed, 265 insertions(+), 39 deletions(-) diff --git a/core/clingutils/res/TClingUtils.h b/core/clingutils/res/TClingUtils.h index d6fa5a960206a..91165709c5e69 100644 --- a/core/clingutils/res/TClingUtils.h +++ b/core/clingutils/res/TClingUtils.h @@ -567,6 +567,16 @@ void WriteClassInit(std::ostream& finalString, const RConstructorTypes& ctorTypes, bool& needCollectionProxy); +//______________________________________________________________________________ +void WriteStandaloneReadRules(std::ostream &finalString, bool rawrules, + std::vector &standaloneTargets, + const cling::Interpreter &interp); + +//______________________________________________________________________________ +void WriteRulesRegistration(std::ostream &finalString, + const std::string &dictName, + const std::vector &standaloneTargets); + //______________________________________________________________________________ bool HasCustomStreamerMemberFunction(const AnnotatedRecordDecl &cl, const clang::CXXRecordDecl* clxx, diff --git a/core/clingutils/src/TClingUtils.cxx b/core/clingutils/src/TClingUtils.cxx index 3746617a150be..b73201de3c25c 100644 --- a/core/clingutils/src/TClingUtils.cxx +++ b/core/clingutils/src/TClingUtils.cxx @@ -1822,8 +1822,8 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, if( rulesIt1 != ROOT::gReadRules.end() ) { int i = 0; finalString << "\n // Schema evolution read functions\n"; - std::list::iterator rIt = rulesIt1->second.begin(); - while( rIt != rulesIt1->second.end() ) { + std::list::iterator rIt = rulesIt1->second.fRules.begin(); + while( rIt != rulesIt1->second.fRules.end() ) { //-------------------------------------------------------------------- // Check if the rules refer to valid data members @@ -1832,7 +1832,7 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, std::string error_string; if( !HasValidDataMembers( *rIt, nameTypeMap, error_string ) ) { Warning(nullptr, "%s", error_string.c_str()); - rIt = rulesIt1->second.erase(rIt); + rIt = rulesIt1->second.fRules.erase(rIt); continue; } @@ -1857,8 +1857,8 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, if( rulesIt2 != ROOT::gReadRawRules.end() ) { int i = 0; finalString << "\n // Schema evolution read raw functions\n"; - std::list::iterator rIt = rulesIt2->second.begin(); - while( rIt != rulesIt2->second.end() ) { + std::list::iterator rIt = rulesIt2->second.fRules.begin(); + while( rIt != rulesIt2->second.fRules.end() ) { //-------------------------------------------------------------------- // Check if the rules refer to valid data members @@ -1867,7 +1867,7 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, std::string error_string; if( !HasValidDataMembers( *rIt, nameTypeMap, error_string ) ) { Warning(nullptr, "%s", error_string.c_str()); - rIt = rulesIt2->second.erase(rIt); + rIt = rulesIt2->second.fRules.erase(rIt); continue; } @@ -2052,14 +2052,16 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, if( rulesIt1 != ROOT::gReadRules.end() ) { finalString << "\n" << " // the io read rules" << "\n" << " std::vector<::ROOT::Internal::TSchemaHelper> readrules(" << rulesIt1->second.size() << ");" << "\n"; - ROOT::WriteSchemaList( rulesIt1->second, "readrules", finalString ); + ROOT::WriteSchemaList( rulesIt1->second.fRules, "readrules", finalString ); finalString << " instance.SetReadRules( readrules );" << "\n"; + rulesIt1->second.fGenerated = true; } if( rulesIt2 != ROOT::gReadRawRules.end() ) { finalString << "\n" << " // the io read raw rules" << "\n" << " std::vector<::ROOT::Internal::TSchemaHelper> readrawrules(" << rulesIt2->second.size() << ");" << "\n"; - ROOT::WriteSchemaList( rulesIt2->second, "readrawrules", finalString ); + ROOT::WriteSchemaList( rulesIt2->second.fRules, "readrawrules", finalString ); finalString << " instance.SetReadRawRules( readrawrules );" << "\n"; + rulesIt2->second.fGenerated = true; } finalString << " return &instance;" << "\n" << " }" << "\n"; @@ -2189,6 +2191,114 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, finalString << "} // end of namespace ROOT" << "\n" << "\n"; } +void ROOT::TMetaUtils::WriteStandaloneReadRules(std::ostream &finalString, bool rawrules, + std::vector &standaloneTargets, + const cling::Interpreter& interp) +{ + for (auto &rulesIt1 : rawrules? ROOT::gReadRawRules : ROOT::gReadRules) { + if (!rulesIt1.second.fGenerated) { + const clang::Type *typeptr = nullptr; + const clang::CXXRecordDecl *target + = ROOT::TMetaUtils::ScopeSearch(rulesIt1.first.c_str(), interp, + true /*diag*/, &typeptr); + + + if (!target && !rulesIt1.second.fTargetDecl) { + ROOT::TMetaUtils::Warning(nullptr, "%d Rule(s) for target class %s was not used!\n", rulesIt1.second.size(), + rulesIt1.first.c_str()); + continue; + } + + ROOT::MembersTypeMap_t nameTypeMap; + CreateNameTypeMap(*target, nameTypeMap); + + std::string name; + TClassEdit::GetNormalizedName(name, rulesIt1.first); + + std::string mappedname; + ROOT::TMetaUtils::GetCppName(mappedname, name.c_str()); + + finalString << "namespace ROOT {" << "\n"; + // Also TClingUtils.cxx:1823 + int i = 0; + finalString << "\n // Schema evolution read functions\n"; + std::list::iterator rIt = rulesIt1.second.fRules.begin(); + while (rIt != rulesIt1.second.fRules.end()) { + + //-------------------------------------------------------------------- + // Check if the rules refer to valid data members + /////////////////////////////////////////////////////////////////////// + + std::string error_string; + if (!HasValidDataMembers(*rIt, nameTypeMap, error_string)) { + ROOT::TMetaUtils::Warning(nullptr, "%s", error_string.c_str()); + rIt = rulesIt1.second.fRules.erase(rIt); + continue; + } + + //--------------------------------------------------------------------- + // Write the conversion function if necessary + /////////////////////////////////////////////////////////////////////// + + if (rIt->find("code") != rIt->end()) { + if (rawrules) + WriteReadRawRuleFunc(*rIt, i++, mappedname, nameTypeMap, finalString); + else + WriteReadRuleFunc(*rIt, i++, mappedname, nameTypeMap, finalString); + } + ++rIt; + } + finalString << "} // namespace ROOT" << "\n"; + + standaloneTargets.push_back(rulesIt1.first); + rulesIt1.second.fGenerated = true; + } + } +} + +void ROOT::TMetaUtils::WriteRulesRegistration(std::ostream &finalString, + const std::string &dictName, + const std::vector &standaloneTargets) +{ + std::string functionname("RecordReadRules_"); + functionname += dictName; + + finalString << "namespace ROOT {" << "\n"; + finalString << " // Registration Schema evolution read functions\n"; + finalString << " int " << functionname << "() {" << "\n"; + if (!standaloneTargets.empty()) + finalString << "\n" << " ::ROOT::Internal::TSchemaHelper* rule;" << "\n"; + for (const auto &target : standaloneTargets) + { + std::string name; + TClassEdit::GetNormalizedName(name, target); + + ROOT::SchemaRuleClassMap_t::iterator rulesIt1 = ROOT::gReadRules.find( target.c_str() ); + finalString << " {\n"; + if (rulesIt1 != ROOT::gReadRules.end()) { + finalString << " // the io read rules for " << target << "\n"; + finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrules(" << rulesIt1->second.size() << ");" << "\n"; + ROOT::WriteSchemaList( rulesIt1->second.fRules, "readrules", finalString ); + finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRule, \"" << name << "\", std::move(readrules));\n"; + rulesIt1->second.fGenerated = true; + } + ROOT::SchemaRuleClassMap_t::iterator rulesIt2 = ROOT::gReadRawRules.find( target.c_str() ); + if (rulesIt2 != ROOT::gReadRawRules.end()) { + finalString << "\n // the io read raw rules for " << target << "\n"; + finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrawrules(" << rulesIt2->second.size() << ");" << "\n"; + ROOT::WriteSchemaList( rulesIt2->second.fRules, "readrawrules", finalString ); + finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRawRule, \"" << name << "\", std::move(readrawrules));\n"; + rulesIt2->second.fGenerated = true; + } + finalString << " }\n"; + } + finalString << " return 0;\n"; + finalString << " }\n"; + finalString << " static int _R__UNIQUE_DICT_(ReadRules_" << dictName << ") = " << functionname << "();"; + finalString<< "R__UseDummy(_R__UNIQUE_DICT_(ReadRules_" << dictName << "));" << "\n"; + finalString << "} // namespace ROOT" << "\n"; +} + //////////////////////////////////////////////////////////////////////////////// /// Return true if one of the class' enclosing scope is a namespace and /// set fullname to the fully qualified name, diff --git a/core/dictgen/src/rootcling_impl.cxx b/core/dictgen/src/rootcling_impl.cxx index a321eda47f367..d16093967c6cb 100644 --- a/core/dictgen/src/rootcling_impl.cxx +++ b/core/dictgen/src/rootcling_impl.cxx @@ -2650,6 +2650,7 @@ int FinalizeStreamerInfoWriting(cling::Interpreter &interp, bool writeEmptyRootP //////////////////////////////////////////////////////////////////////////////// int GenerateFullDict(std::ostream &dictStream, + std::string dictName, cling::Interpreter &interp, RScanner &scan, const ROOT::TMetaUtils::RConstructorTypes &ctorTypes, @@ -2795,6 +2796,11 @@ int GenerateFullDict(std::ostream &dictStream, EmitStreamerInfo); } + std::vector standaloneTargets; + ROOT::TMetaUtils::WriteStandaloneReadRules(dictStream, false, standaloneTargets, interp); + ROOT::TMetaUtils::WriteStandaloneReadRules(dictStream, true, standaloneTargets, interp); + ROOT::TMetaUtils::WriteRulesRegistration(dictStream, dictName, standaloneTargets); + if (!gDriverConfig->fBuildingROOTStage1) { EmitTypedefs(scan.fSelectedTypedefs); EmitEnums(scan.fSelectedEnums); @@ -4962,6 +4968,7 @@ int RootClingMain(int argc, } } else { rootclingRetCode += GenerateFullDict(*splitDictStream, + modGen.GetDictionaryName(), interp, scan, constructorTypes, diff --git a/core/foundation/res/RConversionRuleParser.h b/core/foundation/res/RConversionRuleParser.h index fa323ff708c59..cf5c7c360da98 100644 --- a/core/foundation/res/RConversionRuleParser.h +++ b/core/foundation/res/RConversionRuleParser.h @@ -14,13 +14,24 @@ #include "TSchemaType.h" #include "DllImport.h" +namespace clang { + class CXXRecordDecl; +} + namespace ROOT { //--------------------------------------------------------------------------- // Global variables //--------------------------------------------------------------------------- typedef std::map SchemaRuleMap_t; - typedef std::map > SchemaRuleClassMap_t; + struct RRulesList { + bool fGenerated = false; + std::list fRules; + const clang::CXXRecordDecl *fTargetDecl; + + size_t size() const { return fRules.size(); } + }; + typedef std::map SchemaRuleClassMap_t; R__EXTERN SchemaRuleClassMap_t gReadRules; R__EXTERN SchemaRuleClassMap_t gReadRawRules; diff --git a/core/foundation/src/RConversionRuleParser.cxx b/core/foundation/src/RConversionRuleParser.cxx index 35694ac5c65b4..e0ccd0d1aa557 100644 --- a/core/foundation/src/RConversionRuleParser.cxx +++ b/core/foundation/src/RConversionRuleParser.cxx @@ -864,7 +864,7 @@ namespace ROOT ////////////////////////////////////////////////////////////////////////// for( it = gReadRules.begin(); it != gReadRules.end(); ++it ) { - for( rule = it->second.begin(); rule != it->second.end(); ++rule ) { + for( rule = it->second.fRules.begin(); rule != it->second.fRules.end(); ++rule ) { attr = rule->find( "include" ); if( attr == rule->end() ) continue; TSchemaRuleProcessor::SplitList( attr->second, tmp ); @@ -877,7 +877,7 @@ namespace ROOT ////////////////////////////////////////////////////////////////////////// for( it = gReadRawRules.begin(); it != gReadRawRules.end(); ++it ) { - for( rule = it->second.begin(); rule != it->second.end(); ++rule ) { + for( rule = it->second.fRules.begin(); rule != it->second.fRules.end(); ++rule ) { attr = rule->find( "include" ); if( attr == rule->end() ) continue; TSchemaRuleProcessor::SplitList( attr->second, tmp ); @@ -923,10 +923,10 @@ namespace ROOT if( it == gReadRules.end() ) { std::list lst; lst.push_back( rule ); - gReadRules[normalizedTargetName] = lst; + gReadRules[normalizedTargetName].fRules = lst; } else - it->second.push_back( rule ); + it->second.fRules.push_back( rule ); } ///////////////////////////////////////////////////////////////////////////// @@ -958,10 +958,10 @@ namespace ROOT if( it == gReadRawRules.end() ) { std::list lst; lst.push_back( rule ); - gReadRawRules[normalizedTargetName] = lst; + gReadRawRules[normalizedTargetName].fRules = lst; } else - it->second.push_back( rule ); + it->second.fRules.push_back( rule ); } diff --git a/core/meta/inc/TClass.h b/core/meta/inc/TClass.h index 56365cd720a0d..5d98f3b21ee22 100644 --- a/core/meta/inc/TClass.h +++ b/core/meta/inc/TClass.h @@ -22,6 +22,7 @@ #include "TDictionary.h" #include "TString.h" +#include "TSchemaRule.h" #ifdef R__LESS_INCLUDES class TObjArray; @@ -36,6 +37,7 @@ class TObjArray; #include #include #include +#include #include #include #include @@ -72,6 +74,7 @@ namespace ROOT { } namespace Internal { class TCheckHashRecursiveRemoveConsistency; + struct TSchemaHelper; } } @@ -361,6 +364,9 @@ friend class TStreamerInfo; void GetMissingDictionariesWithRecursionCheck(TCollection &result, TCollection &visited, bool recurse); void GetMissingDictionariesForPairElements(TCollection &result, TCollection &visited, bool recurse); + using SchemaHelperMap_t = std::unordered_map>; + static SchemaHelperMap_t &GetReadRulesRegistry(ROOT::TSchemaRule::RuleType_t type); + public: TClass(); TClass(const char *name, Bool_t silent = kFALSE); @@ -539,6 +545,7 @@ friend class TStreamerInfo; Long_t Property() const override; Int_t ReadBuffer(TBuffer &b, void *pointer, Int_t version, UInt_t start, UInt_t count); Int_t ReadBuffer(TBuffer &b, void *pointer); + static void RegisterReadRules(ROOT::TSchemaRule::RuleType_t, const char *classname, std::vector<::ROOT::Internal::TSchemaHelper> &&rules); void RegisterStreamerInfo(TVirtualStreamerInfo *info); void RemoveStreamerInfo(Int_t slot); void ReplaceWith(TClass *newcl) const; diff --git a/core/meta/inc/TSchemaRule.h b/core/meta/inc/TSchemaRule.h index a373028ff99fe..48c0d093b38cd 100644 --- a/core/meta/inc/TSchemaRule.h +++ b/core/meta/inc/TSchemaRule.h @@ -13,6 +13,11 @@ class TBuffer; class TVirtualObject; class TObjArray; +namespace ROOT { +namespace Internal { + struct TSchemaHelper; +} +} namespace ROOT { @@ -76,6 +81,7 @@ namespace ROOT { typedef void (*ReadRawFuncPtr_t)( char*, TBuffer&); TSchemaRule(); + TSchemaRule(RuleType_t type, const char *targetClass, const ROOT::Internal::TSchemaHelper& helper); virtual ~TSchemaRule(); TSchemaRule( const TSchemaRule& rhs ); diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index d906002109c82..35ebdd2f6e4a7 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -1719,6 +1719,22 @@ void TClass::Init(const char *name, Version_t cversion, // std::pairs have implicit conversions GetSchemaRules(kTRUE); } + for(auto ruletype : {ROOT::TSchemaRule::kReadRule, ROOT::TSchemaRule::kReadRawRule}) { + auto ®istry = GetReadRulesRegistry(ruletype); + auto rulesiter = registry.find(GetName()); + if (rulesiter != registry.end()) { + auto rset = GetSchemaRules( kTRUE ); + for(const auto &helper : rulesiter->second) { + auto rule = new ROOT::TSchemaRule(ruletype, GetName(), helper); + TString errmsg; + if( !rset->AddRule( rule, ROOT::Detail::TSchemaRuleSet::kCheckAll, &errmsg ) ) { + Warning( "Init", "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", + GetName(), helper.fVersion.c_str(), helper.fTarget.c_str(), errmsg.Data() ); + delete rule; + } + } + } + } ResetBit(kLoading); } @@ -1995,6 +2011,20 @@ void TClass::AdoptSchemaRules( ROOT::Detail::TSchemaRuleSet *rules ) fSchemaRules->SetClass( this ); } +//////////////////////////////////////////////////////////////////////////////// +/// Return the registry for the unassigned read rules. + +TClass::SchemaHelperMap_t &TClass::GetReadRulesRegistry(ROOT::TSchemaRule::RuleType_t type) +{ + if (type == ROOT::TSchemaRule::kReadRule) { + static SchemaHelperMap_t gReadRulesRegistry; + return gReadRulesRegistry; + } else { + static SchemaHelperMap_t gReadRawRulesRegistry; + return gReadRawRulesRegistry; + } +} + //////////////////////////////////////////////////////////////////////////////// /// Return the set of the schema rules if any. @@ -5010,8 +5040,8 @@ const void *TClass::DynamicCast(const TClass *cl, const void *obj, Bool_t up) /// If quiet is true, do not issue a message via Error in case /// of problems, just return `nullptr`. /// -/// This method is also used by the I/O subsystem to allocate the right amount -/// of memory for the objects. If a default constructor is not defined for a +/// This method is also used by the I/O subsystem to allocate the right amount +/// of memory for the objects. If a default constructor is not defined for a /// certain class, some options are available. /// The simplest is to define the default I/O constructor, for example /// ~~~{.cpp} @@ -5028,7 +5058,7 @@ const void *TClass::DynamicCast(const TClass *cl, const void *obj, Bool_t up) /// ~~~ {.cpp} /// #pragma link C++ ioctortype UserClass; /// ~~~ -/// `TClass::New` will then look for a constructor (for a class `MyClass` in the +/// `TClass::New` will then look for a constructor (for a class `MyClass` in the /// following example) in the following order, constructing the object using the /// first one in the list that exists and is declared public: /// ~~~ {.cpp} @@ -7366,6 +7396,38 @@ TVirtualStreamerInfo *TClass::FindConversionStreamerInfo( const TClass* cl, UInt return info; } +//////////////////////////////////////////////////////////////////////////////// +/// Register a set of read rules for a target class. +/// +/// Rules will end up here if they are created in a dictionary file that does not +/// contain the dictionary for the target class. + +void TClass::RegisterReadRules(ROOT::TSchemaRule::RuleType_t type, + const char *classname, std::vector<::ROOT::Internal::TSchemaHelper> &&rules) +{ + R__WRITE_LOCKGUARD(ROOT::gCoreMutex); + + auto cl = TClass::GetClass(classname, false, false); + if (cl) { + auto rset = cl->GetSchemaRules( kTRUE ); + for(const auto &it : rules) { + auto rule = new ROOT::TSchemaRule(type, cl->GetName(), it); + TString errmsg; + if( !rset->AddRule( rule, ROOT::Detail::TSchemaRuleSet::kCheckAll, &errmsg ) ) { + ::Warning( "TGenericClassInfo", "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", + cl->GetName(), it.fVersion.c_str(), it.fTarget.c_str(), errmsg.Data() ); + delete rule; + } + } + } else { + auto ®istry = GetReadRulesRegistry(type); + auto ans = registry.try_emplace(classname, std::move(rules)); + if (!ans.second) { + ans.first->second.insert(ans.first->second.end(), rules.begin(), rules.end()); + } + } +} + //////////////////////////////////////////////////////////////////////////////// /// Register the StreamerInfo in the given slot, change the State of the /// TClass as appropriate. diff --git a/core/meta/src/TGenericClassInfo.cxx b/core/meta/src/TGenericClassInfo.cxx index 961a66e8b3bd2..48d01304d1b29 100644 --- a/core/meta/src/TGenericClassInfo.cxx +++ b/core/meta/src/TGenericClassInfo.cxx @@ -331,27 +331,10 @@ namespace Internal { TString errmsg; std::vector::iterator it; for( it = vect.begin(); it != vect.end(); ++it ) { - rule = new TSchemaRule(); - rule->SetTarget( it->fTarget ); - rule->SetTargetClass( fClass->GetName() ); - rule->SetSourceClass( it->fSourceClass ); - rule->SetSource( it->fSource ); - rule->SetCode( it->fCode ); - rule->SetVersion( it->fVersion ); - rule->SetChecksum( it->fChecksum ); - rule->SetEmbed( it->fEmbed ); - rule->SetInclude( it->fInclude ); - rule->SetAttributes( it->fAttributes ); - - if( ProcessReadRules ) { - rule->SetRuleType( TSchemaRule::kReadRule ); - rule->SetReadFunctionPointer( (TSchemaRule::ReadFuncPtr_t)it->fFunctionPtr ); - } - else { - rule->SetRuleType( TSchemaRule::kReadRawRule ); - rule->SetReadRawFunctionPointer( (TSchemaRule::ReadRawFuncPtr_t)it->fFunctionPtr ); - } - if( !rset->AddRule( rule, TSchemaRuleSet::kCheckAll, &errmsg ) ) { + rule = new TSchemaRule(ProcessReadRules ? TSchemaRule::kReadRule : TSchemaRule::kReadRawRule, + fClass->GetName(), *it); + + if( !rset->AddRule( rule, TSchemaRuleSet::kCheckAll, &errmsg ) ) { ::Warning( "TGenericClassInfo", "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", GetClassName(), it->fVersion.c_str(), it->fTarget.c_str(), errmsg.Data() ); delete rule; diff --git a/core/meta/src/TSchemaRule.cxx b/core/meta/src/TSchemaRule.cxx index 474d874cc6a54..34f2e102c7df7 100644 --- a/core/meta/src/TSchemaRule.cxx +++ b/core/meta/src/TSchemaRule.cxx @@ -10,6 +10,7 @@ #include "TObjArray.h" #include "TObjString.h" #include "TROOT.h" +#include "TSchemaHelper.h" #include #include @@ -94,6 +95,35 @@ TSchemaRule::TSchemaRule(): fVersionVect( nullptr ), fChecksumVect( nullptr ), { } +//////////////////////////////////////////////////////////////////////////////// +/// Constructor. +TSchemaRule::TSchemaRule(TSchemaRule::RuleType_t type, const char *targetClass, + const ROOT::Internal::TSchemaHelper& helper) : + fVersionVect( nullptr ), fChecksumVect( nullptr ), + fTargetVect( nullptr ), fSourceVect( nullptr ), + fIncludeVect( nullptr ), fEmbed( kTRUE ), + fReadFuncPtr( nullptr ), fReadRawFuncPtr( nullptr ), + fRuleType( type ) +{ + SetTarget( helper.fTarget ); + SetTargetClass( targetClass ); + SetSourceClass( helper.fSourceClass ); + SetSource( helper.fSource ); + SetCode( helper.fCode ); + SetVersion( helper.fVersion ); + SetChecksum( helper.fChecksum ); + SetEmbed( helper.fEmbed ); + SetInclude( helper.fInclude ); + SetAttributes( helper.fAttributes ); + + if( type == TSchemaRule::kReadRule ) { + SetReadFunctionPointer( (TSchemaRule::ReadFuncPtr_t)helper.fFunctionPtr ); + } else { + SetReadRawFunctionPointer( (TSchemaRule::ReadRawFuncPtr_t)helper.fFunctionPtr ); + } +} + + //////////////////////////////////////////////////////////////////////////////// /// Destructor. From 915cf10d933cd5b5e9ff2cea4bc887a3e79f4cef Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 13 Jan 2025 11:22:36 -0600 Subject: [PATCH 27/30] io: TStreamerInfo::kObject don't use virtual function if class changed --- core/meta/src/TClass.cxx | 13 ++++++++++--- io/io/src/TStreamerInfoReadBuffer.cxx | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 35ebdd2f6e4a7..81e74c4c7966a 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -6918,10 +6918,17 @@ void TClass::StreamerTObject(const TClass* pThis, void *object, TBuffer &b, cons //////////////////////////////////////////////////////////////////////////////// /// Case of TObjects when fIsOffsetStreamerSet is known to have been set. -void TClass::StreamerTObjectInitialized(const TClass* pThis, void *object, TBuffer &b, const TClass * /* onfile_class */) +void TClass::StreamerTObjectInitialized(const TClass* pThis, void *object, TBuffer &b, const TClass *onfile_class) { - TObject *tobj = (TObject*)((Longptr_t)object + pThis->fOffsetStreamer); - tobj->Streamer(b); + if (R__likely(onfile_class == nullptr || pThis == onfile_class)) { + TObject *tobj = (TObject*)((Longptr_t)object + pThis->fOffsetStreamer); + tobj->Streamer(b); + } else { + // This is the case where we are reading an object of a derived class + // but the class is not the same as the one we are streaming. + // We need to call the Streamer of the base class. + StreamerTObjectEmulated(pThis, object, b, onfile_class); + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index 308dae0298718..dc8930c9cb010 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -1362,7 +1362,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, continue; case TStreamerInfo::kObject: // Class derived from TObject - if (cle->IsStartingWithTObject() && cle->GetState() > TClass::kEmulated) { + if (cle == newCle && cle->IsStartingWithTObject() && cle->GetState() > TClass::kEmulated) { DOLOOP {((TObject*)(arr[k]+ioffset))->Streamer(b);} continue; // intentionally inside the if statement. // if the class does not start with its TObject part (or does From 10efc6949e2a5b64aea6d83af6f78505770048af Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 13 Jan 2025 11:24:22 -0600 Subject: [PATCH 28/30] io: ReadClassEmulated actually use ConversionStreamerInfo --- io/io/src/TBufferFile.cxx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index a267ba24b21df..18ba00dbe8894 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -3410,21 +3410,21 @@ Int_t TBufferFile::ReadClassEmulated(const TClass *cl, void *object, const TClas //We attempt to recover if a version count was not written Version_t v = ReadVersion(&start,&count); - if (count) { - TStreamerInfo *sinfo = nullptr; - if( onFileClass ) { - sinfo = (TStreamerInfo*)cl->GetConversionStreamerInfo( onFileClass, v ); - if( !sinfo ) - return 0; - } - + TStreamerInfo *sinfo = nullptr; + if( onFileClass ) { + sinfo = (TStreamerInfo*)cl->GetConversionStreamerInfo( onFileClass, v ); + if( !sinfo ) + return 0; + } + if (!sinfo) sinfo = (TStreamerInfo*)cl->GetStreamerInfo(v); + + if (count) { ApplySequence(*(sinfo->GetReadObjectWiseActions()), object); if (sinfo->IsRecovered()) count=0; CheckByteCount(start,count,cl); } else { SetBufferOffset(start); - TStreamerInfo *sinfo = ((TStreamerInfo*)cl->GetStreamerInfo()); ApplySequence(*(sinfo->GetReadObjectWiseActions()), object); } return 0; From 197d3ba69ab812d0f8afa7914662101ca446b72c Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 13 Jan 2025 11:28:11 -0600 Subject: [PATCH 29/30] io: for rule input don't use virtual function if class changed --- io/io/src/TStreamerInfo.cxx | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index b28d57d9bf38d..f00ec70de9809 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -288,10 +288,13 @@ namespace { memType = TVirtualStreamerInfo::kAnyP; } } else { - if (memClass->IsTObject()) { - memType = TVirtualStreamerInfo::kObject; - } else if (memClass->GetCollectionProxy()) { + if (memClass->GetCollectionProxy()) { memType = TVirtualStreamerInfo::kSTL; + } else if(memClass->IsTObject() && memClass == element->GetClassPointer()) { + // If there is a change in the class type, we can't use the TObject::Streamer + // virtual function: it would streame the data using the in-memory type rather + // than the onfile type. + memType = TVirtualStreamerInfo::kObject; } else { memType = TVirtualStreamerInfo::kAny; } @@ -349,6 +352,13 @@ namespace { void UpdateFromRule(const TStreamerInfo *info, const ROOT::TSchemaRule::TSources *s, TStreamerElement *element) { auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(s, element); + if (element->GetType() == TVirtualStreamerInfo::kObject && memClass != element->GetClassPointer()) + { + // If there is a change in the class type, we can't use the TObject::Streamer + // virtual function: it would streame the data using the in-memory type rather + // than the onfile type. + element->SetType(TVirtualStreamerInfo::kAny); + } element->SetNewType( memType ); element->SetNewClass( memClass ); // We can not change the recorded dimensions. Let's check that From 1965fb424c796cde1b34dec8846148bb4f496475 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sun, 19 Jan 2025 10:13:31 -0600 Subject: [PATCH 30/30] [NFC] clang-format white space --- core/clingutils/res/TClingUtils.h | 6 +- core/clingutils/src/TClingUtils.cxx | 51 +++++++-------- core/dictgen/src/rootcling_impl.cxx | 23 ++----- core/foundation/res/RConversionRuleParser.h | 2 +- core/foundation/src/RConversionRuleParser.cxx | 8 +-- core/meta/inc/TClass.h | 3 +- core/meta/inc/TSchemaRule.h | 13 ++-- core/meta/src/TClass.cxx | 40 ++++++------ core/meta/src/TGenericClassInfo.cxx | 2 +- core/meta/src/TSchemaRule.cxx | 45 +++++++------- core/meta/src/TSchemaRuleSet.cxx | 5 +- io/io/src/TBufferFile.cxx | 6 +- io/io/src/TKey.cxx | 5 +- io/io/src/TStreamerInfo.cxx | 62 ++++++++----------- io/io/src/TStreamerInfoReadBuffer.cxx | 40 +++++------- io/io/src/TStreamerInfoWriteBuffer.cxx | 16 ++--- tree/tree/src/TBranchElement.cxx | 30 ++++----- tree/treeplayer/src/TBranchProxy.cxx | 22 ++++--- 18 files changed, 176 insertions(+), 203 deletions(-) diff --git a/core/clingutils/res/TClingUtils.h b/core/clingutils/res/TClingUtils.h index 91165709c5e69..e99e4990a7c55 100644 --- a/core/clingutils/res/TClingUtils.h +++ b/core/clingutils/res/TClingUtils.h @@ -568,13 +568,11 @@ void WriteClassInit(std::ostream& finalString, bool& needCollectionProxy); //______________________________________________________________________________ -void WriteStandaloneReadRules(std::ostream &finalString, bool rawrules, - std::vector &standaloneTargets, +void WriteStandaloneReadRules(std::ostream &finalString, bool rawrules, std::vector &standaloneTargets, const cling::Interpreter &interp); //______________________________________________________________________________ -void WriteRulesRegistration(std::ostream &finalString, - const std::string &dictName, +void WriteRulesRegistration(std::ostream &finalString, const std::string &dictName, const std::vector &standaloneTargets); //______________________________________________________________________________ diff --git a/core/clingutils/src/TClingUtils.cxx b/core/clingutils/src/TClingUtils.cxx index b73201de3c25c..1e1ce43a49588 100644 --- a/core/clingutils/src/TClingUtils.cxx +++ b/core/clingutils/src/TClingUtils.cxx @@ -1823,7 +1823,7 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, int i = 0; finalString << "\n // Schema evolution read functions\n"; std::list::iterator rIt = rulesIt1->second.fRules.begin(); - while( rIt != rulesIt1->second.fRules.end() ) { + while (rIt != rulesIt1->second.fRules.end()) { //-------------------------------------------------------------------- // Check if the rules refer to valid data members @@ -1858,7 +1858,7 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, int i = 0; finalString << "\n // Schema evolution read raw functions\n"; std::list::iterator rIt = rulesIt2->second.fRules.begin(); - while( rIt != rulesIt2->second.fRules.end() ) { + while (rIt != rulesIt2->second.fRules.end()) { //-------------------------------------------------------------------- // Check if the rules refer to valid data members @@ -2052,14 +2052,14 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, if( rulesIt1 != ROOT::gReadRules.end() ) { finalString << "\n" << " // the io read rules" << "\n" << " std::vector<::ROOT::Internal::TSchemaHelper> readrules(" << rulesIt1->second.size() << ");" << "\n"; - ROOT::WriteSchemaList( rulesIt1->second.fRules, "readrules", finalString ); + ROOT::WriteSchemaList(rulesIt1->second.fRules, "readrules", finalString); finalString << " instance.SetReadRules( readrules );" << "\n"; rulesIt1->second.fGenerated = true; } if( rulesIt2 != ROOT::gReadRawRules.end() ) { finalString << "\n" << " // the io read raw rules" << "\n" << " std::vector<::ROOT::Internal::TSchemaHelper> readrawrules(" << rulesIt2->second.size() << ");" << "\n"; - ROOT::WriteSchemaList( rulesIt2->second.fRules, "readrawrules", finalString ); + ROOT::WriteSchemaList(rulesIt2->second.fRules, "readrawrules", finalString); finalString << " instance.SetReadRawRules( readrawrules );" << "\n"; rulesIt2->second.fGenerated = true; } @@ -2193,19 +2193,17 @@ void ROOT::TMetaUtils::WriteClassInit(std::ostream& finalString, void ROOT::TMetaUtils::WriteStandaloneReadRules(std::ostream &finalString, bool rawrules, std::vector &standaloneTargets, - const cling::Interpreter& interp) + const cling::Interpreter &interp) { - for (auto &rulesIt1 : rawrules? ROOT::gReadRawRules : ROOT::gReadRules) { + for (auto &rulesIt1 : rawrules ? ROOT::gReadRawRules : ROOT::gReadRules) { if (!rulesIt1.second.fGenerated) { const clang::Type *typeptr = nullptr; - const clang::CXXRecordDecl *target - = ROOT::TMetaUtils::ScopeSearch(rulesIt1.first.c_str(), interp, - true /*diag*/, &typeptr); - + const clang::CXXRecordDecl *target = + ROOT::TMetaUtils::ScopeSearch(rulesIt1.first.c_str(), interp, true /*diag*/, &typeptr); if (!target && !rulesIt1.second.fTargetDecl) { ROOT::TMetaUtils::Warning(nullptr, "%d Rule(s) for target class %s was not used!\n", rulesIt1.second.size(), - rulesIt1.first.c_str()); + rulesIt1.first.c_str()); continue; } @@ -2256,8 +2254,7 @@ void ROOT::TMetaUtils::WriteStandaloneReadRules(std::ostream &finalString, bool } } -void ROOT::TMetaUtils::WriteRulesRegistration(std::ostream &finalString, - const std::string &dictName, +void ROOT::TMetaUtils::WriteRulesRegistration(std::ostream &finalString, const std::string &dictName, const std::vector &standaloneTargets) { std::string functionname("RecordReadRules_"); @@ -2267,27 +2264,31 @@ void ROOT::TMetaUtils::WriteRulesRegistration(std::ostream &finalString, finalString << " // Registration Schema evolution read functions\n"; finalString << " int " << functionname << "() {" << "\n"; if (!standaloneTargets.empty()) - finalString << "\n" << " ::ROOT::Internal::TSchemaHelper* rule;" << "\n"; - for (const auto &target : standaloneTargets) - { + finalString << "\n" + << " ::ROOT::Internal::TSchemaHelper* rule;" << "\n"; + for (const auto &target : standaloneTargets) { std::string name; TClassEdit::GetNormalizedName(name, target); - ROOT::SchemaRuleClassMap_t::iterator rulesIt1 = ROOT::gReadRules.find( target.c_str() ); + ROOT::SchemaRuleClassMap_t::iterator rulesIt1 = ROOT::gReadRules.find(target.c_str()); finalString << " {\n"; if (rulesIt1 != ROOT::gReadRules.end()) { finalString << " // the io read rules for " << target << "\n"; - finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrules(" << rulesIt1->second.size() << ");" << "\n"; - ROOT::WriteSchemaList( rulesIt1->second.fRules, "readrules", finalString ); - finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRule, \"" << name << "\", std::move(readrules));\n"; + finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrules(" << rulesIt1->second.size() + << ");" << "\n"; + ROOT::WriteSchemaList(rulesIt1->second.fRules, "readrules", finalString); + finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRule, \"" << name + << "\", std::move(readrules));\n"; rulesIt1->second.fGenerated = true; } - ROOT::SchemaRuleClassMap_t::iterator rulesIt2 = ROOT::gReadRawRules.find( target.c_str() ); + ROOT::SchemaRuleClassMap_t::iterator rulesIt2 = ROOT::gReadRawRules.find(target.c_str()); if (rulesIt2 != ROOT::gReadRawRules.end()) { finalString << "\n // the io read raw rules for " << target << "\n"; - finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrawrules(" << rulesIt2->second.size() << ");" << "\n"; - ROOT::WriteSchemaList( rulesIt2->second.fRules, "readrawrules", finalString ); - finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRawRule, \"" << name << "\", std::move(readrawrules));\n"; + finalString << " std::vector<::ROOT::Internal::TSchemaHelper> readrawrules(" << rulesIt2->second.size() + << ");" << "\n"; + ROOT::WriteSchemaList(rulesIt2->second.fRules, "readrawrules", finalString); + finalString << " TClass::RegisterReadRules(TSchemaRule::kReadRawRule, \"" << name + << "\", std::move(readrawrules));\n"; rulesIt2->second.fGenerated = true; } finalString << " }\n"; @@ -2295,7 +2296,7 @@ void ROOT::TMetaUtils::WriteRulesRegistration(std::ostream &finalString, finalString << " return 0;\n"; finalString << " }\n"; finalString << " static int _R__UNIQUE_DICT_(ReadRules_" << dictName << ") = " << functionname << "();"; - finalString<< "R__UseDummy(_R__UNIQUE_DICT_(ReadRules_" << dictName << "));" << "\n"; + finalString << "R__UseDummy(_R__UNIQUE_DICT_(ReadRules_" << dictName << "));" << "\n"; finalString << "} // namespace ROOT" << "\n"; } diff --git a/core/dictgen/src/rootcling_impl.cxx b/core/dictgen/src/rootcling_impl.cxx index d16093967c6cb..22fc097f59b15 100644 --- a/core/dictgen/src/rootcling_impl.cxx +++ b/core/dictgen/src/rootcling_impl.cxx @@ -2649,15 +2649,9 @@ int FinalizeStreamerInfoWriting(cling::Interpreter &interp, bool writeEmptyRootP //////////////////////////////////////////////////////////////////////////////// -int GenerateFullDict(std::ostream &dictStream, - std::string dictName, - cling::Interpreter &interp, - RScanner &scan, - const ROOT::TMetaUtils::RConstructorTypes &ctorTypes, - bool isSplit, - bool isGenreflex, - bool isSelXML, - bool writeEmptyRootPCM) +int GenerateFullDict(std::ostream &dictStream, std::string dictName, cling::Interpreter &interp, RScanner &scan, + const ROOT::TMetaUtils::RConstructorTypes &ctorTypes, bool isSplit, bool isGenreflex, + bool isSelXML, bool writeEmptyRootPCM) { ROOT::TMetaUtils::TNormalizedCtxt normCtxt(interp.getLookupHelper()); @@ -4967,15 +4961,8 @@ int RootClingMain(int argc, rootclingRetCode += FinalizeStreamerInfoWriting(interp); } } else { - rootclingRetCode += GenerateFullDict(*splitDictStream, - modGen.GetDictionaryName(), - interp, - scan, - constructorTypes, - gOptSplit, - isGenreflex, - isSelXML, - gOptWriteEmptyRootPCM); + rootclingRetCode += GenerateFullDict(*splitDictStream, modGen.GetDictionaryName(), interp, scan, constructorTypes, + gOptSplit, isGenreflex, isSelXML, gOptWriteEmptyRootPCM); } if (rootclingRetCode != 0) { diff --git a/core/foundation/res/RConversionRuleParser.h b/core/foundation/res/RConversionRuleParser.h index cf5c7c360da98..8d04040c039ec 100644 --- a/core/foundation/res/RConversionRuleParser.h +++ b/core/foundation/res/RConversionRuleParser.h @@ -15,7 +15,7 @@ #include "DllImport.h" namespace clang { - class CXXRecordDecl; +class CXXRecordDecl; } namespace ROOT diff --git a/core/foundation/src/RConversionRuleParser.cxx b/core/foundation/src/RConversionRuleParser.cxx index e0ccd0d1aa557..0e80dc8c093c5 100644 --- a/core/foundation/src/RConversionRuleParser.cxx +++ b/core/foundation/src/RConversionRuleParser.cxx @@ -864,7 +864,7 @@ namespace ROOT ////////////////////////////////////////////////////////////////////////// for( it = gReadRules.begin(); it != gReadRules.end(); ++it ) { - for( rule = it->second.fRules.begin(); rule != it->second.fRules.end(); ++rule ) { + for (rule = it->second.fRules.begin(); rule != it->second.fRules.end(); ++rule) { attr = rule->find( "include" ); if( attr == rule->end() ) continue; TSchemaRuleProcessor::SplitList( attr->second, tmp ); @@ -877,7 +877,7 @@ namespace ROOT ////////////////////////////////////////////////////////////////////////// for( it = gReadRawRules.begin(); it != gReadRawRules.end(); ++it ) { - for( rule = it->second.fRules.begin(); rule != it->second.fRules.end(); ++rule ) { + for (rule = it->second.fRules.begin(); rule != it->second.fRules.end(); ++rule) { attr = rule->find( "include" ); if( attr == rule->end() ) continue; TSchemaRuleProcessor::SplitList( attr->second, tmp ); @@ -926,7 +926,7 @@ namespace ROOT gReadRules[normalizedTargetName].fRules = lst; } else - it->second.fRules.push_back( rule ); + it->second.fRules.push_back(rule); } ///////////////////////////////////////////////////////////////////////////// @@ -961,7 +961,7 @@ namespace ROOT gReadRawRules[normalizedTargetName].fRules = lst; } else - it->second.fRules.push_back( rule ); + it->second.fRules.push_back(rule); } diff --git a/core/meta/inc/TClass.h b/core/meta/inc/TClass.h index 5d98f3b21ee22..6de61a9009f58 100644 --- a/core/meta/inc/TClass.h +++ b/core/meta/inc/TClass.h @@ -545,7 +545,8 @@ friend class TStreamerInfo; Long_t Property() const override; Int_t ReadBuffer(TBuffer &b, void *pointer, Int_t version, UInt_t start, UInt_t count); Int_t ReadBuffer(TBuffer &b, void *pointer); - static void RegisterReadRules(ROOT::TSchemaRule::RuleType_t, const char *classname, std::vector<::ROOT::Internal::TSchemaHelper> &&rules); + static void RegisterReadRules(ROOT::TSchemaRule::RuleType_t, const char *classname, + std::vector<::ROOT::Internal::TSchemaHelper> &&rules); void RegisterStreamerInfo(TVirtualStreamerInfo *info); void RemoveStreamerInfo(Int_t slot); void ReplaceWith(TClass *newcl) const; diff --git a/core/meta/inc/TSchemaRule.h b/core/meta/inc/TSchemaRule.h index 48c0d093b38cd..ad2da507eb0c5 100644 --- a/core/meta/inc/TSchemaRule.h +++ b/core/meta/inc/TSchemaRule.h @@ -15,9 +15,9 @@ class TVirtualObject; class TObjArray; namespace ROOT { namespace Internal { - struct TSchemaHelper; -} +struct TSchemaHelper; } +} // namespace ROOT namespace ROOT { @@ -47,17 +47,14 @@ namespace ROOT { Int_t GetPointerLevel() const { return fPointerLevel; } - const char* GetUnderlyingTypeName() const - { - return fTitle; - } + const char *GetUnderlyingTypeName() const { return fTitle; } // The source can be declared with: // "%s %s%s;", GetTypeForDeclaration().Data(), GetName(), GetDimensions); TString GetTypeForDeclaration() { TString type = fTitle; - for(Int_t s = 0; s < fPointerLevel; ++s) + for (Int_t s = 0; s < fPointerLevel; ++s) type.Append('*'); return type; } @@ -81,7 +78,7 @@ namespace ROOT { typedef void (*ReadRawFuncPtr_t)( char*, TBuffer&); TSchemaRule(); - TSchemaRule(RuleType_t type, const char *targetClass, const ROOT::Internal::TSchemaHelper& helper); + TSchemaRule(RuleType_t type, const char *targetClass, const ROOT::Internal::TSchemaHelper &helper); virtual ~TSchemaRule(); TSchemaRule( const TSchemaRule& rhs ); diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 81e74c4c7966a..af551fbff7ec2 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -1719,17 +1719,19 @@ void TClass::Init(const char *name, Version_t cversion, // std::pairs have implicit conversions GetSchemaRules(kTRUE); } - for(auto ruletype : {ROOT::TSchemaRule::kReadRule, ROOT::TSchemaRule::kReadRawRule}) { + for (auto ruletype : {ROOT::TSchemaRule::kReadRule, ROOT::TSchemaRule::kReadRawRule}) { auto ®istry = GetReadRulesRegistry(ruletype); auto rulesiter = registry.find(GetName()); if (rulesiter != registry.end()) { - auto rset = GetSchemaRules( kTRUE ); - for(const auto &helper : rulesiter->second) { + auto rset = GetSchemaRules(kTRUE); + for (const auto &helper : rulesiter->second) { auto rule = new ROOT::TSchemaRule(ruletype, GetName(), helper); TString errmsg; - if( !rset->AddRule( rule, ROOT::Detail::TSchemaRuleSet::kCheckAll, &errmsg ) ) { - Warning( "Init", "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", - GetName(), helper.fVersion.c_str(), helper.fTarget.c_str(), errmsg.Data() ); + if (!rset->AddRule(rule, ROOT::Detail::TSchemaRuleSet::kCheckAll, &errmsg)) { + Warning( + "Init", + "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", + GetName(), helper.fVersion.c_str(), helper.fTarget.c_str(), errmsg.Data()); delete rule; } } @@ -3624,16 +3626,16 @@ TRealData* TClass::GetRealData(const char* name) const // Try ignoring the array dimensions. std::string::size_type firstBracket = givenName.find_first_of("["); std::string nameNoDim(givenName.substr(0, firstBracket)); - TObjLink* lnk = fRealData->FirstLink(); + TObjLink *lnk = fRealData->FirstLink(); while (lnk) { - TObject* obj = lnk->GetObject(); + TObject *obj = lnk->GetObject(); std::string objName(obj->GetName()); std::string::size_type pos = objName.find_first_of("["); if (pos != std::string::npos) { objName.erase(pos); } if (objName == nameNoDim) { - return static_cast(obj); + return static_cast(obj); } lnk = lnk->Next(); } @@ -6918,10 +6920,10 @@ void TClass::StreamerTObject(const TClass* pThis, void *object, TBuffer &b, cons //////////////////////////////////////////////////////////////////////////////// /// Case of TObjects when fIsOffsetStreamerSet is known to have been set. -void TClass::StreamerTObjectInitialized(const TClass* pThis, void *object, TBuffer &b, const TClass *onfile_class) +void TClass::StreamerTObjectInitialized(const TClass *pThis, void *object, TBuffer &b, const TClass *onfile_class) { if (R__likely(onfile_class == nullptr || pThis == onfile_class)) { - TObject *tobj = (TObject*)((Longptr_t)object + pThis->fOffsetStreamer); + TObject *tobj = (TObject *)((Longptr_t)object + pThis->fOffsetStreamer); tobj->Streamer(b); } else { // This is the case where we are reading an object of a derived class @@ -7409,20 +7411,22 @@ TVirtualStreamerInfo *TClass::FindConversionStreamerInfo( const TClass* cl, UInt /// Rules will end up here if they are created in a dictionary file that does not /// contain the dictionary for the target class. -void TClass::RegisterReadRules(ROOT::TSchemaRule::RuleType_t type, - const char *classname, std::vector<::ROOT::Internal::TSchemaHelper> &&rules) +void TClass::RegisterReadRules(ROOT::TSchemaRule::RuleType_t type, const char *classname, + std::vector<::ROOT::Internal::TSchemaHelper> &&rules) { R__WRITE_LOCKGUARD(ROOT::gCoreMutex); auto cl = TClass::GetClass(classname, false, false); if (cl) { - auto rset = cl->GetSchemaRules( kTRUE ); - for(const auto &it : rules) { + auto rset = cl->GetSchemaRules(kTRUE); + for (const auto &it : rules) { auto rule = new ROOT::TSchemaRule(type, cl->GetName(), it); TString errmsg; - if( !rset->AddRule( rule, ROOT::Detail::TSchemaRuleSet::kCheckAll, &errmsg ) ) { - ::Warning( "TGenericClassInfo", "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", - cl->GetName(), it.fVersion.c_str(), it.fTarget.c_str(), errmsg.Data() ); + if (!rset->AddRule(rule, ROOT::Detail::TSchemaRuleSet::kCheckAll, &errmsg)) { + ::Warning( + "TGenericClassInfo", + "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", + cl->GetName(), it.fVersion.c_str(), it.fTarget.c_str(), errmsg.Data()); delete rule; } } diff --git a/core/meta/src/TGenericClassInfo.cxx b/core/meta/src/TGenericClassInfo.cxx index 48d01304d1b29..534ff12ffd80d 100644 --- a/core/meta/src/TGenericClassInfo.cxx +++ b/core/meta/src/TGenericClassInfo.cxx @@ -334,7 +334,7 @@ namespace Internal { rule = new TSchemaRule(ProcessReadRules ? TSchemaRule::kReadRule : TSchemaRule::kReadRawRule, fClass->GetName(), *it); - if( !rset->AddRule( rule, TSchemaRuleSet::kCheckAll, &errmsg ) ) { + if (!rset->AddRule(rule, TSchemaRuleSet::kCheckAll, &errmsg)) { ::Warning( "TGenericClassInfo", "The rule for class: \"%s\": version, \"%s\" and data members: \"%s\" has been skipped because %s.", GetClassName(), it->fVersion.c_str(), it->fTarget.c_str(), errmsg.Data() ); delete rule; diff --git a/core/meta/src/TSchemaRule.cxx b/core/meta/src/TSchemaRule.cxx index 34f2e102c7df7..bf8a2a6ceace3 100644 --- a/core/meta/src/TSchemaRule.cxx +++ b/core/meta/src/TSchemaRule.cxx @@ -98,32 +98,35 @@ TSchemaRule::TSchemaRule(): fVersionVect( nullptr ), fChecksumVect( nullptr ), //////////////////////////////////////////////////////////////////////////////// /// Constructor. TSchemaRule::TSchemaRule(TSchemaRule::RuleType_t type, const char *targetClass, - const ROOT::Internal::TSchemaHelper& helper) : - fVersionVect( nullptr ), fChecksumVect( nullptr ), - fTargetVect( nullptr ), fSourceVect( nullptr ), - fIncludeVect( nullptr ), fEmbed( kTRUE ), - fReadFuncPtr( nullptr ), fReadRawFuncPtr( nullptr ), - fRuleType( type ) + const ROOT::Internal::TSchemaHelper &helper) + : fVersionVect(nullptr), + fChecksumVect(nullptr), + fTargetVect(nullptr), + fSourceVect(nullptr), + fIncludeVect(nullptr), + fEmbed(kTRUE), + fReadFuncPtr(nullptr), + fReadRawFuncPtr(nullptr), + fRuleType(type) { - SetTarget( helper.fTarget ); - SetTargetClass( targetClass ); - SetSourceClass( helper.fSourceClass ); - SetSource( helper.fSource ); - SetCode( helper.fCode ); - SetVersion( helper.fVersion ); - SetChecksum( helper.fChecksum ); - SetEmbed( helper.fEmbed ); - SetInclude( helper.fInclude ); - SetAttributes( helper.fAttributes ); - - if( type == TSchemaRule::kReadRule ) { - SetReadFunctionPointer( (TSchemaRule::ReadFuncPtr_t)helper.fFunctionPtr ); + SetTarget(helper.fTarget); + SetTargetClass(targetClass); + SetSourceClass(helper.fSourceClass); + SetSource(helper.fSource); + SetCode(helper.fCode); + SetVersion(helper.fVersion); + SetChecksum(helper.fChecksum); + SetEmbed(helper.fEmbed); + SetInclude(helper.fInclude); + SetAttributes(helper.fAttributes); + + if (type == TSchemaRule::kReadRule) { + SetReadFunctionPointer((TSchemaRule::ReadFuncPtr_t)helper.fFunctionPtr); } else { - SetReadRawFunctionPointer( (TSchemaRule::ReadRawFuncPtr_t)helper.fFunctionPtr ); + SetReadRawFunctionPointer((TSchemaRule::ReadRawFuncPtr_t)helper.fFunctionPtr); } } - //////////////////////////////////////////////////////////////////////////////// /// Destructor. diff --git a/core/meta/src/TSchemaRuleSet.cxx b/core/meta/src/TSchemaRuleSet.cxx index ca72b79adc060..b9f29feebe5b6 100644 --- a/core/meta/src/TSchemaRuleSet.cxx +++ b/core/meta/src/TSchemaRuleSet.cxx @@ -113,8 +113,7 @@ Bool_t TSchemaRuleSet::AddRule( TSchemaRule* rule, EConsistencyCheck checkConsis // Do not generate/build the StreamerInfo if it is not already there. TVirtualStreamerInfo *info = fClass->GetCurrentStreamerInfo(); - if (rule->GetTarget() && (fClass->GetState() > TClass::kEmulated || info)) - { + if (rule->GetTarget() && (fClass->GetState() > TClass::kEmulated || info)) { TObjArrayIter titer( rule->GetTarget() ); TObject* obj; while( (obj = titer.Next()) ) { @@ -125,7 +124,7 @@ Bool_t TSchemaRuleSet::AddRule( TSchemaRule* rule, EConsistencyCheck checkConsis // been set and would make detecting if the data member is local to // the current class (as opposed to a base class) more difficult. if (fClass->GetState() > TClass::kEmulated) { - found = fClass->GetDataMember( str->GetString() ) || fClass->GetBaseClass( str->GetString() ); + found = fClass->GetDataMember(str->GetString()) || fClass->GetBaseClass(str->GetString()); } else if (info) { found = info->GetElements()->FindObject(str->GetString()); } diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 18ba00dbe8894..84081aa021a83 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -3411,9 +3411,9 @@ Int_t TBufferFile::ReadClassEmulated(const TClass *cl, void *object, const TClas Version_t v = ReadVersion(&start,&count); TStreamerInfo *sinfo = nullptr; - if( onFileClass ) { - sinfo = (TStreamerInfo*)cl->GetConversionStreamerInfo( onFileClass, v ); - if( !sinfo ) + if (onFileClass) { + sinfo = (TStreamerInfo *)cl->GetConversionStreamerInfo(onFileClass, v); + if (!sinfo) return 0; } if (!sinfo) diff --git a/io/io/src/TKey.cxx b/io/io/src/TKey.cxx index 7bc0c0e349571..6d0a2271523c6 100644 --- a/io/io/src/TKey.cxx +++ b/io/io/src/TKey.cxx @@ -1071,8 +1071,9 @@ void *TKey::ReadObjectAny(const TClass* expectedClass) baseOffset = 0; // For now we do not support requesting from a class that is the base of one of the class for which there is transformation to .... clOnfile = cl; cl = const_cast(expectedClass); - if (gDebug >0) - Info("ReadObjectAny","Using Converter StreamerInfo from %s to %s",clOnfile->GetName(),expectedClass->GetName()); + if (gDebug > 0) + Info("ReadObjectAny", "Using Converter StreamerInfo from %s to %s", clOnfile->GetName(), + expectedClass->GetName()); } if (cl->GetState() > TClass::kEmulated && expectedClass->GetState() <= TClass::kEmulated) { //we cannot mix a compiled class with an emulated class in the inheritance diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index f00ec70de9809..e01541e49846c 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -266,11 +266,8 @@ namespace { if (isStdArray) { totaldim = 1; std::array localMaxIndices; - TClassEdit::GetStdArrayProperties(memClass->GetName(), - localtypename, - localMaxIndices, - ndim); - for(Int_t i = 0; i < ndim; ++i) { + TClassEdit::GetStdArrayProperties(memClass->GetName(), localtypename, localMaxIndices, ndim); + for (Int_t i = 0; i < ndim; ++i) { auto d = localMaxIndices[i]; dimensions.push_back(d); totaldim *= d; @@ -278,7 +275,7 @@ namespace { memClass = TClass::GetClass(localtypename.c_str()); } if (memClass) { - //cached->SetNewType( cached->GetType() ); + // cached->SetNewType( cached->GetType() ); if (s->GetPointerLevel()) { if (memClass->IsTObject()) { memType = TVirtualStreamerInfo::kObjectP; @@ -290,7 +287,7 @@ namespace { } else { if (memClass->GetCollectionProxy()) { memType = TVirtualStreamerInfo::kSTL; - } else if(memClass->IsTObject() && memClass == element->GetClassPointer()) { + } else if (memClass->IsTObject() && memClass == element->GetClassPointer()) { // If there is a change in the class type, we can't use the TObject::Streamer // virtual function: it would streame the data using the in-memory type rather // than the onfile type. @@ -300,8 +297,7 @@ namespace { } } if ((!s->GetPointerLevel() || memType == TVirtualStreamerInfo::kSTLp) && - (isStdArray ? ndim >0 : s->GetDimensions()[0])) - { + (isStdArray ? ndim > 0 : s->GetDimensions()[0])) { memType += TVirtualStreamerInfo::kOffsetL; } datasize = memClass->GetClassSize(); @@ -321,7 +317,7 @@ namespace { if (!totaldim) totaldim = 1; auto dims = s->GetDimensions(); - while(*dims == '[') { + while (*dims == '[') { ++dims; uint32_t res = 0; do { @@ -339,9 +335,8 @@ namespace { } } if (element->GetType() == TStreamerInfo::kStreamLoop && - (memType == TStreamerInfo::kAnyp || memType == TStreamerInfo::kAnyP - || memType == TStreamerInfo::kObjectp || memType == TStreamerInfo::kObjectP)) - { + (memType == TStreamerInfo::kAnyp || memType == TStreamerInfo::kAnyP || memType == TStreamerInfo::kObjectp || + memType == TStreamerInfo::kObjectP)) { memType = TStreamerInfo::kStreamLoop; } if (element->GetType() == TStreamerInfo::kStreamer) @@ -352,20 +347,20 @@ namespace { void UpdateFromRule(const TStreamerInfo *info, const ROOT::TSchemaRule::TSources *s, TStreamerElement *element) { auto [memClass, memType, datasize, dimensions, totaldim] = GetSourceType(s, element); - if (element->GetType() == TVirtualStreamerInfo::kObject && memClass != element->GetClassPointer()) - { + if (element->GetType() == TVirtualStreamerInfo::kObject && memClass != element->GetClassPointer()) { // If there is a change in the class type, we can't use the TObject::Streamer // virtual function: it would streame the data using the in-memory type rather // than the onfile type. element->SetType(TVirtualStreamerInfo::kAny); } - element->SetNewType( memType ); - element->SetNewClass( memClass ); + element->SetNewType(memType); + element->SetNewClass(memClass); // We can not change the recorded dimensions. Let's check that // the total number of elements is still the same. if (totaldim != element->GetArrayLength()) { Error("UpdateFromRule", - "For %s::%s the number of array elements in the rule (%d) does not match the number in the StreamerElement (%d)", + "For %s::%s the number of array elements in the rule (%d) does not match the number in the " + "StreamerElement (%d)", info->GetName(), element->GetFullName(), totaldim, element->GetArrayLength()); } element->SetSize(totaldim ? totaldim * datasize : datasize); @@ -780,9 +775,9 @@ void TStreamerInfo::Build(Bool_t isTransient) cached->SetBit(TStreamerElement::kCache); // Get one of the potentially many rules applicable // We should check that we don't have a second rule - auto r = rules.GetRuleWithSource( element->GetName() ); + auto r = rules.GetRuleWithSource(element->GetName()); assert(r && r->GetSource()); - auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( element->GetName() )); + auto s = (ROOT::TSchemaRule::TSources *)(r->GetSource()->FindObject(element->GetName())); assert(s); UpdateFromRule(this, s, cached); } @@ -807,7 +802,7 @@ void TStreamerInfo::Build(Bool_t isTransient) if (rules.HasRuleWithSource(alloc_element->GetName(), kTRUE)) { auto r = rules.GetRuleWithSource(alloc_element->GetName()); assert(r && r->GetSource()); - auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( alloc_element->GetName() )); + auto s = (ROOT::TSchemaRule::TSources *)(r->GetSource()->FindObject(alloc_element->GetName())); assert(s); UpdateFromRule(this, s, alloc_element); } @@ -2650,7 +2645,8 @@ void TStreamerInfo::BuildOld() if (rules.HasRuleWithSource(alloc_element->GetName(), kTRUE)) { auto r = rules.GetRuleWithSource(alloc_element->GetName()); assert(r && r->GetSource()); - auto s = (ROOT::TSchemaRule::TSources*)(r->GetSource()->FindObject( alloc_element->GetName() )); + auto s = + (ROOT::TSchemaRule::TSources *)(r->GetSource()->FindObject(alloc_element->GetName())); assert(s); UpdateFromRule(this, s, alloc_element); } @@ -4735,7 +4731,7 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetAttributes()[0] != 0) { - TString attr( r->GetAttributes() ); + TString attr(r->GetAttributes()); attr.ToLower(); return attr.Contains("canignore"); } else @@ -4816,10 +4812,8 @@ void TStreamerInfo::InsertArtificialElements(std::vectorGetDataMember( newName ) ) { TRealData::GetName(realDataName, dm); - newel = new TStreamerArtificial(realDataName, "", - fClass->GetDataMemberOffset(realDataName), - TStreamerInfo::kArtificial, - dm->GetTypeName()); + newel = new TStreamerArtificial(realDataName, "", fClass->GetDataMemberOffset(realDataName), + TStreamerInfo::kArtificial, dm->GetTypeName()); newel->SetReadFunc( rule->GetReadFunctionPointer() ); newel->SetReadRawFunc( rule->GetReadRawFunctionPointer() ); toAdd.push_back(newel); @@ -4833,10 +4827,8 @@ void TStreamerInfo::InsertArtificialElements(std::vectorString(); if ( TDataMember* dm = fClass->GetDataMember( newName ) ) { TRealData::GetName(realDataName, dm); - newel = new TStreamerArtificial(realDataName, "", - fClass->GetDataMemberOffset(realDataName), - TStreamerInfo::kArtificial, - dm->GetTypeName()); + newel = new TStreamerArtificial(realDataName, "", fClass->GetDataMemberOffset(realDataName), + TStreamerInfo::kArtificial, dm->GetTypeName()); toAdd.push_back(newel); } } @@ -4991,7 +4983,7 @@ void* TStreamerInfo::New(void *obj) // Skip elements for which we do not have any class // information. FIXME: Document how this could happen. - TClass* cle = element->GetNewClass(); + TClass *cle = element->GetNewClass(); if (!cle) cle = element->GetClassPointer(); if (!cle) @@ -5179,7 +5171,6 @@ void TStreamerInfo::DestructorImpl(void* obj, Bool_t dtorOnly) if (ele->GetOffset() == kMissing) continue; char* eaddr = p + ele->GetOffset(); - Int_t etype = ele->GetNewType(); // in memory type if (etype == TStreamerInfo::kNoType) etype = ele->GetType(); @@ -5203,15 +5194,12 @@ void TStreamerInfo::DestructorImpl(void* obj, Bool_t dtorOnly) case TStreamerInfo::kCharStar: DeleteBasicPointer(eaddr,ele,Char_t); continue; } - - - TClass* cle = ele->GetNewClass(); // in memory type + TClass *cle = ele->GetNewClass(); // in memory type if (!cle) cle = ele->GetClassPointer(); if (!cle) continue; - if (etype == kObjectp || etype == kAnyp) { // Destroy an array of pre-allocated objects. Int_t len = ele->GetArrayLength(); diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index dc8930c9cb010..8135f9416fc82 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -327,10 +327,9 @@ Int_t TStreamerInfo::ReadBufferSkip(TBuffer &b, const T &arr, const TCompInfo *c // skip Class * not derived from TObject with comment field //-> case TStreamerInfo::kSkip + TStreamerInfo::kAnyp: - case TStreamerInfo::kSkip + TStreamerInfo::kAnyp + TStreamerInfo::kOffsetL: - { + case TStreamerInfo::kSkip + TStreamerInfo::kAnyp + TStreamerInfo::kOffsetL: { DOLOOP { - for (Int_t j=0;jfLength;j++) { + for (Int_t j = 0; j < compinfo->fLength; j++) { b.SkipObjectAny(); } } @@ -339,8 +338,7 @@ Int_t TStreamerInfo::ReadBufferSkip(TBuffer &b, const T &arr, const TCompInfo *c // skip Class* not derived from TObject case TStreamerInfo::kSkip + TStreamerInfo::kAnyP: - case TStreamerInfo::kSkip + TStreamerInfo::kAnyP + TStreamerInfo::kOffsetL: - { + case TStreamerInfo::kSkip + TStreamerInfo::kAnyP + TStreamerInfo::kOffsetL: { DOLOOP { for (Int_t j=0;jfLength;j++) { b.SkipObjectAny(); @@ -351,10 +349,9 @@ Int_t TStreamerInfo::ReadBufferSkip(TBuffer &b, const T &arr, const TCompInfo *c // skip Any Class not derived from TObject case TStreamerInfo::kSkip + TStreamerInfo::kAny: - case TStreamerInfo::kSkip + TStreamerInfo::kAny + TStreamerInfo::kOffsetL: - { + case TStreamerInfo::kSkip + TStreamerInfo::kAny + TStreamerInfo::kOffsetL: { DOLOOP { - for (Int_t j=0;jfLength;j++) { + for (Int_t j = 0; j < compinfo->fLength; j++) { b.SkipObjectAny(); } } @@ -382,8 +379,8 @@ Int_t TStreamerInfo::ReadBufferSkip(TBuffer &b, const T &arr, const TCompInfo *c case TStreamerInfo::kSkip + TStreamerInfo::kStreamLoop + TStreamerInfo::kOffsetL: case TStreamerInfo::kSkip + TStreamerInfo::kStreamer: { DOLOOP { - for (Int_t j=0;jfLength;j++) { - b.SkipObjectAny(); + for (Int_t j = 0; j < compinfo->fLength; j++) { + b.SkipObjectAny(); } } break; @@ -795,17 +792,17 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, if (R__TestUseCache(aElement)) { Int_t bufpos = b.Length(); - if (b.PeekDataCache()==0) { + if (b.PeekDataCache() == 0) { Warning("ReadBuffer","Skipping %s::%s because the cache is missing.",thisVar->GetName(),aElement->GetName()); thisVar->ReadBufferSkip(b,arr,compinfo[i],compinfo[i]->fType+TStreamerInfo::kSkip,aElement,narr,eoffset); } else { if (gDebug > 1) { printf("ReadBuffer, class:%s, name=%s, fType[%d]=%d," - " %s, bufpos=%d, arr=%p, eoffset=%d, Redirect=%p\n", - fClass->GetName(),aElement->GetName(),i,compinfo[i]->fType, - aElement->ClassName(),b.Length(),arr[0], eoffset,b.PeekDataCache()->GetObjectAt(0)); + " %s, bufpos=%d, arr=%p, eoffset=%d, Redirect=%p\n", + fClass->GetName(), aElement->GetName(), i, compinfo[i]->fType, aElement->ClassName(), b.Length(), + arr[0], eoffset, b.PeekDataCache()->GetObjectAt(0)); } - thisVar->ReadBuffer(b,*b.PeekDataCache(),compinfo,i,i+1,narr,eoffset, arrayMode); + thisVar->ReadBuffer(b, *b.PeekDataCache(), compinfo, i, i + 1, narr, eoffset, arrayMode); } if (aElement->TestBit(TStreamerElement::kRepeat)) { b.SetBufferOffset(bufpos); } continue; @@ -1095,7 +1092,8 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, case TStreamerInfo::kAnyP: // Class* not derived from TObject with no comment field NOTE:: Re-added by Phil case TStreamerInfo::kAnyP+TStreamerInfo::kOffsetL: { DOLOOP { - b.ReadFastArray((void**)(arr[k]+ioffset), newCle ? newCle : cle, compinfo[i]->fLength, isPreAlloc, pstreamer, cle); + b.ReadFastArray((void **)(arr[k] + ioffset), newCle ? newCle : cle, compinfo[i]->fLength, isPreAlloc, + pstreamer, cle); } } continue; @@ -1393,7 +1391,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, case TStreamerInfo::kAny+TStreamerInfo::kOffsetL: { DOLOOP { - b.ReadFastArray((void*)(arr[k]+ioffset), newCle ? newCle : cle, compinfo[i]->fLength, pstreamer, cle); + b.ReadFastArray((void *)(arr[k] + ioffset), newCle ? newCle : cle, compinfo[i]->fLength, pstreamer, cle); } continue; } @@ -1698,12 +1696,8 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, continue; } - case TStreamerInfo::kCacheNew: - b.PushDataCache( new TVirtualArray( aElement->GetClassPointer(), narr ) ); - continue; - case TStreamerInfo::kCacheDelete: - delete b.PopDataCache(); - continue; + case TStreamerInfo::kCacheNew: b.PushDataCache(new TVirtualArray(aElement->GetClassPointer(), narr)); continue; + case TStreamerInfo::kCacheDelete: delete b.PopDataCache(); continue; case -1: // -- Skip an ignored TObject base class. diff --git a/io/io/src/TStreamerInfoWriteBuffer.cxx b/io/io/src/TStreamerInfoWriteBuffer.cxx index ee5ff66cc08a1..7a62f21ae346b 100644 --- a/io/io/src/TStreamerInfoWriteBuffer.cxx +++ b/io/io/src/TStreamerInfoWriteBuffer.cxx @@ -146,16 +146,16 @@ Int_t TStreamerInfo::WriteBufferAux(TBuffer &b, const T &arr, if (R__TestUseCache(aElement)) { if (aElement->TestBit(TStreamerElement::kWrite)) { - if (b.PeekDataCache()==0) { + if (b.PeekDataCache() == 0) { Warning("WriteBuffer","Skipping %s::%s because the cache is missing.",GetName(),aElement->GetName()); } else { if (gDebug > 1) { printf("WriteBuffer, class:%s, name=%s, fType[%d]=%d," " %s, bufpos=%d, arr=%p, eoffset=%d, Redirect=%p\n", - fClass->GetName(),aElement->GetName(),i,compinfo[i]->fType, - aElement->ClassName(),b.Length(),arr[0], eoffset,b.PeekDataCache()->GetObjectAt(0)); + fClass->GetName(), aElement->GetName(), i, compinfo[i]->fType, aElement->ClassName(), + b.Length(), arr[0], eoffset, b.PeekDataCache()->GetObjectAt(0)); } - WriteBufferAux(b,*b.PeekDataCache(),compinfo,i,i+1,narr,eoffset, arrayMode); + WriteBufferAux(b, *b.PeekDataCache(), compinfo, i, i + 1, narr, eoffset, arrayMode); } continue; } else { @@ -800,12 +800,8 @@ Int_t TStreamerInfo::WriteBufferAux(TBuffer &b, const T &arr, continue; } - case TStreamerInfo::kCacheNew: - b.PushDataCache( new TVirtualArray( aElement->GetClassPointer(), narr ) ); - continue; - case TStreamerInfo::kCacheDelete: - delete b.PopDataCache(); - continue; + case TStreamerInfo::kCacheNew: b.PushDataCache(new TVirtualArray(aElement->GetClassPointer(), narr)); continue; + case TStreamerInfo::kCacheDelete: delete b.PopDataCache(); continue; case TStreamerInfo::kArtificial: #if 0 ROOT::TSchemaRule::WriteFuncPtr_t writefunc = ((TStreamerArtificial*)aElement)->GetWriteFunc(); diff --git a/tree/tree/src/TBranchElement.cxx b/tree/tree/src/TBranchElement.cxx index 6ff6a5b34eb93..38381476234fa 100644 --- a/tree/tree/src/TBranchElement.cxx +++ b/tree/tree/src/TBranchElement.cxx @@ -67,7 +67,8 @@ namespace { TBuffer &fBuffer; TVirtualArray *fOnfileObject; - R__PushCache(TBuffer &b, TVirtualArray *in, UInt_t size) : fBuffer(b), fOnfileObject(in) { + R__PushCache(TBuffer &b, TVirtualArray *in, UInt_t size) : fBuffer(b), fOnfileObject(in) + { if (fOnfileObject) { fOnfileObject->SetSize(size); fBuffer.PushDataCache( fOnfileObject ); @@ -2744,7 +2745,7 @@ Int_t TBranchElement::GetEntry(Long64_t entry, Int_t getall) if (clones->IsZombie()) { return -1; } - R__PushCache onfileObject(b,fOnfileObject,ndata); + R__PushCache onfileObject(b, fOnfileObject, ndata); char **arr = (char **)clones->GetObjectRef(); char **end = arr + fNdata; @@ -2757,7 +2758,7 @@ Int_t TBranchElement::GetEntry(Long64_t entry, Int_t getall) auto ndata = GetNdata(); - R__PushCache onfileObject(b,fOnfileObject,ndata); + R__PushCache onfileObject(b, fOnfileObject, ndata); TVirtualCollectionProxy *proxy = GetCollectionProxy(); TVirtualCollectionProxy::TPushPop helper(proxy, fObject); @@ -2767,7 +2768,7 @@ Int_t TBranchElement::GetEntry(Long64_t entry, Int_t getall) // Apply the unattached rules; by definition they do not need any // input from a buffer. TBufferFile b(TBufferFile::kRead, 1); - R__PushCache onfileObject(b,fOnfileObject,fNdata); + R__PushCache onfileObject(b, fOnfileObject, fNdata); b.ApplySequence(*fReadActionSequence, fObject); } } @@ -4289,7 +4290,7 @@ void TBranchElement::ReadLeavesCollection(TBuffer& b) } fNdata = n; - R__PushCache onfileObject(b,fOnfileObject,1); + R__PushCache onfileObject(b, fOnfileObject, 1); // Note: Proxy-helper needs to "embrace" the entire // streaming of this STL container if the container @@ -4379,7 +4380,7 @@ void TBranchElement::ReadLeavesCollectionSplitPtrMember(TBuffer& b) return; } - R__PushCache onfileObject(b,fOnfileObject,fNdata); + R__PushCache onfileObject(b, fOnfileObject, fNdata); TStreamerInfo *info = GetInfoImp(); if (info == nullptr) return; @@ -4411,7 +4412,7 @@ void TBranchElement::ReadLeavesCollectionSplitVectorPtrMember(TBuffer& b) if (!fNdata) { return; } - R__PushCache onfileObject(b,fOnfileObject,fNdata); + R__PushCache onfileObject(b, fOnfileObject, fNdata); TStreamerInfo *info = GetInfoImp(); if (info == nullptr) return; @@ -4442,7 +4443,7 @@ void TBranchElement::ReadLeavesCollectionMember(TBuffer& b) if (!fNdata) { return; } - R__PushCache onfileObject(b,fOnfileObject,fNdata); + R__PushCache onfileObject(b, fOnfileObject, fNdata); TStreamerInfo *info = GetInfoImp(); if (info == nullptr) return; @@ -4522,7 +4523,7 @@ void TBranchElement::ReadLeavesClonesMember(TBuffer& b) // Note, we could (possibly) save some more, by configuring the action // based on the value of fOnfileObject rather than pushing in on a stack. - R__PushCache onfileObject(b,fOnfileObject,fNdata); + R__PushCache onfileObject(b, fOnfileObject, fNdata); char **arr = (char **)clones->GetObjectRef(); char **end = arr + fNdata; @@ -4547,7 +4548,7 @@ void TBranchElement::ReadLeavesMember(TBuffer& b) return; } - R__PushCache onfileObject(b,fOnfileObject,1); + R__PushCache onfileObject(b, fOnfileObject, 1); // If not a TClonesArray or STL container master branch // or sub-branch and branch inherits from tobject, // then register with the buffer so that pointers are @@ -4599,8 +4600,9 @@ void TBranchElement::ReadLeavesMemberBranchCount(TBuffer& b) if (!info) { return; } - R__PushCache onfileObject(b,fOnfileObject,1); // Here we have a single object that contains a variable size C-style array. - // Since info is not null, fReadActionSequence is not null either. + R__PushCache onfileObject(b, fOnfileObject, + 1); // Here we have a single object that contains a variable size C-style array. + // Since info is not null, fReadActionSequence is not null either. b.ApplySequence(*fReadActionSequence, fObject); } @@ -4634,7 +4636,7 @@ void TBranchElement::ReadLeavesMemberCounter(TBuffer& b) return; } - R__PushCache onfileObject(b,fOnfileObject,1); + R__PushCache onfileObject(b, fOnfileObject, 1); // Since info is not null, fReadActionSequence is not null either. b.ApplySequence(*fReadActionSequence, fObject); @@ -4655,7 +4657,7 @@ void TBranchElement::ReadLeavesCustomStreamer(TBuffer& b) return; } - R__PushCache onfileObject(b,fOnfileObject,1); + R__PushCache onfileObject(b, fOnfileObject, 1); fBranchClass->Streamer(fObject,b); } diff --git a/tree/treeplayer/src/TBranchProxy.cxx b/tree/treeplayer/src/TBranchProxy.cxx index 671c27713c293..23f3f3a21c32f 100644 --- a/tree/treeplayer/src/TBranchProxy.cxx +++ b/tree/treeplayer/src/TBranchProxy.cxx @@ -523,9 +523,9 @@ bool ROOT::Detail::TBranchProxy::Setup() bcount?bcount->GetName():"unknown")); fMemberOffset = 0; } else if (fMemberOffset == TVirtualStreamerInfo::kMissing) { - Error("Setup","%s",Form("Missing data member in a TClonesArray, %s in %s and %s", - fDataMember.Data(), fBranch->GetName(), - bcount ? bcount->GetName() : "unknown")); + Error("Setup", "%s", + Form("Missing data member in a TClonesArray, %s in %s and %s", fDataMember.Data(), + fBranch->GetName(), bcount ? bcount->GetName() : "unknown")); fMemberOffset = 0; } @@ -545,21 +545,23 @@ bool ROOT::Detail::TBranchProxy::Setup() } if (fMemberOffset < 0) { - Error("Setup","%s",Form("Negative offset %d for %s in %s, class: %s", - fMemberOffset, fDataMember.Data(), fBranch->GetName(), fClass->GetName())); + Error("Setup", "%s", + Form("Negative offset %d for %s in %s, class: %s", fMemberOffset, fDataMember.Data(), + fBranch->GetName(), fClass->GetName())); fMemberOffset = 0; } else if (fMemberOffset == TVirtualStreamerInfo::kMissing) { - Error("Setup","%s",Form("Missing data member %s in %s, class: %s", - fDataMember.Data(), fBranch->GetName(), fClass->GetName())); + Error("Setup", "%s", + Form("Missing data member %s in %s, class: %s", fDataMember.Data(), fBranch->GetName(), + fClass->GetName())); fMemberOffset = 0; } // The extra condition (fElement is not a TStreamerSTL) is to handle the case where fBranch is a // TBranchElement and fElement is a TStreamerSTL. Without the extra condition we get an error // message, although the vector (i.e. the TBranchElement) is accessible. - } else if (fParent && fBranch->IsA() != TBranch::Class() && fElement->IsA() != TStreamerBasicType::Class() - && fElement->IsA() != TStreamerSTL::Class()) { - Error("Setup","%s",Form("Missing TClass object for %s",fClassName.Data())); + } else if (fParent && fBranch->IsA() != TBranch::Class() && fElement->IsA() != TStreamerBasicType::Class() && + fElement->IsA() != TStreamerSTL::Class()) { + Error("Setup", "%s", Form("Missing TClass object for %s", fClassName.Data())); } if ( fBranch->IsA()==TBranchElement::Class()