From 955068062e0f8888e94099334d352c4df82e77f8 Mon Sep 17 00:00:00 2001 From: Philippe Canal <pcanal@fnal.gov> Date: Tue, 9 Feb 2021 11:21:22 -0600 Subject: [PATCH] TFileMerger: Use obj in memory even if it has no key (yet) --- io/io/inc/TFileMerger.h | 7 + io/io/src/TFileMerger.cxx | 781 ++++++++++++++++++-------------------- tree/tree/src/TBranch.cxx | 3 +- 3 files changed, 384 insertions(+), 407 deletions(-) diff --git a/io/io/inc/TFileMerger.h b/io/io/inc/TFileMerger.h index a7b04ee9a94..c2d4e6414e8 100644 --- a/io/io/inc/TFileMerger.h +++ b/io/io/inc/TFileMerger.h @@ -22,6 +22,8 @@ class TList; class TFile; class TDirectory; +class THashList; +class TKey; namespace ROOT { class TIOFeatures; @@ -59,6 +61,11 @@ protected: virtual Bool_t AddFile(TFile *source, Bool_t own, Bool_t cpProgress); virtual Bool_t MergeRecursive(TDirectory *target, TList *sourcelist, Int_t type = kRegular | kAll); + virtual Bool_t MergeOne(TDirectory *target, TList *sourcelist, Int_t type, + TFileMergeInfo &info, TString &oldkeyname, THashList &allNames, Bool_t &status, Bool_t &onlyListed, + const TString &path, + TDirectory *current_sourcedir, TFile *current_file, + TKey *key, TObject *obj); public: /// Type of the partial merge enum EPartialMergeType { diff --git a/io/io/src/TFileMerger.cxx b/io/io/src/TFileMerger.cxx index 991bd24a424..b6dd9d233c7 100644 --- a/io/io/src/TFileMerger.cxx +++ b/io/io/src/TFileMerger.cxx @@ -373,6 +373,362 @@ Bool_t TFileMerger::Merge(Bool_t) return PartialMerge(kAll | kRegular); } +Bool_t TFileMerger::MergeOne(TDirectory *target, TList *sourcelist, Int_t type, TFileMergeInfo &info, + TString &oldkeyname, THashList &allNames, Bool_t &status, Bool_t &onlyListed, + const TString &path, TDirectory *current_sourcedir, TFile *current_file, TKey *key, + TObject *obj) +{ + const char *keyname = obj ? obj->GetName() : key->GetName(); + const char *keyclassname = obj ? obj->IsA()->GetName() : key->GetClassName(); + const char *keytitle = obj ? obj->GetTitle() : key->GetTitle(); + + // Keep only the highest cycle number for each key for mergeable objects. They are stored + // in the (hash) list consecutively and in decreasing order of cycles, so we can continue + // until the name changes. We flag the case here and we act consequently later. + Bool_t alreadyseen = (oldkeyname == keyname) ? kTRUE : kFALSE; + Bool_t ownobj = kFALSE; + + // Read in but do not copy directly the processIds. + if (strcmp(keyclassname, "TProcessID") == 0 && key) { + key->ReadObj(); + return kTRUE; + } + + // If we have already seen this object [name], we already processed + // the whole list of files for this objects and we can just skip it + // and any related cycles. + if (allNames.FindObject(keyname)) { + oldkeyname = keyname; + return kTRUE; + } + + TClass *cl = TClass::GetClass(keyclassname); + if (!cl) { + Info("MergeRecursive", "cannot indentify object type (%s), name: %s title: %s", + keyclassname, keyname, keytitle); + return kTRUE; + } + // For mergeable objects we add the names in a local hashlist handling them + // again (see above) + if (cl->GetMerge() || cl->InheritsFrom(TDirectory::Class()) || + (cl->IsTObject() && !cl->IsLoaded() && + /* If it has a dictionary and GetMerge() is nullptr then we already know the answer + to the next question is 'no, if we were to ask we would useless trigger + auto-parsing */ + (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") || + cl->GetMethodWithPrototype("Merge", "TCollection*")))) + allNames.Add(new TObjString(keyname)); + + if (fNoTrees && cl->InheritsFrom(R__TTree_Class)) { + // Skip the TTree objects and any related cycles. + oldkeyname = keyname; + return kTRUE; + } + // Check if only the listed objects are to be merged + if (type & kOnlyListed) { + onlyListed = kFALSE; + oldkeyname = keyname; + oldkeyname += " "; + onlyListed = fObjectNames.Contains(oldkeyname); + oldkeyname = keyname; + if ((!onlyListed) && (!cl->InheritsFrom(TDirectory::Class()))) return kTRUE; + } + + if (!(type&kResetable && type&kNonResetable)) { + // If neither or both are requested at the same time, we merger both types. + if (!(type&kResetable)) { + if (cl->GetResetAfterMerge()) { + // Skip the object with a reset after merge routine (TTree and other incrementally mergeable objects) + oldkeyname = keyname; + return kTRUE; + } + } + if (!(type&kNonResetable)) { + if (!cl->GetResetAfterMerge()) { + // Skip the object without a reset after merge routine (Histograms and other non incrementally mergeable objects) + oldkeyname = keyname; + return kTRUE; + } + } + } + // read object from first source file + if (type & kIncremental) { + if (!obj) + obj = current_sourcedir->GetList()->FindObject(keyname); + if (!obj && key) { + obj = key->ReadObj(); + ownobj = kTRUE; + } else if (obj && info.fIsFirst && current_sourcedir != target) { + R__ASSERT(cl->IsTObject()); + TDirectory::TContext ctxt(current_sourcedir); + obj = obj->Clone(); + ownobj = kTRUE; + } + } else if (key) { + obj = key->ReadObj(); + ownobj = kTRUE; + } + if (!obj) { + Info("MergeRecursive", "could not read object for key {%s, %s}", + keyname, keytitle); + return kTRUE; + } + Bool_t canBeFound = (type & kIncremental) && (current_sourcedir->GetList()->FindObject(keyname) != nullptr); + + // if (cl->IsTObject()) + // obj->ResetBit(kMustCleanup); + if (cl->IsTObject() && cl != obj->IsA()) { + Error("MergeRecursive", "TKey and object retrieve disagree on type (%s vs %s). Continuing with %s.", + keyclassname, obj->IsA()->GetName(), obj->IsA()->GetName()); + cl = obj->IsA(); + } + Bool_t canBeMerged = kTRUE; + + if ( cl->InheritsFrom( TDirectory::Class() ) ) { + // it's a subdirectory + + target->cd(); + TDirectory *newdir; + + // For incremental or already seen we may have already a directory created + if (type & kIncremental || alreadyseen) { + newdir = target->GetDirectory(obj->GetName()); + if (!newdir) { + newdir = target->mkdir( obj->GetName(), obj->GetTitle() ); + // newdir->ResetBit(kMustCleanup); + } + } else { + newdir = target->mkdir( obj->GetName(), obj->GetTitle() ); + // newdir->ResetBit(kMustCleanup); + } + + // newdir is now the starting point of another round of merging + // newdir still knows its depth within the target file via + // GetPath(), so we can still figure out where we are in the recursion + + // If this folder is a onlyListed object, merge everything inside. + if (onlyListed) type &= ~kOnlyListed; + status = MergeRecursive(newdir, sourcelist, type); + if (onlyListed) type |= kOnlyListed; + if (!status) return kFALSE; + } else if (!cl->IsTObject() && cl->GetMerge()) { + // merge objects that don't derive from TObject + TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First(); + Error("MergeRecursive", "Merging objects that don't inherit from TObject is unimplemented (key: %s of type %s in file %s)", + keyname, keyclassname, nextsource->GetName()); + canBeMerged = kFALSE; + } else if (cl->IsTObject() && cl->GetMerge()) { + // Check if already treated + if (alreadyseen) return kTRUE; + + TList inputs; + TList todelete; + Bool_t oneGo = fHistoOneGo && cl->InheritsFrom(R__TH1_Class); + + // Loop over all source files and merge same-name object + TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First(); + if (nextsource == 0) { + // There is only one file in the list + ROOT::MergeFunc_t func = cl->GetMerge(); + func(obj, &inputs, &info); + info.fIsFirst = kFALSE; + } else { + do { + // make sure we are at the correct directory level by cd'ing to path + TDirectory *ndir = nextsource->GetDirectory(path); + if (ndir) { + // For consistency (and persformance), we reset the MustCleanup be also for those + // 'key' retrieved indirectly. + // ndir->ResetBit(kMustCleanup); + ndir->cd(); + TObject *hobj = ndir->GetList()->FindObject(keyname); + if (!hobj) { + TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(keyname); + if (key2) { + hobj = key2->ReadObj(); + if (!hobj) { + Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s", + keyname, keytitle, nextsource->GetName()); + nextsource = (TFile*)sourcelist->After(nextsource); + return kTRUE; + } + todelete.Add(hobj); + } + } + if (hobj) { + // Set ownership for collections + if (hobj->InheritsFrom(TCollection::Class())) { + ((TCollection*)hobj)->SetOwner(); + } + hobj->ResetBit(kMustCleanup); + inputs.Add(hobj); + if (!oneGo) { + ROOT::MergeFunc_t func = cl->GetMerge(); + Long64_t result = func(obj, &inputs, &info); + info.fIsFirst = kFALSE; + if (result < 0) { + Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", + keyname, nextsource->GetName()); + } + inputs.Clear(); + todelete.Delete(); + } + } + } + nextsource = (TFile*)sourcelist->After( nextsource ); + } while (nextsource); + // Merge the list, if still to be done + if (oneGo || info.fIsFirst) { + ROOT::MergeFunc_t func = cl->GetMerge(); + func(obj, &inputs, &info); + info.fIsFirst = kFALSE; + inputs.Clear(); + todelete.Delete(); + } + } + } else if (cl->IsTObject()) { + // try synthesizing the Merge method call according to the TObject + TList listH; + TString listHargs; + if (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*")) { + listHargs.Form("(TCollection*)0x%lx,(TFileMergeInfo*)0x%lx", + (ULong_t)&listH, (ULong_t)&info); + } else if (cl->GetMethodWithPrototype("Merge", "TCollection*")) { + listHargs.Form("((TCollection*)0x%lx)", (ULong_t)&listH); + } else { + // pass unmergeable objects through to the output file + canBeMerged = kFALSE; + } + if (canBeMerged) { + if (alreadyseen) { + // skip already seen mergeable objects, don't skip unmergeable objects + return kTRUE; + } + // Loop over all source files and merge same-name object + TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First(); + if (nextsource == 0) { + // There is only one file in the list + Int_t error = 0; + obj->Execute("Merge", listHargs.Data(), &error); + info.fIsFirst = kFALSE; + if (error) { + Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", + obj->GetName(), keyname); + } + } else { + while (nextsource) { + // make sure we are at the correct directory level by cd'ing to path + TDirectory *ndir = nextsource->GetDirectory(path); + if (ndir) { + ndir->cd(); + TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(keyname); + if (key2) { + TObject *hobj = key2->ReadObj(); + if (!hobj) { + Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s", + keyname, keytitle, nextsource->GetName()); + nextsource = (TFile*)sourcelist->After(nextsource); + return kTRUE; + } + // Set ownership for collections + if (hobj->InheritsFrom(TCollection::Class())) { + ((TCollection*)hobj)->SetOwner(); + } + hobj->ResetBit(kMustCleanup); + listH.Add(hobj); + Int_t error = 0; + obj->Execute("Merge", listHargs.Data(), &error); + info.fIsFirst = kFALSE; + if (error) { + Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", + obj->GetName(), nextsource->GetName()); + } + listH.Delete(); + } + } + nextsource = (TFile*)sourcelist->After( nextsource ); + } + // Merge the list, if still to be done + if (info.fIsFirst) { + Int_t error = 0; + obj->Execute("Merge", listHargs.Data(), &error); + info.fIsFirst = kFALSE; + listH.Delete(); + } + } + } + } else { + // Object is of no type that we can merge + canBeMerged = kFALSE; + } + + // now write the merged histogram (which is "in" obj) to the target file + // note that this will just store obj in the current directory level, + // which is not persistent until the complete directory itself is stored + // by "target->SaveSelf()" below + target->cd(); + + oldkeyname = keyname; + //!!if the object is a tree, it is stored in globChain... + if (cl->InheritsFrom(TDirectory::Class())) { + // printf("cas d'une directory\n"); + + auto dirobj = dynamic_cast<TDirectory *>(obj); + TString dirpath(dirobj->GetPath()); + // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename: + dirpath.Remove(0, std::strlen(dirobj->GetFile()->GetPath())); + + // Do not delete the directory if it is part of the output + // and we are in incremental mode (because it will be reuse + // and has not been written to disk (for performance reason). + // coverity[var_deref_model] the IsA()->InheritsFrom guarantees that the dynamic_cast will succeed. + if (!(type & kIncremental) || dirobj->GetFile() != target) { + dirobj->ResetBit(kMustCleanup); + delete dirobj; + } + // Let's also delete the directory from the other source (thanks to the 'allNames' + // mechanism above we will not process the directories when tranversing the next + // files). + TFile *nextsource = current_file ? (TFile *)sourcelist->After(current_file) : (TFile *)sourcelist->First(); + while (nextsource) { + TDirectory *ndir = nextsource->GetDirectory(dirpath); + // For consistency (and persformance), we reset the MustCleanup be also for those + // 'key' retrieved indirectly. + if (ndir) { + ndir->ResetBit(kMustCleanup); + delete ndir; + } + nextsource = (TFile *)sourcelist->After(nextsource); + } + } else if (cl->InheritsFrom(TCollection::Class())) { + // Don't overwrite, if the object were not merged. + if (obj->Write(oldkeyname, canBeMerged ? TObject::kSingleKey | TObject::kOverwrite : TObject::kSingleKey) <= + 0) { + status = kFALSE; + } + ((TCollection *)obj)->SetOwner(); + delete obj; + } else if (!canBeFound) { // Don't write the partial result for TTree and TH1 + // Don't overwrite, if the object were not merged. + // NOTE: this is probably wrong for emulated objects. + if (cl->IsTObject()) { + if (obj->Write(oldkeyname, canBeMerged ? TObject::kOverwrite : 0) <= 0) { + status = kFALSE; + } + obj->ResetBit(kMustCleanup); + } else { + if (target->WriteObjectAny((void *)obj, cl, oldkeyname, canBeMerged ? "OverWrite" : "") <= 0) { + status = kFALSE; + } + } + if (ownobj) + cl->Destructor(obj); // just in case the class is not loaded. + } + info.Reset(); + + return kTRUE; +} + //////////////////////////////////////////////////////////////////////////////// /// Merge all objects in a directory /// @@ -425,418 +781,31 @@ Bool_t TFileMerger::MergeRecursive(TDirectory *target, TList *sourcelist, Int_t // When current_sourcedir != 0 and current_file == 0 we are going over the target // for an incremental merge. if (current_sourcedir && (current_file == 0 || current_sourcedir != target)) { + TString oldkeyname; + + // Loop over live objects + TIter nextobj( current_sourcedir->GetList() ); + TObject *obj; + while ( (obj = (TKey*)nextobj())) { + auto result = MergeOne(target, sourcelist, type, + info, oldkeyname, allNames, status, onlyListed, path, + current_sourcedir, current_file, + nullptr, obj); + if (!result) + return kFALSE; // Stop completely in case of error. + } // while ( (obj = (TKey*)nextobj())) // loop over all keys in this directory TIter nextkey( current_sourcedir->GetListOfKeys() ); TKey *key; - TString oldkeyname; while ( (key = (TKey*)nextkey())) { - - // Keep only the highest cycle number for each key for mergeable objects. They are stored - // in the (hash) list consecutively and in decreasing order of cycles, so we can continue - // until the name changes. We flag the case here and we act consequently later. - Bool_t alreadyseen = (oldkeyname == key->GetName()) ? kTRUE : kFALSE; - - // Read in but do not copy directly the processIds. - if (strcmp(key->GetClassName(),"TProcessID") == 0) { key->ReadObj(); continue;} - - // If we have already seen this object [name], we already processed - // the whole list of files for this objects and we can just skip it - // and any related cycles. - if (allNames.FindObject(key->GetName())) { - oldkeyname = key->GetName(); - continue; - } - - TClass *cl = TClass::GetClass(key->GetClassName()); - if (!cl) { - Info("MergeRecursive", "cannot indentify object type (%s), name: %s title: %s", - key->GetClassName(), key->GetName(), key->GetTitle()); - continue; - } - // For mergeable objects we add the names in a local hashlist handling them - // again (see above) - if (cl->GetMerge() || cl->InheritsFrom(TDirectory::Class()) || - (cl->IsTObject() && !cl->IsLoaded() && - /* If it has a dictionary and GetMerge() is nullptr then we already know the answer - to the next question is 'no, if we were to ask we would useless trigger - auto-parsing */ - (cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") || - cl->GetMethodWithPrototype("Merge", "TCollection*")))) - allNames.Add(new TObjString(key->GetName())); - - if (fNoTrees && cl->InheritsFrom(R__TTree_Class)) { - // Skip the TTree objects and any related cycles. - oldkeyname = key->GetName(); - continue; - } - // Check if only the listed objects are to be merged - if (type & kOnlyListed) { - onlyListed = kFALSE; - oldkeyname = key->GetName(); - oldkeyname += " "; - onlyListed = fObjectNames.Contains(oldkeyname); - oldkeyname = key->GetName(); - if ((!onlyListed) && (!cl->InheritsFrom(TDirectory::Class()))) continue; - } - - if (!(type&kResetable && type&kNonResetable)) { - // If neither or both are requested at the same time, we merger both types. - if (!(type&kResetable)) { - if (cl->GetResetAfterMerge()) { - // Skip the object with a reset after merge routine (TTree and other incrementally mergeable objects) - oldkeyname = key->GetName(); - continue; - } - } - if (!(type&kNonResetable)) { - if (!cl->GetResetAfterMerge()) { - // Skip the object without a reset after merge routine (Histograms and other non incrementally mergeable objects) - oldkeyname = key->GetName(); - continue; - } - } - } - // read object from first source file - TObject *obj; - if (type & kIncremental) { - obj = current_sourcedir->GetList()->FindObject(key->GetName()); - if (!obj) { - obj = key->ReadObj(); - } else if (info.fIsFirst && current_sourcedir != target) { - R__ASSERT(cl->IsTObject()); - obj = obj->Clone(); - } - } else { - obj = key->ReadObj(); - } - if (!obj) { - Info("MergeRecursive", "could not read object for key {%s, %s}", - key->GetName(), key->GetTitle()); - continue; - } - Bool_t canBeFound = (type & kIncremental) && (current_sourcedir->GetList()->FindObject(key->GetName()) != nullptr); - Bool_t needWriteAnyWay = kFALSE; - if (canBeFound) { - // For now the code require a key (that might be ignored see search through current_sourcedir->GetList()) - // in the output file in order for it to be even considered. - needWriteAnyWay = target->GetListOfKeys()->FindObject(key->GetName()) == nullptr; - } - // if (cl->IsTObject()) - // obj->ResetBit(kMustCleanup); - if (cl->IsTObject() && cl != obj->IsA()) { - Error("MergeRecursive", "TKey and object retrieve disagree on type (%s vs %s). Continuing with %s.", - key->GetClassName(), obj->IsA()->GetName(), obj->IsA()->GetName()); - cl = obj->IsA(); - } - Bool_t canBeMerged = kTRUE; - - if ( cl->InheritsFrom( TDirectory::Class() ) ) { - // it's a subdirectory - - target->cd(); - TDirectory *newdir; - - // For incremental or already seen we may have already a directory created - if (type & kIncremental || alreadyseen) { - newdir = target->GetDirectory(obj->GetName()); - if (!newdir) { - newdir = target->mkdir( obj->GetName(), obj->GetTitle() ); - // newdir->ResetBit(kMustCleanup); - } - } else { - newdir = target->mkdir( obj->GetName(), obj->GetTitle() ); - // newdir->ResetBit(kMustCleanup); - } - - // newdir is now the starting point of another round of merging - // newdir still knows its depth within the target file via - // GetPath(), so we can still figure out where we are in the recursion - - // If this folder is a onlyListed object, merge everything inside. - if (onlyListed) type &= ~kOnlyListed; - status = MergeRecursive(newdir, sourcelist, type); - if (onlyListed) type |= kOnlyListed; - if (!status) return status; - } else if (cl->GetMerge()) { - - // Check if already treated - if (alreadyseen) continue; - - TList inputs; - TList todelete; - Bool_t oneGo = fHistoOneGo && cl->InheritsFrom(R__TH1_Class); - - // Loop over all source files and merge same-name object - TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First(); - if (nextsource == 0) { - // There is only one file in the list - ROOT::MergeFunc_t func = cl->GetMerge(); - func(obj, &inputs, &info); - info.fIsFirst = kFALSE; - } else { - do { - // make sure we are at the correct directory level by cd'ing to path - TDirectory *ndir = nextsource->GetDirectory(path); - if (ndir) { - // For consistency (and persformance), we reset the MustCleanup be also for those - // 'key' retrieved indirectly. - // ndir->ResetBit(kMustCleanup); - ndir->cd(); - TObject *hobj = ndir->GetList()->FindObject(key->GetName()); - if (!hobj) { - TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName()); - if (key2) { - hobj = key2->ReadObj(); - if (!hobj) { - Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s", - key->GetName(), key->GetTitle(), nextsource->GetName()); - nextsource = (TFile*)sourcelist->After(nextsource); - continue; - } - todelete.Add(hobj); - } - } - if (hobj) { - // Set ownership for collections - if (hobj->InheritsFrom(TCollection::Class())) { - ((TCollection*)hobj)->SetOwner(); - } - hobj->ResetBit(kMustCleanup); - inputs.Add(hobj); - if (!oneGo) { - ROOT::MergeFunc_t func = cl->GetMerge(); - Long64_t result = func(obj, &inputs, &info); - info.fIsFirst = kFALSE; - if (result < 0) { - Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", - obj->GetName(), nextsource->GetName()); - } - inputs.Clear(); - todelete.Delete(); - } - } - } - nextsource = (TFile*)sourcelist->After( nextsource ); - } while (nextsource); - // Merge the list, if still to be done - if (oneGo || info.fIsFirst) { - ROOT::MergeFunc_t func = cl->GetMerge(); - func(obj, &inputs, &info); - info.fIsFirst = kFALSE; - inputs.Clear(); - todelete.Delete(); - } - } - } else if (cl->IsTObject() && - cl->GetMethodWithPrototype("Merge", "TCollection*,TFileMergeInfo*") ) { - // Object implements Merge(TCollection*,TFileMergeInfo*) and has a reflex dictionary ... - - // Check if already treated - if (alreadyseen) continue; - - TList listH; - TString listHargs; - listHargs.Form("(TCollection*)0x%lx,(TFileMergeInfo*)0x%lx", (ULong_t)&listH,(ULong_t)&info); - - // Loop over all source files and merge same-name object - TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First(); - if (nextsource == 0) { - // There is only one file in the list - Int_t error = 0; - obj->Execute("Merge", listHargs.Data(), &error); - info.fIsFirst = kFALSE; - if (error) { - Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", - obj->GetName(), key->GetName()); - } - } else { - while (nextsource) { - // make sure we are at the correct directory level by cd'ing to path - TDirectory *ndir = nextsource->GetDirectory(path); - if (ndir) { - // For consistency (and persformance), we reset the MustCleanup be also for those - // 'key' retrieved indirectly. - //ndir->ResetBit(kMustCleanup); - ndir->cd(); - TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName()); - if (key2) { - TObject *hobj = key2->ReadObj(); - if (!hobj) { - Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s", - key->GetName(), key->GetTitle(), nextsource->GetName()); - nextsource = (TFile*)sourcelist->After(nextsource); - continue; - } - // Set ownership for collections - if (hobj->InheritsFrom(TCollection::Class())) { - ((TCollection*)hobj)->SetOwner(); - } - hobj->ResetBit(kMustCleanup); - listH.Add(hobj); - Int_t error = 0; - obj->Execute("Merge", listHargs.Data(), &error); - info.fIsFirst = kFALSE; - if (error) { - Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", - obj->GetName(), nextsource->GetName()); - } - listH.Delete(); - } - } - nextsource = (TFile*)sourcelist->After( nextsource ); - } - // Merge the list, if still to be done - if (info.fIsFirst) { - Int_t error = 0; - obj->Execute("Merge", listHargs.Data(), &error); - info.fIsFirst = kFALSE; - listH.Delete(); - } - } - } else if (cl->IsTObject() && - cl->GetMethodWithPrototype("Merge", "TCollection*") ) { - // Object implements Merge(TCollection*) and has a reflex dictionary ... - - // Check if already treated - if (alreadyseen) continue; - - TList listH; - TString listHargs; - listHargs.Form("((TCollection*)0x%lx)", (ULong_t)&listH); - - // Loop over all source files and merge same-name object - TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First(); - if (nextsource == 0) { - // There is only one file in the list - Int_t error = 0; - obj->Execute("Merge", listHargs.Data(), &error); - if (error) { - Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", - obj->GetName(), key->GetName()); - } - } else { - while (nextsource) { - // make sure we are at the correct directory level by cd'ing to path - TDirectory *ndir = nextsource->GetDirectory(path); - if (ndir) { - // For consistency (and persformance), we reset the MustCleanup be also for those - // 'key' retrieved indirectly. - //ndir->ResetBit(kMustCleanup); - ndir->cd(); - TKey *key2 = (TKey*)ndir->GetListOfKeys()->FindObject(key->GetName()); - if (key2) { - TObject *hobj = key2->ReadObj(); - if (!hobj) { - Info("MergeRecursive", "could not read object for key {%s, %s}; skipping file %s", - key->GetName(), key->GetTitle(), nextsource->GetName()); - nextsource = (TFile*)sourcelist->After(nextsource); - continue; - } - // Set ownership for collections - if (hobj->InheritsFrom(TCollection::Class())) { - ((TCollection*)hobj)->SetOwner(); - } - hobj->ResetBit(kMustCleanup); - listH.Add(hobj); - Int_t error = 0; - obj->Execute("Merge", listHargs.Data(), &error); - info.fIsFirst = kFALSE; - if (error) { - Error("MergeRecursive", "calling Merge() on '%s' with the corresponding object in '%s'", - obj->GetName(), nextsource->GetName()); - } - listH.Delete(); - } - } - nextsource = (TFile*)sourcelist->After( nextsource ); - } - // Merge the list, if still to be done - if (info.fIsFirst) { - Int_t error = 0; - obj->Execute("Merge", listHargs.Data(), &error); - info.fIsFirst = kFALSE; - listH.Delete(); - } - } - } else { - // Object is of no type that we can merge - canBeMerged = kFALSE; - } - - // now write the merged histogram (which is "in" obj) to the target file - // note that this will just store obj in the current directory level, - // which is not persistent until the complete directory itself is stored - // by "target->SaveSelf()" below - target->cd(); - - oldkeyname = key->GetName(); - //!!if the object is a tree, it is stored in globChain... - if(cl->InheritsFrom( TDirectory::Class() )) { - //printf("cas d'une directory\n"); - - auto dirobj = dynamic_cast<TDirectory*>(obj); - TString dirpath(dirobj->GetPath()); - // coverity[unchecked_value] 'target' is from a file so GetPath always returns path starting with filename: - dirpath.Remove(0, std::strlen(dirobj->GetFile()->GetPath())); - - // Do not delete the directory if it is part of the output - // and we are in incremental mode (because it will be reuse - // and has not been written to disk (for performance reason). - // coverity[var_deref_model] the IsA()->InheritsFrom guarantees that the dynamic_cast will succeed. - if (!(type&kIncremental) || dirobj->GetFile() != target) { - dirobj->ResetBit(kMustCleanup); - delete dirobj; - } - // Let's also delete the directory from the other source (thanks to the 'allNames' - // mechanism above we will not process the directories when tranversing the next - // files). - TFile *nextsource = current_file ? (TFile*)sourcelist->After( current_file ) : (TFile*)sourcelist->First(); - while (nextsource) { - TDirectory *ndir = nextsource->GetDirectory(dirpath); - // For consistency (and persformance), we reset the MustCleanup be also for those - // 'key' retrieved indirectly. - if (ndir) { - ndir->ResetBit(kMustCleanup); - delete ndir; - } - nextsource = (TFile*)sourcelist->After( nextsource ); - } - } else if (cl->InheritsFrom( TCollection::Class() )) { - // Don't overwrite, if the object were not merged. - if ( obj->Write( oldkeyname, canBeMerged ? TObject::kSingleKey | TObject::kOverwrite : TObject::kSingleKey) <= 0 ) { - status = kFALSE; - } - ((TCollection*)obj)->SetOwner(); - delete obj; - } else if (!canBeFound) { // Don't write the partial result for TTree and TH1 - // Don't overwrite, if the object were not merged. - // NOTE: this is probably wrong for emulated objects. - if (cl->IsTObject()) { - if ( obj->Write( oldkeyname, canBeMerged ? TObject::kOverwrite : 0) <= 0) { - status = kFALSE; - } - obj->ResetBit(kMustCleanup); - } else { - if ( target->WriteObjectAny( (void*)obj, cl, oldkeyname, canBeMerged ? "OverWrite" : "" ) <= 0) { - status = kFALSE; - } - } - cl->Destructor(obj); // just in case the class is not loaded. - } else if (needWriteAnyWay) { - if (cl->IsTObject()) { - if ( obj->Write( oldkeyname, canBeMerged ? TObject::kOverwrite : 0) <= 0) { - status = kFALSE; - } - obj->ResetBit(kMustCleanup); - } else { - if ( target->WriteObjectAny( (void*)obj, cl, oldkeyname, canBeMerged ? "OverWrite" : "" ) <= 0) { - status = kFALSE; - } - } - } - info.Reset(); + auto result = MergeOne(target, sourcelist, type, + info, oldkeyname, allNames, status, onlyListed, path, + current_sourcedir, current_file, + key, nullptr); + if (!result) + return kFALSE; // Stop completely in case of error. } // while ( ( TKey *key = (TKey*)nextkey() ) ) } current_file = current_file ? (TFile*)sourcelist->After(current_file) : (TFile*)sourcelist->First(); diff --git a/tree/tree/src/TBranch.cxx b/tree/tree/src/TBranch.cxx index 0d2f68d7684..2722d8c6a88 100644 --- a/tree/tree/src/TBranch.cxx +++ b/tree/tree/src/TBranch.cxx @@ -3068,7 +3068,8 @@ Int_t TBranch::WriteBasketImpl(TBasket* basket, Int_t where, ROOT::Internal::TBr // itself might be modified after `WriteBasketImpl` exits. auto doUpdates = [=]() { Int_t nout = basket->WriteBuffer(); // Write buffer - if (nout < 0) Error("TBranch::WriteBasketImpl", "basket's WriteBuffer failed.\n"); + if (nout < 0) + Error("WriteBasketImpl", "basket's WriteBuffer failed."); fBasketBytes[where] = basket->GetNbytes(); fBasketSeek[where] = basket->GetSeekKey(); Int_t addbytes = basket->GetObjlen() + basket->GetKeylen(); -- GitLab