Skip to content

Commit 4be5f02

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 4be5f02

13 files changed

+191
-273
lines changed

tree/ntuple/inc/ROOT/RNTupleDescriptor.hxx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,9 +1762,6 @@ public:
17621762
/// - Logical columns of piece two
17631763
/// - ...
17641764
void ShiftAliasColumns(std::uint32_t offset);
1765-
1766-
/// Get the streamer info records for custom classes. Currently requires the corresponding dictionaries to be loaded.
1767-
ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t BuildStreamerInfos() const;
17681765
};
17691766

17701767
inline RNTupleDescriptor CloneDescriptorSchema(const RNTupleDescriptor &desc)

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/RNTupleDescriptor.cxx

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424

2525
#include <RZip.h>
2626
#include <TError.h>
27-
#include <TVirtualStreamerInfo.h>
2827

2928
#include <algorithm>
3029
#include <cstdint>
@@ -1412,48 +1411,6 @@ ROOT::Internal::RNTupleDescriptorBuilder::AddAttributeSet(Experimental::RNTupleA
14121411
return RResult<void>::Success();
14131412
}
14141413

1415-
RNTupleSerializer::StreamerInfoMap_t ROOT::Internal::RNTupleDescriptorBuilder::BuildStreamerInfos() const
1416-
{
1417-
RNTupleSerializer::StreamerInfoMap_t streamerInfoMap;
1418-
const auto &desc = GetDescriptor();
1419-
1420-
std::function<void(const RFieldDescriptor &)> fnWalkFieldTree;
1421-
fnWalkFieldTree = [&desc, &streamerInfoMap, &fnWalkFieldTree](const RFieldDescriptor &fieldDesc) {
1422-
if (fieldDesc.IsCustomClass()) {
1423-
// Add streamer info for this class to streamerInfoMap
1424-
auto cl = TClass::GetClass(fieldDesc.GetTypeName().c_str());
1425-
if (!cl) {
1426-
throw RException(R__FAIL(std::string("cannot get TClass for ") + fieldDesc.GetTypeName()));
1427-
}
1428-
auto streamerInfo = cl->GetStreamerInfo(fieldDesc.GetTypeVersion());
1429-
if (!streamerInfo) {
1430-
throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + fieldDesc.GetTypeName()));
1431-
}
1432-
streamerInfoMap[streamerInfo->GetNumber()] = streamerInfo;
1433-
}
1434-
1435-
// Recursively traverse sub fields
1436-
for (const auto &subFieldDesc : desc.GetFieldIterable(fieldDesc)) {
1437-
fnWalkFieldTree(subFieldDesc);
1438-
}
1439-
};
1440-
1441-
fnWalkFieldTree(desc.GetFieldZero());
1442-
1443-
// Add the streamer info records from streamer fields: because of runtime polymorphism we may need to add additional
1444-
// types not covered by the type names stored in the field headers
1445-
for (const auto &extraTypeInfo : desc.GetExtraTypeInfoIterable()) {
1446-
if (extraTypeInfo.GetContentId() != EExtraTypeInfoIds::kStreamerInfo)
1447-
continue;
1448-
// Ideally, we would avoid deserializing the streamer info records of the streamer fields that we just serialized.
1449-
// However, this happens only once at the end of writing and only when streamer fields are used, so the
1450-
// preference here is for code simplicity.
1451-
streamerInfoMap.merge(RNTupleSerializer::DeserializeStreamerInfos(extraTypeInfo.GetContent()).Unwrap());
1452-
}
1453-
1454-
return streamerInfoMap;
1455-
}
1456-
14571414
ROOT::RClusterDescriptor::RColumnRangeIterable ROOT::RClusterDescriptor::GetColumnRangeIterable() const
14581415
{
14591416
return RColumnRangeIterable(*this);

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/CMakeLists.txt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,6 @@ ROOT_GENERATE_DICTIONARY(RXTupleDict ${CMAKE_CURRENT_SOURCE_DIR}/RXTuple.hxx
3030
LINKDEF RXTupleLinkDef.h
3131
DEPENDENCIES RIO)
3232
ROOT_ADD_GTEST(ntuple_descriptor ntuple_descriptor.cxx LIBRARIES ROOTNTuple)
33-
ROOT_GENERATE_DICTIONARY(RNTupleDescriptorDict ${CMAKE_CURRENT_SOURCE_DIR}/RNTupleDescriptorDict.hxx
34-
MODULE ntuple_descriptor
35-
LINKDEF RNTupleDescriptorLinkDef.h
36-
OPTIONS -inlineInputHeader
37-
DEPENDENCIES RIO)
3833

3934
ROOT_ADD_GTEST(ntuple_endian ntuple_endian.cxx LIBRARIES ROOTNTuple)
4035
ROOT_ADD_GTEST(ntuple_evolution_type ntuple_evolution_type.cxx LIBRARIES ROOTNTuple)
@@ -76,6 +71,11 @@ ROOT_ADD_GTEST(ntuple_zip ntuple_zip.cxx LIBRARIES ROOTNTuple)
7671

7772
ROOT_ADD_GTEST(rfield_basics rfield_basics.cxx LIBRARIES ROOTNTuple CustomStruct)
7873
ROOT_ADD_GTEST(rfield_class rfield_class.cxx LIBRARIES ROOTNTuple CustomStruct Physics)
74+
ROOT_GENERATE_DICTIONARY(RNTupleClassFieldDict ${CMAKE_CURRENT_SOURCE_DIR}/RNTupleClassFieldDict.hxx
75+
MODULE rfield_class
76+
LINKDEF RNTupleClassFieldLinkDef.h
77+
OPTIONS -inlineInputHeader
78+
DEPENDENCIES RIO)
7979
ROOT_ADD_GTEST(rfield_string rfield_string.cxx LIBRARIES ROOTNTuple)
8080
ROOT_ADD_GTEST(rfield_variant rfield_variant.cxx LIBRARIES ROOTNTuple CustomStruct)
8181
if(MSVC)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef ROOT_RNTuple_Test_RNTupleClassFieldDict
2+
#define ROOT_RNTuple_Test_RNTupleClassFieldDict
3+
4+
#include "CustomStruct.hxx"
5+
6+
#include <map>
7+
8+
#endif // ROOT_RNTuple_Test_RNTupleClassFieldDict

tree/ntuple/test/RNTupleDescriptorLinkDef.h renamed to tree/ntuple/test/RNTupleClassFieldLinkDef.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22

33
#pragma link C++ class std::map < int, CustomStruct> + ;
44
#pragma link C++ class std::map < int, float> + ;
5-
#pragma link C++ class std::unordered_map < int, float> + ;
65

76
#endif

tree/ntuple/test/RNTupleDescriptorDict.hxx

Lines changed: 0 additions & 9 deletions
This file was deleted.

tree/ntuple/test/ntuple_descriptor.cxx

Lines changed: 0 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -609,88 +609,6 @@ TEST(RNTupleDescriptor, Clone)
609609
EXPECT_EQ(desc, clone);
610610
}
611611

612-
TEST(RNTupleDescriptor, BuildStreamerInfos)
613-
{
614-
auto fnBuildStreamerInfosOf = [](const RFieldBase &field) -> RNTupleSerializer::StreamerInfoMap_t {
615-
RNTupleDescriptorBuilder descBuilder;
616-
descBuilder.SetNTuple("test", "");
617-
descBuilder.AddField(
618-
RFieldDescriptorBuilder().FieldId(0).Structure(ROOT::ENTupleStructure::kRecord).MakeDescriptor().Unwrap());
619-
auto fieldBuilder = RFieldDescriptorBuilder::FromField(field);
620-
descBuilder.AddField(fieldBuilder.FieldId(1).MakeDescriptor().Unwrap());
621-
descBuilder.AddFieldLink(0, 1);
622-
int i = 2;
623-
// In this test, we only support field hierarchies up to 2 levels
624-
for (const auto &child : field.GetConstSubfields()) {
625-
fieldBuilder = RFieldDescriptorBuilder::FromField(*child);
626-
descBuilder.AddField(fieldBuilder.FieldId(i).MakeDescriptor().Unwrap());
627-
descBuilder.AddFieldLink(1, i);
628-
const auto childId = i;
629-
i++;
630-
for (const auto &grandChild : child->GetConstSubfields()) {
631-
fieldBuilder = RFieldDescriptorBuilder::FromField(*grandChild);
632-
descBuilder.AddField(fieldBuilder.FieldId(i).MakeDescriptor().Unwrap());
633-
descBuilder.AddFieldLink(childId, i);
634-
i++;
635-
}
636-
}
637-
return descBuilder.BuildStreamerInfos();
638-
};
639-
640-
RNTupleSerializer::StreamerInfoMap_t streamerInfoMap;
641-
642-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "float").Unwrap());
643-
EXPECT_TRUE(streamerInfoMap.empty());
644-
645-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::vector<float>").Unwrap());
646-
EXPECT_TRUE(streamerInfoMap.empty());
647-
648-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::pair<float, float>").Unwrap());
649-
EXPECT_TRUE(streamerInfoMap.empty());
650-
651-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::map<int, float>").Unwrap());
652-
EXPECT_TRUE(streamerInfoMap.empty());
653-
654-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::unordered_map<int, float>").Unwrap());
655-
EXPECT_TRUE(streamerInfoMap.empty());
656-
657-
std::vector<std::unique_ptr<RFieldBase>> itemFields;
658-
streamerInfoMap = fnBuildStreamerInfosOf(ROOT::RRecordField("f", std::move(itemFields)));
659-
EXPECT_TRUE(streamerInfoMap.empty());
660-
661-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "CustomStruct").Unwrap());
662-
EXPECT_EQ(1u, streamerInfoMap.size());
663-
EXPECT_STREQ("CustomStruct", streamerInfoMap.begin()->second->GetName());
664-
665-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::vector<CustomStruct>").Unwrap());
666-
EXPECT_EQ(1u, streamerInfoMap.size());
667-
EXPECT_STREQ("CustomStruct", streamerInfoMap.begin()->second->GetName());
668-
669-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::map<int, CustomStruct>").Unwrap());
670-
EXPECT_EQ(1u, streamerInfoMap.size());
671-
EXPECT_STREQ("CustomStruct", streamerInfoMap.begin()->second->GetName());
672-
673-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "DerivedA").Unwrap());
674-
EXPECT_EQ(2u, streamerInfoMap.size());
675-
std::vector<std::string> typeNames;
676-
for (const auto &[_, si] : streamerInfoMap) {
677-
typeNames.emplace_back(si->GetName());
678-
}
679-
std::sort(typeNames.begin(), typeNames.end());
680-
EXPECT_STREQ("CustomStruct", typeNames[0].c_str());
681-
EXPECT_STREQ("DerivedA", typeNames[1].c_str());
682-
683-
streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::pair<CustomStruct, DerivedA>").Unwrap());
684-
EXPECT_EQ(2u, streamerInfoMap.size());
685-
typeNames.clear();
686-
for (const auto &[_, si] : streamerInfoMap) {
687-
typeNames.emplace_back(si->GetName());
688-
}
689-
std::sort(typeNames.begin(), typeNames.end());
690-
EXPECT_STREQ("CustomStruct", typeNames[0].c_str());
691-
EXPECT_STREQ("DerivedA", typeNames[1].c_str());
692-
}
693-
694612
TEST(RNTupleDescriptor, CloneSchema)
695613
{
696614
FileRaii fileGuard("test_ntuple_desc_cloneschema.root");

0 commit comments

Comments
 (0)