Skip to content

Commit c231ed6

Browse files
committed
[ntuple] create streamer infos from TClass names
Get the streamer info records of class fields from their meta (TClass) normalized name. Previously, the names came from the descriptor, which has them already RNTuple normalized.
1 parent 2d27aed commit c231ed6

File tree

6 files changed

+178
-130
lines changed

6 files changed

+178
-130
lines changed

tree/ntuple/inc/ROOT/RPageStorage.hxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,7 @@ public:
521521

522522
/// Updates the descriptor and calls InitImpl() that handles the backend-specific details (file, DAOS, etc.)
523523
void InitImpl(RNTupleModel &model) final;
524-
void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry) final;
524+
void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry) override;
525525
void UpdateExtraTypeInfo(const ROOT::RExtraTypeInfoDescriptor &extraTypeInfo) final;
526526

527527
/// Initialize sink based on an existing descriptor and fill into the descriptor builder, optionally copying over

tree/ntuple/inc/ROOT/RPageStorageFile.hxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,10 @@ private:
6565
std::unique_ptr<ROOT::Internal::RNTupleFileWriter> fWriter;
6666
/// Number of bytes committed to storage in the current cluster
6767
std::uint64_t fNBytesCurrentCluster = 0;
68+
/// On UpdateSchema(), the new class fields register the corresponding streamer info here so that the
69+
/// streamer info records in the file can be properly updated on dataset commit
70+
ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t fInfosOfClassFields;
71+
6872
RPageSinkFile(std::string_view ntupleName, const ROOT::RNTupleWriteOptions &options);
6973

7074
/// We pass bytesPacked so that TFile::ls() reports a reasonable value for the compression ratio of the corresponding
@@ -98,6 +102,8 @@ public:
98102
RPageSinkFile(RPageSinkFile &&) = default;
99103
RPageSinkFile &operator=(RPageSinkFile &&) = default;
100104
~RPageSinkFile() override;
105+
106+
void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry) final;
101107
}; // class RPageSinkFile
102108

103109
// clang-format off

tree/ntuple/src/RPageStorageFile.cxx

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <RVersion.h>
3131
#include <TDirectory.h>
3232
#include <TError.h>
33+
#include <TVirtualStreamerInfo.h>
3334

3435
#include <algorithm>
3536
#include <cstdio>
@@ -85,6 +86,37 @@ void ROOT::Internal::RPageSinkFile::InitImpl(unsigned char *serializedHeader, st
8586
fWriter->WriteNTupleHeader(zipBuffer.get(), szZipHeader, length);
8687
}
8788

89+
void ROOT::Internal::RPageSinkFile::UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset,
90+
ROOT::NTupleSize_t firstEntry)
91+
{
92+
RPagePersistentSink::UpdateSchema(changeset, firstEntry);
93+
94+
auto fnAddStreamerInfo = [this](const ROOT::RFieldBase *field) {
95+
auto clField = dynamic_cast<const RClassField *>(field);
96+
if (!clField)
97+
return;
98+
99+
const auto clName = clField->GetClass()->GetName();
100+
auto cl = TClass::GetClass(clName);
101+
if (!cl) {
102+
throw RException(R__FAIL(std::string("cannot get TClass for ") + clName));
103+
}
104+
auto streamerInfo = cl->GetStreamerInfo(clField->GetTypeVersion());
105+
if (!streamerInfo) {
106+
throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + clName + " [" +
107+
std::to_string(clField->GetTypeVersion()) + "]"));
108+
}
109+
fInfosOfClassFields[streamerInfo->GetNumber()] = streamerInfo;
110+
};
111+
112+
for (const auto field : changeset.fAddedFields) {
113+
fnAddStreamerInfo(field);
114+
for (const auto &subField : *field) {
115+
fnAddStreamerInfo(&subField);
116+
}
117+
}
118+
}
119+
88120
inline ROOT::RNTupleLocator
89121
ROOT::Internal::RPageSinkFile::WriteSealedPage(const RPageStorage::RSealedPage &sealedPage, std::size_t bytesPacked)
90122
{
@@ -244,7 +276,18 @@ ROOT::Internal::RPageSinkFile::CommitClusterGroupImpl(unsigned char *serializedP
244276

245277
void ROOT::Internal::RPageSinkFile::CommitDatasetImpl(unsigned char *serializedFooter, std::uint32_t length)
246278
{
247-
fWriter->UpdateStreamerInfos(fDescriptorBuilder.BuildStreamerInfos());
279+
// Add the streamer info records from streamer fields: because of runtime polymorphism we may need to add additional
280+
// types not covered by the type names of the class fields
281+
for (const auto &extraTypeInfo : fDescriptorBuilder.GetDescriptor().GetExtraTypeInfoIterable()) {
282+
if (extraTypeInfo.GetContentId() != EExtraTypeInfoIds::kStreamerInfo)
283+
continue;
284+
// Ideally, we would avoid deserializing the streamer info records of the streamer fields that we just serialized.
285+
// However, this happens only once at the end of writing and only when streamer fields are used, so the
286+
// preference here is for code simplicity.
287+
fInfosOfClassFields.merge(RNTupleSerializer::DeserializeStreamerInfos(extraTypeInfo.GetContent()).Unwrap());
288+
}
289+
fWriter->UpdateStreamerInfos(fInfosOfClassFields);
290+
248291
auto bufFooterZip = MakeUninitArray<unsigned char>(length);
249292
auto szFooterZip =
250293
RNTupleCompressor::Zip(serializedFooter, length, GetWriteOptions().GetCompression(), bufFooterZip.get());

tree/ntuple/test/ntuple_emulated.cxx

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ TEST(RNTupleEmulated, EmulatedFields_Simple)
3232
auto model = RNTupleModel::Create();
3333
model->AddField(RFieldBase::Create("f", "Outer_Simple").Unwrap());
3434

35+
// TStreamerInfo::Build will report a warning for interpreted classes (but only for members).
36+
// See also https://github.com/root-project/root/issues/9371
37+
ROOT::TestSupport::CheckDiagsRAII diagRAII;
38+
diagRAII.optionalDiag(kWarning, "TStreamerInfo::Build", "has no streamer or dictionary",
39+
/*matchFullMessage=*/false);
40+
3541
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
3642
writer->Fill();
3743

@@ -42,13 +48,6 @@ TEST(RNTupleEmulated, EmulatedFields_Simple)
4248
ProcessLine("ptrOuter->fInner.fInt2 = 82;");
4349
ProcessLine("ptrOuter->fInt1 = 93;");
4450
writer->Fill();
45-
46-
// TStreamerInfo::Build will report a warning for interpreted classes (but only for members).
47-
// See also https://github.com/root-project/root/issues/9371
48-
ROOT::TestSupport::CheckDiagsRAII diagRAII;
49-
diagRAII.optionalDiag(kWarning, "TStreamerInfo::Build", "has no streamer or dictionary",
50-
/*matchFullMessage=*/false);
51-
writer.reset();
5251
});
5352

5453
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
@@ -145,6 +144,12 @@ TEST(RNTupleEmulated, EmulatedFields_Vecs)
145144
auto model = RNTupleModel::Create();
146145
model->AddField(RFieldBase::Create("outers", "std::vector<Outer_Vecs>").Unwrap());
147146

147+
// TStreamerInfo::Build will report a warning for interpreted classes (but only for members).
148+
// See also https://github.com/root-project/root/issues/9371
149+
ROOT::TestSupport::CheckDiagsRAII diagRAII;
150+
diagRAII.optionalDiag(kWarning, "TStreamerInfo::Build", "has no streamer or dictionary",
151+
/*matchFullMessage=*/false);
152+
148153
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
149154
writer->Fill();
150155

@@ -155,13 +160,6 @@ TEST(RNTupleEmulated, EmulatedFields_Vecs)
155160
ProcessLine("(*ptrOuters)[0].fInners.push_back(Inner_Vecs{42.f});");
156161
ProcessLine("(*ptrOuters)[0].fInner.fFlt = 84.f;");
157162
writer->Fill();
158-
159-
// TStreamerInfo::Build will report a warning for interpreted classes (but only for members).
160-
// See also https://github.com/root-project/root/issues/9371
161-
ROOT::TestSupport::CheckDiagsRAII diagRAII;
162-
diagRAII.optionalDiag(kWarning, "TStreamerInfo::Build", "has no streamer or dictionary",
163-
/*matchFullMessage=*/false);
164-
writer.reset();
165163
});
166164

167165
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());
@@ -318,15 +316,14 @@ TEST(RNTupleEmulated, EmulatedFields_EmptyStruct)
318316
auto model = RNTupleModel::Create();
319317
model->AddField(RFieldBase::Create("f", "Outer_EmptyStruct").Unwrap());
320318

321-
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
322-
writer->Fill();
323-
324319
// TStreamerInfo::Build will report a warning for interpreted classes (but only for members).
325320
// See also https://github.com/root-project/root/issues/9371
326321
ROOT::TestSupport::CheckDiagsRAII diagRAII;
327322
diagRAII.optionalDiag(kWarning, "TStreamerInfo::Build", "has no streamer or dictionary",
328323
/*matchFullMessage=*/false);
329-
writer.reset();
324+
325+
auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath());
326+
writer->Fill();
330327
});
331328

332329
auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath());

0 commit comments

Comments
 (0)