diff --git a/.github/workflows/root-ci.yml b/.github/workflows/root-ci.yml index 636efa1d18c26..1af2addfa21cf 100644 --- a/.github/workflows/root-ci.yml +++ b/.github/workflows/root-ci.yml @@ -240,6 +240,10 @@ jobs: config: Debug - event_name: push config: Debug + # This is this platform is subject to timeouts when building from + # scratch. + - target_arch: x86 + config: Debug name: Windows 10 ${{ matrix.target_arch }} ${{ matrix.config }} diff --git a/core/clingutils/res/TClingUtils.h b/core/clingutils/res/TClingUtils.h index de5a174ac835b..bac84e1414f77 100644 --- a/core/clingutils/res/TClingUtils.h +++ b/core/clingutils/res/TClingUtils.h @@ -613,12 +613,14 @@ static inline std::string DemangleNameForDlsym(const std::string& name) //______________________________________________________________________________ // Return the type with all parts fully qualified (most typedefs), // including template arguments, appended to name. -void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter); +void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const cling::Interpreter &interpreter, + const clang::ClassTemplateSpecializationDecl *Spec = nullptr); //______________________________________________________________________________ // Return the type with all parts fully qualified (most typedefs), // including template arguments, appended to name, without using the interpreter -void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const clang::ASTContext &); +void GetFullyQualifiedTypeName(std::string &name, const clang::QualType &type, const clang::ASTContext &, + const clang::ClassTemplateSpecializationDecl *Spec = nullptr); //______________________________________________________________________________ // Return the type normalized for ROOT, diff --git a/core/clingutils/src/TClingUtils.cxx b/core/clingutils/src/TClingUtils.cxx index b977d67cd193b..5b92bf16ec835 100644 --- a/core/clingutils/src/TClingUtils.cxx +++ b/core/clingutils/src/TClingUtils.cxx @@ -1358,7 +1358,8 @@ bool ROOT::TMetaUtils::HasCustomConvStreamerMemberFunction(const AnnotatedRecord void ROOT::TMetaUtils::GetQualifiedName(std::string &qual_name, const clang::QualType &type, const clang::NamedDecl &forcontext) { - ROOT::TMetaUtils::GetFullyQualifiedTypeName(qual_name, type, forcontext.getASTContext()); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(qual_name, type, forcontext.getASTContext(), + llvm::dyn_cast_or_null(&forcontext)); } //---- @@ -1473,6 +1474,7 @@ static void CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::MembersTypeM std::string typenameStr; const clang::ASTContext& astContext = cl.getASTContext(); + auto CTSD = llvm::dyn_cast(&cl); // Loop over the non static data member. for(clang::RecordDecl::field_iterator field_iter = cl.field_begin(), end = cl.field_end(); @@ -1497,7 +1499,7 @@ static void CreateNameTypeMap(const clang::CXXRecordDecl &cl, ROOT::MembersTypeM } } - ROOT::TMetaUtils::GetFullyQualifiedTypeName(typenameStr, fieldType, astContext); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(typenameStr, fieldType, astContext, CTSD); nameType[field_iter->getName().str()] = ROOT::Internal::TSchemaType(typenameStr.c_str(),dims.str().c_str()); } @@ -3489,9 +3491,10 @@ std::string ROOT::TMetaUtils::GetFileName(const clang::Decl& decl, void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, const clang::QualType &qtype, - const clang::ASTContext &astContext) + const clang::ASTContext &astContext, + const clang::ClassTemplateSpecializationDecl *Spec /*= nullptr*/) { - std::string fqname = cling::utils::TypeName::GetFullyQualifiedName(qtype, astContext); + std::string fqname = cling::utils::TypeName::GetFullyQualifiedName(qtype, astContext, Spec); TClassEdit::TSplitType splitname(fqname.c_str(), (TClassEdit::EModType)(TClassEdit::kLong64 | TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kKeepOuterConst)); splitname.ShortType(typenamestr,TClassEdit::kDropStd | TClassEdit::kDropStlDefault | TClassEdit::kKeepOuterConst); @@ -3501,7 +3504,8 @@ void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, const clang::QualType &qtype, - const cling::Interpreter &interpreter) + const cling::Interpreter &interpreter, + const clang::ClassTemplateSpecializationDecl *Spec /*= nullptr*/) { // We need this because GetFullyQualifiedTypeName is triggering deserialization // This calling the same name function GetFullyQualifiedTypeName, but this should stay here because @@ -3510,7 +3514,8 @@ void ROOT::TMetaUtils::GetFullyQualifiedTypeName(std::string &typenamestr, GetFullyQualifiedTypeName(typenamestr, qtype, - interpreter.getCI()->getASTContext()); + interpreter.getCI()->getASTContext(), + Spec); } //////////////////////////////////////////////////////////////////////////////// diff --git a/core/dictgen/res/DictSelectionReader.h b/core/dictgen/res/DictSelectionReader.h index b19135badd4f2..c3f7ec7539e64 100644 --- a/core/dictgen/res/DictSelectionReader.h +++ b/core/dictgen/res/DictSelectionReader.h @@ -260,25 +260,16 @@ class DictSelectionReader : public clang::RecursiveASTVisitor fUnsplittableMembers {}; }; - inline bool - InSelectionNamespace(const clang::RecordDecl &, - const std::string &str = - ""); ///< Check if in the ROOT::Meta::Selection namespace - inline bool FirstPass(const clang::RecordDecl &); ///< First pass on the AST - inline bool SecondPass(const clang::RecordDecl &); ///< Second pass on the AST, using the information of the first one - inline void - ManageFields(const clang::RecordDecl &, - const std::string &, - ClassSelectionRule &, - bool); ///< Take care of the class fields - inline void - ManageBaseClasses(const clang::CXXRecordDecl &, const std::string &, bool &); ///< Take care of the class bases + bool InSelectionNamespace(const clang::RecordDecl &, + const std::string &str = ""); ///< Check if in the ROOT::Meta::Selection namespace + bool FirstPass(const clang::RecordDecl &); ///< First pass on the AST + bool SecondPass(const clang::RecordDecl &); ///< Second pass on the AST, using the information of the first one + void ManageFields(const clang::RecordDecl &, const std::string &, + ClassSelectionRule &, bool); ///< Take care of the class fields + void ManageBaseClasses(const clang::CXXRecordDecl &, const std::string &, bool &); ///< Take care of the class bases template - inline unsigned int ExtractTemplateArgValue( - const T &, - const std::string &); ///< Extract the value of the template parameter - inline const clang::TemplateArgumentList *GetTmplArgList( - const clang::CXXRecordDecl &); ///< Get the template arguments list if any + unsigned int ExtractTemplateArgValue(const T &, const std::string &); ///< Extract the value of the template parameter + const clang::TemplateArgumentList *GetTmplArgList(const clang::CXXRecordDecl &); ///< Get the template arguments list if any std::string PatternifyName(const std::string &className); ///< Transform instance name in pattern for selection void GetPointeeType(std::string &typeName); ///< Get name of the pointee type diff --git a/core/dictgen/src/DictSelectionReader.cxx b/core/dictgen/src/DictSelectionReader.cxx index b034c9a0b5ce2..9cac67101da2c 100644 --- a/core/dictgen/src/DictSelectionReader.cxx +++ b/core/dictgen/src/DictSelectionReader.cxx @@ -383,12 +383,27 @@ bool DictSelectionReader::SecondPass(const clang::RecordDecl &recordDecl) bool DictSelectionReader::VisitRecordDecl(clang::RecordDecl *recordDecl) { + if (auto CXXRD = llvm::dyn_cast(recordDecl)) { + if (CXXRD->getDescribedClassTemplate()) { + // this is a member of a template; ignore: dictionaries can only + // select instances / specializations. + return true; + } + } + if (auto CXXRD = llvm::dyn_cast(recordDecl->getDeclContext())) { + if (CXXRD->getDescribedClassTemplate()) { + // this is a member of a template; ignore: dictionaries can only + // select instances / specializations. + return true; + } + } if (fIsFirstPass) return FirstPass(*recordDecl); else return SecondPass(*recordDecl); } + //////////////////////////////////////////////////////////////////////////////// /** diff --git a/core/meta/src/TClass.cxx b/core/meta/src/TClass.cxx index 4ea5f8dcac0e8..f1709d78544d7 100644 --- a/core/meta/src/TClass.cxx +++ b/core/meta/src/TClass.cxx @@ -698,7 +698,7 @@ void TDumpMembers::Inspect(TClass *cl, const char *pname, const char *mname, con } } if (isPrintable) { - strncpy(line + kvalue, *ppointer, std::min( i, kline - kvalue)); + strncpy(line + kvalue, *ppointer, kline - kvalue); line[kvalue+i] = 0; } else { line[kvalue] = 0; diff --git a/core/metacling/src/TClingDataMemberInfo.cxx b/core/metacling/src/TClingDataMemberInfo.cxx index 57d64ef3c7c32..69f4f22051102 100644 --- a/core/metacling/src/TClingDataMemberInfo.cxx +++ b/core/metacling/src/TClingDataMemberInfo.cxx @@ -36,6 +36,7 @@ from the Clang C++ compiler, not CINT. #include "clang/AST/Attr.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/GlobalDecl.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" @@ -586,7 +587,8 @@ const char *TClingDataMemberInfo::TypeName() const // if (we_need_to_do_the_subst_because_the_class_is_a_template_instance_of_double32_t) vdType = ROOT::TMetaUtils::ReSubstTemplateArg(vdType, GetClassAsType() ); - ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf, vdType, *fInterp); + auto CTSD = llvm::dyn_cast(vd->getDeclContext()); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf, vdType, *fInterp, CTSD); return buf.c_str(); } diff --git a/core/metacling/src/TClingMethodArgInfo.cxx b/core/metacling/src/TClingMethodArgInfo.cxx index f796ec3fde688..656fe04bb6881 100644 --- a/core/metacling/src/TClingMethodArgInfo.cxx +++ b/core/metacling/src/TClingMethodArgInfo.cxx @@ -29,6 +29,7 @@ the Clang C++ compiler, not CINT. #include "cling/Interpreter/Interpreter.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/PrettyPrinter.h" @@ -148,6 +149,10 @@ const char *TClingMethodArgInfo::TypeName() const if (!IsValid()) { return nullptr; } - return Type()->Name(); + + const clang::ClassTemplateSpecializationDecl *spec = nullptr; + if (const auto FD = llvm::cast_or_null(TClingDeclInfo::GetDecl())) + spec = llvm::dyn_cast(FD->getDeclContext()); + return Type()->Name(spec); } diff --git a/core/metacling/src/TClingMethodInfo.cxx b/core/metacling/src/TClingMethodInfo.cxx index baee85922eee5..55797f36a40d5 100644 --- a/core/metacling/src/TClingMethodInfo.cxx +++ b/core/metacling/src/TClingMethodInfo.cxx @@ -634,7 +634,9 @@ const char *TClingMethodInfo::GetPrototype() if (const clang::TypeDecl *td = llvm::dyn_cast(GetDecl()->getDeclContext())) { std::string name; clang::QualType qualType(td->getTypeForDecl(),0); - ROOT::TMetaUtils::GetFullyQualifiedTypeName(name,qualType,*fInterp); + const clang::ClassTemplateSpecializationDecl *spec + = llvm::dyn_cast(td); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(name, qualType, *fInterp, spec); buf += name; buf += "::"; } else if (const clang::NamedDecl *nd = llvm::dyn_cast(FD->getDeclContext())) { @@ -688,9 +690,17 @@ const char *TClingMethodInfo::TypeName() const // The next *two* calls lock the interpreter mutex. Lock here first instead // of locking/unlocking twice. R__LOCKGUARD(gInterpreterMutex); - return Type()->Name(); + const clang::ClassTemplateSpecializationDecl *spec + = llvm::dyn_cast(GetDecl()->getDeclContext()); + return Type()->Name(spec); } +std::string TClingMethodInfo::NormalizedName(ROOT::TMetaUtils::TNormalizedCtxt &ctx) const +{ + return Type()->NormalizedName(ctx); +} + + const char *TClingMethodInfo::Title() { if (!IsValid()) { diff --git a/core/metacling/src/TClingMethodInfo.h b/core/metacling/src/TClingMethodInfo.h index 46cbeedf86618..563ebf1479903 100644 --- a/core/metacling/src/TClingMethodInfo.h +++ b/core/metacling/src/TClingMethodInfo.h @@ -165,6 +165,7 @@ class TClingMethodInfo final : public TClingDeclInfo { const char *GetPrototype(); const char *Name() const override; const char *TypeName() const; + std::string NormalizedName(ROOT::TMetaUtils::TNormalizedCtxt &ctx) const; const char *Title(); }; diff --git a/core/metacling/src/TClingTypeInfo.cxx b/core/metacling/src/TClingTypeInfo.cxx index e5bb88fda7468..7136ff107e702 100644 --- a/core/metacling/src/TClingTypeInfo.cxx +++ b/core/metacling/src/TClingTypeInfo.cxx @@ -33,6 +33,7 @@ but the type metadata comes from the Clang C++ compiler, not CINT. #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" #include "clang/AST/Type.h" #include "clang/AST/PrettyPrinter.h" #include "clang/Frontend/CompilerInstance.h" @@ -97,7 +98,7 @@ void TClingTypeInfo::Init(const char *name) //////////////////////////////////////////////////////////////////////////////// -const char *TClingTypeInfo::Name() const +const char *TClingTypeInfo::Name(const clang::ClassTemplateSpecializationDecl *spec) const { if (!IsValid()) { return ""; @@ -109,7 +110,7 @@ const char *TClingTypeInfo::Name() const // TODO: This needs to be locked, but the lock cannot be placed in TClingUtils.cxx as it cannot depend from // TInterpreter.h for the declaration of gInterpreterMutex. Or can it? R__LOCKGUARD(gInterpreterMutex); - ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf,fQualType,*fInterp); + ROOT::TMetaUtils::GetFullyQualifiedTypeName(buf,fQualType,*fInterp, spec); return buf.c_str(); // NOLINT } diff --git a/core/metacling/src/TClingTypeInfo.h b/core/metacling/src/TClingTypeInfo.h index c68a9ba7b2cf2..f66fc4cd71148 100644 --- a/core/metacling/src/TClingTypeInfo.h +++ b/core/metacling/src/TClingTypeInfo.h @@ -30,6 +30,10 @@ #include "clang/AST/Type.h" +namespace clang { + class ClassTemplateSpecializationDecl; +} + namespace cling { class Interpreter; } @@ -65,7 +69,8 @@ class TClingTypeInfo final : public TClingDeclInfo { void Init(const char *name); // Set type by name. void Init(clang::QualType ty) { fQualType = ty; } bool IsValid() const override { return !fQualType.isNull(); } - const char *Name() const override; // Get name of type. + const char *Name() const override { return Name(nullptr); } + const char *Name(const clang::ClassTemplateSpecializationDecl *spec) const; // Get name of type. long Property() const; // Get properties of type. int RefType() const; // Get CINT reftype of type. int Size() const; // Get size in bytes of type. diff --git a/core/metacling/test/TClingMethodInfoTests.cxx b/core/metacling/test/TClingMethodInfoTests.cxx index de3da8684afa3..74a2235b63944 100644 --- a/core/metacling/test/TClingMethodInfoTests.cxx +++ b/core/metacling/test/TClingMethodInfoTests.cxx @@ -5,10 +5,13 @@ #include "TList.h" #include "TListOfFunctions.h" #include "TMethod.h" +#include "TMethodArg.h" #include "TROOT.h" #include "gtest/gtest.h" +#include + TEST(TClingMethodInfo, Prototype) { TClass *cl = TClass::GetClass("TObject"); @@ -555,3 +558,142 @@ namespace BUG6578 { std::cerr << " " << iCtorExpected.first << '\n'; } } + +// https://github.com/root-project/root/issues/7955 +TEST(TClingMethodInfo, NonDependentMemberTypes) +{ + gInterpreter->Declare(R"CODE( +struct AScope { + using TheType = int; +}; + + +template +struct Template1 { + using type1 = int; + using type2 = AScope; + void f0(type1); + void f1(type1*); + void g0(type2); + void g1(type2*); + void g2(type2::TheType); + void g3(type2::TheType*); +}; + +Template1 xf1; + +struct X1 { + void f1(Template1::type1) {} + void f2(Template1::type2) {} + void g1(Template1::type1) {} + void g2(Template1::type2) {} +}; + +struct Y1: Template1 { + void yf1(type1); + void yf2(type2); + void yf3(type2::TheType); +}; + +template using TAlias1 = Template1; +struct Z1 { + void f1(TAlias1::type1) {} + void f2(TAlias1::type2) {} + void g1(TAlias1::type1) {} + void g2(TAlias1::type2) {} +}; + +template +struct Template2 { + struct Inner { + using type = AScope; + }; + void f(typename Inner::type); +}; + +Template2 xf2; + +struct Y2 { + void g(Template2::Inner::type) {} + void h(Template2::Inner::type) {} +}; + +struct Z2: Template2 { + void one(Inner::type); +}; +)CODE"); + + + auto checkClass = [](const char *className, std::unordered_map funcPar0) { + TClass *cl = TClass::GetClass(className); + ASSERT_NE(cl, nullptr); + TListOfFunctions *methods = (TListOfFunctions *)cl->GetListOfMethods(); + for (TMethod *method : TRangeDynCast(methods)) { + auto args = method->GetListOfMethodArgs(); + if (args->GetSize() != 1) + continue; + auto arg0 = (TMethodArg*) args->At(0); + auto iExpected = funcPar0.find(std::string(method->GetName())); + if (iExpected == funcPar0.end()) + continue; + EXPECT_EQ(iExpected->second, arg0->GetFullTypeName()) + << " in function " << className << "::" << method->GetName(); + funcPar0.erase(iExpected); + } + EXPECT_EQ(funcPar0.size(), 0); + }; + checkClass("Template1", { + {"f0", "Template1::type1"}, + {"f1", "Template1::type1*"}, + {"g0", "Template1::type2"}, + {"g1", "Template1::type2*"}, + {"g2", "AScope::TheType"}, + {"g3", "AScope::TheType*"}}); + + // This one was giving "Template1::type1" before: + checkClass("Template1", { + {"f0", "Template1::type1"}, + {"f1", "Template1::type1*"}, + {"g0", "Template1::type2"}, + {"g1", "Template1::type2*"}, + {"g2", "AScope::TheType"}, + {"g3", "AScope::TheType*"}}); + + checkClass("TAlias1", { + {"f0", "Template1::type1"}, + {"f1", "Template1::type1*"}, + {"g0", "Template1::type2"}, + {"g1", "Template1::type2*"}, + {"g2", "AScope::TheType"}, + {"g3", "AScope::TheType*"}}); + + checkClass("X1", { + {"f1", "Template1::type1"}, + {"f2", "Template1::type2"}, + {"g1", "Template1::type1"}, + {"g2", "Template1::type2"}}); + + checkClass("Y1", { + {"yf1", "Template1::type1"}, + {"yf2", "Template1::type2"}, + {"yf3", "AScope::TheType"}}); + + checkClass("Z1", { + {"f1", "Template1::type1"}, + {"f2", "Template1::type2"}, + {"g1", "Template1::type1"}, + {"g2", "Template1::type2"}}); + + checkClass("Template2", { + {"f", "Template2::Inner::type"}}); + + checkClass("Template2", { + {"f", "Template2::Inner::type"}}); + + checkClass("Y2", { + {"g", "Template2::Inner::type"}, + {"h", "Template2::Inner::type"}}); + + checkClass("Z2", { + {"one", "Template2::Inner::type"}}); +} diff --git a/core/metacling/test/TClingTests.cxx b/core/metacling/test/TClingTests.cxx index 674ec865c761e..c94cc0815f7f7 100644 --- a/core/metacling/test/TClingTests.cxx +++ b/core/metacling/test/TClingTests.cxx @@ -4,6 +4,8 @@ #include "TInterpreter.h" #include "TROOT.h" #include "TSystem.h" +#include "TMethod.h" +#include "TMethodArg.h" #include "gmock/gmock.h" @@ -364,3 +366,13 @@ auto val2 = Ln10_pushforward(); EXPECT_DOUBLE_EQ(2.3025850929940459, val1); EXPECT_DOUBLE_EQ(2.3025850929940459, val2); } + +// https://github.com/root-project/root/issues/7955 +TEST(ClingAST, Issue7955) +{ + auto cls = TClass::GetClass("std::vector"); + auto meth = cls->GetMethodWithPrototype("operator[]","int",true, ROOT::kConversionMatch); + auto args = meth->GetListOfMethodArgs(); + auto methArg = dynamic_cast(args->First()); + EXPECT_STREQ(methArg->GetTypeName(), "vector::size_type"); +} diff --git a/documentation/doxygen/MakeRCanvasJS.C b/documentation/doxygen/MakeRCanvasJS.C index 7a59d648a4ece..96feeed432fb0 100644 --- a/documentation/doxygen/MakeRCanvasJS.C +++ b/documentation/doxygen/MakeRCanvasJS.C @@ -37,22 +37,12 @@ void MakeRCanvasJS(const char *MacroName, const char *IN, const char *OutDir, bo // Build the html file inlining the json picture FILE *fh = fopen(TString::Format("%s/macros/%s.html",OutDir,IN), "w"); - fprintf(fh,"
\n", IN); - fprintf(fh,"\n"); fclose(fh); } diff --git a/documentation/doxygen/MakeTCanvasJS.C b/documentation/doxygen/MakeTCanvasJS.C index ab89c1dfd5572..4cd3476565f4c 100644 --- a/documentation/doxygen/MakeTCanvasJS.C +++ b/documentation/doxygen/MakeTCanvasJS.C @@ -37,28 +37,18 @@ void MakeTCanvasJS(const char *MacroName, const char *IN, const char *OutDir, bo while ((canvas = (TCanvas*) next()) != nullptr) { ImageNum++; json_codes.push_back(TBufferJSON::ToJSON(canvas, TBufferJSON::kNoSpaces + TBufferJSON::kSameSuppression)); - fprintf(fh,"
\n", + fprintf(fh,"
\n", ImageNum,IN,canvas->GetWindowWidth(),canvas->GetWindowHeight()); } fprintf(fh,"\n"); - fprintf(fh,"\n"); fclose(fh); diff --git a/gui/gui/src/TGFSContainer.cxx b/gui/gui/src/TGFSContainer.cxx index dddc6c87bab3d..d0ae718d83928 100644 --- a/gui/gui/src/TGFSContainer.cxx +++ b/gui/gui/src/TGFSContainer.cxx @@ -465,7 +465,7 @@ void TGFileItem::SetDNDData(TDNDData *data) { if (fDNDData.fDataLength > 0) free(fDNDData.fData); - fDNDData.fData = calloc(sizeof(unsigned char), data->fDataLength); + fDNDData.fData = calloc(data->fDataLength, sizeof(unsigned char)); if (fDNDData.fData) memcpy(fDNDData.fData, data->fData, data->fDataLength); fDNDData.fDataLength = data->fDataLength; diff --git a/interpreter/cling/include/cling/Utils/AST.h b/interpreter/cling/include/cling/Utils/AST.h index c287626299b4c..46d2a32f72804 100644 --- a/interpreter/cling/include/cling/Utils/AST.h +++ b/interpreter/cling/include/cling/Utils/AST.h @@ -16,6 +16,7 @@ namespace clang { class ASTContext; + class ClassTemplateSpecializationDecl; class Expr; class Decl; class DeclContext; @@ -301,7 +302,8 @@ namespace utils { /// returned. ///\param[in] Ctx - the ASTContext to be used. clang::QualType GetFullyQualifiedType(clang::QualType QT, - const clang::ASTContext& Ctx); + const clang::ASTContext& Ctx, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Get the fully qualified name for a type. This includes full /// qualification of all template parameters etc. @@ -310,7 +312,8 @@ namespace utils { /// returned. ///\param[in] Ctx - the ASTContext to be used. std::string GetFullyQualifiedName(clang::QualType QT, - const clang::ASTContext &Ctx); + const clang::ASTContext &Ctx, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Create a NestedNameSpecifier for Namesp and its enclosing /// scopes. @@ -332,7 +335,8 @@ namespace utils { /// qualified names. clang::NestedNameSpecifier* CreateNestedNameSpecifier(const clang::ASTContext& Ctx, - const clang::TagDecl *TD, bool FullyQualify); + const clang::TagDecl *TD, bool FullyQualify, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Create a NestedNameSpecifier for TypedefDecl and its enclosing /// scopes. @@ -345,7 +349,8 @@ namespace utils { clang::NestedNameSpecifier* CreateNestedNameSpecifier(const clang::ASTContext& Ctx, const clang::TypedefNameDecl *TD, - bool FullyQualify); + bool FullyQualify, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); ///\brief Create a NestedNameSpecifier for UsingShadowDecl and its enclosing /// scopes. @@ -358,7 +363,8 @@ namespace utils { clang::NestedNameSpecifier* CreateNestedNameSpecifier(const clang::ASTContext& Ctx, const clang::UsingShadowDecl *USD, - bool FullyQualify); + bool FullyQualify, + const clang::ClassTemplateSpecializationDecl* Spec = nullptr); } // end namespace TypeName } // end namespace utils diff --git a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp index 80aba72672e90..7b6519f5f656f 100644 --- a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp +++ b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.cpp @@ -113,7 +113,9 @@ namespace cling { } void ForwardDeclPrinter::Visit(clang::QualType QT) { - QT = utils::TypeName::GetFullyQualifiedType(QT, m_Ctx); + QT = utils::TypeName::GetFullyQualifiedType(QT, m_Ctx, + m_SpecContextStack.empty() ? nullptr + : m_SpecContextStack.top()); Visit(QT.getTypePtr()); } @@ -938,7 +940,9 @@ namespace cling { } if (!ArgQT.isNull()) { QualType ArgFQQT - = utils::TypeName::GetFullyQualifiedType(ArgQT, m_Ctx); + = utils::TypeName::GetFullyQualifiedType(ArgQT, m_Ctx, + m_SpecContextStack.empty() ? nullptr + : m_SpecContextStack.top()); Visit(ArgFQQT); if (m_SkipFlag) { skipDecl(nullptr, "type template param default failed"); @@ -1094,6 +1098,19 @@ namespace cling { // return; // } + struct PopStackRAII{ + std::stack &m_Stk; + PopStackRAII(std::stack &stk, + ClassTemplateSpecializationDecl* D): + m_Stk(stk) + { + m_Stk.push(D); + } + ~PopStackRAII() { + m_Stk.pop(); + } + } popStackRAII(m_SpecContextStack, D); + const TemplateArgumentList& iargs = D->getTemplateInstantiationArgs(); for (const TemplateArgument& TA: iargs.asArray()) { VisitTemplateArgument(TA); diff --git a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h index abe241867bed5..3392e27d01d46 100644 --- a/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h +++ b/interpreter/cling/lib/Interpreter/ForwardDeclPrinter.h @@ -107,6 +107,7 @@ namespace cling { llvm::DenseMap m_Visited; // fwd decl success std::stack m_StreamStack; + std::stack m_SpecContextStack; std::set m_BuiltinNames; IgnoreFilesFunc_t m_IgnoreFile; // Call back to ignore some top level files. diff --git a/interpreter/cling/lib/Utils/AST.cpp b/interpreter/cling/lib/Utils/AST.cpp index ef34d9595c710..d41fc821ed7aa 100644 --- a/interpreter/cling/lib/Utils/AST.cpp +++ b/interpreter/cling/lib/Utils/AST.cpp @@ -48,7 +48,8 @@ namespace utils { QualType QT, const Transform::Config& TypeConfig, bool fullyQualifyType, - bool fullyQualifyTmpltArg); + bool fullyQualifyTmpltArg, + const ClassTemplateSpecializationDecl* Spec); static NestedNameSpecifier* GetPartiallyDesugaredNNS(const ASTContext& Ctx, @@ -58,11 +59,12 @@ namespace utils { static NestedNameSpecifier* CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx, const Decl *decl, - bool FullyQualified); + bool FullyQualified, + const ClassTemplateSpecializationDecl* Spec); static NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx, - NestedNameSpecifier* scope); + NestedNameSpecifier& scope); bool Analyze::IsWrapper(const FunctionDecl* ND) { if (!ND) @@ -196,7 +198,8 @@ namespace utils { } static bool - GetFullyQualifiedTemplateName(const ASTContext& Ctx, TemplateName &tname) { + GetFullyQualifiedTemplateName(const ASTContext& Ctx, TemplateName &tname, + const ClassTemplateSpecializationDecl* Spec) { bool changed = false; NestedNameSpecifier *NNS = nullptr; @@ -206,7 +209,7 @@ namespace utils { if (qtname && !qtname->hasTemplateKeyword()) { NNS = qtname->getQualifier(); - NestedNameSpecifier *qNNS = GetFullyQualifiedNameSpecifier(Ctx,NNS); + NestedNameSpecifier *qNNS = GetFullyQualifiedNameSpecifier(Ctx, *NNS); if (qNNS != NNS) { changed = true; NNS = qNNS; @@ -214,7 +217,7 @@ namespace utils { NNS = nullptr; } } else { - NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true); + NNS = CreateNestedNameSpecifierForScopeOf(Ctx, argtdecl, true, Spec); } if (NNS) { TemplateName UnderlyingTN(argtdecl); @@ -230,6 +233,7 @@ namespace utils { static bool GetFullyQualifiedTemplateArgument(const ASTContext& Ctx, + const ClassTemplateSpecializationDecl* Spec, TemplateArgument &arg) { bool changed = false; @@ -239,14 +243,14 @@ namespace utils { if (arg.getKind() == TemplateArgument::Template) { TemplateName tname = arg.getAsTemplate(); - changed = GetFullyQualifiedTemplateName(Ctx, tname); + changed = GetFullyQualifiedTemplateName(Ctx, tname, Spec); if (changed) { arg = TemplateArgument(tname); } } else if (arg.getKind() == TemplateArgument::Type) { QualType SubTy = arg.getAsType(); // Check if the type needs more desugaring and recurse. - QualType QTFQ = TypeName::GetFullyQualifiedType(SubTy, Ctx); + QualType QTFQ = TypeName::GetFullyQualifiedType(SubTy, Ctx, Spec); if (QTFQ != SubTy) { arg = TemplateArgument(QTFQ); changed = true; @@ -255,7 +259,7 @@ namespace utils { SmallVector desArgs; for (auto I = arg.pack_begin(), E = arg.pack_end(); I != E; ++I) { TemplateArgument pack_arg(*I); - changed = GetFullyQualifiedTemplateArgument(Ctx,pack_arg); + changed = GetFullyQualifiedTemplateArgument(Ctx, Spec, pack_arg); desArgs.push_back(pack_arg); } if (changed) { @@ -271,6 +275,7 @@ namespace utils { static const Type* GetFullyQualifiedLocalType(const ASTContext& Ctx, + const ClassTemplateSpecializationDecl* Spec, const Type *typeptr) { // We really just want to handle the template parameter if any .... // In case of template specializations iterate over the arguments and @@ -284,7 +289,7 @@ namespace utils { // cheap to copy and potentially modified by // GetFullyQualifedTemplateArgument TemplateArgument CopiedArg = Arg; - mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, CopiedArg); + mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx, Spec, CopiedArg); desArgs.push_back(CopiedArg); } @@ -316,7 +321,8 @@ namespace utils { // cheap to copy and potentially modified by // GetFullyQualifedTemplateArgument TemplateArgument arg(templateArgs[I]); - mightHaveChanged |= GetFullyQualifiedTemplateArgument(Ctx,arg); + mightHaveChanged + |= GetFullyQualifiedTemplateArgument(Ctx, Spec, arg); desArgs.push_back(arg); } @@ -336,7 +342,9 @@ namespace utils { static NestedNameSpecifier* CreateOuterNNS(const ASTContext& Ctx, const Decl* D, - bool FullyQualify) { + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) + { const DeclContext* DC = D->getDeclContext(); if (const NamespaceDecl* NS = dyn_cast(DC)) { while (NS && NS->isInline()) { @@ -347,23 +355,24 @@ namespace utils { return TypeName::CreateNestedNameSpecifier(Ctx, NS); return nullptr; // no starting '::', no anonymous } else if (const TagDecl* TD = dyn_cast(DC)) { - return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualify); + return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualify, Spec); } else if (const TypedefNameDecl* TDD = dyn_cast(DC)) { - return TypeName::CreateNestedNameSpecifier(Ctx, TDD, FullyQualify); + return TypeName::CreateNestedNameSpecifier(Ctx, TDD, FullyQualify, Spec); } return nullptr; // no starting '::' } static NestedNameSpecifier* GetFullyQualifiedNameSpecifier(const ASTContext& Ctx, - NestedNameSpecifier* scope) { + NestedNameSpecifier& scope) + { // Return a fully qualified version of this name specifier - if (scope->getKind() == NestedNameSpecifier::Global) { + if (scope.getKind() == NestedNameSpecifier::Global) { // Already fully qualified. - return scope; + return &scope; } - if (const Type *type = scope->getAsType()) { + if (const Type *type = scope.getAsType()) { // Find decl context. const TagDecl* TD = nullptr; if (const TagType* tagdecltype = dyn_cast(type)) { @@ -373,29 +382,32 @@ namespace utils { } if (TD) { return TypeName::CreateNestedNameSpecifier(Ctx, TD, - true /*FullyQualified*/); + true /*FullyQualified*/, + /*Spec*/ nullptr); } else if (const TypedefType* TDD = dyn_cast(type)) { return TypeName::CreateNestedNameSpecifier(Ctx, TDD->getDecl(), - true /*FullyQualified*/); + true /*FullyQualified*/, + /*Spec*/ nullptr); } else if (const UsingType* UT = dyn_cast(type)) { return TypeName::CreateNestedNameSpecifier(Ctx, UT->getFoundDecl(), true /*FullyQualified*/); } - } else if (const NamespaceDecl* NS = scope->getAsNamespace()) { + } else if (const NamespaceDecl* NS = scope.getAsNamespace()) { return TypeName::CreateNestedNameSpecifier(Ctx, NS); - } else if (const NamespaceAliasDecl* alias = scope->getAsNamespaceAlias()) { + } else if (const NamespaceAliasDecl* alias = scope.getAsNamespaceAlias()) { const NamespaceDecl* CanonNS = alias->getNamespace()->getCanonicalDecl(); return TypeName::CreateNestedNameSpecifier(Ctx, CanonNS); } - return scope; + return &scope; } static NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx, const DeclContext *declContext, NestedNameSpecifier *original_prefix, - const Transform::Config& TypeConfig) { + const Transform::Config& TypeConfig) + { // We have to also desugar the prefix. NestedNameSpecifier* prefix = nullptr; if (declContext) { @@ -420,7 +432,8 @@ namespace utils { if (old_ns == new_ns) { // This is the same namespace, use the original prefix // as a starting point. - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + assert(original_prefix && "Must have original_prefix!"); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); } else { prefix = TypeName::CreateNestedNameSpecifier(Ctx, dyn_cast(new_ns)); @@ -442,7 +455,8 @@ namespace utils { const TagDecl *tdecl = dyn_cast(declContext); if (tdecl) { prefix = TypeName::CreateNestedNameSpecifier(Ctx, tdecl, - false /*FullyQualified*/); + false /*FullyQualified*/, + nullptr /*Spec*/); } } } else { @@ -452,12 +466,13 @@ namespace utils { const TagDecl *tdecl = dyn_cast(declContext); if (tdecl) { prefix = TypeName::CreateNestedNameSpecifier(Ctx,tdecl, - false /*FullyQualified*/); + false /*FullyQualified*/, + nullptr /*Spec*/); } } } } else { - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); } return prefix; } @@ -466,7 +481,8 @@ namespace utils { NestedNameSpecifier* SelectPrefix(const ASTContext& Ctx, const ElaboratedType *etype, NestedNameSpecifier *original_prefix, - const Transform::Config& TypeConfig) { + const Transform::Config& TypeConfig) + { // We have to also desugar the prefix. NestedNameSpecifier* prefix = etype->getQualifier(); @@ -512,12 +528,12 @@ namespace utils { if (old_ns == new_ns) { // This is the same namespace, use the original prefix // as a starting point. - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); } else { - prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *prefix); } } else { - prefix = GetFullyQualifiedNameSpecifier(Ctx,prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *prefix); } } } @@ -538,7 +554,8 @@ namespace utils { QualType(scope_type,0), TypeConfig, /*qualifyType=*/false, - /*qualifyTmpltArg=*/true); + /*qualifyTmpltArg=*/true, + nullptr /*Spec*/); NestedNameSpecifier* outer_scope = scope->getPrefix(); const ElaboratedType* etype @@ -596,7 +613,7 @@ namespace utils { false /* template keyword wanted */, desugared.getTypePtr()); } else { - return GetFullyQualifiedNameSpecifier(Ctx,scope); + return GetFullyQualifiedNameSpecifier(Ctx, *scope); } } @@ -841,13 +858,14 @@ namespace utils { static bool GetPartiallyDesugaredTypeImpl(const ASTContext& Ctx, TemplateArgument &arg, const Transform::Config& TypeConfig, - bool fullyQualifyTmpltArg) { + bool fullyQualifyTmpltArg, + const ClassTemplateSpecializationDecl* Spec) { bool changed = false; if (arg.getKind() == TemplateArgument::Template) { TemplateName tname = arg.getAsTemplate(); // Note: should we not also desugar? - changed = GetFullyQualifiedTemplateName(Ctx, tname); + changed = GetFullyQualifiedTemplateName(Ctx, tname, Spec); if (changed) { arg = TemplateArgument(tname); } @@ -864,7 +882,7 @@ namespace utils { QualType PDQT = GetPartiallyDesugaredTypeImpl(Ctx, SubTy, TypeConfig, /*fullyQualifyType=*/true, - /*fullyQualifyTmpltArg=*/true); + /*fullyQualifyTmpltArg=*/true, Spec); arg = TemplateArgument(PDQT); } } else if (arg.getKind() == TemplateArgument::Pack) { @@ -873,7 +891,7 @@ namespace utils { TemplateArgument pack_arg(*I); changed = GetPartiallyDesugaredTypeImpl(Ctx,pack_arg, TypeConfig, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, Spec); desArgs.push_back(pack_arg); } if (changed) { @@ -927,7 +945,8 @@ namespace utils { static QualType GetPartiallyDesugaredTypeImpl(const ASTContext& Ctx, QualType QT, const Transform::Config& TypeConfig, - bool fullyQualifyType, bool fullyQualifyTmpltArg) + bool fullyQualifyType, bool fullyQualifyTmpltArg, + const ClassTemplateSpecializationDecl* Spec) { if (QT.isNull()) return QT; @@ -942,7 +961,8 @@ namespace utils { Qualifiers quals = QT.getQualifiers(); QualType nQT; nQT = GetPartiallyDesugaredTypeImpl(Ctx, QT->getPointeeType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (nQT == QT->getPointeeType()) return QT; QT = Ctx.getPointerType(nQT); @@ -969,7 +989,8 @@ namespace utils { Qualifiers quals = QT.getQualifiers(); QualType nQT; nQT = GetPartiallyDesugaredTypeImpl(Ctx, QT->getPointeeType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (nQT == QT->getPointeeType()) return QT; // Add the r- or l-value reference type back to the desugared one. @@ -993,7 +1014,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == arr->getElementType()) return QT; QT = Ctx.getConstantArrayType (newQT, arr->getSize(), @@ -1006,7 +1028,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == QT) return QT; QT = Ctx.getDependentSizedArrayType (newQT, arr->getSizeExpr(), @@ -1019,7 +1042,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == arr->getElementType()) return QT; QT = Ctx.getIncompleteArrayType (newQT, arr->getSizeModifier(), @@ -1030,7 +1054,8 @@ namespace utils { = dyn_cast(QT.getTypePtr()); QualType newQT = GetPartiallyDesugaredTypeImpl(Ctx,arr->getElementType(), TypeConfig, - fullyQualifyType,fullyQualifyTmpltArg); + fullyQualifyType,fullyQualifyTmpltArg, + Spec); if (newQT == arr->getElementType()) return QT; QT = Ctx.getVariableArrayType (newQT, arr->getSizeExpr(), @@ -1115,7 +1140,8 @@ namespace utils { isa(QT.getTypePtr())) { return GetPartiallyDesugaredTypeImpl(Ctx, QT, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); } NestedNameSpecifier* prefix = nullptr; @@ -1183,7 +1209,7 @@ namespace utils { if (old_ns == new_ns) { // This is the same namespace, use the original prefix // as a starting point. - prefix = GetFullyQualifiedNameSpecifier(Ctx,original_prefix); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *original_prefix); outer = nullptr; // Cancel the later creation. } } @@ -1203,7 +1229,8 @@ namespace utils { TagDecl *tdecl = dyn_cast(outer); if (tdecl) { prefix = TypeName::CreateNestedNameSpecifier(Ctx,tdecl, - false /*FullyQualified*/); + false /*FullyQualified*/, + Spec); prefix = GetPartiallyDesugaredNNS(Ctx,prefix,TypeConfig); } } @@ -1246,7 +1273,8 @@ namespace utils { // So for now just return move on with the least lose we can do return GetPartiallyDesugaredTypeImpl(Ctx, targetType, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); } bool mightHaveChanged = false; @@ -1284,7 +1312,7 @@ namespace utils { if (I->getKind() == TemplateArgument::Template) { TemplateName tname = I->getAsTemplate(); // Note: should we not also desugar? - bool changed = GetFullyQualifiedTemplateName(Ctx, tname); + bool changed = GetFullyQualifiedTemplateName(Ctx, tname, Spec); if (changed) { desArgs.push_back(TemplateArgument(tname)); mightHaveChanged = true; @@ -1308,7 +1336,8 @@ namespace utils { QualType PDQT = GetPartiallyDesugaredTypeImpl(Ctx, SubTy, TypeConfig, fullyQualifyType, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); mightHaveChanged |= (SubTy != PDQT); desArgs.push_back(TemplateArgument(PDQT)); } else { @@ -1344,13 +1373,13 @@ namespace utils { I != E; ++I) { #if 1 - // cheap to copy and potentially modified by // GetPartiallyDesugaredTypeImpl TemplateArgument arg(templateArgs[I]); mightHaveChanged |= GetPartiallyDesugaredTypeImpl(Ctx,arg, TypeConfig, - fullyQualifyTmpltArg); + fullyQualifyTmpltArg, + Spec); desArgs.push_back(arg); #else if (templateArgs[I].getKind() == TemplateArgument::Template) { @@ -1381,7 +1410,8 @@ namespace utils { QualType PDQT = GetPartiallyDesugaredTypeImpl(Ctx, SubTy, TypeConfig, /*fullyQualifyType=*/true, - /*fullyQualifyTmpltArg=*/true); + /*fullyQualifyTmpltArg=*/true, + Spec); desArgs.push_back(TemplateArgument(PDQT)); } else { desArgs.push_back(templateArgs[I]); @@ -1419,7 +1449,8 @@ namespace utils { { return GetPartiallyDesugaredTypeImpl(Ctx,QT,TypeConfig, /*qualifyType*/fullyQualify, - /*qualifyTmpltArg*/fullyQualify); + /*qualifyTmpltArg*/fullyQualify, + nullptr /*Spec*/); } NamespaceDecl* Lookup::Namespace(Sema* S, const char* Name, @@ -1508,55 +1539,63 @@ namespace utils { static NestedNameSpecifier* CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx, const Decl *decl, - bool FullyQualified) + bool FullyQualified, + const ClassTemplateSpecializationDecl* Spec) { // Create a nested name specifier for the declaring context of the type. assert(decl); - const NamedDecl* outer - = llvm::dyn_cast_or_null(decl->getDeclContext()); - const NamespaceDecl* outer_ns - = llvm::dyn_cast_or_null(decl->getDeclContext()); - if (outer && !(outer_ns && outer_ns->isAnonymousNamespace())) { - - if (const CXXRecordDecl *cxxdecl - = llvm::dyn_cast(decl->getDeclContext())) { - - if (ClassTemplateDecl *clTempl = cxxdecl->getDescribedClassTemplate()) { - // We are in the case of a type(def) that was declared in a - // class template but is *not* type dependent. In clang, it gets - // attached to the class template declaration rather than any - // specific class template instantiation. This result in 'odd' - // fully qualified typename: - // vector<_Tp,_Alloc>::size_type - // Make the situation is 'useable' but looking a bit odd by - // picking a random instance as the declaring context. - // FIXME: We should not use the iterators here to check if we are in - // a template specialization. clTempl != cxxdecl already tell us that - // is the case. It seems that we rely on a side-effect from triggering - // deserializations to support 'some' use-case. See ROOT-9709. - if (clTempl->spec_begin() != clTempl->spec_end()) { - decl = *(clTempl->spec_begin()); - outer = llvm::dyn_cast(decl); - outer_ns = llvm::dyn_cast(decl); - } + if (auto outer_ns = llvm::dyn_cast(decl->getDeclContext())) { + if (!outer_ns->isAnonymousNamespace()) + return TypeName::CreateNestedNameSpecifier(Ctx,outer_ns); + return nullptr; + } + + auto outer = llvm::dyn_cast(decl->getDeclContext()); + if (!outer) + return nullptr; + + if (auto cxxdecl = llvm::dyn_cast(outer)) { + if (ClassTemplateDecl *clTempl = cxxdecl->getDescribedClassTemplate()) { + assert(Spec && "Cannot determine type's scope"); + while (Spec && clTempl->getCanonicalDecl() != + Spec->getSpecializedTemplate()->getCanonicalDecl()) { + // This might be due to + // Spec: A::B + // decl: A::type + // Check Spec's scopes to see if any is a match. + Spec = llvm::dyn_cast( + Spec->getDeclContext()); } - } - if (outer_ns) { - return TypeName::CreateNestedNameSpecifier(Ctx,outer_ns); - } else if (const TagDecl* TD = llvm::dyn_cast(outer)) { - return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualified); + // We are in the case of a type(def) that was declared in a + // class template but is *not* type dependent. In clang, it gets + // attached to the class template declaration rather than any + // specific class template instantiation. This result in 'odd' + // fully qualified typename: + // vector<_Tp,_Alloc>::size_type + // Make the situation 'useable' by using the provided specialization + // as the declaring context. + assert(clTempl->getCanonicalDecl() + == Spec->getSpecializedTemplate()->getCanonicalDecl() + && "incompatible context"); + outer = Spec; } } + + if (const TagDecl* TD = llvm::dyn_cast(outer)) { + return TypeName::CreateNestedNameSpecifier(Ctx, TD, FullyQualified, + nullptr /*Spec*/); + } return nullptr; } static NestedNameSpecifier* CreateNestedNameSpecifierForScopeOf(const ASTContext& Ctx, const Type *TypePtr, - bool FullyQualified) + bool FullyQualified, + const ClassTemplateSpecializationDecl* Spec) { // Create a nested name specifier for the declaring context of the type. @@ -1579,7 +1618,7 @@ namespace utils { if (!decl) return nullptr; - return CreateNestedNameSpecifierForScopeOf(Ctx, decl, FullyQualified); + return CreateNestedNameSpecifierForScopeOf(Ctx, decl, FullyQualified, Spec); } NestedNameSpecifier* @@ -1593,16 +1632,19 @@ namespace utils { bool FullyQualified = true; // doesn't matter, DeclContexts are namespaces return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, Namesp, - FullyQualified), + FullyQualified, + nullptr /*Spec*/), Namesp); } NestedNameSpecifier* TypeName::CreateNestedNameSpecifier(const ASTContext& Ctx, const TypedefNameDecl* TD, - bool FullyQualify) { + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) + { return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, TD, - FullyQualify), + FullyQualify, Spec), true /*Template*/, TD->getTypeForDecl()); } @@ -1610,28 +1652,35 @@ namespace utils { NestedNameSpecifier* TypeName::CreateNestedNameSpecifier(const ASTContext& Ctx, const UsingShadowDecl* USD, - bool FullyQualify) { + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) { return NestedNameSpecifier::Create(Ctx, CreateOuterNNS(Ctx, USD, - FullyQualify), + FullyQualify, Spec), true /*Template*/, cast(USD)->getTypeForDecl()); } NestedNameSpecifier* TypeName::CreateNestedNameSpecifier(const ASTContext& Ctx, - const TagDecl *TD, bool FullyQualify) { + const TagDecl *TD, + bool FullyQualify, + const ClassTemplateSpecializationDecl* Spec) + { const Type* Ty = Ctx.getTypeDeclType(TD).getTypePtr(); if (FullyQualify) - Ty = GetFullyQualifiedLocalType(Ctx, Ty); + Ty = GetFullyQualifiedLocalType(Ctx, Spec, Ty); return NestedNameSpecifier::Create(Ctx, - CreateOuterNNS(Ctx, TD, FullyQualify), + CreateOuterNNS(Ctx, TD, FullyQualify, + Spec), false /* template keyword wanted */, Ty); } QualType - TypeName::GetFullyQualifiedType(QualType QT, const ASTContext& Ctx) { + TypeName::GetFullyQualifiedType(QualType QT, const ASTContext& Ctx, + const ClassTemplateSpecializationDecl* Spec) + { // Return the fully qualified type, if we need to recurse through any // template parameter, this needs to be merged somehow with // GetPartialDesugaredType. @@ -1641,7 +1690,7 @@ namespace utils { if (llvm::isa(QT.getTypePtr())) { // Get the qualifiers. Qualifiers quals = QT.getQualifiers(); - QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx); + QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx, Spec); QT = Ctx.getPointerType(QT); // Add back the qualifiers. QT = Ctx.getQualifiedType(QT, quals); @@ -1654,7 +1703,7 @@ namespace utils { // Get the qualifiers. bool isLValueRefTy = llvm::isa(QT.getTypePtr()); Qualifiers quals = QT.getQualifiers(); - QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx); + QT = GetFullyQualifiedType(QT->getPointeeType(), Ctx, Spec); // Add the r- or l-value reference type back to the desugared one. if (isLValueRefTy) QT = Ctx.getLValueReferenceType(QT); @@ -1668,7 +1717,7 @@ namespace utils { // Strip deduced types. if (const AutoType* AutoTy = dyn_cast(QT.getTypePtr())) { if (!AutoTy->getDeducedType().isNull()) - return GetFullyQualifiedType(AutoTy->getDeducedType(), Ctx); + return GetFullyQualifiedType(AutoTy->getDeducedType(), Ctx, Spec); } // Remove the part of the type related to the type being a template @@ -1696,7 +1745,9 @@ namespace utils { const NamespaceDecl *ns = prefix->getAsNamespace(); if (prefix != NestedNameSpecifier::GlobalSpecifier(Ctx) && !(ns && ns->isAnonymousNamespace())) { - prefix = GetFullyQualifiedNameSpecifier(Ctx, prefix); + prefix_qualifiers = QT.getLocalQualifiers(); + prefix = GetFullyQualifiedNameSpecifier(Ctx, *prefix); + QT = QualType(etype_input->getNamedType().getTypePtr(),0); } else { prefix = nullptr; } @@ -1707,7 +1758,8 @@ namespace utils { // Create a nested name specifier if needed (i.e. if the decl context // is not the global scope. prefix = CreateNestedNameSpecifierForScopeOf(Ctx,QT.getTypePtr(), - true /*FullyQualified*/); + true /*FullyQualified*/, + Spec); // move the qualifiers on the outer type (avoid 'std::const string'!) if (prefix) { @@ -1721,7 +1773,8 @@ namespace utils { if(llvm::isa(QT.getTypePtr())) { Qualifiers qualifiers = QT.getLocalQualifiers(); - const Type *TypePtr = GetFullyQualifiedLocalType(Ctx,QT.getTypePtr()); + const Type *TypePtr + = GetFullyQualifiedLocalType(Ctx, Spec, QT.getTypePtr()); QT = Ctx.getQualifiedType(TypePtr, qualifiers); } else if (llvm::isa(QT.getTypePtr())) { @@ -1730,7 +1783,8 @@ namespace utils { // its template argument, however we still need to fully qualify them. Qualifiers qualifiers = QT.getLocalQualifiers(); - const Type *TypePtr = GetFullyQualifiedLocalType(Ctx,QT.getTypePtr()); + const Type *TypePtr + = GetFullyQualifiedLocalType(Ctx, Spec, QT.getTypePtr()); QT = Ctx.getQualifiedType(TypePtr, qualifiers); } @@ -1746,8 +1800,9 @@ namespace utils { } std::string TypeName::GetFullyQualifiedName(QualType QT, - const ASTContext &Ctx) { - QualType FQQT = GetFullyQualifiedType(QT, Ctx); + const ASTContext &Ctx, + const ClassTemplateSpecializationDecl* Spec) { + QualType FQQT = GetFullyQualifiedType(QT, Ctx, Spec); PrintingPolicy Policy(Ctx.getPrintingPolicy()); Policy.SuppressScope = false; Policy.AnonymousTagLocations = false; diff --git a/interpreter/cling/test/AST/typeName.C b/interpreter/cling/test/AST/typeName.C new file mode 100644 index 0000000000000..22f9f5be226ea --- /dev/null +++ b/interpreter/cling/test/AST/typeName.C @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// CLING - the C++ LLVM-based InterpreterG :) +// +// This file is dual-licensed: you can choose to license it under the University +// of Illinois Open Source License or the GNU Lesser General Public License. See +// LICENSE.TXT for details. +//------------------------------------------------------------------------------ + +// RUN: cat %s | %cling -I%p | FileCheck %s + +// This test covers cling::utils::GetFullyQualifiedName() interfaces. +// https://github.com/root-project/root/issues/7955 + + +#include "cling/Interpreter/Interpreter.h" +#include "cling/Interpreter/LookupHelper.h" +#include "cling/Utils/AST.h" + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclTemplate.h" + +#include "llvm/Support/Casting.h" + +.rawInput 1 + +template +struct Template1 { + using type1 = int; + void f0(type1); +}; + +Template1 xf1; + +.rawInput 0 + +const auto& lookup = gCling->getLookupHelper(); +using namespace clang; + +auto *T1s = llvm::dyn_cast(lookup.findScope("Template1", cling::LookupHelper::WithDiagnostics)); +// Find first function decl: +auto iFunc = std::find_if(T1s->decls_begin(), T1s->decls_end(), + [](const Decl* D) {if (auto FD = llvm::dyn_cast(D)) return FD->isUserProvided(); return false;}); +using cling::utils::TypeName::GetFullyQualifiedName; +GetFullyQualifiedName(llvm::dyn_cast(*iFunc)->getParamDecl(0)->getType(), T1s->getASTContext(), T1s) // CHECK: (std::string) "Template1::type1" diff --git a/math/minuit2/src/MnPlot.cxx b/math/minuit2/src/MnPlot.cxx index 9406e480d6c90..fcd728e15dfe6 100644 --- a/math/minuit2/src/MnPlot.cxx +++ b/math/minuit2/src/MnPlot.cxx @@ -9,6 +9,7 @@ #include "Minuit2/MnPlot.h" +#include namespace ROOT { namespace Minuit2 { @@ -22,17 +23,16 @@ void MnPlot::operator()(const std::vector> &points) co x.reserve(points.size()); std::vector y; y.reserve(points.size()); - std::vector chpt; - chpt.reserve(points.size()); + std::string chpt; + chpt.reserve(points.size() + 1); - for (std::vector>::const_iterator ipoint = points.begin(); ipoint != points.end(); - ++ipoint) { - x.push_back((*ipoint).first); - y.push_back((*ipoint).second); - chpt.push_back('*'); + for (auto &ipoint : points) { + x.push_back(ipoint.first); + y.push_back(ipoint.second); + chpt += '*'; } - mnplot(&(x.front()), &(y.front()), &(chpt.front()), points.size(), Width(), Length()); + mnplot(x.data(), y.data(), chpt.data(), points.size(), Width(), Length()); } void MnPlot::operator()(double xmin, double ymin, const std::vector> &points) const @@ -46,19 +46,18 @@ void MnPlot::operator()(double xmin, double ymin, const std::vector chpt; - chpt.reserve(points.size() + 2); - chpt.push_back(' '); - chpt.push_back('X'); + std::string chpt; + chpt.reserve(points.size() + 3); + chpt += ' '; + chpt += 'X'; - for (std::vector>::const_iterator ipoint = points.begin(); ipoint != points.end(); - ++ipoint) { - x.push_back((*ipoint).first); - y.push_back((*ipoint).second); - chpt.push_back('*'); + for (auto &ipoint : points) { + x.push_back(ipoint.first); + y.push_back(ipoint.second); + chpt += '*'; } - mnplot(&(x.front()), &(y.front()), &(chpt.front()), points.size() + 2, Width(), Length()); + mnplot(x.data(), y.data(), chpt.data(), points.size() + 2, Width(), Length()); } } // namespace Minuit2 diff --git a/roofit/roofitcore/CMakeLists.txt b/roofit/roofitcore/CMakeLists.txt index a28fa53f05980..af7c322195902 100644 --- a/roofit/roofitcore/CMakeLists.txt +++ b/roofit/roofitcore/CMakeLists.txt @@ -16,10 +16,6 @@ if(roofit_multiprocess) endif() if(roofit_legacy_eval_backend) - set(LegacyEvalBackendHeaders - RooRealMPFE.h - ) - set(LegacyEvalBackendSources src/BidirMMapPipe.cxx src/BidirMMapPipe.h @@ -239,7 +235,6 @@ ROOT_STANDARD_LIBRARY_PACKAGE(RooFitCore RooWorkspace.h RooWorkspaceHandle.h RooWrapperPdf.h - ${LegacyEvalBackendHeaders} SOURCES src/BatchModeDataHelpers.cxx src/ConstraintHelpers.cxx diff --git a/roofit/roofitcore/inc/LinkDef.h b/roofit/roofitcore/inc/LinkDef.h index da1211cf68fa8..4e643d16459fb 100644 --- a/roofit/roofitcore/inc/LinkDef.h +++ b/roofit/roofitcore/inc/LinkDef.h @@ -171,7 +171,6 @@ #pragma link C++ class RooRealBinding+ ; #pragma link C++ class RooRealConstant+ ; #pragma link C++ class RooRealIntegral+ ; -#pragma link C++ class RooRealMPFE+ ; #pragma link C++ class RooRealProxy+; #pragma read sourceClass="RooRealProxy" targetClass="RooTemplateProxy"; #pragma link C++ class RooTemplateProxy+; diff --git a/roofit/roofitcore/src/RooNLLVar.cxx b/roofit/roofitcore/src/RooNLLVar.cxx index ec5760571359e..1aa6fc2597b59 100644 --- a/roofit/roofitcore/src/RooNLLVar.cxx +++ b/roofit/roofitcore/src/RooNLLVar.cxx @@ -39,7 +39,7 @@ In extended mode, a #include #include #include -#include +#include "RooRealMPFE.h" #include #include diff --git a/roofit/roofitcore/src/RooRealMPFE.cxx b/roofit/roofitcore/src/RooRealMPFE.cxx index 29ed88357d832..daa6ae53cb357 100644 --- a/roofit/roofitcore/src/RooRealMPFE.cxx +++ b/roofit/roofitcore/src/RooRealMPFE.cxx @@ -89,11 +89,9 @@ RooMPSentinel& RooMPSentinel::instance() { } -using std::cout, std::endl, std::string, std::ostringstream, std::list, std::pair; +using std::cout, std::endl, std::string, std::ostringstream, std::list; using namespace RooFit; -ClassImp(RooRealMPFE); - //////////////////////////////////////////////////////////////////////////////// /// Construct front-end object for object 'arg' whose evaluation will be calculated @@ -327,7 +325,7 @@ void RooRealMPFE::serverLoop() printStream(oss2,kName|kClassName|kArgs,kInline); objidstr = oss2.str(); } - std::map > >::const_iterator iter = evalErrorIter(); + std::map > >::const_iterator iter = evalErrorIter(); const RooAbsArg* ptr = nullptr; for (int i = 0; i < numEvalErrorItems(); ++i) { list::const_iterator iter2 = iter->second.second.begin(); diff --git a/roofit/roofitcore/inc/RooRealMPFE.h b/roofit/roofitcore/src/RooRealMPFE.h similarity index 96% rename from roofit/roofitcore/inc/RooRealMPFE.h rename to roofit/roofitcore/src/RooRealMPFE.h index 0252f96280187..dce9429d4586c 100644 --- a/roofit/roofitcore/inc/RooRealMPFE.h +++ b/roofit/roofitcore/src/RooRealMPFE.h @@ -85,8 +85,6 @@ class RooRealMPFE : public RooAbsReal { RooRealMPFE* _updateMaster ; ///SetName(newname); + return out; + } + /// destructor ~HypoTestResult() override; @@ -67,13 +74,13 @@ namespace RooStats { /// familiar name for the Null p-value in terms of 1-sided Gaussian significance virtual double Significance() const {return RooStats::PValueToSignificance( NullPValue() ); } - SamplingDistribution* GetNullDistribution(void) const { return fNullDistr; } - SamplingDistribution* GetAltDistribution(void) const { return fAltDistr; } - RooDataSet* GetNullDetailedOutput(void) const { return fNullDetailedOutput; } - RooDataSet* GetAltDetailedOutput(void) const { return fAltDetailedOutput; } + SamplingDistribution* GetNullDistribution(void) const { return fNullDistr.get(); } + SamplingDistribution* GetAltDistribution(void) const { return fAltDistr.get(); } + RooDataSet* GetNullDetailedOutput(void) const { return fNullDetailedOutput.get(); } + RooDataSet* GetAltDetailedOutput(void) const { return fAltDetailedOutput.get(); } RooDataSet* GetFitInfo() const { return fFitInfo.get(); } double GetTestStatisticData(void) const { return fTestStatisticData; } - const RooArgList* GetAllTestStatisticsData(void) const { return fAllTestStatisticsData; } + const RooArgList* GetAllTestStatisticsData(void) const { return fAllTestStatisticsData.get(); } bool HasTestStatisticData(void) const; void SetNullPValue(double pvalue) { fNullPValue = pvalue; } @@ -82,8 +89,8 @@ namespace RooStats { void SetAltPValueError(double err) { fAlternatePValueError = err; } void SetAltDistribution(SamplingDistribution *alt); void SetNullDistribution(SamplingDistribution *null); - void SetAltDetailedOutput(RooDataSet* d) { fAltDetailedOutput = d; } - void SetNullDetailedOutput(RooDataSet* d) { fNullDetailedOutput = d; } + void SetAltDetailedOutput(RooDataSet* d) { fAltDetailedOutput.reset(d); } + void SetNullDetailedOutput(RooDataSet* d) { fNullDetailedOutput.reset(d); } void SetFitInfo(RooDataSet* d) { fFitInfo.reset(d); } void SetTestStatisticData(const double tsd); void SetAllTestStatisticsData(const RooArgList* tsd); @@ -123,16 +130,16 @@ namespace RooStats { mutable double fNullPValueError; ///< error of p-value for the null hypothesis (small number means disfavoured) mutable double fAlternatePValueError; ///< error of p-value for the alternate hypothesis (small number means disfavoured) double fTestStatisticData; ///< result of the test statistic evaluated on data - const RooArgList* fAllTestStatisticsData; ///< for the case of multiple test statistics, holds all the results - SamplingDistribution *fNullDistr; - SamplingDistribution *fAltDistr; - RooDataSet* fNullDetailedOutput; - RooDataSet* fAltDetailedOutput; + std::unique_ptr fAllTestStatisticsData; ///< for the case of multiple test statistics, holds all the results + std::unique_ptr fNullDistr; + std::unique_ptr fAltDistr; + std::unique_ptr fNullDetailedOutput; + std::unique_ptr fAltDetailedOutput; std::unique_ptr fFitInfo; bool fPValueIsRightTail; bool fBackgroundIsAlt; - ClassDefOverride(HypoTestResult,4) // Base class to represent results of a hypothesis test + ClassDefOverride(HypoTestResult,5) // Base class to represent results of a hypothesis test }; } diff --git a/roofit/roostats/inc/RooStats/RooStatsUtils.h b/roofit/roostats/inc/RooStats/RooStatsUtils.h index ddf553a636d90..547fdac87ab87 100644 --- a/roofit/roostats/inc/RooStats/RooStatsUtils.h +++ b/roofit/roostats/inc/RooStats/RooStatsUtils.h @@ -139,8 +139,8 @@ namespace RooStats { bool IsNLLOffset(); /// function that clones a workspace, copying all needed components and discarding all others - RooWorkspace* MakeCleanWorkspace(RooWorkspace *oldWS, const char *newName, bool copySnapshots, - const char *mcname, const char *newmcname); + RooWorkspace* MakeReducedWorkspace(RooWorkspace *oldWS, const char *newName, bool copySnapshots, + const char *mcname, const char *newmcname, bool copyData=true); } diff --git a/roofit/roostats/src/HypoTestResult.cxx b/roofit/roostats/src/HypoTestResult.cxx index 7c254c6983c03..1058542d89d0b 100644 --- a/roofit/roostats/src/HypoTestResult.cxx +++ b/roofit/roostats/src/HypoTestResult.cxx @@ -127,13 +127,7 @@ HypoTestResult::HypoTestResult(const HypoTestResult& other) : HypoTestResult::~HypoTestResult() { - if( fNullDistr ) delete fNullDistr; - if( fAltDistr ) delete fAltDistr; - if( fNullDetailedOutput ) delete fNullDetailedOutput; - if( fAltDetailedOutput ) delete fAltDetailedOutput; - - if( fAllTestStatisticsData ) delete fAllTestStatisticsData; } //////////////////////////////////////////////////////////////////////////////// @@ -149,12 +143,13 @@ HypoTestResult & HypoTestResult::operator=(const HypoTestResult& other) { fAlternatePValueError = other.fAlternatePValueError; fTestStatisticData = other.fTestStatisticData; - if( fAllTestStatisticsData ) delete fAllTestStatisticsData; + fAllTestStatisticsData = nullptr; - if( fNullDistr ) { delete fNullDistr; fNullDistr = nullptr; } - if( fAltDistr ) { delete fAltDistr; fAltDistr = nullptr; } - if( fNullDetailedOutput ) { delete fNullDetailedOutput; fNullDetailedOutput = nullptr; } - if( fAltDetailedOutput ) { delete fAltDetailedOutput; fAltDetailedOutput = nullptr; } + fNullDistr = nullptr; + fAltDistr = nullptr; + fNullDetailedOutput = nullptr; + fAltDetailedOutput = nullptr; + fFitInfo = nullptr; fPValueIsRightTail = other.GetPValueIsRightTail(); @@ -174,25 +169,25 @@ void HypoTestResult::Append(const HypoTestResult* other) { if (fNullDistr) { fNullDistr->Add(other->GetNullDistribution()); } else if (other->GetNullDistribution()) { - fNullDistr = new SamplingDistribution(*other->GetNullDistribution()); + fNullDistr = std::make_unique(*other->GetNullDistribution()); } if (fAltDistr) { fAltDistr->Add(other->GetAltDistribution()); } else if (other->GetAltDistribution()) { - fAltDistr = new SamplingDistribution(*other->GetAltDistribution()); + fAltDistr = std::make_unique(*other->GetAltDistribution()); } if( fNullDetailedOutput ) { if( other->GetNullDetailedOutput() ) fNullDetailedOutput->append( *other->GetNullDetailedOutput() ); }else{ - if( other->GetNullDetailedOutput() ) fNullDetailedOutput = new RooDataSet( *other->GetNullDetailedOutput() ); + if( other->GetNullDetailedOutput() ) fNullDetailedOutput = std::make_unique( *other->GetNullDetailedOutput() ); } if( fAltDetailedOutput ) { if( other->GetAltDetailedOutput() ) fAltDetailedOutput->append( *other->GetAltDetailedOutput() ); }else{ - if( other->GetAltDetailedOutput() ) fAltDetailedOutput = new RooDataSet( *other->GetAltDetailedOutput() ); + if( other->GetAltDetailedOutput() ) fAltDetailedOutput = std::make_unique( *other->GetAltDetailedOutput() ); } if( fFitInfo ) { @@ -204,22 +199,22 @@ void HypoTestResult::Append(const HypoTestResult* other) { // if no data is present use the other HypoTestResult's data if(IsNaN(fTestStatisticData)) fTestStatisticData = other->GetTestStatisticData(); - UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, true); - UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, false); + UpdatePValue(fNullDistr.get(), fNullPValue, fNullPValueError, true); + UpdatePValue(fAltDistr.get(), fAlternatePValue, fAlternatePValueError, false); } //////////////////////////////////////////////////////////////////////////////// void HypoTestResult::SetAltDistribution(SamplingDistribution *alt) { - fAltDistr = alt; - UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, false); + fAltDistr.reset(alt); + UpdatePValue(fAltDistr.get(), fAlternatePValue, fAlternatePValueError, false); } //////////////////////////////////////////////////////////////////////////////// void HypoTestResult::SetNullDistribution(SamplingDistribution *null) { - fNullDistr = null; - UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, true); + fNullDistr.reset(null); + UpdatePValue(fNullDistr.get(), fNullPValue, fNullPValueError, true); } //////////////////////////////////////////////////////////////////////////////// @@ -227,18 +222,14 @@ void HypoTestResult::SetNullDistribution(SamplingDistribution *null) { void HypoTestResult::SetTestStatisticData(const double tsd) { fTestStatisticData = tsd; - UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, true); - UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, false); + UpdatePValue(fNullDistr.get(), fNullPValue, fNullPValueError, true); + UpdatePValue(fAltDistr.get(), fAlternatePValue, fAlternatePValueError, false); } //////////////////////////////////////////////////////////////////////////////// void HypoTestResult::SetAllTestStatisticsData(const RooArgList* tsd) { - if (fAllTestStatisticsData) { - delete fAllTestStatisticsData; - fAllTestStatisticsData = nullptr; - } - if (tsd) fAllTestStatisticsData = static_cast(tsd->snapshot()); + if (tsd) fAllTestStatisticsData.reset(static_cast(tsd->snapshot())); if( fAllTestStatisticsData && !fAllTestStatisticsData->empty() ) { RooRealVar* firstTS = static_cast(fAllTestStatisticsData->at(0)); @@ -251,8 +242,8 @@ void HypoTestResult::SetAllTestStatisticsData(const RooArgList* tsd) { void HypoTestResult::SetPValueIsRightTail(bool pr) { fPValueIsRightTail = pr; - UpdatePValue(fNullDistr, fNullPValue, fNullPValueError, true); - UpdatePValue(fAltDistr, fAlternatePValue, fAlternatePValueError, false); + UpdatePValue(fNullDistr.get(), fNullPValue, fNullPValueError, true); + UpdatePValue(fAltDistr.get(), fAlternatePValue, fAlternatePValueError, false); } //////////////////////////////////////////////////////////////////////////////// diff --git a/roofit/roostats/src/RooStatsUtils.cxx b/roofit/roostats/src/RooStatsUtils.cxx index 927264d707439..ccd8c1012b283 100644 --- a/roofit/roostats/src/RooStatsUtils.cxx +++ b/roofit/roostats/src/RooStatsUtils.cxx @@ -344,13 +344,12 @@ namespace RooStats { // clone a workspace, copying all needed components and discarding all others // start off with the old workspace - RooWorkspace* MakeCleanWorkspace(RooWorkspace *oldWS, const char *newName, + RooWorkspace* MakeReducedWorkspace(RooWorkspace *oldWS, const char *newName, bool copySnapshots, const char *mcname, - const char *newmcname) { + const char *newmcname, bool copyData) { auto objects = oldWS->allGenericObjects(); RooStats::ModelConfig *oldMC = dynamic_cast(oldWS->obj(mcname)); - auto data = oldWS->allData(); for (auto it : objects) { if (!oldMC) { oldMC = dynamic_cast(it); @@ -398,8 +397,10 @@ namespace RooStats { RooAbsPdf *newPdf = newWS->pdf(pdf->GetName()); newMC->SetPdf(*newPdf); - for (auto d : data) { - newWS->import(*d); + if (copyData) { + for (auto d : oldWS->allData()) { + newWS->import(*d); + } } RooArgSet poiset; diff --git a/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx b/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx index 7ab241761bc63..5e2314fa6469a 100644 --- a/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx +++ b/tree/dataframe/inc/ROOT/RDF/InterfaceUtils.hxx @@ -266,10 +266,10 @@ BuildAction(const ColumnNames_t &colNames, const std::shared_ptrfOptions; auto makeIsDefine = [&] { - std::vector isDef; - isDef.reserve(sizeof...(ColTypes)); - for (auto i = 0u; i < sizeof...(ColTypes); ++i) - isDef.push_back(colRegister.IsDefineOrAlias(colNames[i])); + auto sz = sizeof...(ColTypes); + std::vector isDef(sz); + for (auto i = 0u; i < sz; ++i) + isDef[i] = colRegister.IsDefineOrAlias(colNames[i]); return isDef; }; std::vector isDefine = makeIsDefine(); diff --git a/tree/dataframe/inc/ROOT/RDF/RTreeColumnReader.hxx b/tree/dataframe/inc/ROOT/RDF/RTreeColumnReader.hxx index 37468874eae4c..3421f3adb80a5 100644 --- a/tree/dataframe/inc/ROOT/RDF/RTreeColumnReader.hxx +++ b/tree/dataframe/inc/ROOT/RDF/RTreeColumnReader.hxx @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -165,6 +166,41 @@ public: } }; +/// RTreeColumnReader specialization for TTree values read via TTreeReaderArrays. +/// +/// This specialization is used when the requested type for reading is std::array +template +class R__CLING_PTRCHECK(off) RTreeColumnReader> final : public ROOT::Detail::RDF::RColumnReaderBase { + std::unique_ptr> fTreeArray; + + /// We return a reference to this RVec to clients, to guarantee a stable address and contiguous memory layout + RVec fArray; + + Long64_t fLastEntry = -1; + + void *GetImpl(Long64_t entry) final + { + if (entry == fLastEntry) + return fArray.data(); + + // This is a non-owning view on the contents of the TTreeReaderArray + RVec view{&fTreeArray->At(0), fTreeArray->GetSize()}; + swap(fArray, view); + + fLastEntry = entry; + // The data member of this class is an RVec, to avoid an extra copy + // but we need to return the array buffer as the reader expects + // a std::array + return fArray.data(); + } + +public: + RTreeColumnReader(TTreeReader &r, const std::string &colName) + : fTreeArray(std::make_unique>(r, colName.c_str())) + { + } +}; + } // namespace RDF } // namespace Internal } // namespace ROOT diff --git a/tree/dataframe/test/dataframe_simple.cxx b/tree/dataframe/test/dataframe_simple.cxx index d318c7ed747a3..8da6471d9a570 100644 --- a/tree/dataframe/test/dataframe_simple.cxx +++ b/tree/dataframe/test/dataframe_simple.cxx @@ -1041,6 +1041,41 @@ TEST_P(RDFSimpleTests, FillWithCustomClassJitted) EXPECT_EQ(h.GetEntries(), 10); } +TEST_P(RDFSimpleTests, ReadStdArray) +{ + struct ArrayDataset { + std::string treename{"rdf_simple_array_dataset"}; + std::string filename{"rdf_simple_array_dataset.root"}; + std::string branchname{"arr"}; + std::array arr{11, 22, 33, 44, 55, 66}; + ArrayDataset() + { + TFile f{filename.c_str(), "recreate"}; + TTree t{treename.c_str(), treename.c_str()}; + t.Branch(branchname.c_str(), &arr); + t.Fill(); + f.WriteObject(&t, treename.c_str()); + } + ~ArrayDataset() { std::remove(filename.c_str()); } + } dataset; + + ROOT::RDataFrame df{dataset.treename, dataset.filename}; + + auto col = df.Take>(dataset.branchname); + EXPECT_EQ(col->size(), 1); + std::array value = col->at(0); + for (unsigned i = 0; i < 6; i++) { + EXPECT_EQ(value[i], dataset.arr[i]); + } + + auto colrvec = df.Take>(dataset.branchname); + EXPECT_EQ(colrvec->size(), 1); + ROOT::RVec valuervec = colrvec->at(0); + for (unsigned i = 0; i < 6; i++) { + EXPECT_EQ(valuervec[i], dataset.arr[i]); + } +} + // run single-thread tests INSTANTIATE_TEST_SUITE_P(Seq, RDFSimpleTests, ::testing::Values(false)); diff --git a/tree/ntuple/CMakeLists.txt b/tree/ntuple/CMakeLists.txt index b9f2cbbcdde89..ea5784fc5e50b 100644 --- a/tree/ntuple/CMakeLists.txt +++ b/tree/ntuple/CMakeLists.txt @@ -18,7 +18,7 @@ HEADERS ROOT/RCluster.hxx ROOT/RClusterPool.hxx ROOT/RColumn.hxx - ROOT/RColumnElement.hxx + ROOT/RColumnElementBase.hxx ROOT/REntry.hxx ROOT/RField.hxx ROOT/RFieldBase.hxx diff --git a/tree/ntuple/v7/inc/ROOT/RColumn.hxx b/tree/ntuple/v7/inc/ROOT/RColumn.hxx index 08b9db9544555..697216bf83e9b 100644 --- a/tree/ntuple/v7/inc/ROOT/RColumn.hxx +++ b/tree/ntuple/v7/inc/ROOT/RColumn.hxx @@ -17,7 +17,7 @@ #define ROOT7_RColumn #include // for R__likely -#include +#include #include #include #include @@ -265,8 +265,7 @@ public: // +1 to go from 0-based indexing to 1-based number of items nItems = fReadPageRef.Get().GetGlobalRangeLast() - globalIndex + 1; return reinterpret_cast(static_cast(fReadPageRef.Get().GetBuffer()) + - (globalIndex - fReadPageRef.Get().GetGlobalRangeFirst()) * - RColumnElement::kSize); + (globalIndex - fReadPageRef.Get().GetGlobalRangeFirst()) * sizeof(CppT)); } template @@ -279,7 +278,7 @@ public: nItems = fReadPageRef.Get().GetClusterRangeLast() - clusterIndex.GetIndex() + 1; return reinterpret_cast(static_cast(fReadPageRef.Get().GetBuffer()) + (clusterIndex.GetIndex() - fReadPageRef.Get().GetClusterRangeFirst()) * - RColumnElement::kSize); + sizeof(CppT)); } NTupleSize_t GetGlobalIndex(RClusterIndex clusterIndex) diff --git a/tree/ntuple/v7/inc/ROOT/RColumnElementBase.hxx b/tree/ntuple/v7/inc/ROOT/RColumnElementBase.hxx new file mode 100644 index 0000000000000..2267c00af7796 --- /dev/null +++ b/tree/ntuple/v7/inc/ROOT/RColumnElementBase.hxx @@ -0,0 +1,166 @@ +/// \file ROOT/RColumnElementBase.hxx +/// \ingroup NTuple ROOT7 +/// \author Jakob Blomer +/// \date 2018-10-09 +/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback +/// is welcome! + +/************************************************************************* + * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifndef ROOT7_RColumnElementBase +#define ROOT7_RColumnElementBase + +#include "RtypesCore.h" +#include +#include +#include + +#include +#include + +#include // for memcpy +#include // for std::byte +#include +#include +#include +#include +#include +#include + +namespace ROOT::Experimental::Internal { + +// clang-format off +/** +\class ROOT::Experimental::Internal::RColumnElementBase +\ingroup NTuple +\brief A column element encapsulates the translation between basic C++ types and their column representation. + +Usually the on-disk element should map bitwise to the in-memory element. Sometimes that's not the case +though, for instance on big endian platforms or for bools. + +There is a template specialization for every valid pair of C++ type and column representation. +These specialized child classes are responsible for overriding `Pack()` / `Unpack()` for packing / unpacking elements +as appropriate. +*/ +// clang-format on +class RColumnElementBase { +protected: + /// Size of the C++ value that corresponds to the on-disk element + std::size_t fSize; + std::size_t fBitsOnStorage; + + explicit RColumnElementBase(std::size_t size, std::size_t bitsOnStorage = 0) + : fSize(size), fBitsOnStorage(bitsOnStorage ? bitsOnStorage : 8 * size) + { + } + +public: + RColumnElementBase(const RColumnElementBase &other) = default; + RColumnElementBase(RColumnElementBase &&other) = default; + RColumnElementBase &operator=(const RColumnElementBase &other) = delete; + RColumnElementBase &operator=(RColumnElementBase &&other) = default; + virtual ~RColumnElementBase() = default; + + /// If CppT == void, use the default C++ type for the given column type + template + static std::unique_ptr Generate(EColumnType type); + static std::string GetTypeName(EColumnType type); + /// Most types have a fixed on-disk bit width. Some low-precision column types + /// have a range of possible bit widths. Return the minimum and maximum allowed + /// bit size per type. + static std::pair GetValidBitRange(EColumnType type); + + /// Derived, typed classes tell whether the on-storage layout is bitwise identical to the memory layout + virtual bool IsMappable() const + { + R__ASSERT(false); + return false; + } + + /// If the on-storage layout and the in-memory layout differ, packing creates an on-disk page from an in-memory page + virtual void Pack(void *destination, const void *source, std::size_t count) const + { + std::memcpy(destination, source, count); + } + + /// If the on-storage layout and the in-memory layout differ, unpacking creates a memory page from an on-storage page + virtual void Unpack(void *destination, const void *source, std::size_t count) const + { + std::memcpy(destination, source, count); + } + + std::size_t GetSize() const { return fSize; } + std::size_t GetBitsOnStorage() const { return fBitsOnStorage; } + std::size_t GetPackedSize(std::size_t nElements = 1U) const { return (nElements * fBitsOnStorage + 7) / 8; } +}; // class RColumnElementBase + +// All supported C++ in-memory types +enum class EColumnCppType { + kChar, + kBool, + kByte, + kUint8, + kUint16, + kUint32, + kUint64, + kInt8, + kInt16, + kInt32, + kInt64, + kFloat, + kDouble, + kClusterSize, + kColumnSwitch, +}; + +std::unique_ptr GenerateColumnElement(EColumnCppType cppType, EColumnType colType); + +template +std::unique_ptr RColumnElementBase::Generate(EColumnType type) +{ + if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kChar, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kBool, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kByte, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kUint8, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kUint16, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kUint32, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kUint64, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kInt8, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kInt16, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kInt32, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kInt64, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kFloat, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kDouble, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kClusterSize, type); + else if constexpr (std::is_same_v) + return GenerateColumnElement(EColumnCppType::kColumnSwitch, type); + else + static_assert(!sizeof(CppT), "Unsupported Cpp type"); +} + +template <> +std::unique_ptr RColumnElementBase::Generate(EColumnType type); + +} // namespace ROOT::Experimental::Internal + +#endif diff --git a/tree/ntuple/v7/inc/ROOT/RMiniFile.hxx b/tree/ntuple/v7/inc/ROOT/RMiniFile.hxx index f8e5b8c64f69e..0db3701a313ef 100644 --- a/tree/ntuple/v7/inc/ROOT/RMiniFile.hxx +++ b/tree/ntuple/v7/inc/ROOT/RMiniFile.hxx @@ -18,6 +18,8 @@ #include #include +#include +#include #include #include @@ -28,6 +30,7 @@ class TCollection; class TFile; class TFileMergeInfo; +class TVirtualStreamerInfo; namespace ROOT { @@ -172,6 +175,9 @@ private: std::string fFileName; /// Header and footer location of the ntuple, written on Commit() RNTuple fNTupleAnchor; + /// Set of streamer info records that should be written to the file. + /// The RNTuple class description is always present. + RNTupleSerializer::StreamerInfoMap_t fStreamerInfoMap; explicit RNTupleFileWriter(std::string_view name, std::uint64_t maxKeySize); @@ -220,6 +226,8 @@ public: /// Write into a reserved record; the caller is responsible for making sure that the written byte range is in the /// previously reserved key. void WriteIntoReservedBlob(const void *buffer, size_t nbytes, std::int64_t offset); + /// Ensures that the pass streamer info is written to the file + void UpdateStreamerInfos(const RNTupleSerializer::StreamerInfoMap_t &streamerInfos); /// Writes the RNTuple key to the file so that the header and footer keys can be found void Commit(); }; diff --git a/tree/ntuple/v7/inc/ROOT/RNTupleDescriptor.hxx b/tree/ntuple/v7/inc/ROOT/RNTupleDescriptor.hxx index 9a85abb5e9d58..2a8523581d9b4 100644 --- a/tree/ntuple/v7/inc/ROOT/RNTupleDescriptor.hxx +++ b/tree/ntuple/v7/inc/ROOT/RNTupleDescriptor.hxx @@ -137,6 +137,9 @@ public: std::uint32_t GetColumnCardinality() const { return fColumnCardinality; } std::optional GetTypeChecksum() const { return fTypeChecksum; } bool IsProjectedField() const { return fProjectionSourceId != kInvalidDescriptorId; } + /// Tells if the field describes a user-defined class with a dictionary. + /// The dictionary does not need to be available for this method. + bool IsCustomClass() const; }; // clang-format off @@ -1431,6 +1434,9 @@ public: /// Mark the beginning of the header extension; any fields and columns added after a call to this function are /// annotated as begin part of the header extension. void BeginHeaderExtension(); + + /// Get the streamer info records for custom classes. Currently requires the corresponding dictionaires to be loaded. + RNTupleSerializer::StreamerInfoMap_t BuildStreamerInfos() const; }; } // namespace Internal diff --git a/tree/ntuple/v7/inc/ROOT/RNTupleSerialize.hxx b/tree/ntuple/v7/inc/ROOT/RNTupleSerialize.hxx index dd968e6c37f88..67380b6d90c28 100644 --- a/tree/ntuple/v7/inc/ROOT/RNTupleSerialize.hxx +++ b/tree/ntuple/v7/inc/ROOT/RNTupleSerialize.hxx @@ -76,8 +76,8 @@ public: static constexpr int64_t kSuppressedColumnMarker = std::numeric_limits::min(); // In the page sink and the unsplit field, the seen streamer infos are stored in a map - // with the unique streamer info number being the key. - using StreamerInfoMap_t = std::unordered_map; + // with the unique streamer info number being the key. Sorted by unique number. + using StreamerInfoMap_t = std::map; struct REnvelopeLink { std::uint64_t fLength = 0; diff --git a/tree/ntuple/v7/inc/ROOT/RNTupleWriteOptions.hxx b/tree/ntuple/v7/inc/ROOT/RNTupleWriteOptions.hxx index ab212590b721b..e1b212453c8df 100644 --- a/tree/ntuple/v7/inc/ROOT/RNTupleWriteOptions.hxx +++ b/tree/ntuple/v7/inc/ROOT/RNTupleWriteOptions.hxx @@ -81,9 +81,6 @@ protected: bool fUseDirectIO = false; /// Whether to use implicit multi-threading to compress pages. Only has an effect if buffered writing is turned on. EImplicitMT fUseImplicitMT = EImplicitMT::kDefault; - /// If set, 64bit index columns are replaced by 32bit index columns. This limits the cluster size to 512MB - /// but it can result in smaller file sizes for data sets with many collections and lz4 or no compression. - bool fHasSmallClusters = false; /// If set, checksums will be calculated and written for every page. bool fEnablePageChecksums = true; /// Specifies the max size of a payload storeable into a single TKey. When writing an RNTuple to a ROOT file, @@ -127,9 +124,6 @@ public: EImplicitMT GetUseImplicitMT() const { return fUseImplicitMT; } void SetUseImplicitMT(EImplicitMT val) { fUseImplicitMT = val; } - bool GetHasSmallClusters() const { return fHasSmallClusters; } - void SetHasSmallClusters(bool val) { fHasSmallClusters = val; } - bool GetEnablePageChecksums() const { return fEnablePageChecksums; } /// Note that turning off page checksums will also turn off the same page merging optimization (see tuning.md) void SetEnablePageChecksums(bool val) { fEnablePageChecksums = val; } diff --git a/tree/ntuple/v7/src/RColumnElement.cxx b/tree/ntuple/v7/src/RColumnElement.cxx index 45e4abc566bc2..530e10c3b1f70 100644 --- a/tree/ntuple/v7/src/RColumnElement.cxx +++ b/tree/ntuple/v7/src/RColumnElement.cxx @@ -13,7 +13,10 @@ * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ -#include +#include "ROOT/RColumn.hxx" +#include + +#include "RColumnElement.hxx" #include #include @@ -22,47 +25,6 @@ #include #include -template <> -std::unique_ptr -ROOT::Experimental::Internal::RColumnElementBase::Generate(EColumnType type) -{ - switch (type) { - case EColumnType::kIndex64: return std::make_unique>(); - case EColumnType::kIndex32: return std::make_unique>(); - case EColumnType::kSwitch: return std::make_unique>(); - case EColumnType::kByte: return std::make_unique>(); - case EColumnType::kChar: return std::make_unique>(); - case EColumnType::kBit: return std::make_unique>(); - case EColumnType::kReal64: return std::make_unique>(); - case EColumnType::kReal32: return std::make_unique>(); - // TODO: Change to std::float16_t in-memory type once available (from C++23). - case EColumnType::kReal16: return std::make_unique>(); - case EColumnType::kInt64: return std::make_unique>(); - case EColumnType::kUInt64: return std::make_unique>(); - case EColumnType::kInt32: return std::make_unique>(); - case EColumnType::kUInt32: return std::make_unique>(); - case EColumnType::kInt16: return std::make_unique>(); - case EColumnType::kUInt16: return std::make_unique>(); - case EColumnType::kInt8: return std::make_unique>(); - case EColumnType::kUInt8: return std::make_unique>(); - case EColumnType::kSplitIndex64: - return std::make_unique>(); - case EColumnType::kSplitIndex32: - return std::make_unique>(); - case EColumnType::kSplitReal64: return std::make_unique>(); - case EColumnType::kSplitReal32: return std::make_unique>(); - case EColumnType::kSplitInt64: return std::make_unique>(); - case EColumnType::kSplitUInt64: return std::make_unique>(); - case EColumnType::kSplitInt32: return std::make_unique>(); - case EColumnType::kSplitUInt32: return std::make_unique>(); - case EColumnType::kSplitInt16: return std::make_unique>(); - case EColumnType::kSplitUInt16: return std::make_unique>(); - default: assert(false); - } - // never here - return nullptr; -} - std::pair ROOT::Experimental::Internal::RColumnElementBase::GetValidBitRange(EColumnType type) { @@ -134,36 +96,68 @@ std::string ROOT::Experimental::Internal::RColumnElementBase::GetTypeName(EColum } } -void ROOT::Experimental::Internal::RColumnElement::Pack( - void *dst, const void *src, std::size_t count) const +template <> +std::unique_ptr +ROOT::Experimental::Internal::RColumnElementBase::Generate(EColumnType type) { - const bool *boolArray = reinterpret_cast(src); - char *charArray = reinterpret_cast(dst); - std::bitset<8> bitSet; - std::size_t i = 0; - for (; i < count; ++i) { - bitSet.set(i % 8, boolArray[i]); - if (i % 8 == 7) { - char packed = bitSet.to_ulong(); - charArray[i / 8] = packed; - } - } - if (i % 8 != 0) { - char packed = bitSet.to_ulong(); - charArray[i / 8] = packed; + switch (type) { + case EColumnType::kIndex64: return std::make_unique>(); + case EColumnType::kIndex32: return std::make_unique>(); + case EColumnType::kSwitch: return std::make_unique>(); + case EColumnType::kByte: return std::make_unique>(); + case EColumnType::kChar: return std::make_unique>(); + case EColumnType::kBit: return std::make_unique>(); + case EColumnType::kReal64: return std::make_unique>(); + case EColumnType::kReal32: return std::make_unique>(); + // TODO: Change to std::float16_t in-memory type once available (from C++23). + case EColumnType::kReal16: return std::make_unique>(); + case EColumnType::kInt64: return std::make_unique>(); + case EColumnType::kUInt64: return std::make_unique>(); + case EColumnType::kInt32: return std::make_unique>(); + case EColumnType::kUInt32: return std::make_unique>(); + case EColumnType::kInt16: return std::make_unique>(); + case EColumnType::kUInt16: return std::make_unique>(); + case EColumnType::kInt8: return std::make_unique>(); + case EColumnType::kUInt8: return std::make_unique>(); + case EColumnType::kSplitIndex64: + return std::make_unique>(); + case EColumnType::kSplitIndex32: + return std::make_unique>(); + case EColumnType::kSplitReal64: return std::make_unique>(); + case EColumnType::kSplitReal32: return std::make_unique>(); + case EColumnType::kSplitInt64: return std::make_unique>(); + case EColumnType::kSplitUInt64: return std::make_unique>(); + case EColumnType::kSplitInt32: return std::make_unique>(); + case EColumnType::kSplitUInt32: return std::make_unique>(); + case EColumnType::kSplitInt16: return std::make_unique>(); + case EColumnType::kSplitUInt16: return std::make_unique>(); + default: assert(false); } + // never here + return nullptr; } -void ROOT::Experimental::Internal::RColumnElement::Unpack( - void *dst, const void *src, std::size_t count) const +std::unique_ptr +ROOT::Experimental::Internal::GenerateColumnElement(EColumnCppType cppType, EColumnType type) { - bool *boolArray = reinterpret_cast(dst); - const char *charArray = reinterpret_cast(src); - std::bitset<8> bitSet; - for (std::size_t i = 0; i < count; i += 8) { - bitSet = charArray[i / 8]; - for (std::size_t j = i; j < std::min(count, i + 8); ++j) { - boolArray[j] = bitSet[j % 8]; - } + switch (cppType) { + case EColumnCppType::kChar: return GenerateColumnElementInternal(type); + case EColumnCppType::kBool: return GenerateColumnElementInternal(type); + case EColumnCppType::kByte: return GenerateColumnElementInternal(type); + case EColumnCppType::kUint8: return GenerateColumnElementInternal(type); + case EColumnCppType::kUint16: return GenerateColumnElementInternal(type); + case EColumnCppType::kUint32: return GenerateColumnElementInternal(type); + case EColumnCppType::kUint64: return GenerateColumnElementInternal(type); + case EColumnCppType::kInt8: return GenerateColumnElementInternal(type); + case EColumnCppType::kInt16: return GenerateColumnElementInternal(type); + case EColumnCppType::kInt32: return GenerateColumnElementInternal(type); + case EColumnCppType::kInt64: return GenerateColumnElementInternal(type); + case EColumnCppType::kFloat: return GenerateColumnElementInternal(type); + case EColumnCppType::kDouble: return GenerateColumnElementInternal(type); + case EColumnCppType::kClusterSize: return GenerateColumnElementInternal(type); + case EColumnCppType::kColumnSwitch: return GenerateColumnElementInternal(type); + default: R__ASSERT(!"Invalid column cpp type"); } + // never here + return nullptr; } diff --git a/tree/ntuple/v7/inc/ROOT/RColumnElement.hxx b/tree/ntuple/v7/src/RColumnElement.hxx similarity index 83% rename from tree/ntuple/v7/inc/ROOT/RColumnElement.hxx rename to tree/ntuple/v7/src/RColumnElement.hxx index ef1b67ce5ac95..157406b3d6640 100644 --- a/tree/ntuple/v7/inc/ROOT/RColumnElement.hxx +++ b/tree/ntuple/v7/src/RColumnElement.hxx @@ -1,38 +1,19 @@ -/// \file ROOT/RColumnElement.hxx -/// \ingroup NTuple ROOT7 -/// \author Jakob Blomer -/// \date 2018-10-09 -/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback -/// is welcome! - -/************************************************************************* - * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. * - * All rights reserved. * - * * - * For the licensing terms see $ROOTSYS/LICENSE. * - * For the list of contributors see $ROOTSYS/README/CREDITS. * - *************************************************************************/ - -#ifndef ROOT7_RColumnElement -#define ROOT7_RColumnElement +// RColumnElement concrete implementations +// +// Note that this file is in the src directory and not in the inc directory because we need the ability +// to override R__LITTLE_ENDIAN for testing purposes. +// This is not a particularly clean or correct solution, as the tests that do this will end up with two different +// definitions of some RColumnElements, so we might want to change this mechanism in the future. In any case, these +// definitions are implementation details and should not be exposed to a public interface. -#include -#include -#include +#include #include - +#include #include -#include -#include // for memcpy -#include // for std::byte -#include -#include -#include -#include -#include -#include +#include +// NOTE: some tests might define R__LITTLE_ENDIAN to simulate a different-endianness machine #ifndef R__LITTLE_ENDIAN #ifdef R__BYTESWAP // `R__BYTESWAP` is defined in RConfig.hxx for little-endian architectures; undefined otherwise @@ -71,7 +52,7 @@ namespace { /// Used on big-endian architectures for packing/unpacking elements whose column type requires /// a little-endian on-disk representation. template -void CopyBswap(void *destination, const void *source, std::size_t count) +inline void CopyBswap(void *destination, const void *source, std::size_t count) { auto dst = reinterpret_cast::value_type *>(destination); auto src = reinterpret_cast::value_type *>(source); @@ -83,7 +64,7 @@ void CopyBswap(void *destination, const void *source, std::size_t count) /// Casts T to one of the ints used in RByteSwap and back to its original type, which may be float or double #if R__LITTLE_ENDIAN == 0 template -void ByteSwapIfNecessary(T &value) +inline void ByteSwapIfNecessary(T &value) { constexpr auto N = sizeof(T); using bswap_value_type = typename RByteSwap::value_type; @@ -100,7 +81,7 @@ void ByteSwapIfNecessary(T &value) /// Used to convert in-memory elements to smaller column types of comatible types /// (e.g., double to float, int64 to int32). Takes care of byte swap if necessary. template -static void CastPack(void *destination, const void *source, std::size_t count) +inline void CastPack(void *destination, const void *source, std::size_t count) { static_assert(std::is_convertible_v); auto dst = reinterpret_cast(destination); @@ -116,7 +97,7 @@ static void CastPack(void *destination, const void *source, std::size_t count) /// Used to convert on-disk elements to larger C++ types of comatible types /// (e.g., float to double, int32 to int64). Takes care of byte swap if necessary. template -static void CastUnpack(void *destination, const void *source, std::size_t count) +inline void CastUnpack(void *destination, const void *source, std::size_t count) { auto dst = reinterpret_cast(destination); auto src = reinterpret_cast(source); @@ -131,7 +112,7 @@ static void CastUnpack(void *destination, const void *source, std::size_t count) /// /// Used to first cast and then split-encode in-memory values to the on-disk column. Swap bytes if necessary. template -static void CastSplitPack(void *destination, const void *source, std::size_t count) +inline void CastSplitPack(void *destination, const void *source, std::size_t count) { constexpr std::size_t N = sizeof(DestT); auto splitArray = reinterpret_cast(destination); @@ -149,7 +130,7 @@ static void CastSplitPack(void *destination, const void *source, std::size_t cou /// /// Used to first unsplit a column, possibly storing elements in wider C++ types. Swaps bytes if necessary template -static void CastSplitUnpack(void *destination, const void *source, std::size_t count) +inline void CastSplitUnpack(void *destination, const void *source, std::size_t count) { constexpr std::size_t N = sizeof(SourceT); auto dst = reinterpret_cast(destination); @@ -168,7 +149,7 @@ static void CastSplitUnpack(void *destination, const void *source, std::size_t c /// /// Apply split encoding to delta-encoded values, currently used only for index columns template -static void CastDeltaSplitPack(void *destination, const void *source, std::size_t count) +inline void CastDeltaSplitPack(void *destination, const void *source, std::size_t count) { constexpr std::size_t N = sizeof(DestT); auto src = reinterpret_cast(source); @@ -186,7 +167,7 @@ static void CastDeltaSplitPack(void *destination, const void *source, std::size_ /// /// Unsplit a column and reverse the delta encoding, currently used only for index columns template -static void CastDeltaSplitUnpack(void *destination, const void *source, std::size_t count) +inline void CastDeltaSplitUnpack(void *destination, const void *source, std::size_t count) { constexpr std::size_t N = sizeof(SourceT); auto splitArray = reinterpret_cast(source); @@ -205,7 +186,7 @@ static void CastDeltaSplitUnpack(void *destination, const void *source, std::siz /// /// Apply split encoding to zigzag-encoded values, used for signed integers template -static void CastZigzagSplitPack(void *destination, const void *source, std::size_t count) +inline void CastZigzagSplitPack(void *destination, const void *source, std::size_t count) { using UDestT = std::make_unsigned_t; constexpr std::size_t kNBitsDestT = sizeof(DestT) * 8; @@ -225,7 +206,7 @@ static void CastZigzagSplitPack(void *destination, const void *source, std::size /// /// Unsplit a column and reverse the zigzag encoding, used for signed integer columns template -static void CastZigzagSplitUnpack(void *destination, const void *source, std::size_t count) +inline void CastZigzagSplitUnpack(void *destination, const void *source, std::size_t count) { using USourceT = std::make_unsigned_t; constexpr std::size_t N = sizeof(SourceT); @@ -240,77 +221,53 @@ static void CastZigzagSplitUnpack(void *destination, const void *source, std::si dst[i] = static_cast((val >> 1) ^ -(static_cast(val) & 1)); } } +} // namespace -} // anonymous namespace +// anonymous namespace because these definitions are not meant to be exported. +namespace { -namespace ROOT { -namespace Experimental { -namespace Internal { +using ROOT::Experimental::EColumnType; +using ROOT::Experimental::Internal::RColumnElementBase; -// clang-format off -/** -\class ROOT::Experimental::Internal::RColumnElementBase -\ingroup NTuple -\brief A column element encapsulates the translation between basic C++ types and their column representation. - -Usually the on-disk element should map bitwise to the in-memory element. Sometimes that's not the case -though, for instance on big endian platforms or for bools. - -There is a template specialization for every valid pair of C++ type and column representation. -These specialized child classes are responsible for overriding `Pack()` / `Unpack()` for packing / unpacking elements -as appropriate. -*/ -// clang-format on -class RColumnElementBase { -protected: - /// Size of the C++ value that corresponds to the on-disk element - std::size_t fSize; - std::size_t fBitsOnStorage; +template +class RColumnElement; - explicit RColumnElementBase(std::size_t size, std::size_t bitsOnStorage = 0) - : fSize(size), fBitsOnStorage(bitsOnStorage ? bitsOnStorage : 8 * size) - { - } - -public: - RColumnElementBase(const RColumnElementBase &other) = default; - RColumnElementBase(RColumnElementBase &&other) = default; - RColumnElementBase &operator=(const RColumnElementBase &other) = delete; - RColumnElementBase &operator=(RColumnElementBase &&other) = default; - virtual ~RColumnElementBase() = default; - - /// If CppT == void, use the default C++ type for the given column type - template - static std::unique_ptr Generate(EColumnType type); - static std::string GetTypeName(EColumnType type); - /// Most types have a fixed on-disk bit width. Some low-precision column types - /// have a range of possible bit widths. Return the minimum and maximum allowed - /// bit size per type. - static std::pair GetValidBitRange(EColumnType type); - - /// Derived, typed classes tell whether the on-storage layout is bitwise identical to the memory layout - virtual bool IsMappable() const - { - R__ASSERT(false); - return false; - } - - /// If the on-storage layout and the in-memory layout differ, packing creates an on-disk page from an in-memory page - virtual void Pack(void *destination, const void *source, std::size_t count) const - { - std::memcpy(destination, source, count); - } - - /// If the on-storage layout and the in-memory layout differ, unpacking creates a memory page from an on-storage page - virtual void Unpack(void *destination, const void *source, std::size_t count) const - { - std::memcpy(destination, source, count); +template +std::unique_ptr GenerateColumnElementInternal(EColumnType type) +{ + switch (type) { + case EColumnType::kIndex64: return std::make_unique>(); + case EColumnType::kIndex32: return std::make_unique>(); + case EColumnType::kSwitch: return std::make_unique>(); + case EColumnType::kByte: return std::make_unique>(); + case EColumnType::kChar: return std::make_unique>(); + case EColumnType::kBit: return std::make_unique>(); + case EColumnType::kReal64: return std::make_unique>(); + case EColumnType::kReal32: return std::make_unique>(); + case EColumnType::kReal16: return std::make_unique>(); + case EColumnType::kInt64: return std::make_unique>(); + case EColumnType::kUInt64: return std::make_unique>(); + case EColumnType::kInt32: return std::make_unique>(); + case EColumnType::kUInt32: return std::make_unique>(); + case EColumnType::kInt16: return std::make_unique>(); + case EColumnType::kUInt16: return std::make_unique>(); + case EColumnType::kInt8: return std::make_unique>(); + case EColumnType::kUInt8: return std::make_unique>(); + case EColumnType::kSplitIndex64: return std::make_unique>(); + case EColumnType::kSplitIndex32: return std::make_unique>(); + case EColumnType::kSplitReal64: return std::make_unique>(); + case EColumnType::kSplitReal32: return std::make_unique>(); + case EColumnType::kSplitInt64: return std::make_unique>(); + case EColumnType::kSplitUInt64: return std::make_unique>(); + case EColumnType::kSplitInt32: return std::make_unique>(); + case EColumnType::kSplitUInt32: return std::make_unique>(); + case EColumnType::kSplitInt16: return std::make_unique>(); + case EColumnType::kSplitUInt16: return std::make_unique>(); + default: R__ASSERT(false); } - - std::size_t GetSize() const { return fSize; } - std::size_t GetBitsOnStorage() const { return fBitsOnStorage; } - std::size_t GetPackedSize(std::size_t nElements = 1U) const { return (nElements * fBitsOnStorage + 7) / 8; } -}; // class RColumnElementBase + // never here + return nullptr; +} /** * Base class for columns whose on-storage representation is little-endian. @@ -453,8 +410,9 @@ class RColumnElement : public RColumnElementBase { public: RColumnElement() : RColumnElementBase(sizeof(CppT)) { - throw RException(R__FAIL(std::string("internal error: no column mapping for this C++ type: ") + - typeid(CppT).name() + " --> " + GetTypeName(ColumnT))); + throw ROOT::Experimental::RException( + R__FAIL(std::string("internal error: no column mapping for this C++ type: ") + typeid(CppT).name() + " --> " + + GetTypeName(ColumnT))); } }; @@ -550,16 +508,16 @@ public: }; template <> -class RColumnElement : public RColumnElementBase { +class RColumnElement : public RColumnElementBase { public: - static constexpr std::size_t kSize = sizeof(ClusterSize_t); + static constexpr std::size_t kSize = sizeof(ROOT::Experimental::ClusterSize_t); RColumnElement() : RColumnElementBase(kSize) {} }; template <> -class RColumnElement : public RColumnElementBase { +class RColumnElement : public RColumnElementBase { public: - static constexpr std::size_t kSize = sizeof(RColumnSwitch); + static constexpr std::size_t kSize = sizeof(ROOT::Experimental::RColumnSwitch); RColumnElement() : RColumnElementBase(kSize) {} }; @@ -569,7 +527,7 @@ public: //////////////////////////////////////////////////////////////////////////////// template <> -class RColumnElement : public RColumnElementBase { +class RColumnElement : public RColumnElementBase { private: struct RSwitchElement { std::uint64_t fIndex; @@ -608,7 +566,8 @@ public: element.fIndex = RByteSwap<8>::bswap(element.fIndex); element.fTag = RByteSwap<4>::bswap(element.fTag); #endif - dstArray[i] = ROOT::Experimental::RColumnSwitch(ClusterSize_t{element.fIndex}, element.fTag); + dstArray[i] = + ROOT::Experimental::RColumnSwitch(ROOT::Experimental::ClusterSize_t{element.fIndex}, element.fTag); } } }; @@ -641,7 +600,7 @@ public: std::uint16_t *uint16Array = reinterpret_cast(dst); for (std::size_t i = 0; i < count; ++i) { - uint16Array[i] = FloatToHalf(floatArray[i]); + uint16Array[i] = ROOT::Experimental::Internal::FloatToHalf(floatArray[i]); ByteSwapIfNecessary(uint16Array[i]); } } @@ -654,7 +613,7 @@ public: for (std::size_t i = 0; i < count; ++i) { std::uint16_t val = uint16Array[i]; ByteSwapIfNecessary(val); - floatArray[i] = HalfToFloat(val); + floatArray[i] = ROOT::Experimental::Internal::HalfToFloat(val); } } }; @@ -674,7 +633,7 @@ public: std::uint16_t *uint16Array = reinterpret_cast(dst); for (std::size_t i = 0; i < count; ++i) { - uint16Array[i] = FloatToHalf(static_cast(doubleArray[i])); + uint16Array[i] = ROOT::Experimental::Internal::FloatToHalf(static_cast(doubleArray[i])); ByteSwapIfNecessary(uint16Array[i]); } } @@ -687,7 +646,7 @@ public: for (std::size_t i = 0; i < count; ++i) { std::uint16_t val = uint16Array[i]; ByteSwapIfNecessary(val); - doubleArray[i] = static_cast(HalfToFloat(val)); + doubleArray[i] = static_cast(ROOT::Experimental::Internal::HalfToFloat(val)); } } }; @@ -790,56 +749,47 @@ DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kSplitReal64, 64, RColumnElemen DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kReal32, 32, RColumnElementCastLE, ); DECLARE_RCOLUMNELEMENT_SPEC(double, EColumnType::kSplitReal32, 32, RColumnElementSplitLE, ); -DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kIndex64, 64, RColumnElementLE, ); -DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kIndex32, 32, RColumnElementCastLE, - ); -DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kSplitIndex64, 64, RColumnElementDeltaSplitLE, - ); -DECLARE_RCOLUMNELEMENT_SPEC(ClusterSize_t, EColumnType::kSplitIndex32, 32, RColumnElementDeltaSplitLE, +DECLARE_RCOLUMNELEMENT_SPEC(ROOT::Experimental::ClusterSize_t, EColumnType::kIndex64, 64, RColumnElementLE, + ); +DECLARE_RCOLUMNELEMENT_SPEC(ROOT::Experimental::ClusterSize_t, EColumnType::kIndex32, 32, RColumnElementCastLE, ); +DECLARE_RCOLUMNELEMENT_SPEC(ROOT::Experimental::ClusterSize_t, EColumnType::kSplitIndex64, 64, + RColumnElementDeltaSplitLE, ); +DECLARE_RCOLUMNELEMENT_SPEC(ROOT::Experimental::ClusterSize_t, EColumnType::kSplitIndex32, 32, + RColumnElementDeltaSplitLE, ); -template -std::unique_ptr RColumnElementBase::Generate(EColumnType type) +void RColumnElement::Pack(void *dst, const void *src, + std::size_t count) const { - switch (type) { - case EColumnType::kIndex64: return std::make_unique>(); - case EColumnType::kIndex32: return std::make_unique>(); - case EColumnType::kSwitch: return std::make_unique>(); - case EColumnType::kByte: return std::make_unique>(); - case EColumnType::kChar: return std::make_unique>(); - case EColumnType::kBit: return std::make_unique>(); - case EColumnType::kReal64: return std::make_unique>(); - case EColumnType::kReal32: return std::make_unique>(); - case EColumnType::kReal16: return std::make_unique>(); - case EColumnType::kInt64: return std::make_unique>(); - case EColumnType::kUInt64: return std::make_unique>(); - case EColumnType::kInt32: return std::make_unique>(); - case EColumnType::kUInt32: return std::make_unique>(); - case EColumnType::kInt16: return std::make_unique>(); - case EColumnType::kUInt16: return std::make_unique>(); - case EColumnType::kInt8: return std::make_unique>(); - case EColumnType::kUInt8: return std::make_unique>(); - case EColumnType::kSplitIndex64: return std::make_unique>(); - case EColumnType::kSplitIndex32: return std::make_unique>(); - case EColumnType::kSplitReal64: return std::make_unique>(); - case EColumnType::kSplitReal32: return std::make_unique>(); - case EColumnType::kSplitInt64: return std::make_unique>(); - case EColumnType::kSplitUInt64: return std::make_unique>(); - case EColumnType::kSplitInt32: return std::make_unique>(); - case EColumnType::kSplitUInt32: return std::make_unique>(); - case EColumnType::kSplitInt16: return std::make_unique>(); - case EColumnType::kSplitUInt16: return std::make_unique>(); - default: R__ASSERT(false); + const bool *boolArray = reinterpret_cast(src); + char *charArray = reinterpret_cast(dst); + std::bitset<8> bitSet; + std::size_t i = 0; + for (; i < count; ++i) { + bitSet.set(i % 8, boolArray[i]); + if (i % 8 == 7) { + char packed = bitSet.to_ulong(); + charArray[i / 8] = packed; + } + } + if (i % 8 != 0) { + char packed = bitSet.to_ulong(); + charArray[i / 8] = packed; } - // never here - return nullptr; } -template <> -std::unique_ptr RColumnElementBase::Generate(EColumnType type); - -} // namespace Internal -} // namespace Experimental -} // namespace ROOT +void RColumnElement::Unpack(void *dst, const void *src, + std::size_t count) const +{ + bool *boolArray = reinterpret_cast(dst); + const char *charArray = reinterpret_cast(src); + std::bitset<8> bitSet; + for (std::size_t i = 0; i < count; i += 8) { + bitSet = charArray[i / 8]; + for (std::size_t j = i; j < std::min(count, i + 8); ++j) { + boolArray[j] = bitSet[j % 8]; + } + } +} -#endif +} // namespace diff --git a/tree/ntuple/v7/src/RField.cxx b/tree/ntuple/v7/src/RField.cxx index 782647470c5e0..43964bdd35323 100644 --- a/tree/ntuple/v7/src/RField.cxx +++ b/tree/ntuple/v7/src/RField.cxx @@ -1152,18 +1152,6 @@ void ROOT::Experimental::RFieldBase::AutoAdjustColumnTypes(const RNTupleWriteOpt SetColumnRepresentatives({rep}); } - if (options.GetHasSmallClusters()) { - ColumnRepresentation_t rep = GetColumnRepresentations().GetSerializationDefault(); - for (auto &colType : rep) { - switch (colType) { - case EColumnType::kSplitIndex64: colType = EColumnType::kSplitIndex32; break; - case EColumnType::kIndex64: colType = EColumnType::kIndex32; break; - default: break; - } - } - SetColumnRepresentatives({rep}); - } - if (fTypeAlias == "Double32_t") SetColumnRepresentatives({{EColumnType::kSplitReal32}}); } diff --git a/tree/ntuple/v7/src/RMiniFile.cxx b/tree/ntuple/v7/src/RMiniFile.cxx index 1a234b5d30605..0df28c6a57cbb 100644 --- a/tree/ntuple/v7/src/RMiniFile.cxx +++ b/tree/ntuple/v7/src/RMiniFile.cxx @@ -16,18 +16,19 @@ #include "Rtypes.h" #include #include - -#include "ROOT/RMiniFile.hxx" - +#include #include #include #include #include #include +#include #include #include #include +#include +#include #include @@ -152,35 +153,6 @@ class RUInt64BE { } }; -constexpr std::int32_t ChecksumRNTupleClass() -{ - const char ident[] = "ROOT::Experimental::RNTuple" - "fVersionEpoch" - "unsigned short" - "fVersionMajor" - "unsigned short" - "fVersionMinor" - "unsigned short" - "fVersionPatch" - "unsigned short" - "fSeekHeader" - "unsigned long" - "fNBytesHeader" - "unsigned long" - "fLenHeader" - "unsigned long" - "fSeekFooter" - "unsigned long" - "fNBytesFooter" - "unsigned long" - "fLenFooter" - "unsigned long"; - std::int32_t id = 0; - for (unsigned i = 0; i < (sizeof(ident) - 1); i++) - id = static_cast(static_cast(id) * 3 + ident[i]); - return id; -} - #pragma pack(push, 1) /// A name (type, identifies, ...) in the TFile binary format struct RTFString { @@ -490,409 +462,6 @@ struct RTFFreeEntry { std::uint32_t GetSize() { return (fVersion >= 1000) ? 18 : 10; } }; -/// Streamer info for TObject -struct RTFObject { - RUInt16BE fVersion{1}; - RUInt32BE fUniqueID{0}; // unused - RUInt32BE fBits; - explicit RTFObject(std::uint32_t bits) : fBits(bits) {} -}; - -/// Streamer info for data member RNTuple::fVersionEpoch -struct RTFStreamerElementVersionEpoch { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementVersionEpoch) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 15)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 13; - char fName[13]{'f', 'V', 'e', 'r', 's', 'i', 'o', 'n', 'E', 'p', 'o', 'c', 'h'}; - char fLTitle = 0; - - RUInt32BE fType{12}; - RUInt32BE fSize{2}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 14; - char fTypeName[14]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 's', 'h', 'o', 'r', 't'}; -}; - -/// Streamer info for data member RNTuple::fVersionMajor -struct RTFStreamerElementVersionMajor { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementVersionMajor) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 15)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 13; - char fName[13]{'f', 'V', 'e', 'r', 's', 'i', 'o', 'n', 'M', 'a', 'j', 'o', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{12}; - RUInt32BE fSize{2}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 14; - char fTypeName[14]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 's', 'h', 'o', 'r', 't'}; -}; - -/// Streamer info for data member RNTuple::fVersionMajor -struct RTFStreamerElementVersionMinor { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementVersionMinor) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 15)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 13; - char fName[13]{'f', 'V', 'e', 'r', 's', 'i', 'o', 'n', 'M', 'i', 'n', 'o', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{12}; - RUInt32BE fSize{2}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 14; - char fTypeName[14]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 's', 'h', 'o', 'r', 't'}; -}; - -/// Streamer info for data member RNTuple::fVersionPatch -struct RTFStreamerElementVersionPatch { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementVersionPatch) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 15)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 13; - char fName[13]{'f', 'V', 'e', 'r', 's', 'i', 'o', 'n', 'P', 'a', 't', 'c', 'h'}; - char fLTitle = 0; - - RUInt32BE fType{12}; - RUInt32BE fSize{2}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 14; - char fTypeName[14]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 's', 'h', 'o', 'r', 't'}; -}; - -/// Streamer info for data member RNTuple::fSeekHeader -struct RTFStreamerElementSeekHeader { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementSeekHeader) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 13)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 11; - char fName[11]{'f', 'S', 'e', 'e', 'k', 'H', 'e', 'a', 'd', 'e', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{14}; - RUInt32BE fSize{8}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 13; - char fTypeName[13]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'l', 'o', 'n', 'g'}; -}; - -/// Streamer info for data member RNTuple::fNbytesHeader -struct RTFStreamerElementNBytesHeader { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementNBytesHeader) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 15)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 13; - char fName[13]{'f', 'N', 'B', 'y', 't', 'e', 's', 'H', 'e', 'a', 'd', 'e', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{14}; - RUInt32BE fSize{8}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 13; - char fTypeName[13]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'l', 'o', 'n', 'g'}; -}; - -/// Streamer info for data member RNTuple::fLenHeader -struct RTFStreamerElementLenHeader { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementLenHeader) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 12)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 10; - char fName[10]{'f', 'L', 'e', 'n', 'H', 'e', 'a', 'd', 'e', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{14}; - RUInt32BE fSize{8}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 13; - char fTypeName[13]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'l', 'o', 'n', 'g'}; -}; - -/// Streamer info for data member RNTuple::fSeekFooter -struct RTFStreamerElementSeekFooter { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementSeekFooter) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 13)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 11; - char fName[11]{'f', 'S', 'e', 'e', 'k', 'F', 'o', 'o', 't', 'e', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{14}; - RUInt32BE fSize{8}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 13; - char fTypeName[13]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'l', 'o', 'n', 'g'}; -}; - -/// Streamer info for data member RNTuple::fNbytesFooter -struct RTFStreamerElementNBytesFooter { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementNBytesFooter) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 15)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 13; - char fName[13]{'f', 'N', 'B', 'y', 't', 'e', 's', 'F', 'o', 'o', 't', 'e', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{14}; - RUInt32BE fSize{8}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 13; - char fTypeName[13]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'l', 'o', 'n', 'g'}; -}; - -/// Streamer info for data member RNTuple::fLenFooter -struct RTFStreamerElementLenFooter { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementLenFooter) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 12)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 10; - char fName[10]{'f', 'L', 'e', 'n', 'F', 'o', 'o', 't', 'e', 'r'}; - char fLTitle = 0; - - RUInt32BE fType{14}; - RUInt32BE fSize{8}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 13; - char fTypeName[13]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'l', 'o', 'n', 'g'}; -}; - -struct RTFStreamerElementMaxKeySize { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerElementMaxKeySize) - sizeof(RUInt32BE))}; - RUInt16BE fVersion{4}; - - RUInt32BE fByteCountNamed{0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 13)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000}; - char fLName = 11; - char fName[11]{'f', 'M', 'a', 'x', 'K', 'e', 'y', 'S', 'i', 'z', 'e'}; - char fLTitle = 0; - - RUInt32BE fType{14}; - RUInt32BE fSize{8}; - RUInt32BE fArrLength{0}; - RUInt32BE fArrDim{0}; - char fMaxIndex[20]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char fLTypeName = 13; - char fTypeName[13]{'u', 'n', 's', 'i', 'g', 'n', 'e', 'd', ' ', 'l', 'o', 'n', 'g'}; -}; - -/// Streamer info frame for data member RNTuple::fVersionEpoch -struct RTFStreamerVersionEpoch { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerVersionEpoch) - sizeof(RUInt32BE))}; - RUInt32BE fNewClassTag{0xffffffff}; - char fClassName[19]{'T', 'S', 't', 'r', 'e', 'a', 'm', 'e', 'r', 'B', 'a', 's', 'i', 'c', 'T', 'y', 'p', 'e', '\0'}; - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerVersionEpoch) - 2 * sizeof(RUInt32BE) - - 19 /* strlen(fClassName) + 1 */ - sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementVersionEpoch fStreamerElementVersionEpoch; -}; - -/// Streamer info frame for data member RNTuple::fVersionMajor -struct RTFStreamerVersionMajor { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerVersionMajor) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerVersionMajor) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementVersionMajor fStreamerElementVersionMajor; -}; - -/// Streamer info frame for data member RNTuple::fVersionMinor -struct RTFStreamerVersionMinor { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerVersionMinor) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerVersionMinor) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementVersionMinor fStreamerElementVersionMinor; -}; - -/// Streamer info frame for data member RNTuple::fVersionPatch -struct RTFStreamerVersionPatch { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerVersionPatch) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerVersionPatch) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementVersionPatch fStreamerElementVersionPatch; -}; - -/// Streamer info frame for data member RNTuple::fSeekHeader -struct RTFStreamerSeekHeader { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerSeekHeader) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerSeekHeader) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementSeekHeader fStreamerElementSeekHeader; -}; - -/// Streamer info frame for data member RNTuple::fNbytesHeader -struct RTFStreamerNBytesHeader { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerNBytesHeader) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerNBytesHeader) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementNBytesHeader fStreamerElementNBytesHeader; -}; - -/// Streamer info frame for data member RNTuple::fLenHeader -struct RTFStreamerLenHeader { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerLenHeader) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerLenHeader) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementLenHeader fStreamerElementLenHeader; -}; - -/// Streamer info frame for data member RNTuple::fSeekFooter -struct RTFStreamerSeekFooter { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerSeekFooter) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerSeekFooter) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementSeekFooter fStreamerElementSeekFooter; -}; - -/// Streamer info frame for data member RNTuple::fNBytesFooter -struct RTFStreamerNBytesFooter { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerNBytesFooter) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerNBytesFooter) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementNBytesFooter fStreamerElementNBytesFooter; -}; - -/// Streamer info frame for data member RNTuple::fLenFooter -struct RTFStreamerLenFooter { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerLenFooter) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerLenFooter) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementLenFooter fStreamerElementLenFooter; -}; - -/// Streamer info frame for data member RNTuple::fLenFooter -struct RTFStreamerMaxKeySize { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerMaxKeySize) - sizeof(RUInt32BE))}; - RUInt32BE fClassTag{0x80000000}; // Fix-up after construction, or'd with 0x80000000 - RUInt32BE fByteCountRemaining{0x40000000 | (sizeof(RTFStreamerMaxKeySize) - 3 * sizeof(RUInt32BE))}; - RUInt16BE fVersion{2}; - RTFStreamerElementMaxKeySize fStreamerElementMaxKeySize; -}; - -/// Streamer info for class RNTuple -struct RTFStreamerInfoObject { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerInfoObject) - sizeof(fByteCount))}; - RUInt32BE fNewClassTag{0xffffffff}; - char fClassName[14]{'T', 'S', 't', 'r', 'e', 'a', 'm', 'e', 'r', 'I', 'n', 'f', 'o', '\0'}; - RUInt32BE fByteCountRemaining{0x40000000 | - (sizeof(RTFStreamerInfoObject) - 2 * sizeof(RUInt32BE) - 14 - sizeof(RUInt32BE))}; - RUInt16BE fVersion{9}; - - RUInt32BE fByteCountNamed{ - 0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 29 /* strlen("ROOT::Experimental::RNTuple") + 2 */)}; - RUInt16BE fVersionNamed{1}; - RTFObject fObjectNamed{0x02000000 | 0x01000000 | 0x00010000}; - char fLName = 27; - char fName[27]{'R', 'O', 'O', 'T', ':', ':', 'E', 'x', 'p', 'e', 'r', 'i', 'm', 'e', - 'n', 't', 'a', 'l', ':', ':', 'R', 'N', 'T', 'u', 'p', 'l', 'e'}; - char fLTitle = 0; - - RInt32BE fChecksum{ChecksumRNTupleClass()}; - /// NOTE: this needs to be kept in sync with the RNTuple version in RNTuple.hxx - RUInt32BE fVersionRNTuple{6}; - - RUInt32BE fByteCountObjArr{0x40000000 | - (sizeof(RUInt32BE) + 10 /* strlen(TObjArray) + 1 */ + sizeof(RUInt32BE) + - sizeof(RUInt16BE) + sizeof(RTFObject) + 1 + 2 * sizeof(RUInt32BE) + sizeof(fStreamers))}; - RUInt32BE fNewClassTagObjArray{0xffffffff}; - char fClassNameObjArray[10]{'T', 'O', 'b', 'j', 'A', 'r', 'r', 'a', 'y', '\0'}; - RUInt32BE fByteCountObjArrRemaining{ - 0x40000000 | (sizeof(RUInt16BE) + sizeof(RTFObject) + 1 + 2 * sizeof(RUInt32BE) + sizeof(fStreamers))}; - RUInt16BE fVersionObjArr{3}; - RTFObject fObjectObjArr{0x02000000}; - char fNameObjArr{0}; - - RUInt32BE fNObjects{11}; - RUInt32BE fLowerBound{0}; - - struct { - RTFStreamerVersionEpoch fStreamerVersionEpoch; - RTFStreamerVersionMajor fStreamerVersionMajor; - RTFStreamerVersionMinor fStreamerVersionMinor; - RTFStreamerVersionPatch fStreamerVersionPatch; - RTFStreamerSeekHeader fStreamerSeekHeader; - RTFStreamerNBytesHeader fStreamerNBytesHeader; - RTFStreamerLenHeader fStreamerLenHeader; - RTFStreamerSeekFooter fStreamerSeekFooter; - RTFStreamerNBytesFooter fStreamerNBytesFooter; - RTFStreamerLenFooter fStreamerLenFooter; - RTFStreamerMaxKeySize fStreamerMaxKeySize; - } fStreamers; -}; - -/// The list of streamer info objects, for a new ntuple contains only the RNTuple class -struct RTFStreamerInfoList { - RUInt32BE fByteCount{0x40000000 | (sizeof(RTFStreamerInfoList) - sizeof(fByteCount))}; - RUInt16BE fVersion{5}; - RTFObject fObject{0x02000000}; - char fName{0}; - RUInt32BE fNObjects{1}; - RTFStreamerInfoObject fStreamerInfo; - char fEnd{0}; - - std::uint32_t GetSize() const { return sizeof(RTFStreamerInfoList); } -}; - /// The header of the directory key index struct RTFKeyList { RUInt32BE fNKeys; @@ -1504,6 +1073,8 @@ ROOT::Experimental::Internal::RNTupleFileWriter::RNTupleFileWriter(std::string_v { fFileSimple.fControlBlock = std::make_unique(); fNTupleAnchor.fMaxKeySize = maxKeySize; + auto infoRNTuple = RNTuple::Class()->GetStreamerInfo(); + fStreamerInfoMap[infoRNTuple->GetNumber()] = infoRNTuple; } ROOT::Experimental::Internal::RNTupleFileWriter::~RNTupleFileWriter() {} @@ -1567,11 +1138,24 @@ ROOT::Experimental::Internal::RNTupleFileWriter::Append(std::string_view ntupleN return writer; } +void ROOT::Experimental::Internal::RNTupleFileWriter::UpdateStreamerInfos( + const RNTupleSerializer::StreamerInfoMap_t &streamerInfos) +{ + fStreamerInfoMap.insert(streamerInfos.cbegin(), streamerInfos.cend()); +} + void ROOT::Experimental::Internal::RNTupleFileWriter::Commit() { if (fFileProper) { // Easy case, the ROOT file header and the RNTuple streaming is taken care of by TFile fFileProper.fFile->WriteObject(&fNTupleAnchor, fNTupleName.c_str()); + + // Make sure the streamer info records used in the RNTuple are written to the file + TBufferFile buf(TBuffer::kWrite); + buf.SetParent(fFileProper.fFile); + for (auto [_, info] : fStreamerInfoMap) + buf.TagStreamerInfo(info); + fFileProper.fFile->Write(); return; } @@ -1744,30 +1328,42 @@ void ROOT::Experimental::Internal::RNTupleFileWriter::WriteBareFileSkeleton(int void ROOT::Experimental::Internal::RNTupleFileWriter::WriteTFileStreamerInfo() { + // The streamer info record is a TList of TStreamerInfo object. We cannot use + // RNTupleSerializer::SerializeStreamerInfos because that uses TBufferIO::WriteObject. + // This would prepend the streamed TList with self-decription information. + // The streamer info record is just the streamed TList. + + TList streamerInfoList; + for (auto [_, info] : fStreamerInfoMap) { + streamerInfoList.Add(info); + } + + // We will stream the list with a TBufferFile. When reading the streamer info records back, + // the read buffer includes the key and the streamed list. Therefore, we need to start streaming + // with an offset of the key length. Otherwise, the offset for referencing duplicate objects in the + // buffer will point to the wrong places. + + // Figure out key length RTFString strTList{"TList"}; RTFString strStreamerInfo{"StreamerInfo"}; RTFString strStreamerTitle{"Doubly linked list"}; - fFileSimple.fControlBlock->fHeader.SetSeekInfo(fFileSimple.fKeyOffset); - RTFKey keyStreamerInfo(fFileSimple.fControlBlock->fHeader.GetSeekInfo(), 100, strTList, strStreamerInfo, - strStreamerTitle, 0); - RTFStreamerInfoList streamerInfo; - auto classTagOffset = keyStreamerInfo.fKeyLen + offsetof(struct RTFStreamerInfoList, fStreamerInfo) + - offsetof(struct RTFStreamerInfoObject, fStreamers) + - offsetof(struct RTFStreamerVersionEpoch, fNewClassTag) + 2; - streamerInfo.fStreamerInfo.fStreamers.fStreamerVersionMajor.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerVersionMinor.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerVersionPatch.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerSeekHeader.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerNBytesHeader.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerLenHeader.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerSeekFooter.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerNBytesFooter.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerLenFooter.fClassTag = 0x80000000 | classTagOffset; - streamerInfo.fStreamerInfo.fStreamers.fStreamerMaxKeySize.fClassTag = 0x80000000 | classTagOffset; + auto keyLen = + RTFKey(fFileSimple.fControlBlock->fHeader.GetSeekInfo(), 100, strTList, strStreamerInfo, strStreamerTitle, 0) + .fKeyLen; + + TBufferFile buffer(TBuffer::kWrite, keyLen + 1); + buffer.SetBufferOffset(keyLen); + streamerInfoList.Streamer(buffer); + assert(buffer.Length() > keyLen); + const auto bufPayload = buffer.Buffer() + keyLen; + const auto lenPayload = buffer.Length() - keyLen; + RNTupleCompressor compressor; - auto szStreamerInfo = compressor.Zip(&streamerInfo, streamerInfo.GetSize(), 1); - fFileSimple.WriteKey(compressor.GetZipBuffer(), szStreamerInfo, streamerInfo.GetSize(), + auto zipStreamerInfos = std::make_unique(lenPayload); + auto szZipStreamerInfos = compressor.Zip(bufPayload, lenPayload, 1, zipStreamerInfos.get()); + + fFileSimple.WriteKey(zipStreamerInfos.get(), szZipStreamerInfos, lenPayload, fFileSimple.fControlBlock->fHeader.GetSeekInfo(), 100, "TList", "StreamerInfo", "Doubly linked list"); fFileSimple.fControlBlock->fHeader.SetNbytesInfo(fFileSimple.fFilePos - diff --git a/tree/ntuple/v7/src/RNTupleDescriptor.cxx b/tree/ntuple/v7/src/RNTupleDescriptor.cxx index c270743dd1e4d..fb338789d76f3 100644 --- a/tree/ntuple/v7/src/RNTupleDescriptor.cxx +++ b/tree/ntuple/v7/src/RNTupleDescriptor.cxx @@ -24,10 +24,12 @@ #include #include +#include #include #include #include +#include #include #include #include @@ -102,6 +104,25 @@ ROOT::Experimental::RFieldDescriptor::CreateField(const RNTupleDescriptor &ntplD return field; } +bool ROOT::Experimental::RFieldDescriptor::IsCustomClass() const +{ + if (fStructure != ENTupleStructure::kRecord && fStructure != ENTupleStructure::kUnsplit) + return false; + + // Skip untyped structs + if (fTypeName.empty()) + return false; + + if (fStructure == ENTupleStructure::kRecord) { + if (fTypeName.compare(0, 10, "std::pair<") == 0) + return false; + if (fTypeName.compare(0, 11, "std::tuple<") == 0) + return false; + } + + return true; +} + //////////////////////////////////////////////////////////////////////////////// bool ROOT::Experimental::RColumnDescriptor::operator==(const RColumnDescriptor &other) const @@ -1046,6 +1067,46 @@ ROOT::Experimental::Internal::RNTupleDescriptorBuilder::AddExtraTypeInfo(RExtraT return RResult::Success(); } +ROOT::Experimental::Internal::RNTupleSerializer::StreamerInfoMap_t +ROOT::Experimental::Internal::RNTupleDescriptorBuilder::BuildStreamerInfos() const +{ + RNTupleSerializer::StreamerInfoMap_t streamerInfoMap; + const auto &desc = GetDescriptor(); + + std::function fnWalkFieldTree; + fnWalkFieldTree = [&desc, &streamerInfoMap, &fnWalkFieldTree](const RFieldDescriptor &fieldDesc) { + if (fieldDesc.IsCustomClass()) { + // Add streamer info for this class to streamerInfoMap + auto cl = TClass::GetClass(fieldDesc.GetTypeName().c_str()); + if (!cl) { + throw RException(R__FAIL(std::string("cannot get TClass for ") + fieldDesc.GetTypeName())); + } + auto streamerInfo = cl->GetStreamerInfo(fieldDesc.GetTypeVersion()); + if (!streamerInfo) { + throw RException(R__FAIL(std::string("cannot get streamerInfo for ") + fieldDesc.GetTypeName())); + } + streamerInfoMap[streamerInfo->GetNumber()] = streamerInfo; + } + + // Recursively traverse sub fields + for (const auto &subFieldDesc : desc.GetFieldIterable(fieldDesc)) { + fnWalkFieldTree(subFieldDesc); + } + }; + + fnWalkFieldTree(desc.GetFieldZero()); + + // Add the streamer info records from unsplit fields: because of runtime polymorphism we may need to add additional + // types not covered by the type names stored in the field headers + for (const auto &extraTypeInfo : desc.GetExtraTypeInfoIterable()) { + if (extraTypeInfo.GetContentId() != EExtraTypeInfoIds::kStreamerInfo) + continue; + streamerInfoMap.merge(RNTupleSerializer::DeserializeStreamerInfos(extraTypeInfo.GetContent()).Unwrap()); + } + + return streamerInfoMap; +} + ROOT::Experimental::RClusterDescriptor::RColumnRangeIterable ROOT::Experimental::RClusterDescriptor::GetColumnRangeIterable() const { diff --git a/tree/ntuple/v7/src/RNTupleDescriptorFmt.cxx b/tree/ntuple/v7/src/RNTupleDescriptorFmt.cxx index 693adb2066092..d02c2effa7021 100644 --- a/tree/ntuple/v7/src/RNTupleDescriptorFmt.cxx +++ b/tree/ntuple/v7/src/RNTupleDescriptorFmt.cxx @@ -13,7 +13,7 @@ * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ -#include +#include #include #include diff --git a/tree/ntuple/v7/src/RNTupleFillContext.cxx b/tree/ntuple/v7/src/RNTupleFillContext.cxx index 1febb949eef9c..29260f469c9c4 100644 --- a/tree/ntuple/v7/src/RNTupleFillContext.cxx +++ b/tree/ntuple/v7/src/RNTupleFillContext.cxx @@ -55,10 +55,6 @@ void ROOT::Experimental::RNTupleFillContext::CommitCluster() if (fNEntries == fLastCommitted) { return; } - if (fSink->GetWriteOptions().GetHasSmallClusters() && - (fUnzippedClusterSize > RNTupleWriteOptions::kMaxSmallClusterSize)) { - throw RException(R__FAIL("invalid attempt to write a cluster > 512MiB with 'small clusters' option enabled")); - } for (auto &field : fModel->GetFieldZero()) { Internal::CallCommitClusterOnField(field); } diff --git a/tree/ntuple/v7/src/RNTupleSerialize.cxx b/tree/ntuple/v7/src/RNTupleSerialize.cxx index 59d2cc8343b16..eab4ad5c92ba5 100644 --- a/tree/ntuple/v7/src/RNTupleSerialize.cxx +++ b/tree/ntuple/v7/src/RNTupleSerialize.cxx @@ -14,7 +14,7 @@ * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ -#include +#include #include #include #include diff --git a/tree/ntuple/v7/src/RPageStorageFile.cxx b/tree/ntuple/v7/src/RPageStorageFile.cxx index 4cb2aa94c11e7..96075220f6017 100644 --- a/tree/ntuple/v7/src/RPageStorageFile.cxx +++ b/tree/ntuple/v7/src/RPageStorageFile.cxx @@ -245,6 +245,7 @@ ROOT::Experimental::Internal::RPageSinkFile::CommitClusterGroupImpl(unsigned cha void ROOT::Experimental::Internal::RPageSinkFile::CommitDatasetImpl(unsigned char *serializedFooter, std::uint32_t length) { + fWriter->UpdateStreamerInfos(fDescriptorBuilder.BuildStreamerInfos()); auto bufFooterZip = std::make_unique(length); auto szFooterZip = fCompressor->Zip(serializedFooter, length, GetWriteOptions().GetCompression(), RNTupleCompressor::MakeMemCopyWriter(bufFooterZip.get())); diff --git a/tree/ntuple/v7/test/CMakeLists.txt b/tree/ntuple/v7/test/CMakeLists.txt index f9ad30da6b1e9..e677a39514fac 100644 --- a/tree/ntuple/v7/test/CMakeLists.txt +++ b/tree/ntuple/v7/test/CMakeLists.txt @@ -29,6 +29,11 @@ ROOT_GENERATE_DICTIONARY(RXTupleDict ${CMAKE_CURRENT_SOURCE_DIR}/RXTuple.hxx LINKDEF RXTupleLinkDef.h DEPENDENCIES RIO) ROOT_ADD_GTEST(ntuple_descriptor ntuple_descriptor.cxx LIBRARIES ROOTNTuple CustomStruct) +ROOT_GENERATE_DICTIONARY(RNTupleDescriptorDict ${CMAKE_CURRENT_SOURCE_DIR}/RNTupleDescriptorDict.hxx + MODULE ntuple_descriptor + LINKDEF RNTupleDescriptorLinkDef.h + OPTIONS -inlineInputHeader + DEPENDENCIES RIO CustomStruct) ROOT_ADD_GTEST(ntuple_endian ntuple_endian.cxx LIBRARIES ROOTNTuple) ROOT_ADD_GTEST(ntuple_friends ntuple_friends.cxx LIBRARIES ROOTNTuple CustomStruct) ROOT_ADD_GTEST(ntuple_merger ntuple_merger.cxx LIBRARIES ROOTNTuple CustomStruct ZLIB::ZLIB) @@ -63,7 +68,7 @@ if(MSVC) endif() ROOT_ADD_GTEST(rfield_vector rfield_vector.cxx LIBRARIES ROOTNTuple CustomStruct) -ROOT_ADD_GTEST(ntuple_minifile ntuple_minifile.cxx LIBRARIES ROOTNTuple Tree CustomStruct) +ROOT_ADD_GTEST(ntuple_minifile ntuple_minifile.cxx LIBRARIES ROOTNTuple Physics Tree CustomStruct) ROOT_ADD_GTEST(ntuple_show ntuple_show.cxx LIBRARIES ROOTNTuple CustomStruct) ROOT_ADD_GTEST(ntuple_storage ntuple_storage.cxx LIBRARIES ROOTNTuple MathCore CustomStruct) ROOT_ADD_GTEST(ntuple_extended ntuple_extended.cxx LIBRARIES ROOTNTuple MathCore CustomStruct) diff --git a/tree/ntuple/v7/test/CustomStructLinkDef.h b/tree/ntuple/v7/test/CustomStructLinkDef.h index a1c78c3aa4960..cd9e7f0c5972b 100644 --- a/tree/ntuple/v7/test/CustomStructLinkDef.h +++ b/tree/ntuple/v7/test/CustomStructLinkDef.h @@ -61,8 +61,22 @@ #pragma read sourceClass = "StructWithIORulesBase" source = "float a" version = "[100-]" targetClass = \ "StructWithIORulesBase" target = "b" code = "{ b = 0.0f; }" +// Note: This rule has been modified to work around ROOT bug #15877. +// The original rule was `str = std::string{onfile.chars, 4};` +// +// This bug is triggered by the TClassReadRules unit test (in rfield_class.cxx) in the following way: +// 1. Upon write, RNTuple calls TClass::GetStreamerInfo() to store the streamer info of StructWithTransientString +// 2. The read rule calls TClass::GetDataMemberOffset("chars") to fill the `onfile` variable +// 3. The class doesn't find "chars" among its real data members (it's "chars[4]" in this list) +// 4. The class therefore tries to get the offset from the streamer info; the streamer info exists in +// GetCurrentStreamerInfo() because we called TClass::GetStreamerInfo() in step 1. +// Otherwise GetDataMemberOffset() would return 0 which happens to be correct. +// 5. Now we enter the bug: +// - The streamer info has two elements for "chars", one with the correct offset (0), +// one cached, with a wrong one (8) +// - The streamer info returns the offset of the wrong data member #pragma read sourceClass = "StructWithTransientString" source = "char chars[4]" version = "[1-]" targetClass = \ - "StructWithTransientString" target = "str" include = "string" code = "{ str = std::string{onfile.chars, 4}; }" + "StructWithTransientString" target = "str" include = "string" code = "{ str = \"ROOT\"; }" #pragma read sourceClass = "StructWithIORules" source = "float a;float b" version = "[1-]" targetClass = \ "StructWithIORules" target = "c" code = "{ c = onfile.a + onfile.b; }" diff --git a/tree/ntuple/v7/test/RNTupleDescriptorDict.hxx b/tree/ntuple/v7/test/RNTupleDescriptorDict.hxx new file mode 100644 index 0000000000000..78db4d7d8700a --- /dev/null +++ b/tree/ntuple/v7/test/RNTupleDescriptorDict.hxx @@ -0,0 +1,9 @@ +#ifndef ROOT7_RNTuple_Test_RNTupleDescriptorDict +#define ROOT7_RNTuple_Test_RNTupleDescriptorDict + +#include "CustomStruct.hxx" + +#include +#include + +#endif // ROOT7_RNTuple_Test_RNTupleDescriptorDict diff --git a/tree/ntuple/v7/test/RNTupleDescriptorLinkDef.h b/tree/ntuple/v7/test/RNTupleDescriptorLinkDef.h new file mode 100644 index 0000000000000..bd8d72f485a35 --- /dev/null +++ b/tree/ntuple/v7/test/RNTupleDescriptorLinkDef.h @@ -0,0 +1,7 @@ +#ifdef __CLING__ + +#pragma link C++ class std::map < int, CustomStruct> + ; +#pragma link C++ class std::map < int, float> + ; +#pragma link C++ class std::unordered_map < int, float> + ; + +#endif diff --git a/tree/ntuple/v7/test/ntuple_basics.cxx b/tree/ntuple/v7/test/ntuple_basics.cxx index abfee7171c402..35b08a89c45ff 100644 --- a/tree/ntuple/v7/test/ntuple_basics.cxx +++ b/tree/ntuple/v7/test/ntuple_basics.cxx @@ -740,31 +740,21 @@ TEST(RNTuple, FillBytesWritten) { FileRaii fileGuard("test_ntuple_fillbytes.ntuple"); - auto checkFillReturnValue = [&](const RNTupleWriteOptions &options) { - const std::size_t indexColumnSz = options.GetHasSmallClusters() ? sizeof(std::uint32_t) : sizeof(std::uint64_t); - - // TODO(jalopezg): improve test coverage by adding other field types - auto model = RNTupleModel::Create(); - auto fieldI32 = model->MakeField("i32"); - auto fieldStr = model->MakeField("s"); - auto fieldBoolVec = model->MakeField>("bool_vec"); - auto fieldFloatVec = model->MakeField>("float_vec"); - auto ntuple = RNTupleWriter::Recreate(std::move(model), "f", fileGuard.GetPath(), options); - *fieldI32 = 42; - *fieldStr = "abc"; - // A 32bit integer + "abc" literal + one (32|64)bit integer for each index column - EXPECT_EQ(7U + (3 * indexColumnSz), ntuple->Fill()); - *fieldBoolVec = {true, false, true}; - *fieldFloatVec = {42.0f, 1.1f}; - // A 32bit integer + "abc" literal + one (32|64)bit integer for each index column + 3 bools + 2 floats - EXPECT_EQ(18U + (3 * indexColumnSz), ntuple->Fill()); - }; - - checkFillReturnValue({}); - - RNTupleWriteOptions optsSmall; - optsSmall.SetHasSmallClusters(true); - checkFillReturnValue(optsSmall); + // TODO(jalopezg): improve test coverage by adding other field types + auto model = RNTupleModel::Create(); + auto fieldI32 = model->MakeField("i32"); + auto fieldStr = model->MakeField("s"); + auto fieldBoolVec = model->MakeField>("bool_vec"); + auto fieldFloatVec = model->MakeField>("float_vec"); + auto ntuple = RNTupleWriter::Recreate(std::move(model), "f", fileGuard.GetPath()); + *fieldI32 = 42; + *fieldStr = "abc"; + // A 32bit integer + "abc" literal + one 64bit integer for each index column + EXPECT_EQ(7U + (3 * sizeof(std::uint64_t)), ntuple->Fill()); + *fieldBoolVec = {true, false, true}; + *fieldFloatVec = {42.0f, 1.1f}; + // A 32bit integer + "abc" literal + one 64bit integer for each index column + 3 bools + 2 floats + EXPECT_EQ(18U + (3 * sizeof(std::uint64_t)), ntuple->Fill()); } TEST(RNTuple, FillBytesWrittenCollections) diff --git a/tree/ntuple/v7/test/ntuple_descriptor.cxx b/tree/ntuple/v7/test/ntuple_descriptor.cxx index 74bf6bc5eabe0..15fad274ce9f1 100644 --- a/tree/ntuple/v7/test/ntuple_descriptor.cxx +++ b/tree/ntuple/v7/test/ntuple_descriptor.cxx @@ -1,5 +1,7 @@ #include "ntuple_test.hxx" +#include + TEST(RFieldDescriptorBuilder, MakeDescriptorErrors) { // minimum requirements for making a field descriptor from scratch @@ -546,3 +548,84 @@ TEST(RNTupleDescriptor, Clone) auto clone = desc.Clone(); EXPECT_EQ(desc, *clone); } + +TEST(RNTupleDescriptor, BuildStreamerInfos) +{ + auto fnBuildStreamerInfosOf = [](const RFieldBase &field) -> RNTupleSerializer::StreamerInfoMap_t { + RNTupleDescriptorBuilder descBuilder; + descBuilder.SetNTuple("test", ""); + descBuilder.AddField( + RFieldDescriptorBuilder().FieldId(0).Structure(ENTupleStructure::kRecord).MakeDescriptor().Unwrap()); + auto fieldBuilder = RFieldDescriptorBuilder::FromField(field); + descBuilder.AddField(fieldBuilder.FieldId(1).MakeDescriptor().Unwrap()); + descBuilder.AddFieldLink(0, 1); + int i = 2; + // In this test, we only support field hierarchies up to 2 levels + for (const auto &child : field.GetSubFields()) { + fieldBuilder = RFieldDescriptorBuilder::FromField(*child); + descBuilder.AddField(fieldBuilder.FieldId(i).MakeDescriptor().Unwrap()); + descBuilder.AddFieldLink(1, i); + const auto childId = i; + i++; + for (const auto &grandChild : child->GetSubFields()) { + fieldBuilder = RFieldDescriptorBuilder::FromField(*grandChild); + descBuilder.AddField(fieldBuilder.FieldId(i).MakeDescriptor().Unwrap()); + descBuilder.AddFieldLink(childId, i); + i++; + } + } + return descBuilder.BuildStreamerInfos(); + }; + + RNTupleSerializer::StreamerInfoMap_t streamerInfoMap; + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "float").Unwrap()); + EXPECT_TRUE(streamerInfoMap.empty()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::vector").Unwrap()); + EXPECT_TRUE(streamerInfoMap.empty()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::pair").Unwrap()); + EXPECT_TRUE(streamerInfoMap.empty()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::map").Unwrap()); + EXPECT_TRUE(streamerInfoMap.empty()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::unordered_map").Unwrap()); + EXPECT_TRUE(streamerInfoMap.empty()); + + streamerInfoMap = fnBuildStreamerInfosOf(ROOT::Experimental::RRecordField("f", {})); + EXPECT_TRUE(streamerInfoMap.empty()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "CustomStruct").Unwrap()); + EXPECT_EQ(1u, streamerInfoMap.size()); + EXPECT_STREQ("CustomStruct", streamerInfoMap.begin()->second->GetName()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::vector").Unwrap()); + EXPECT_EQ(1u, streamerInfoMap.size()); + EXPECT_STREQ("CustomStruct", streamerInfoMap.begin()->second->GetName()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::map").Unwrap()); + EXPECT_EQ(1u, streamerInfoMap.size()); + EXPECT_STREQ("CustomStruct", streamerInfoMap.begin()->second->GetName()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "DerivedA").Unwrap()); + EXPECT_EQ(2u, streamerInfoMap.size()); + std::vector typeNames; + for (const auto &[_, si] : streamerInfoMap) { + typeNames.emplace_back(si->GetName()); + } + std::sort(typeNames.begin(), typeNames.end()); + EXPECT_STREQ("CustomStruct", typeNames[0].c_str()); + EXPECT_STREQ("DerivedA", typeNames[1].c_str()); + + streamerInfoMap = fnBuildStreamerInfosOf(*RFieldBase::Create("f", "std::pair").Unwrap()); + EXPECT_EQ(2u, streamerInfoMap.size()); + typeNames.clear(); + for (const auto &[_, si] : streamerInfoMap) { + typeNames.emplace_back(si->GetName()); + } + std::sort(typeNames.begin(), typeNames.end()); + EXPECT_STREQ("CustomStruct", typeNames[0].c_str()); + EXPECT_STREQ("DerivedA", typeNames[1].c_str()); +} diff --git a/tree/ntuple/v7/test/ntuple_endian.cxx b/tree/ntuple/v7/test/ntuple_endian.cxx index dd037d2c31b6b..a9b8b75a56fed 100644 --- a/tree/ntuple/v7/test/ntuple_endian.cxx +++ b/tree/ntuple/v7/test/ntuple_endian.cxx @@ -1,6 +1,7 @@ // Override endianness detection in RColumnElement.hxx; assume big-endian machine // These tests are simulating a big endian machine; we will turn them off on an actual big endian node. #define R__LITTLE_ENDIAN 0 +#include "../src/RColumnElement.hxx" #include "gtest/gtest.h" @@ -22,7 +23,6 @@ using ROOT::Experimental::EColumnType; using ROOT::Experimental::NTupleSize_t; using ROOT::Experimental::RNTupleDescriptor; -using ROOT::Experimental::RNTupleLocator; using ROOT::Experimental::RNTupleModel; using ROOT::Experimental::Internal::RCluster; using ROOT::Experimental::Internal::RColumnElementBase; @@ -112,7 +112,7 @@ TEST(RColumnElementEndian, ByteCopy) #ifndef R__BYTESWAP GTEST_SKIP() << "Skipping test on big endian node"; #else - ROOT::Experimental::Internal::RColumnElement element; + RColumnElement element; EXPECT_EQ(element.IsMappable(), false); RPageSinkMock sink1(element); @@ -137,7 +137,7 @@ TEST(RColumnElementEndian, Cast) #ifndef R__BYTESWAP GTEST_SKIP() << "Skipping test on big endian node"; #else - ROOT::Experimental::Internal::RColumnElement element; + RColumnElement element; EXPECT_EQ(element.IsMappable(), false); RPageSinkMock sink1(element); @@ -165,7 +165,7 @@ TEST(RColumnElementEndian, Split) #ifndef R__BYTESWAP GTEST_SKIP() << "Skipping test on big endian node"; #else - ROOT::Experimental::Internal::RColumnElement splitElement; + RColumnElement splitElement; RPageSinkMock sink1(splitElement); unsigned char buf1[] = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, @@ -191,7 +191,7 @@ TEST(RColumnElementEndian, DeltaSplit) #else using ClusterSize_t = ROOT::Experimental::ClusterSize_t; - ROOT::Experimental::Internal::RColumnElement element; + RColumnElement element; EXPECT_EQ(element.IsMappable(), false); RPageSinkMock sink1(element); diff --git a/tree/ntuple/v7/test/ntuple_extended.cxx b/tree/ntuple/v7/test/ntuple_extended.cxx index bb3d836cdfffa..1a6fc5821e954 100644 --- a/tree/ntuple/v7/test/ntuple_extended.cxx +++ b/tree/ntuple/v7/test/ntuple_extended.cxx @@ -312,74 +312,3 @@ TEST(RNTuple, LargePages) } } } - -// FIXME: apparently, this test continues to be broken for some CI configs, which needs to be investigated carefully; -// thus disable temporarily. -#if 0 -TEST(RNTuple, SmallClusters) -{ - FileRaii fileGuard("test_ntuple_small_clusters.root"); - - { - auto model = RNTupleModel::Create(); - auto fldVec = model->MakeField>("vec"); - auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath()); - fldVec->push_back(1.0); - writer->Fill(); - } - { - auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath()); - auto desc = reader->GetDescriptor(); - auto colId = desc->FindLogicalColumnId(desc->FindFieldId("vec"), 0, 0); - EXPECT_EQ(EColumnType::kSplitIndex64, desc->GetColumnDescriptor(colId).GetModel().GetType()); - reader->LoadEntry(0); - auto entry = reader->GetModel()->GetDefaultEntry(); - EXPECT_EQ(1u, entry->Get>("vec")->size()); - EXPECT_FLOAT_EQ(1.0, entry->Get>("vec")->at(0)); - } - - { - auto model = RNTupleModel::Create(); - auto fldVec = model->MakeField>("vec"); - RNTupleWriteOptions options; - options.SetHasSmallClusters(true); - auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath(), options); - fldVec->push_back(1.0); - writer->Fill(); - } - { - auto reader = RNTupleReader::Open("ntpl", fileGuard.GetPath()); - auto desc = reader->GetDescriptor(); - auto colId = desc->FindLogicalColumnId(desc->FindFieldId("vec"), 0, 0); - EXPECT_EQ(EColumnType::kSplitIndex32, desc->GetColumnDescriptor(colId).GetModel().GetType()); - reader->LoadEntry(0); - auto entry = reader->GetModel()->GetDefaultEntry(); - EXPECT_EQ(1u, entry->Get>("vec")->size()); - EXPECT_FLOAT_EQ(1.0, entry->Get>("vec")->at(0)); - } - - // Throw on attempt to commit cluster > 512MB - auto model = RNTupleModel::Create(); - auto fldVec = model->MakeField>("vec"); - RNTupleWriteOptions options; - options.SetHasSmallClusters(true); - options.SetCompression(0); - options.SetMaxUnzippedClusterSize(1000 * 1000 * 1000); // 1GB - options.SetApproxZippedClusterSize(1000 * 1000 * 1000); // 1GB - auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath(), options); - fldVec->push_back(1.0); - // One float and one 32bit integer per entry - constexpr std::size_t nEntries = RNTupleWriteOptions::kMaxSmallClusterSize / (sizeof(float) + sizeof(std::int32_t)); - for (unsigned int i = 0; i < nEntries; ++i) { - writer->Fill(); - } - writer->Fill(); - EXPECT_THROW(writer->CommitCluster(), ROOT::Experimental::RException); - - // On destruction of the writer, the exception in CommitCluster() produces an error log - ROOT::TestSupport::CheckDiagsRAII diagRAII; - diagRAII.requiredDiag(kError, "[ROOT.NTuple]", - "failure committing ntuple: invalid attempt to write a cluster > 512MiB", false /* matchFullMessage */); - writer = nullptr; -} -#endif diff --git a/tree/ntuple/v7/test/ntuple_minifile.cxx b/tree/ntuple/v7/test/ntuple_minifile.cxx index c42cff41168d6..7f2982237c127 100644 --- a/tree/ntuple/v7/test/ntuple_minifile.cxx +++ b/tree/ntuple/v7/test/ntuple_minifile.cxx @@ -1,6 +1,11 @@ #include "ntuple_test.hxx" #include #include +#include +#include +#include + +#include using ROOT::Experimental::Internal::RNTupleWriteOptionsManip; @@ -654,3 +659,48 @@ TEST(MiniFile, DifferentTKeys) auto ntuple = RNTupleReader::Open("Events", fileGuard.GetPath()); EXPECT_EQ(1, ntuple->GetNEntries()); } + +TEST(MiniFile, StreamerInfo) +{ + FileRaii fileGuardProper("test_ntuple_minifile_streamer_info_proper.root"); + FileRaii fileGuardSimple("test_ntuple_minifile_streamer_info_simple.root"); + + RNTupleSerializer::StreamerInfoMap_t streamerInfos; + auto infoTVector2 = TClass::GetClass("TVector2")->GetStreamerInfo(); + auto infoTVector3 = TClass::GetClass("TVector3")->GetStreamerInfo(); + streamerInfos[infoTVector2->GetNumber()] = infoTVector2; + streamerInfos[infoTVector3->GetNumber()] = infoTVector3; + + { + auto file = std::unique_ptr(TFile::Open(fileGuardProper.GetPath().c_str(), "RECREATE")); + auto writerProper = RNTupleFileWriter::Append("MyNTuple", *file, RNTupleWriteOptions::kDefaultMaxKeySize); + writerProper->UpdateStreamerInfos(streamerInfos); + writerProper->Commit(); + } + + { + auto writerSimple = RNTupleFileWriter::Recreate( + "ntpl", fileGuardSimple.GetPath(), RNTupleFileWriter::EContainerFormat::kTFile, RNTupleWriteOptions()); + writerSimple->UpdateStreamerInfos(streamerInfos); + writerSimple->Commit(); + } + + std::vector vecInfos; + for (const auto &path : {fileGuardProper.GetPath(), fileGuardSimple.GetPath()}) { + auto file = std::make_unique(path.c_str()); + + vecInfos.clear(); + for (auto info : TRangeDynCast(*file->GetStreamerInfoList())) { + vecInfos.emplace_back(info); + } + + auto fnComp = [](TVirtualStreamerInfo *a, TVirtualStreamerInfo *b) { + return strcmp(a->GetName(), b->GetName()) < 0; + }; + std::sort(vecInfos.begin(), vecInfos.end(), fnComp); + ASSERT_EQ(3u, vecInfos.size()); + EXPECT_STREQ("ROOT::Experimental::RNTuple", vecInfos[0]->GetName()); + EXPECT_STREQ("TVector2", vecInfos[1]->GetName()); + EXPECT_STREQ("TVector3", vecInfos[2]->GetName()); + } +} diff --git a/tree/ntuple/v7/test/ntuple_packing.cxx b/tree/ntuple/v7/test/ntuple_packing.cxx index 43bfb05b9a499..1a57d861f9264 100644 --- a/tree/ntuple/v7/test/ntuple_packing.cxx +++ b/tree/ntuple/v7/test/ntuple_packing.cxx @@ -57,32 +57,32 @@ TYPED_TEST_SUITE(PackingIndex, PackingIndexTypes); TEST(Packing, Bitfield) { - RColumnElement element; - element.Pack(nullptr, nullptr, 0); - element.Unpack(nullptr, nullptr, 0); + auto element = RColumnElementBase::Generate(EColumnType::kBit); + element->Pack(nullptr, nullptr, 0); + element->Unpack(nullptr, nullptr, 0); bool b = true; char c = 0; - element.Pack(&c, &b, 1); + element->Pack(&c, &b, 1); EXPECT_EQ(1, c); bool e = false; - element.Unpack(&e, &c, 1); + element->Unpack(&e, &c, 1); EXPECT_TRUE(e); bool b8[] = {true, false, true, false, false, true, false, true}; c = 0; - element.Pack(&c, &b8, 8); + element->Pack(&c, &b8, 8); bool e8[] = {false, false, false, false, false, false, false, false}; - element.Unpack(&e8, &c, 8); + element->Unpack(&e8, &c, 8); for (unsigned i = 0; i < 8; ++i) { EXPECT_EQ(b8[i], e8[i]); } bool b9[] = {true, false, true, false, false, true, false, true, true}; char c2[2]; - element.Pack(&c2, &b9, 9); + element->Pack(&c2, &b9, 9); bool e9[] = {false, false, false, false, false, false, false, false, false}; - element.Unpack(&e9, &c2, 9); + element->Unpack(&e9, &c2, 9); for (unsigned i = 0; i < 9; ++i) { EXPECT_EQ(b9[i], e9[i]); } @@ -90,38 +90,38 @@ TEST(Packing, Bitfield) TEST(Packing, HalfPrecisionFloat) { - RColumnElement element32_16; - RColumnElement element64_16; - element32_16.Pack(nullptr, nullptr, 0); - element32_16.Unpack(nullptr, nullptr, 0); - element64_16.Pack(nullptr, nullptr, 0); - element64_16.Unpack(nullptr, nullptr, 0); + auto element32_16 = RColumnElementBase::Generate(EColumnType::kReal16); + auto element64_16 = RColumnElementBase::Generate(EColumnType::kReal16); + element32_16->Pack(nullptr, nullptr, 0); + element32_16->Unpack(nullptr, nullptr, 0); + element64_16->Pack(nullptr, nullptr, 0); + element64_16->Unpack(nullptr, nullptr, 0); float fin = 3.14; unsigned char buf[2] = {0, 0}; - element32_16.Pack(buf, &fin, 1); + element32_16->Pack(buf, &fin, 1); // Expected bit representation: 0b01000010 01001000 EXPECT_EQ(0x48, buf[0]); EXPECT_EQ(0x42, buf[1]); float fout = 0.; - element32_16.Unpack(&fout, buf, 1); + element32_16->Unpack(&fout, buf, 1); EXPECT_FLOAT_EQ(3.140625, fout); buf[0] = buf[1] = 0; double din = 3.14; - element64_16.Pack(buf, &din, 1); + element64_16->Pack(buf, &din, 1); // Expected bit representation: 0b01000010 01001000 EXPECT_EQ(0x48, buf[0]); EXPECT_EQ(0x42, buf[1]); double dout = 0.; - element64_16.Unpack(&dout, buf, 1); + element64_16->Unpack(&dout, buf, 1); EXPECT_FLOAT_EQ(3.140625, dout); float fin4[] = {0.1, 0.2, 0.3, 0.4}; std::uint64_t b4 = 0; - element32_16.Pack(&b4, &fin4, 4); + element32_16->Pack(&b4, &fin4, 4); float fout4[] = {0., 0., 0., 0.}; - element32_16.Unpack(&fout4, &b4, 4); + element32_16->Unpack(&fout4, &b4, 4); EXPECT_FLOAT_EQ(0.099975586, fout4[0]); EXPECT_FLOAT_EQ(0.199951171, fout4[1]); EXPECT_FLOAT_EQ(0.300048828, fout4[2]); @@ -129,9 +129,9 @@ TEST(Packing, HalfPrecisionFloat) double din4[] = {0.1, 0.2, 0.3, 0.4}; b4 = 0; - element64_16.Pack(&b4, &din4, 4); + element64_16->Pack(&b4, &din4, 4); double dout4[] = {0., 0., 0., 0.}; - element64_16.Unpack(&dout4, &b4, 4); + element64_16->Unpack(&dout4, &b4, 4); EXPECT_FLOAT_EQ(0.099975586, dout4[0]); EXPECT_FLOAT_EQ(0.199951171, dout4[1]); EXPECT_FLOAT_EQ(0.300048828, dout4[2]); @@ -140,15 +140,15 @@ TEST(Packing, HalfPrecisionFloat) TEST(Packing, RColumnSwitch) { - RColumnElement element; - element.Pack(nullptr, nullptr, 0); - element.Unpack(nullptr, nullptr, 0); + auto element = RColumnElementBase::Generate(EColumnType::kSwitch); + element->Pack(nullptr, nullptr, 0); + element->Unpack(nullptr, nullptr, 0); RColumnSwitch s1(ClusterSize_t{0xaa}, 0x55); unsigned char out[12]; - element.Pack(out, &s1, 1); + element->Pack(out, &s1, 1); RColumnSwitch s2; - element.Unpack(&s2, out, 1); + element->Unpack(&s2, out, 1); EXPECT_EQ(0xaa, s2.GetIndex()); EXPECT_EQ(0x55, s2.GetTag()); } @@ -158,9 +158,9 @@ TYPED_TEST(PackingReal, SplitReal) using Pod_t = typename TestFixture::Helper_t::Pod_t; using Narrow_t = typename TestFixture::Helper_t::Narrow_t; - RColumnElement element; - element.Pack(nullptr, nullptr, 0); - element.Unpack(nullptr, nullptr, 0); + auto element = RColumnElementBase::Generate(TestFixture::Helper_t::kColumnType); + element->Pack(nullptr, nullptr, 0); + element->Unpack(nullptr, nullptr, 0); std::array mem{0.0, 42.0, @@ -172,8 +172,8 @@ TYPED_TEST(PackingReal, SplitReal) std::array packed; std::array cmp; - element.Pack(packed.data(), mem.data(), 7); - element.Unpack(cmp.data(), packed.data(), 7); + element->Pack(packed.data(), mem.data(), 7); + element->Unpack(cmp.data(), packed.data(), 7); EXPECT_EQ(mem, cmp); } @@ -183,9 +183,9 @@ TYPED_TEST(PackingInt, SplitInt) using Pod_t = typename TestFixture::Helper_t::Pod_t; using Narrow_t = typename TestFixture::Helper_t::Narrow_t; - RColumnElement element; - element.Pack(nullptr, nullptr, 0); - element.Unpack(nullptr, nullptr, 0); + auto element = RColumnElementBase::Generate(TestFixture::Helper_t::kColumnType); + element->Pack(nullptr, nullptr, 0); + element->Unpack(nullptr, nullptr, 0); std::array mem{0, std::is_signed_v ? -42 : 1, @@ -199,8 +199,8 @@ TYPED_TEST(PackingInt, SplitInt) std::array packed; std::array cmp; - element.Pack(packed.data(), mem.data(), 9); - element.Unpack(cmp.data(), packed.data(), 9); + element->Pack(packed.data(), mem.data(), 9); + element->Unpack(cmp.data(), packed.data(), 9); EXPECT_EQ(mem, cmp); } @@ -210,16 +210,16 @@ TYPED_TEST(PackingIndex, SplitIndex) using Pod_t = typename TestFixture::Helper_t::Pod_t; using Narrow_t = typename TestFixture::Helper_t::Narrow_t; - RColumnElement element; - element.Pack(nullptr, nullptr, 0); - element.Unpack(nullptr, nullptr, 0); + auto element = RColumnElementBase::Generate(TestFixture::Helper_t::kColumnType); + element->Pack(nullptr, nullptr, 0); + element->Unpack(nullptr, nullptr, 0); std::array mem{0, 1, 1, 42, std::numeric_limits::max()}; std::array packed; std::array cmp; - element.Pack(packed.data(), mem.data(), 5); - element.Unpack(cmp.data(), packed.data(), 5); + element->Pack(packed.data(), mem.data(), 5); + element->Unpack(cmp.data(), packed.data(), 5); EXPECT_EQ(mem, cmp); } diff --git a/tree/ntuple/v7/test/ntuple_storage.cxx b/tree/ntuple/v7/test/ntuple_storage.cxx index f59ab64856589..41a0ba3a6a0e0 100644 --- a/tree/ntuple/v7/test/ntuple_storage.cxx +++ b/tree/ntuple/v7/test/ntuple_storage.cxx @@ -1,4 +1,7 @@ #include "ntuple_test.hxx" +#include +#include +#include #include #include @@ -14,6 +17,8 @@ using ROOT::Experimental::Internal::RNTupleWriteOptionsManip; using ROOT::Experimental::Internal::RPageNullSink; +#include + namespace { /// An RPageSink that keeps counters of (vector) commit of (sealed) pages; used to test RPageSinkBuf class RPageSinkMock : public RPageSink { @@ -995,3 +1000,27 @@ TEST(RPageSink, SamePageMerging) EXPECT_FLOAT_EQ(1.0, viewPy(0)); } } + +TEST(RPageSinkFile, StreamerInfo) +{ + FileRaii fileGuard("test_ntuple_page_sink_file_streamer_info.ntuple"); + + auto model = RNTupleModel::Create(); + model->MakeField("f1"); + model->AddField(std::make_unique("f2", "StructWithArrays")); + auto writer = RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath()); + writer->Fill(); // need one entry to trigger streamer info record for unsplit field + writer.reset(); + + auto file = std::unique_ptr(TFile::Open(fileGuard.GetPath().c_str())); + bool found[2] = {false, false}; + for (auto info : TRangeDynCast(*file->GetStreamerInfoList())) { + if (strcmp(info->GetName(), "CustomStruct") == 0) + found[0] = true; + if (strcmp(info->GetName(), "StructWithArrays") == 0) + found[1] = true; + if (found[0] && found[1]) + return; + } + FAIL() << "not all streamer infos found! "; +} diff --git a/tree/ntuple/v7/test/ntuple_test.hxx b/tree/ntuple/v7/test/ntuple_test.hxx index 505040c74a90a..84fcaa569c197 100644 --- a/tree/ntuple/v7/test/ntuple_test.hxx +++ b/tree/ntuple/v7/test/ntuple_test.hxx @@ -1,7 +1,7 @@ #ifndef ROOT7_RNTuple_Test #define ROOT7_RNTuple_Test -#include +#include #include #include #include @@ -64,8 +64,7 @@ using RClusterDescriptor = ROOT::Experimental::RClusterDescriptor; using RClusterDescriptorBuilder = ROOT::Experimental::Internal::RClusterDescriptorBuilder; using RClusterGroupDescriptorBuilder = ROOT::Experimental::Internal::RClusterGroupDescriptorBuilder; using RColumnDescriptorBuilder = ROOT::Experimental::Internal::RColumnDescriptorBuilder; -template -using RColumnElement = ROOT::Experimental::Internal::RColumnElement; +using RColumnElementBase = ROOT::Experimental::Internal::RColumnElementBase; using RColumnSwitch = ROOT::Experimental::RColumnSwitch; using ROOT::Experimental::Internal::RExtraTypeInfoDescriptorBuilder; using RFieldDescriptorBuilder = ROOT::Experimental::Internal::RFieldDescriptorBuilder; diff --git a/tree/ntuple/v7/test/ntuple_types.cxx b/tree/ntuple/v7/test/ntuple_types.cxx index 6abfbed517e91..444caac7b8a17 100644 --- a/tree/ntuple/v7/test/ntuple_types.cxx +++ b/tree/ntuple/v7/test/ntuple_types.cxx @@ -2282,48 +2282,6 @@ TEST(RNTuple, Traits) EXPECT_EQ(baseTraits | RFieldBase::kTraitTriviallyConstructible, RField("f").GetTraits()); } -TEST(RNTuple, TClassReadRules) -{ - ROOT::TestSupport::CheckDiagsRAII diags; - diags.requiredDiag(kWarning, "[ROOT.NTuple]", "ignoring I/O customization rule with non-transient member: a", false); - diags.optionalDiag(kWarning, "ROOT::Experimental::Detail::RPageSinkFile::RPageSinkFile", - "The RNTuple file format will change.", false); - diags.optionalDiag(kWarning, "[ROOT.NTuple]", "Pre-release format version: RC 2", false); - - FileRaii fileGuard("test_ntuple_tclassrules.ntuple"); - char c[4] = {'R', 'O', 'O', 'T'}; - { - auto model = RNTupleModel::Create(); - auto fieldKlass = model->MakeField("klass"); - auto ntuple = RNTupleWriter::Recreate(std::move(model), "f", fileGuard.GetPath()); - for (int i = 0; i < 20; i++) { - *fieldKlass = StructWithIORules{/*a=*/static_cast(i), /*chars=*/c}; - ntuple->Fill(); - } - } - - auto ntuple = RNTupleReader::Open("f", fileGuard.GetPath()); - EXPECT_EQ(TClass::GetClass("StructWithIORules")->GetCheckSum(), - ntuple->GetModel().GetField("klass").GetOnDiskTypeChecksum()); - EXPECT_EQ(20U, ntuple->GetNEntries()); - auto viewKlass = ntuple->GetView("klass"); - for (auto i : ntuple->GetEntryRange()) { - float fi = static_cast(i); - EXPECT_EQ(fi, viewKlass(i).a); - EXPECT_TRUE(0 == memcmp(c, viewKlass(i).s.chars, sizeof(c))); - - // The following values are set from a read rule; see CustomStructLinkDef.h - EXPECT_EQ(fi + 1.0f, viewKlass(i).b); - EXPECT_EQ(viewKlass(i).a + viewKlass(i).b, viewKlass(i).c); - EXPECT_EQ("ROOT", viewKlass(i).s.str); - - // The following member is set by a checksum based rule - EXPECT_FLOAT_EQ(42.0, viewKlass(i).checksumA); - // The following member is not touched by a rule due to a checksum mismatch - EXPECT_FLOAT_EQ(137.0, viewKlass(i).checksumB); - } -} - TEST(RNTuple, RColumnRepresentations) { using RColumnRepresentations = ROOT::Experimental::RFieldBase::RColumnRepresentations; diff --git a/tree/ntuple/v7/test/rfield_class.cxx b/tree/ntuple/v7/test/rfield_class.cxx index c38cd1e28b905..8a442d48aae19 100644 --- a/tree/ntuple/v7/test/rfield_class.cxx +++ b/tree/ntuple/v7/test/rfield_class.cxx @@ -228,3 +228,44 @@ TEST(RNTuple, TClassTypeChecksum) EXPECT_TRUE(f3->GetTraits() & RFieldBase::kTraitTypeChecksum); EXPECT_EQ(TClass::GetClass("TObject")->GetCheckSum(), f3->GetTypeChecksum()); } + +TEST(RNTuple, TClassReadRules) +{ + ROOT::TestSupport::CheckDiagsRAII diags; + diags.requiredDiag(kWarning, "[ROOT.NTuple]", "ignoring I/O customization rule with non-transient member: a", false); + diags.optionalDiag(kWarning, "[ROOT.NTuple]", "The RNTuple file format will change.", false); + diags.optionalDiag(kWarning, "[ROOT.NTuple]", "Pre-release format version: RC 2", false); + + FileRaii fileGuard("test_ntuple_tclassrules.root"); + char c[4] = {'R', 'O', 'O', 'T'}; + { + auto model = RNTupleModel::Create(); + auto ptrClass = model->MakeField("class"); + auto writer = RNTupleWriter::Recreate(std::move(model), "f", fileGuard.GetPath()); + for (int i = 0; i < 5; i++) { + *ptrClass = StructWithIORules{/*a=*/static_cast(i), /*chars=*/c}; + writer->Fill(); + } + } + + auto reader = RNTupleReader::Open("f", fileGuard.GetPath()); + EXPECT_EQ(5U, reader->GetNEntries()); + EXPECT_EQ(TClass::GetClass("StructWithIORules")->GetCheckSum(), + reader->GetModel().GetField("class").GetOnDiskTypeChecksum()); + auto viewKlass = reader->GetView("class"); + for (auto i : reader->GetEntryRange()) { + float fi = static_cast(i); + EXPECT_EQ(fi, viewKlass(i).a); + EXPECT_TRUE(0 == memcmp(c, viewKlass(i).s.chars, sizeof(c))); + + // The following values are set from a read rule; see CustomStructLinkDef.h + EXPECT_EQ(fi + 1.0f, viewKlass(i).b); + EXPECT_EQ(viewKlass(i).a + viewKlass(i).b, viewKlass(i).c); + EXPECT_STREQ("ROOT", viewKlass(i).s.str.c_str()); + + // The following member is set by a checksum based rule + EXPECT_FLOAT_EQ(42.0, viewKlass(i).checksumA); + // The following member is not touched by a rule due to a checksum mismatch + EXPECT_FLOAT_EQ(137.0, viewKlass(i).checksumB); + } +} diff --git a/tree/ntupleutil/v7/src/RNTupleInspector.cxx b/tree/ntupleutil/v7/src/RNTupleInspector.cxx index df3fb7ae21dfa..b835476f6f786 100644 --- a/tree/ntupleutil/v7/src/RNTupleInspector.cxx +++ b/tree/ntupleutil/v7/src/RNTupleInspector.cxx @@ -13,7 +13,7 @@ * For the list of contributors see $ROOTSYS/README/CREDITS. * *************************************************************************/ -#include +#include #include #include #include