Skip to content

Commit 7f7d64a

Browse files
prakharsing7guitargeek
authored andcommitted
[io] fix TFileMerger kOnlyListed suffix key leak
An unlisted key whose name is a prefix or suffix of a listed key was incorrectly included in the merged output, because the name check was a plain substring search with no leading word boundary. Fixes #22414 (cherry picked from commit 2c052eb)
1 parent 3f4f55d commit 7f7d64a

2 files changed

Lines changed: 45 additions & 4 deletions

File tree

io/io/src/TFileMerger.cxx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -498,10 +498,13 @@ Bool_t TFileMerger::MergeOne(TDirectory *target, TList *sourcelist, Int_t type,
498498
}
499499
// Check if only the listed objects are to be merged
500500
if (type & kOnlyListed) {
501-
oldkeyname = keyname;
502-
oldkeyname += " ";
503-
onlyListed = fObjectNames.Contains(oldkeyname);
504-
oldkeyname = keyname;
501+
// Search for " key " in " a b c " to match whole words only.
502+
// Without the leading space, a key that is a prefix or suffix of a listed name
503+
// would match. AddObjectNames() guarantees a trailing space in fObjectNames.
504+
TString searchName = " ";
505+
searchName += keyname;
506+
searchName += " ";
507+
onlyListed = (" " + fObjectNames).Contains(searchName);
505508
if ((!onlyListed) && (!cl->InheritsFrom(TDirectory::Class()))) return kTRUE;
506509
}
507510

io/io/test/TFileMergerTests.cxx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,44 @@ TEST(TFileMerger, MergeSingleOnlyListed)
118118
EXPECT_EQ(output->GetListOfKeys()->GetSize(), 2);
119119
}
120120

121+
TEST(TFileMerger, OnlyListedNoSuffixLeak)
122+
{
123+
// Regression test for https://github.com/root-project/root/issues/22414:
124+
// keys whose names are a prefix or suffix of a listed key must not appear in the output.
125+
TMemFile src("OnlyListedNoSuffixLeakSrc.root", "CREATE");
126+
127+
// "short" is a suffix of "long_short"; "long" is a prefix — both must be excluded.
128+
auto hLongShort = new TH1F("long_short", "long_short", 1, 0, 2);
129+
auto hShort = new TH1F("short", "short", 1, 0, 2);
130+
auto hLong = new TH1F("long", "long", 1, 0, 2);
131+
auto hUnrelated = new TH1F("unrelated", "unrelated", 1, 0, 2);
132+
for (auto h : {hLongShort, hShort, hLong, hUnrelated})
133+
h->SetDirectory(&src);
134+
src.Write();
135+
136+
TFileMerger merger;
137+
auto output = std::unique_ptr<TFile>(new TFile("OnlyListedNoSuffixLeak.root", "RECREATE"));
138+
ASSERT_TRUE(merger.OutputFile(std::move(output)));
139+
140+
merger.AddObjectNames("long_short"); // only this one should appear in output
141+
merger.AddFile(&src, false);
142+
143+
const Int_t mode = TFileMerger::kAll | TFileMerger::kRegular | TFileMerger::kOnlyListed;
144+
ASSERT_TRUE(merger.PartialMerge(mode));
145+
146+
output = std::unique_ptr<TFile>(TFile::Open("OnlyListedNoSuffixLeak.root"));
147+
ASSERT_TRUE(output.get() && output->GetListOfKeys());
148+
149+
// Exactly one key: "long_short". Suffix, prefix, and unrelated keys must be absent.
150+
EXPECT_EQ(output->GetListOfKeys()->GetSize(), 1);
151+
EXPECT_NE(output->Get("long_short"), nullptr);
152+
EXPECT_EQ(output->Get("short"), nullptr);
153+
EXPECT_EQ(output->Get("long"), nullptr);
154+
EXPECT_EQ(output->Get("unrelated"), nullptr);
155+
output->Close();
156+
gSystem->Unlink("OnlyListedNoSuffixLeak.root");
157+
}
158+
121159
// https://github.com/root-project/root/issues/14558 aka https://its.cern.ch/jira/browse/ROOT-4716
122160
TEST(TFileMerger, MergeBranches)
123161
{

0 commit comments

Comments
 (0)