Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RFieldUtils.cxx
Go to the documentation of this file.
1/// \file RFieldUtils.cxx
2/// \ingroup NTuple
3/// \author Jonas Hahnfeld <jonas.hahnfeld@cern.ch>
4/// \date 2024-11-19
5
7
8#include <ROOT/RField.hxx>
9#include <ROOT/RLogger.hxx>
11#include <ROOT/RNTupleTypes.hxx>
12#include <ROOT/RNTupleUtils.hxx>
13
14#include <TClass.h>
15#include <TClassEdit.h>
16#include <TDictAttributeMap.h>
17
18#include <algorithm>
19#include <charconv>
20#include <cstdlib>
21#include <limits>
22#include <string>
23#include <string_view>
24#include <system_error>
25#include <unordered_map>
26#include <utility>
27#include <vector>
28
29namespace {
30
31std::string GetRenormalizedDemangledTypeName(const std::string &demangledName, bool renormalizeStdString);
32
33const std::unordered_map<std::string_view, std::string_view> typeTranslationMap{
34 {"Bool_t", "bool"},
35 {"Float_t", "float"},
36 {"Double_t", "double"},
37 {"string", "std::string"},
38
39 {"byte", "std::byte"},
40 {"Char_t", "char"},
41 {"int8_t", "std::int8_t"},
42 {"UChar_t", "unsigned char"},
43 {"uint8_t", "std::uint8_t"},
44
45 {"Short_t", "short"},
46 {"int16_t", "std::int16_t"},
47 {"UShort_t", "unsigned short"},
48 {"uint16_t", "std::uint16_t"},
49
50 {"Int_t", "int"},
51 {"int32_t", "std::int32_t"},
52 {"UInt_t", "unsigned int"},
53 {"unsigned", "unsigned int"},
54 {"uint32_t", "std::uint32_t"},
55
56 // Long_t and ULong_t follow the platform's size of long and unsigned long: They are 64 bit on 64-bit Linux and
57 // macOS, but 32 bit on 32-bit platforms and Windows (regardless of pointer size).
58 {"Long_t", "long"},
59 {"ULong_t", "unsigned long"},
60
61 {"Long64_t", "long long"},
62 {"int64_t", "std::int64_t"},
63 {"ULong64_t", "unsigned long long"},
64 {"uint64_t", "std::uint64_t"}};
65
66// Natively supported types drop the default template arguments and the CV qualifiers in template arguments.
67// Any types used as a template argument of user classes will keep [U]Long64_t template arguments for the type alias,
68// e.g. MyClass<std::vector<Long64_t>> will normalize to `MyClass<std::vector<std::int64_t>>` but keep the original
69// spelling in the type alias.
70bool IsUserClass(const std::string &typeName)
71{
72 return typeName.rfind("std::", 0) != 0 && typeName.rfind("ROOT::VecOps::RVec<", 0) != 0;
73}
74
75/// Parse a type name of the form `T[n][m]...` and return the base type `T` and a vector that contains,
76/// in order, the declared size for each dimension, e.g. for `unsigned char[1][2][3]` it returns the tuple
77/// `{"unsigned char", {1, 2, 3}}`. Extra whitespace in `typeName` should be removed before calling this function.
78///
79/// If `typeName` is not an array type, it returns a tuple `{T, {}}`. On error, it returns a default-constructed tuple.
80std::tuple<std::string, std::vector<std::size_t>> ParseArrayType(const std::string &typeName)
81{
82 std::vector<std::size_t> sizeVec;
83
84 // Only parse outer array definition, i.e. the right `]` should be at the end of the type name
85 std::string prefix{typeName};
86 while (prefix.back() == ']') {
87 auto posRBrace = prefix.size() - 1;
88 auto posLBrace = prefix.rfind('[', posRBrace);
89 if (posLBrace == std::string_view::npos) {
90 throw ROOT::RException(R__FAIL(std::string("invalid array type: ") + typeName));
91 }
92 if (posRBrace - posLBrace <= 1) {
93 throw ROOT::RException(R__FAIL(std::string("invalid array type: ") + typeName));
94 }
95
96 const std::size_t size =
98 if (size == 0) {
99 throw ROOT::RException(R__FAIL(std::string("invalid array size: ") + typeName));
100 }
101
102 sizeVec.insert(sizeVec.begin(), size);
103 prefix.resize(posLBrace);
104 }
105 return std::make_tuple(prefix, sizeVec);
106}
107
108/// Assembles a (nested) std::array<> based type based on the dimensions retrieved from ParseArrayType(). Returns
109/// baseType if there are no dimensions.
110std::string GetStandardArrayType(const std::string &baseType, const std::vector<std::size_t> &dimensions)
111{
112 std::string typeName = baseType;
113 for (auto i = dimensions.rbegin(), iEnd = dimensions.rend(); i != iEnd; ++i) {
114 typeName = "std::array<" + typeName + "," + std::to_string(*i) + ">";
115 }
116 return typeName;
117}
118
119// Recursively normalizes a template argument using the regular type name normalizer F as a helper.
120template <typename F>
121std::string GetNormalizedTemplateArg(const std::string &arg, bool keepQualifier, F fnTypeNormalizer)
122{
123 R__ASSERT(!arg.empty());
124
125 if (std::isdigit(arg[0]) || arg[0] == '-') {
126 // Integer template argument
128 }
129
130 if (!keepQualifier)
131 return fnTypeNormalizer(arg);
132
133 std::string qualifier;
134 // Type name template argument; template arguments must keep their CV qualifier. We assume that fnTypeNormalizer
135 // strips the qualifier.
136 // Demangled names may have the CV qualifiers suffixed and not prefixed (but const always before volatile).
137 // Note that in the latter case, we may have the CV qualifiers before array brackets, e.g. `int const[2]`.
138 const auto [base, _] = ParseArrayType(arg);
139 if (base.rfind("const ", 0) == 0 || base.rfind("volatile const ", 0) == 0 ||
140 base.find(" const", base.length() - 6) != std::string::npos ||
141 base.find(" const volatile", base.length() - 15) != std::string::npos) {
142 qualifier += "const ";
143 }
144 if (base.rfind("volatile ", 0) == 0 || base.rfind("const volatile ", 0) == 0 ||
145 base.find(" volatile", base.length() - 9) != std::string::npos) {
146 qualifier += "volatile ";
147 }
148 return qualifier + fnTypeNormalizer(arg);
149}
150
151using AnglePos = std::pair<std::string::size_type, std::string::size_type>;
152std::vector<AnglePos> FindTemplateAngleBrackets(const std::string &typeName)
153{
154 std::vector<AnglePos> result;
155 std::string::size_type currentPos = 0;
156 while (currentPos < typeName.size()) {
157 const auto posOpen = typeName.find('<', currentPos);
158 if (posOpen == std::string::npos) {
159 // If there are no more templates, the function is done.
160 break;
161 }
162
163 auto posClose = posOpen + 1;
164 int level = 1;
165 while (posClose < typeName.size()) {
166 const auto c = typeName[posClose];
167 if (c == '<') {
168 level++;
169 } else if (c == '>') {
170 if (level == 1) {
171 break;
172 }
173 level--;
174 }
175 posClose++;
176 }
177 // We should have found a closing angle bracket at the right level.
178 R__ASSERT(posClose < typeName.size());
179 result.emplace_back(posOpen, posClose);
180
181 // If we are not at the end yet, the following two characeters should be :: for nested types.
182 if (posClose < typeName.size() - 1) {
183 R__ASSERT(typeName.substr(posClose + 1, 2) == "::");
184 }
185 currentPos = posClose + 1;
186 }
187
188 return result;
189}
190
191// TClassEdit::CleanType and the name demangling insert blanks between closing angle brackets,
192// as they were required before C++11. Name demangling introduces a blank before array dimensions,
193// which should also be removed.
194void RemoveSpaceBefore(std::string &typeName, char beforeChar)
195{
196 auto dst = typeName.begin();
197 auto end = typeName.end();
198 for (auto src = dst; src != end; ++src) {
199 if (*src == ' ') {
200 auto next = src + 1;
201 if (next != end && *next == beforeChar) {
202 // Skip this space before a closing angle bracket.
203 continue;
204 }
205 }
206 *(dst++) = *src;
207 }
208 typeName.erase(dst, end);
209}
210
211// The demangled name adds spaces after commas
212void RemoveSpaceAfter(std::string &typeName, char afterChar)
213{
214 auto dst = typeName.begin();
215 auto end = typeName.end();
216 for (auto src = dst; src != end; ++src) {
217 *(dst++) = *src;
218 if (*src == afterChar) {
219 auto next = src + 1;
220 if (next != end && *next == ' ') {
221 // Skip this space before a closing angle bracket.
222 ++src;
223 }
224 }
225 }
226 typeName.erase(dst, end);
227}
228
229// We normalize typenames to omit any `class`, `struct`, `enum` prefix
230void RemoveLeadingKeyword(std::string &typeName)
231{
232 if (typeName.rfind("class ", 0) == 0) {
233 typeName.erase(0, 6);
234 } else if (typeName.rfind("struct ", 0) == 0) {
235 typeName.erase(0, 7);
236 } else if (typeName.rfind("enum ", 0) == 0) {
237 typeName.erase(0, 5);
238 }
239}
240
241// Needed for template arguments in demangled names
242void RemoveCVQualifiers(std::string &typeName)
243{
244 if (typeName.rfind("const ", 0) == 0)
245 typeName.erase(0, 6);
246 if (typeName.rfind("volatile ", 0) == 0)
247 typeName.erase(0, 9);
248 if (typeName.find(" volatile", typeName.length() - 9) != std::string::npos)
249 typeName.erase(typeName.length() - 9);
250 if (typeName.find(" const", typeName.length() - 6) != std::string::npos)
251 typeName.erase(typeName.length() - 6);
252}
253
254// Map fundamental integer types to stdint integer types (e.g. int --> std::int32_t)
255void MapIntegerType(std::string &typeName)
256{
257 if (typeName == "signed char") {
259 } else if (typeName == "unsigned char") {
261 } else if (typeName == "short" || typeName == "short int" || typeName == "signed short" ||
262 typeName == "signed short int") {
264 } else if (typeName == "unsigned short" || typeName == "unsigned short int") {
266 } else if (typeName == "int" || typeName == "signed" || typeName == "signed int") {
267 typeName = ROOT::RField<int>::TypeName();
268 } else if (typeName == "unsigned" || typeName == "unsigned int") {
270 } else if (typeName == "long" || typeName == "long int" || typeName == "signed long" ||
271 typeName == "signed long int") {
273 } else if (typeName == "unsigned long" || typeName == "unsigned long int") {
275 } else if (typeName == "long long" || typeName == "long long int" || typeName == "signed long long" ||
276 typeName == "signed long long int") {
278 } else if (typeName == "unsigned long long" || typeName == "unsigned long long int") {
280 } else {
281 // The following two types are 64-bit integers on Windows that we can encounter during renormalization of
282 // demangled std::type_info names.
283 if (typeName == "__int64") {
284 typeName = "std::int64_t";
285 } else if (typeName == "unsigned __int64") {
286 typeName = "std::uint64_t";
287 }
288 }
289}
290
291// Note that ROOT Meta already defines GetDemangledTypeName(), which does both demangling and normalizing.
292std::string GetRawDemangledTypeName(const std::type_info &ti)
293{
294 int e;
295 char *str = TClassEdit::DemangleName(ti.name(), e);
296 R__ASSERT(str && e == 0);
297 std::string result{str};
298 free(str);
299
300 return result;
301}
302
303// Reverse std::string --> std::basic_string<char> from the demangling
305{
306 static const std::string gStringName =
307 GetRenormalizedDemangledTypeName(GetRawDemangledTypeName(typeid(std::string)), false /* renormalizeStdString */);
308
309 // For real nested types of std::string (not typedefs like std::string::size_type), we would need to also check
310 // something like (normalizedTypeName + "::" == gStringName + "::") and replace only the prefix. However, since
311 // such a nested type is not standardized, it currently does not seem necessary to add the logic.
313 normalizedTypeName = "std::string";
314 }
315}
316
317// Reverse "internal" namespace prefix found in demangled names, such as std::vector<T> --> std::__1::vector<T>
319{
320 static std::vector<std::pair<std::string, std::string>> gDistortedStdlibNames = []() {
321 // clang-format off
322 // Listed in order of appearance in the BinaryFormatSpecification.md
323 static const std::vector<std::pair<const std::type_info &, std::string>> gCandidates =
324 {{typeid(std::vector<char>), "std::vector<"},
325 {typeid(std::array<char, 1>), "std::array<"},
326 {typeid(std::variant<char>), "std::variant<"},
327 {typeid(std::pair<char, char>), "std::pair<"},
328 {typeid(std::tuple<char>), "std::tuple<"},
329 {typeid(std::bitset<1>), "std::bitset<"},
330 {typeid(std::unique_ptr<char>), "std::unique_ptr<"},
331 {typeid(std::optional<char>), "std::optional<"},
332 {typeid(std::set<char>), "std::set<"},
333 {typeid(std::unordered_set<char>), "std::unordered_set<"},
334 {typeid(std::multiset<char>), "std::multiset<"},
335 {typeid(std::unordered_multiset<char>), "std::unordered_multiset<"},
336 {typeid(std::map<char, char>), "std::map<"},
337 {typeid(std::unordered_map<char, char>), "std::unordered_map<"},
338 {typeid(std::multimap<char, char>), "std::multimap<"},
339 {typeid(std::unordered_multimap<char, char>), "std::unordered_multimap<"},
340 {typeid(std::atomic<char>), "std::atomic<"}};
341 // clang-format on
342
343 std::vector<std::pair<std::string, std::string>> result;
344 for (const auto &[ti, prefix] : gCandidates) {
345 const auto dm = GetRawDemangledTypeName(ti);
346 if (dm.rfind(prefix, 0) == std::string::npos)
347 result.push_back(std::make_pair(dm.substr(0, dm.find('<') + 1), prefix));
348 }
349
350 return result;
351 }();
352
353 for (const auto &[seenPrefix, canonicalPrefix] : gDistortedStdlibNames) {
354 if (normalizedTypeName.rfind(seenPrefix, 0) == 0) {
356 break;
357 }
358 }
359}
360
361template <typename F>
363{
365 R__ASSERT(!angleBrackets.empty());
366
367 std::string normName;
368 std::string::size_type currentPos = 0;
369 for (std::size_t i = 0; i < angleBrackets.size(); i++) {
370 const auto [posOpen, posClose] = angleBrackets[i];
371 // Append the type prefix until the open angle bracket.
373
374 const auto argList = templatedTypeName.substr(posOpen + 1, posClose - posOpen - 1);
376 R__ASSERT(!templateArgs.empty());
377
379 for (const auto &a : templateArgs) {
381 }
382
383 normName[normName.size() - 1] = '>';
384 currentPos = posClose + 1;
385 }
386
387 // Append the rest of the type from the last closing angle bracket.
388 const auto lastClosePos = angleBrackets.back().second;
390
392}
393
394// Given a type name normalized by ROOT Meta, return the type name normalized according to the RNTuple rules.
395std::string GetRenormalizedMetaTypeName(const std::string &metaNormalizedName)
396{
398 // RNTuple resolves Double32_t for the normalized type name but keeps Double32_t for the type alias
399 // (also in template parameters)
400 if (canonicalTypePrefix == "Double32_t")
401 return "double";
402
403 if (canonicalTypePrefix.find('<') == std::string::npos) {
404 // If there are no templates, the function is done.
405 return canonicalTypePrefix;
406 }
407
408 std::string normName{canonicalTypePrefix};
410
411 return normName;
412}
413
414// Given a demangled name ("normalized by the compiler"), return the type name normalized according to the
415// RNTuple rules.
416std::string GetRenormalizedDemangledTypeName(const std::string &demangledName, bool renormalizeStdString)
417{
418 std::string tn{demangledName};
419 RemoveSpaceBefore(tn, '[');
424
425 if (canonicalTypePrefix.find('<') == std::string::npos) {
426 // If there are no templates, the function is done.
428 }
431 RemoveSpaceBefore(canonicalTypePrefix, ','); // MSVC fancies spaces before commas in the demangled name
433
434 // Remove optional stdlib template arguments
435 int maxTemplateArgs = 0;
436 if (canonicalTypePrefix.rfind("std::vector<", 0) == 0 || canonicalTypePrefix.rfind("std::set<", 0) == 0 ||
437 canonicalTypePrefix.rfind("std::unordered_set<", 0) == 0 ||
438 canonicalTypePrefix.rfind("std::multiset<", 0) == 0 ||
439 canonicalTypePrefix.rfind("std::unordered_multiset<", 0) == 0 ||
440 canonicalTypePrefix.rfind("std::unique_ptr<", 0) == 0) {
441 maxTemplateArgs = 1;
442 } else if (canonicalTypePrefix.rfind("std::map<", 0) == 0 ||
443 canonicalTypePrefix.rfind("std::unordered_map<", 0) == 0 ||
444 canonicalTypePrefix.rfind("std::multimap<", 0) == 0 ||
445 canonicalTypePrefix.rfind("std::unordered_multimap<", 0) == 0) {
446 maxTemplateArgs = 2;
447 }
448
449 std::string normName{canonicalTypePrefix};
452 });
453 // In RenormalizeStdString(), we normalize the demangled type name of `std::string`,
454 // so we need to prevent an endless recursion.
457 }
458
460}
461
462} // namespace
463
464std::string ROOT::Internal::GetCanonicalTypePrefix(const std::string &typeName)
465{
466 // Remove outer cv qualifiers and extra white spaces
467 const std::string cleanedType = TClassEdit::CleanType(typeName.c_str(), /*mode=*/1);
468
469 // Can happen when called from RFieldBase::Create() and is caught there
470 if (cleanedType.empty())
471 return "";
472
474
476 if (canonicalType.substr(0, 2) == "::") {
477 canonicalType.erase(0, 2);
478 }
479
481
482 if (canonicalType.substr(0, 6) == "array<") {
483 canonicalType = "std::" + canonicalType;
484 } else if (canonicalType.substr(0, 7) == "atomic<") {
485 canonicalType = "std::" + canonicalType;
486 } else if (canonicalType.substr(0, 7) == "bitset<") {
487 canonicalType = "std::" + canonicalType;
488 } else if (canonicalType.substr(0, 4) == "map<") {
489 canonicalType = "std::" + canonicalType;
490 } else if (canonicalType.substr(0, 9) == "multimap<") {
491 canonicalType = "std::" + canonicalType;
492 } else if (canonicalType.substr(0, 9) == "multiset<") {
493 canonicalType = "std::" + canonicalType;
494 }
495 if (canonicalType.substr(0, 5) == "pair<") {
496 canonicalType = "std::" + canonicalType;
497 } else if (canonicalType.substr(0, 4) == "set<") {
498 canonicalType = "std::" + canonicalType;
499 } else if (canonicalType.substr(0, 6) == "tuple<") {
500 canonicalType = "std::" + canonicalType;
501 } else if (canonicalType.substr(0, 11) == "unique_ptr<") {
502 canonicalType = "std::" + canonicalType;
503 } else if (canonicalType.substr(0, 14) == "unordered_map<") {
504 canonicalType = "std::" + canonicalType;
505 } else if (canonicalType.substr(0, 19) == "unordered_multimap<") {
506 canonicalType = "std::" + canonicalType;
507 } else if (canonicalType.substr(0, 19) == "unordered_multiset<") {
508 canonicalType = "std::" + canonicalType;
509 } else if (canonicalType.substr(0, 14) == "unordered_set<") {
510 canonicalType = "std::" + canonicalType;
511 } else if (canonicalType.substr(0, 8) == "variant<") {
512 canonicalType = "std::" + canonicalType;
513 } else if (canonicalType.substr(0, 7) == "vector<") {
514 canonicalType = "std::" + canonicalType;
515 } else if (canonicalType.substr(0, 11) == "ROOT::RVec<") {
516 canonicalType = "ROOT::VecOps::RVec<" + canonicalType.substr(11);
517 }
518
519 if (auto it = typeTranslationMap.find(canonicalType); it != typeTranslationMap.end()) {
520 canonicalType = it->second;
521 }
522
524
526}
527
528std::string ROOT::Internal::GetRenormalizedTypeName(const std::type_info &ti)
529{
530 return GetRenormalizedDemangledTypeName(GetRawDemangledTypeName(ti), true /* renormalizeStdString */);
531}
532
537
540{
542 if (canonicalTypePrefix.find('<') == std::string::npos) {
543 // If there are no templates, the function is done.
544 return false;
545 }
546
547 bool result = false;
549 auto fnCheckLong64 = [&](const std::string &arg) -> std::string {
550 if ((arg == "Long64_t" || arg == "ULong64_t") && hasTemplatedUserClassParent) {
551 result = true;
552 return arg;
553 }
554
555 std::string renormalizedArgAlias;
557 result = true;
559 }
560
561 return GetRenormalizedMetaTypeName(arg);
562 };
563
566
567 return result;
568}
569
571{
575 std::string shortType;
576 splitname.ShortType(shortType, modType);
578
579 if (canonicalTypePrefix.find('<') == std::string::npos) {
580 // If there are no templates, the function is done.
581 return canonicalTypePrefix;
582 }
583
585 R__ASSERT(!angleBrackets.empty());
586
587 // For user-defined class types, we will need to get the default-initialized template arguments.
589
590 std::string normName;
591 std::string::size_type currentPos = 0;
592 for (std::size_t i = 0; i < angleBrackets.size(); i++) {
593 const auto [posOpen, posClose] = angleBrackets[i];
594 // Append the type prefix until the open angle bracket.
596
597 const auto argList = canonicalTypePrefix.substr(posOpen + 1, posClose - posOpen - 1);
598 const auto templateArgs = TokenizeTypeList(argList);
599 R__ASSERT(!templateArgs.empty());
600
601 for (const auto &a : templateArgs) {
603 }
604
605 // For user-defined classes, append default-initialized template arguments.
606 if (isUserClass) {
607 const auto cl = TClass::GetClass(canonicalTypePrefix.substr(0, posClose + 1).c_str());
608 if (cl) {
609 const std::string expandedName = cl->GetName();
611 // We can have fewer pairs than angleBrackets, for example in case of type aliases.
613
615 const auto expandedArgList =
618 // Note that we may be in a sitation where expandedTemplateArgs.size() is _smaller_ than
619 // templateArgs.size(), which is when the input type name has the optional template arguments explicitly
620 // spelled out but ROOT Meta is told to ignore some template arguments.
621
622 for (std::size_t j = templateArgs.size(); j < expandedTemplateArgs.size(); ++j) {
623 normName +=
625 }
626 }
627 }
628
629 normName[normName.size() - 1] = '>';
630 currentPos = posClose + 1;
631 }
632
633 // Append the rest of the type from the last closing angle bracket.
634 const auto lastClosePos = angleBrackets.back().second;
636
637 return normName;
638}
639
640std::string ROOT::Internal::GetNormalizedInteger(long long val)
641{
642 return std::to_string(val);
643}
644
645std::string ROOT::Internal::GetNormalizedInteger(unsigned long long val)
646{
647 if (val > std::numeric_limits<std::int64_t>::max())
648 return std::to_string(val) + "u";
649 return std::to_string(val);
650}
651
659
660long long ROOT::Internal::ParseIntTypeToken(const std::string &intToken)
661{
662 std::size_t nChars = 0;
663 long long res = std::stoll(intToken, &nChars);
664 if (nChars == intToken.size())
665 return res;
666
667 assert(nChars < intToken.size());
668 if (nChars == 0) {
669 throw RException(R__FAIL("invalid integer type token: " + intToken));
670 }
671
672 auto suffix = intToken.substr(nChars);
673 std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::toupper);
674 if (suffix == "L" || suffix == "LL")
675 return res;
676 if (res >= 0 && (suffix == "U" || suffix == "UL" || suffix == "ULL"))
677 return res;
678
679 throw RException(R__FAIL("invalid integer type token: " + intToken));
680}
681
682unsigned long long ROOT::Internal::ParseUIntTypeToken(const std::string &uintToken)
683{
684 std::size_t nChars = 0;
685 unsigned long long res = std::stoull(uintToken, &nChars);
686 if (nChars == uintToken.size())
687 return res;
688
689 assert(nChars < uintToken.size());
690 if (nChars == 0) {
691 throw RException(R__FAIL("invalid integer type token: " + uintToken));
692 }
693
694 auto suffix = uintToken.substr(nChars);
695 std::transform(suffix.begin(), suffix.end(), suffix.begin(), ::toupper);
696 if (suffix == "U" || suffix == "L" || suffix == "LL" || suffix == "UL" || suffix == "ULL")
697 return res;
698
699 throw RException(R__FAIL("invalid integer type token: " + uintToken));
700}
701
703{
704 auto am = cl->GetAttributeMap();
705 if (!am || !am->HasKey("rntuple.streamerMode"))
706 return ERNTupleSerializationMode::kUnset;
707
708 std::string value = am->GetPropertyAsString("rntuple.streamerMode");
709 std::transform(value.begin(), value.end(), value.begin(), ::toupper);
710 if (value == "TRUE") {
711 return ERNTupleSerializationMode::kForceStreamerMode;
712 } else if (value == "FALSE") {
713 return ERNTupleSerializationMode::kForceNativeMode;
714 } else {
715 R__LOG_WARNING(ROOT::Internal::NTupleLog()) << "invalid setting for 'rntuple.streamerMode' class attribute: "
716 << am->GetPropertyAsString("rntuple.streamerMode");
717 return ERNTupleSerializationMode::kUnset;
718 }
719}
720
722{
723 auto am = cl->GetAttributeMap();
724 if (am && am->HasKey("rntuple.SoARecord")) {
725 return am->GetPropertyAsString("rntuple.SoARecord");
726 }
727 return "";
728}
729
730std::vector<std::string> ROOT::Internal::TokenizeTypeList(std::string_view templateType, std::size_t maxArgs)
731{
732 std::vector<std::string> result;
733 if (templateType.empty())
734 return result;
735
736 const char *eol = templateType.data() + templateType.length();
737 const char *typeBegin = templateType.data();
738 const char *typeCursor = templateType.data();
739 unsigned int nestingLevel = 0;
740 while (typeCursor != eol) {
741 switch (*typeCursor) {
742 case '<': ++nestingLevel; break;
743 case '>': --nestingLevel; break;
744 case ',':
745 if (nestingLevel == 0) {
746 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
747 if (maxArgs && result.size() == maxArgs)
748 return result;
749 typeBegin = typeCursor + 1;
750 }
751 break;
752 }
753 typeCursor++;
754 }
755 result.push_back(std::string(typeBegin, typeCursor - typeBegin));
756 return result;
757}
758
760 const std::type_info &ti)
761{
762 // Fast path: the caller provided the expected type name (from RField<T>::TypeName())
764 return true;
765
766 // The type name may be equal to the alternative, short type name issued by Meta. This is a rare case used, e.g.,
767 // by the ATLAS DataVector class to hide a default template parameter from the on-disk type name.
768 // Thus, we check again using first ROOT Meta normalization followed by RNTuple re-normalization.
770}
771
773{
774 // Information to print in a single line of the type trace
775 struct RFieldInfo {
776 std::string fFieldName;
777 std::string fTypeName;
779 std::uint32_t fTypeVersion = 0;
780 std::optional<std::uint32_t> fTypeChecksum;
781 };
782
783 std::vector<const RFieldBase *> inMemoryStack;
784 std::vector<const RFieldDescriptor *> onDiskStack;
785
786 auto fnGetLine = [](const RFieldInfo &fieldInfo, int level) -> std::string {
787 std::string line = std::string(2 * level, ' ') + fieldInfo.fFieldName + " [" + fieldInfo.fTypeName;
788 if (fieldInfo.fTypeVersion > 0)
789 line += ", type version: " + std::to_string(fieldInfo.fTypeVersion);
790 if (fieldInfo.fTypeChecksum)
791 line += ", type checksum: " + std::to_string(*fieldInfo.fTypeChecksum);
792 line += "] (id: " + std::to_string(fieldInfo.fFieldId) + ")\n";
793 return line;
794 };
795
796 const RFieldBase *fieldPtr = &field;
797 while (fieldPtr->GetParent()) {
798 inMemoryStack.push_back(fieldPtr);
799 fieldPtr = fieldPtr->GetParent();
800 }
801
802 auto fieldId = field.GetOnDiskId();
803 while (fieldId != kInvalidDescriptorId && fieldId != desc.GetFieldZeroId()) {
804 const auto &fieldDesc = desc.GetFieldDescriptor(fieldId);
805 onDiskStack.push_back(&fieldDesc);
806 fieldId = fieldDesc.GetParentId();
807 }
808
809 std::string report = "In-memory field/type hierarchy:\n";
810 int indentLevel = 0;
811 for (auto itr = inMemoryStack.rbegin(); itr != inMemoryStack.rend(); ++itr, ++indentLevel) {
812 RFieldInfo fieldInfo;
813 fieldInfo.fFieldName = (*itr)->GetFieldName();
814 fieldInfo.fTypeName = (*itr)->GetTypeName();
815 fieldInfo.fFieldId = (*itr)->GetOnDiskId();
816 fieldInfo.fTypeVersion = (*itr)->GetTypeVersion();
817 if ((*itr)->GetTraits() & RFieldBase::kTraitTypeChecksum)
818 fieldInfo.fTypeChecksum = (*itr)->GetTypeChecksum();
819
821 }
822
823 report += "On-disk field/type hierarchy:\n";
824 indentLevel = 0;
825 for (auto itr = onDiskStack.rbegin(); itr != onDiskStack.rend(); ++itr, ++indentLevel) {
826 RFieldInfo fieldInfo;
827 fieldInfo.fFieldName = (*itr)->GetFieldName();
828 fieldInfo.fTypeName = (*itr)->GetTypeName();
829 fieldInfo.fFieldId = (*itr)->GetId();
830 fieldInfo.fTypeVersion = (*itr)->GetTypeVersion();
831 fieldInfo.fTypeChecksum = (*itr)->GetTypeChecksum();
832
834 }
835
836 return report;
837}
838
839std::size_t ROOT::Internal::EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
840{
841 // the size of an RVec<T> is the size of its 4 data-members + optional padding:
842 //
843 // data members:
844 // - void *fBegin
845 // - int32_t fSize
846 // - int32_t fCapacity
847 // - the char[] inline storage, which is aligned like T
848 //
849 // padding might be present:
850 // - between fCapacity and the char[] buffer aligned like T
851 // - after the char[] buffer
852
853 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
854
855 // mimic the logic of RVecInlineStorageSize, but at runtime
856 const auto inlineStorageSz = [&] {
857 constexpr unsigned cacheLineSize = R__HARDWARE_INTERFERENCE_SIZE;
858 const unsigned elementsPerCacheLine = (cacheLineSize - dataMemberSz) / sizeOfT;
859 constexpr unsigned maxInlineByteSize = 1024;
860 const unsigned nElements =
861 elementsPerCacheLine >= 8 ? elementsPerCacheLine : (sizeOfT * 8 > maxInlineByteSize ? 0 : 8);
862 return nElements * sizeOfT;
863 }();
864
865 // compute padding between first 3 datamembers and inline buffer
866 // (there should be no padding between the first 3 data members)
868 if (paddingMiddle != 0)
870
871 // padding at the end of the object
873 if (paddingEnd != 0)
875
877}
878
880{
881 // the alignment of an RVec<T> is the largest among the alignments of its data members
882 // (including the inline buffer which has the same alignment as the RVec::value_type)
883 return std::max({alignof(void *), alignof(std::int32_t), alignOfSubfield});
884}
885
886void ROOT::Internal::DestroyRVecWithChecks(std::size_t alignOfT, unsigned char **beginPtr, std::int32_t *capacityPtr)
887{
888 // figure out if we are in the small state, i.e. begin == &inlineBuffer
889 // there might be padding between fCapacity and the inline buffer, so we compute it here
890 constexpr auto dataMemberSz = sizeof(void *) + 2 * sizeof(std::int32_t);
892 if (paddingMiddle != 0)
894 const bool isSmall = (*beginPtr == (reinterpret_cast<unsigned char *>(beginPtr) + dataMemberSz + paddingMiddle));
895
896 const bool owns = (*capacityPtr != -1);
897 if (!isSmall && owns)
898 free(*beginPtr);
899}
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:300
#define R__LOG_WARNING(...)
Definition RLogger.hxx:358
#define c(i)
Definition RSha256.hxx:101
#define a(i)
Definition RSha256.hxx:99
#define e(i)
Definition RSha256.hxx:103
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t src
#define _(A, B)
Definition cfortran.h:108
#define free
Definition civetweb.c:1578
Base class for all ROOT issued exceptions.
Definition RError.hxx:79
A field translates read and write calls from/to underlying columns to/from tree values.
static std::string TypeName()
Definition RField.hxx:325
The on-storage metadata of an RNTuple.
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
ROOT::DescriptorId_t GetFieldZeroId() const
Returns the logical parent of all top-level RNTuple data fields.
const_iterator begin() const
const_iterator end() const
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2979
TDictAttributeMap * GetAttributeMap() const
TLine * line
const Int_t n
Definition legend1.C:16
ERNTupleSerializationMode
Possible settings for the "rntuple.streamerMode" class attribute in the dictionary.
std::vector< std::string > TokenizeTypeList(std::string_view templateType, std::size_t maxArgs=0)
Used in RFieldBase::Create() in order to get the comma-separated list of template types E....
ROOT::RLogChannel & NTupleLog()
Log channel for RNTuple diagnostics.
unsigned long long ParseUIntTypeToken(const std::string &uintToken)
void DestroyRVecWithChecks(std::size_t alignOfT, unsigned char **beginPtr, std::int32_t *capacityPtr)
std::string GetRNTupleSoARecord(const TClass *cl)
Checks if the "rntuple.SoARecord" class attribute is set in the dictionary.
std::string GetNormalizedInteger(const std::string &intTemplateArg)
Appends 'll' or 'ull' to the where necessary and strips the suffix if not needed.
bool NeedsMetaNameAsAlias(const std::string &metaNormalizedName, std::string &renormalizedAlias, bool isArgInTemplatedUserClass=false)
Checks if the meta normalized name is different from the RNTuple normalized name in a way that would ...
std::string GetTypeTraceReport(const RFieldBase &field, const RNTupleDescriptor &desc)
Prints the hierarchy of types with their field names and field IDs for the given in-memory field and ...
std::string GetCanonicalTypePrefix(const std::string &typeName)
Applies RNTuple specific type name normalization rules (see specs) that help the string parsing in RF...
std::string GetNormalizedUnresolvedTypeName(const std::string &origName)
Applies all RNTuple type normalization rules except typedef resolution.
ERNTupleSerializationMode GetRNTupleSerializationMode(const TClass *cl)
bool IsMatchingFieldType(const std::string &actualTypeName)
Helper to check if a given type name is the one expected of Field<T>.
Definition RField.hxx:562
std::size_t EvalRVecAlignment(std::size_t alignOfSubfield)
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
long long ParseIntTypeToken(const std::string &intToken)
std::size_t EvalRVecValueSize(std::size_t alignOfT, std::size_t sizeOfT, std::size_t alignOfRVecT)
std::string GetDemangledTypeName(const std::type_info &t)
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr DescriptorId_t kInvalidDescriptorId
std::string CleanType(const char *typeDesc, int mode=0, const char **tail=nullptr)
Cleanup type description, redundant blanks removed and redundant tail ignored return *tail = pointer ...
char * DemangleName(const char *mangled_name, int &errorCode)
Definition TClassEdit.h:255
@ kDropComparator
Definition TClassEdit.h:84
@ kDropStlDefault
Definition TClassEdit.h:83