37#include <initializer_list>
38#include <unordered_map>
64 const Ssiz_t wordLen = strlen(word);
65 if (str.Length() < wordLen)
69 return str.Length() == wordLen || str(wordLen) ==
' ';
74 std::initializer_list<std::pair<const char *, T>> validValues)
76 const Ssiz_t patternLen = strlen(pattern);
77 assert(pattern[patternLen - 1] ==
'=');
79 idx >= 0 && opts.
Length() > idx + patternLen) {
80 auto sub =
TString(opts(idx + patternLen, opts.
Length() - idx - patternLen));
81 for (
const auto &[
name,
value] : validValues) {
92 return ParseStringOption<ENTupleMergingMode>(opts,
"rntuple.MergingMode=",
94 {
"Filter", ENTupleMergingMode::kFilter},
95 {
"Union", ENTupleMergingMode::kUnion},
96 {
"Strict", ENTupleMergingMode::kStrict},
102 return ParseStringOption<ENTupleMergeErrBehavior>(opts,
"rntuple.ErrBehavior=",
104 {
"Abort", ENTupleMergeErrBehavior::kAbort},
105 {
"Skip", ENTupleMergeErrBehavior::kSkip},
111 return ParseStringOption<ENTupleMergeVersionBehavior>(
112 opts,
"rntuple.VersionBehavior=",
114 {
"WarnOnHigherVersion", ENTupleMergeVersionBehavior::kWarnOnHigherVersion},
115 {
"AbortOnHigherVersion", ENTupleMergeVersionBehavior::kAbortOnHigherVersion},
125 if (!inputs || inputs->
GetEntries() < 3 || !mergeInfo) {
134 std::string ntupleName = std::string(itr()->GetName());
138 TFile *outFile =
dynamic_cast<TFile *
>(secondArg);
146 TKey *outKey = outFile->
FindKey(ntupleName.c_str());
158 const bool defaultComp = mergeInfo->
fOptions.
Contains(
"DefaultCompression");
159 const bool firstSrcComp = mergeInfo->
fOptions.
Contains(
"FirstSrcCompression");
160 const bool extraVerbose = mergeInfo->
fOptions.
Contains(
"rntuple.ExtraVerbose");
161 if (defaultComp && firstSrcComp) {
164 "only the latter will apply.";
166 std::optional<std::uint32_t> compression;
170 }
else if (!defaultComp) {
180 std::vector<std::unique_ptr<RPageSourceFile>> sources;
181 std::vector<RPageSource *> sourcePtrs;
183 while (
const auto &pitr = itr()) {
196 source->Attach(RNTupleSerializer::EDescriptorDeserializeMode::kRaw);
197 auto descGuard = source->GetSharedDescriptorGuard();
198 auto clusterGroupIterable = descGuard->GetClusterGroupIterable();
199 if (clusterGroupIterable.empty()) {
201 <<
"Asked to use the first source's compression as the output compression, but the "
202 "first source (file '"
204 <<
"') has an empty RNTuple, therefore the output compression could not be "
208 const auto firstClusterGroup = clusterGroupIterable.begin();
209 R__ASSERT(firstClusterGroup->HasClusterDetails());
210 const auto &firstCluster = descGuard->GetClusterDescriptor(firstClusterGroup->GetClusterIds()[0]);
211 auto colRangeIter = firstCluster.GetColumnRangeIterable();
212 auto firstColRange = colRangeIter.begin();
213 if (firstColRange == colRangeIter.end()) {
215 <<
"Asked to use the first source's compression as the output compression, but the "
216 "first source (file '"
218 <<
"') has an empty RNTuple, therefore the output compression could not be "
222 compression = (*firstColRange).GetCompressionSettings();
225 sources.push_back(std::move(source));
231 auto destination = std::make_unique<ROOT::Internal::RPageSinkFile>(ntupleName, *outFile, writeOpts);
232 std::unique_ptr<ROOT::RNTupleModel> model;
236 outSource->Attach(RNTupleSerializer::EDescriptorDeserializeMode::kForWriting);
237 auto desc = outSource->GetSharedDescriptorGuard();
238 model = destination->InitFromDescriptor(desc.GetRef(),
true );
242 sourcePtrs.reserve(sources.size());
243 for (
const auto &s : sources) {
244 sourcePtrs.push_back(s.get());
248 RNTupleMerger merger{std::move(destination), std::move(model)};
261 merger.Merge(sourcePtrs, mergerOpts).ThrowOnError();
268}
catch (
const std::exception &
ex) {
275struct RChangeCompressionFunc {
277 std::uint32_t fCompressionSettings;
281 std::size_t fBufSize;
284 void operator()()
const
290 std::unique_ptr<std::byte[]> unzipBufOwned;
292 if (fCompressionSettings != 0) {
293 unzipBufOwned = MakeUninitArray<std::byte>(bytesPacked);
294 unzipBuf = unzipBufOwned.get();
302 std::size_t nBytesZipped;
303 if (fCompressionSettings != 0) {
305 assert(fBufSize >= bytesPacked + checksumSize);
308 nBytesZipped = bytesPacked;
316 std::optional<ROOT::Experimental::TTaskGroup> &fGroup;
318 template <
typename T>
319 void operator()(T &&
f)
341struct RColReprMapping {
342 std::uint32_t fSource;
348struct RColReprExtension : RColReprMapping {
350 std::vector<ROOT::Internal::RColumnFormat> fSourceRepr;
354 std::uint32_t fOrigFirstElementIndex = 0;
357static std::optional<std::uint32_t>
358FindColumnReprMapping(
const std::vector<RColReprMapping> &mappings, std::uint32_t sourceReprIndex)
360 for (
const auto [
src,
dst] : mappings)
361 if (
src == sourceReprIndex)
367using FieldCollectionMap_t = std::unordered_map<const ROOT::RFieldDescriptor *, std::vector<T>>;
369struct RDescriptorsComparison {
370 std::vector<const ROOT::RFieldDescriptor *> fExtraDstFields;
371 std::vector<const ROOT::RFieldDescriptor *> fExtraSrcFields;
372 std::vector<RCommonField> fCommonFields;
376 FieldCollectionMap_t<RColReprMapping> fColReprMappings;
377 FieldCollectionMap_t<RColReprExtension> fColReprExtensions;
380struct RColumnOutInfo {
385using ColumnIdMap_t = std::unordered_map<std::string, RColumnOutInfo>;
387struct RColumnInfoGroup {
388 std::vector<RColumnMergeInfo> fExtraDstColumns;
389 std::vector<RColumnMergeInfo> fCommonColumns;
435 std::deque<RPageStorage::SealedPageSequence_t>
fPagesV;
436 std::vector<RPageStorage::RSealedPageGroup>
fGroups;
437 std::vector<std::unique_ptr<std::byte[]>>
fBuffers;
446 RDescriptorsComparison &
result, std::vector<std::string> &errors)
454 if (srcColCardinality != dstColCardinality) {
455 std::stringstream ss;
457 <<
"` has a different column cardinality than previously-seen field with the same name (old: "
458 << dstColCardinality <<
", new: " << srcColCardinality <<
")";
459 errors.push_back(ss.str());
463 if (srcColCardinality == 0)
466 const auto srcNColReprs = srcColumns.size() / srcColCardinality;
467 const auto dstNColReprs = dstColumns.size() / dstColCardinality;
468 std::uint32_t nextDstReprIndex = dstNColReprs;
474 for (
auto srcReprIdx = 0u; srcReprIdx < srcNColReprs; ++srcReprIdx) {
475 std::int64_t matchingRepr = -1;
476 for (
auto dstReprIdx = 0u; dstReprIdx < dstNColReprs; ++dstReprIdx) {
478 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
479 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
481 const auto dstColId = dstColumns[dstReprIdx * dstColCardinality + reprColIdx];
483 if (srcCol.GetType() != dstCol.GetType()) {
495 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
496 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
498 const auto dstColId = dstColumns[dstReprIdx * dstColCardinality + reprColIdx];
500 if (srcCol.GetType() != dstCol.GetType() || srcCol.GetBitsOnStorage() != dstCol.GetBitsOnStorage() ||
501 srcCol.GetValueRange() != dstCol.GetValueRange()) {
509 matchingRepr = dstReprIdx;
515 if (errors.empty()) {
516 if (matchingRepr >= 0 && matchingRepr != srcReprIdx) {
518 assert(matchingRepr < std::numeric_limits<std::uint32_t>::max());
519 result.fColReprMappings[&dstField].push_back(
520 RColReprMapping{srcReprIdx,
static_cast<std::uint32_t
>(matchingRepr)});
521 }
else if (matchingRepr < 0) {
523 std::vector<ROOT::Internal::RColumnFormat> newRepr;
524 newRepr.reserve(srcColCardinality);
525 std::uint32_t firstElemIdx = 0;
526 for (
auto reprColIdx = 0u; reprColIdx < srcColCardinality; ++reprColIdx) {
527 const auto srcColId = srcColumns[srcReprIdx * srcColCardinality + reprColIdx];
530 assert(firstElemIdx == 0 || firstElemIdx == srcCol.GetFirstElementIndex());
531 firstElemIdx = srcCol.GetFirstElementIndex();
532 auto &reprElement = newRepr.emplace_back();
533 reprElement.fType = srcCol.GetType();
534 reprElement.fBitWidth = srcCol.GetBitsOnStorage();
535 reprElement.fValueRange = srcCol.GetValueRange();
537 RColReprExtension extension{{srcReprIdx, nextDstReprIndex}, newRepr, firstElemIdx};
538 nextDstReprIndex += newRepr.size();
539 result.fColReprExtensions[&dstField].push_back(extension);
540 result.fColReprMappings[&dstField].push_back(extension);
557 std::vector<std::string> errors;
558 RDescriptorsComparison res;
560 std::vector<RCommonField> commonFields;
562 for (
const auto &dstField :
dst.GetTopLevelFields()) {
563 const auto srcFieldId =
src.FindFieldId(dstField.GetFieldName());
565 const auto &srcField =
src.GetFieldDescriptor(srcFieldId);
566 commonFields.push_back({srcField, dstField});
568 res.fExtraDstFields.emplace_back(&dstField);
571 for (
const auto &srcField :
src.GetTopLevelFields()) {
572 const auto dstFieldId =
dst.FindFieldId(srcField.GetFieldName());
574 res.fExtraSrcFields.push_back(&srcField);
579 auto fieldsToCheck = commonFields;
581 for (std::size_t fieldIdx = 0; fieldIdx < fieldsToCheck.size(); ++fieldIdx) {
582 const auto &field = fieldsToCheck[fieldIdx];
585 const auto &fieldName = field.fSrc->GetFieldName();
588 bool projCompatible = field.fSrc->IsProjectedField() == field.fDst->IsProjectedField();
589 if (!projCompatible) {
590 std::stringstream ss;
591 ss <<
"Field `" << fieldName <<
"` is incompatible with previously-seen field with that name because the "
592 << (field.fSrc->IsProjectedField() ?
"new" :
"old") <<
" one is projected and the other isn't";
593 errors.push_back(ss.str());
594 }
else if (field.fSrc->IsProjectedField()) {
596 const auto srcName =
src.GetQualifiedFieldName(field.fSrc->GetProjectionSourceId());
597 const auto dstName =
dst.GetQualifiedFieldName(field.fDst->GetProjectionSourceId());
598 if (srcName != dstName) {
599 std::stringstream ss;
600 ss <<
"Field `" << fieldName
601 <<
"` is projected to a different field than a previously-seen field with the same name (old: "
602 << dstName <<
", new: " << srcName <<
")";
603 errors.push_back(ss.str());
611 const auto &dstTyName = field.fDst->GetTypeName();
612 if (srcTyName != dstTyName) {
613 std::stringstream ss;
614 ss <<
"Field `" << fieldName
615 <<
"` has a type incompatible with a previously-seen field with the same name: (old: " << dstTyName
616 <<
", new: " << srcTyName <<
")";
617 errors.push_back(ss.str());
621 const auto srcTyChk = field.fSrc->GetTypeChecksum();
622 const auto dstTyChk = field.fDst->GetTypeChecksum();
623 if (srcTyChk && dstTyChk && *srcTyChk != *dstTyChk) {
624 std::stringstream ss;
625 ss <<
"Field `" << field.fSrc->GetFieldName()
626 <<
"` has a different type checksum than previously-seen field with the same name";
627 errors.push_back(ss.str());
631 const auto srcTyVer = field.fSrc->GetTypeVersion();
632 const auto dstTyVer = field.fDst->GetTypeVersion();
633 if (srcTyVer != dstTyVer) {
634 std::stringstream ss;
635 ss <<
"Field `" << field.fSrc->GetFieldName()
636 <<
"` has a different type version than previously-seen field with the same name (old: " << dstTyVer
637 <<
", new: " << srcTyVer <<
")";
638 errors.push_back(ss.str());
642 const auto srcFldVer = field.fSrc->GetFieldVersion();
643 const auto dstFldVer = field.fDst->GetFieldVersion();
644 if (srcFldVer != dstFldVer) {
645 std::stringstream ss;
646 ss <<
"Field `" << field.fSrc->GetFieldName()
647 <<
"` has a different field version than previously-seen field with the same name (old: " << dstFldVer
648 <<
", new: " << srcFldVer <<
")";
649 errors.push_back(ss.str());
652 const auto srcRole = field.fSrc->GetStructure();
653 const auto dstRole = field.fDst->GetStructure();
654 if (srcRole != dstRole) {
655 std::stringstream ss;
656 ss <<
"Field `" << field.fSrc->GetFieldName()
657 <<
"` has a different structural role than previously-seen field with the same name (old: " << dstRole
658 <<
", new: " << srcRole <<
")";
659 errors.push_back(ss.str());
663 if (!field.fSrc->IsProjectedField()) {
668 const auto &srcLinks = field.fSrc->GetLinkIds();
669 const auto &dstLinks = field.fDst->GetLinkIds();
670 if (srcLinks.size() != dstLinks.size()) {
671 std::stringstream ss;
672 ss <<
"Field `" << field.fSrc->GetFieldName()
673 <<
"` has a different number of children than previously-seen field with the same name (old: "
674 << dstLinks.size() <<
", new: " << srcLinks.size() <<
")";
675 errors.push_back(ss.str());
677 for (std::size_t linkIdx = 0, linkNum = srcLinks.size(); linkIdx < linkNum; ++linkIdx) {
678 const auto &srcSubfield =
src.GetFieldDescriptor(srcLinks[linkIdx]);
679 const auto &dstSubfield =
dst.GetFieldDescriptor(dstLinks[linkIdx]);
680 fieldsToCheck.push_back(RCommonField{srcSubfield, dstSubfield});
686 for (
const auto &err : errors)
687 errMsg += std::string(
"\n * ") + err;
690 errMsg = errMsg.substr(1);
695 res.fCommonFields = std::move(commonFields);
711 if (logicalIds.size() == 1) {
713 const auto type = column.GetType();
717 if (!realFloat && !realDouble) {
719 "` has a Real32Trunc/Real32Quant column but is not an RRealField");
721 const auto bits = column.GetBitsOnStorage();
725 realFloat->SetTruncated(bits);
729 const auto valueRange = column.GetValueRange();
732 "` has a Real32Quant column with no value range");
735 realFloat->SetQuantized(bits,
736 {
static_cast<float>(valueRange->fMin),
static_cast<float>(valueRange->fMax)});
738 realDouble->
SetQuantized(bits, {valueRange->fMin, valueRange->fMax});
764 const auto &newFields = descCmp.fExtraSrcFields;
765 auto &commonFields = descCmp.fCommonFields;
771 std::string msg =
"destination doesn't contain field";
772 if (newFields.size() > 1)
775 msg += std::accumulate(newFields.begin(), newFields.end(), std::string{}, [](
const auto &acc,
const auto *field) {
776 return acc + (acc.length() ?
", " :
"") +
'`' + field->GetFieldName() +
'`';
779 <<
" to the destination model (entry #" << mergeData.
fNumDstEntries <<
").";
782 changeset.fAddedFields.reserve(newFields.size());
784 for (
const auto *fieldDesc : newFields) {
785 if (fieldDesc->IsProjectedField())
792 for (
const auto &colId : fieldDesc->GetLogicalColumnIds()) {
794 representatives.push_back(column.GetType());
796 field->SetColumnRepresentatives({representatives});
802 changeset.AddField(std::move(field));
805 for (
const auto *fieldDesc : newFields) {
806 if (!fieldDesc->IsProjectedField())
811 const auto sourceId = fieldDesc->GetProjectionSourceId();
813 fieldMap[field.get()] = &sourceField;
815 for (
const auto &subfield : *field) {
818 const auto &subSourceField =
820 fieldMap[&subfield] = &subSourceField;
822 changeset.fAddedProjectedFields.emplace_back(field.get());
837 commonFields.reserve(commonFields.size() + newFields.size());
865 for (
auto it = newFields.rbegin(); it != newFields.rend(); ++it) {
866 const auto *field = *it;
869 commonFields.insert(commonFields.begin(), RCommonField{*field, newFieldInDst});
882 if (!nEntriesToGenerate)
885 for (
const auto &column : columns) {
895 bool skipColumn =
false;
896 auto nRepetitions = std::max<std::uint64_t>(field->
GetNRepetitions(), 1);
914 ") that is not present in one of the sources. "
915 "Creating a default value for a streamer field is ill-defined, therefore the merging "
916 "process will abort.");
925 const auto nElements = nEntriesToGenerate * nRepetitions;
926 const auto nBytesOnStorage = colElement->GetPackedSize(nElements);
928 constexpr auto kPageSizeLimit = 256 * 1024;
930 const size_t nPages = nBytesOnStorage / kPageSizeLimit + !!(nBytesOnStorage % kPageSizeLimit);
931 for (
size_t i = 0; i < nPages; ++i) {
932 const auto pageSize = (i < nPages - 1) ? kPageSizeLimit : nBytesOnStorage - kPageSizeLimit * (nPages - 1);
934 const auto bufSize = pageSize + checksumSize;
935 assert(pageSize % colElement->GetSize() == 0);
936 const auto nElementsPerPage = pageSize / colElement->GetSize();
937 auto page = pageAlloc.
NewPage(colElement->GetSize(), nElementsPerPage);
939 memset(page.GetBuffer(), 0, page.GetNBytes());
941 auto &buffer = sealedPageData.
fBuffers.emplace_back(
new std::byte[bufSize]);
943 sealConf.
fElement = colElement.get();
944 sealConf.
fPage = &page;
945 sealConf.
fBuffer = buffer.get();
950 sealedPageData.
fPagesV.push_back({sealedPage});
951 sealedPageData.
fGroups.emplace_back(column.fOutputId, sealedPageData.
fPagesV.back().cbegin(),
952 sealedPageData.
fPagesV.back().cend());
963 std::span<RColumnMergeInfo> commonColumns,
967 const auto nCommonColumnsInCluster = commonColumnSet.size();
968 assert(nCommonColumnsInCluster <= commonColumns.size());
970 if (nCommonColumnsInCluster == 0)
980 for (
size_t colIdx = 0; colIdx < nCommonColumnsInCluster; ++colIdx) {
981 const auto &column = commonColumns[colIdx];
982 const auto &columnId = column.fInputId;
986 const auto srcColElement = column.fInMemoryType
994 sealedPages.resize(pages.GetPageInfos().size());
998 assert(!columnRange.IsSuppressed());
999 const auto colRangeCompressionSettings = columnRange.GetCompressionSettings().value();
1006 const bool needsRecompressing = colRangeCompressionSettings != outCompression;
1010 <<
": { compression: " << colRangeCompressionSettings <<
" => "
1013 srcColElement->GetIdentifier().fOnDiskType)
1017 const size_t pageBufferBaseIdx = sealedPageData.
fBuffers.size();
1020 if (needsRecompressing)
1021 sealedPageData.
fBuffers.resize(sealedPageData.
fBuffers.size() + pages.GetPageInfos().size());
1029 const auto nMissingEntries = columnDesc.GetFirstElementIndex() - clusterDesc.
GetFirstEntryIndex();
1037 std::uint64_t pageIdx = 0;
1038 for (
const auto &pageInfo : pages.GetPageInfos()) {
1039 assert(pageIdx < sealedPages.size());
1040 assert(sealedPageData.
fBuffers.size() == 0 || pageIdx < sealedPageData.
fBuffers.size());
1050 sealedPage.
SetBufferSize(pageInfo.GetLocator().GetNBytesOnStorage() + checksumSize);
1051 sealedPage.
SetBuffer(onDiskPage->GetAddress());
1056 if (needsRecompressing) {
1057 const auto uncompressedSize = srcColElement->GetSize() * sealedPage.
GetNElements();
1058 auto &buffer = sealedPageData.
fBuffers[pageBufferBaseIdx + pageIdx];
1059 const auto bufSize = uncompressedSize + checksumSize;
1063 buffer = MakeUninitArray<std::byte>(bufSize);
1066 RTaskVisitor{fTaskGroup}(RChangeCompressionFunc{
1085 sealedPageData.
fPagesV.push_back(std::move(sealedPages));
1086 sealedPageData.
fGroups.emplace_back(column.fOutputId, sealedPageData.
fPagesV.back().cbegin(),
1087 sealedPageData.
fPagesV.back().cend());
1099 std::span<const RColumnMergeInfo> extraDstColumns,
1104 std::vector<RColumnMergeInfo> missingColumns{extraDstColumns.begin(), extraDstColumns.end()};
1108 const auto nClusterEntries = clusterDesc.GetNEntries();
1115 FieldCollectionMap_t<ROOT::DescriptorId_t> columnsInCluster;
1129 int nCommonColumnsInCluster = 0;
1130 std::partition(commonColumns.begin(), commonColumns.end(), [&](
const auto &column) {
1131 if (clusterDesc.ContainsColumn(column.fInputId)) {
1132 const auto &colRange = clusterDesc.GetColumnRange(column.fInputId);
1133 ++nCommonColumnsInCluster;
1134 columnsInCluster[column.fParentFieldDescriptor].push_back(column.fOutputId);
1135 if (!colRange.IsSuppressed()) {
1136 commonColumnSet.emplace(column.fInputId);
1139 mergeData.fDestination.CommitSuppressedColumn(ColumnHandle_t{column.fOutputId});
1154 for (
const auto &[fieldDesc, columnIds] : columnsInCluster) {
1156 const auto cardinality = fieldDesc->GetColumnCardinality();
1157 for (
auto i = 0u; i < fieldDesc->GetLogicalColumnIds().size(); ++i) {
1158 const auto colIndex = i % cardinality;
1159 const auto reprIndex = i / cardinality;
1160 const auto colName =
"." + fieldFQName +
'.' + std::to_string(colIndex) +
'.' + std::to_string(reprIndex);
1161 const auto colIt = mergeData.
fColumnIdMap.find(colName);
1163 const auto colOutId = colIt->second.fColumnId;
1164 if (std::find(columnIds.begin(), columnIds.end(), colOutId) == columnIds.end()) {
1171 auto res = MergeCommonColumns(clusterPool, clusterDesc, commonColumns, commonColumnSet, sealedPageData, mergeData,
1180 missingColumns.resize(extraDstColumns.size());
1181 for (
size_t i = nCommonColumnsInCluster; i < commonColumns.size(); ++i)
1182 missingColumns.push_back(commonColumns[i]);
1214 if (fieldType ==
"bool")
return typeid(
bool);
1215 if (fieldType ==
"std::byte")
return typeid(std::byte);
1216 if (fieldType ==
"char")
return typeid(char);
1217 if (fieldType ==
"std::int8_t")
return typeid(std::int8_t);
1218 if (fieldType ==
"std::uint8_t")
return typeid(std::uint8_t);
1219 if (fieldType ==
"std::int16_t")
return typeid(std::int16_t);
1220 if (fieldType ==
"std::uint16_t")
return typeid(std::uint16_t);
1221 if (fieldType ==
"std::int32_t")
return typeid(std::int32_t);
1222 if (fieldType ==
"std::uint32_t")
return typeid(std::uint32_t);
1223 if (fieldType ==
"std::int64_t")
return typeid(std::int64_t);
1224 if (fieldType ==
"std::uint64_t")
return typeid(std::uint64_t);
1225 if (fieldType ==
"float")
return typeid(float);
1226 if (fieldType ==
"double")
return typeid(
double);
1230 return std::nullopt;
1241 const FieldCollectionMap_t<RColReprMapping> &colReprMappings,
1253 columns.reserve(columns.size() + columnIds.size());
1260 info.
fInputId = srcColumn.GetPhysicalId();
1268 info.fParentFieldDescriptor = &srcFieldDesc;
1272 info.fParentNTupleDescriptor = &srcDesc;
1274 const auto mappingsIt = colReprMappings.find(&dstFieldDesc);
1275 std::uint16_t reprIndex = srcColumn.GetRepresentationIndex();
1276 if (mappingsIt != colReprMappings.end()) {
1277 if (
auto outReprIdx = FindColumnReprMapping(mappingsIt->second, reprIndex); outReprIdx)
1278 reprIndex = *outReprIdx;
1281 info.fColumnName =
name +
'.' + std::to_string(srcColumn.GetIndex()) +
'.' + std::to_string(reprIndex);
1287 info.fOutputId = it->second.fColumnId;
1288 info.fOutputReprIndex = reprIndex;
1296 const auto dstColumnIndex = reprIndex * dstFieldDesc.
GetColumnCardinality() + srcColumn.GetIndex();
1299 columnType = dstColumn.GetType();
1300 info.fOutputReprIndex = reprIndex;
1301 mergeData.
fColumnIdMap[info.fColumnName] = RColumnOutInfo{info.fOutputId};
1306 <<
", phys.id " << srcColumn.GetPhysicalId() <<
", type "
1308 << info.fOutputId <<
", type "
1315 columns.emplace_back(info);
1318 const auto &srcChildrenIds = srcFieldDesc.
GetLinkIds();
1319 const auto &dstChildrenIds = dstFieldDesc.
GetLinkIds();
1320 assert(srcChildrenIds.size() == dstChildrenIds.size());
1321 for (
auto i = 0u; i < srcChildrenIds.size(); ++i) {
1334 RColumnInfoGroup res;
1339 for (
const auto &[srcField, dstField] : descCmp.fCommonFields) {
1340 AddColumnsFromField(res.fCommonColumns, srcDesc, descCmp.fColReprMappings, mergeData, *srcField, *dstField);
1346 ColumnIdMap_t &colIdMap,
const std::string &prefix =
"")
1351 RColumnOutInfo info{};
1353 const auto colName =
1354 name +
'.' + std::to_string(colDesc.GetIndex()) +
'.' + std::to_string(colDesc.GetRepresentationIndex());
1355 colIdMap[colName] = info;
1358 for (
const auto &subId : fieldDesc.
GetLinkIds()) {
1366 const FieldCollectionMap_t<RColReprExtension> &extensions,
1368 std::unordered_map<
ROOT::DescriptorId_t, std::vector<const ROOT::RFieldDescriptor *>> &outProjectionPointees)
1370 const auto it = extensions.find(&field);
1371 if (it != extensions.end())
1372 outExtensions.emplace_back(it->first, it->second);
1384 std::unique_ptr<ROOT::RNTupleModel> model)
1387 : fDestination(std::move(destination)),
1389 fModel(std::move(model))
1412 const auto dstCompSettings =
fDestination->GetWriteOptions().GetCompression();
1416 return R__FAIL(std::string(
"The compression given to RNTupleMergeOptions is different from that of the "
1418 std::to_string(*mergeOpts.
fCompressionSettings) +
", sink: " + std::to_string(dstCompSettings) +
1419 ") This is currently unsupported.");
1424 std::unordered_map<ROOT::DescriptorId_t, std::vector<const ROOT::RFieldDescriptor *>> projectionPointees;
1429 "passing an already-initialized destination to RNTupleMerger::Merge (i.e. trying to do incremental "
1430 "merging) can only be done by providing a valid ROOT::RNTupleModel when constructing the RNTupleMerger.");
1434 mergeData.fNumDstEntries = mergeData.fDestination.GetNEntries();
1439 for (
const auto &field : mergeData.fDstDescriptor.GetTopLevelFields()) {
1445#define SKIP_OR_ABORT(errMsg) \
1446 if (mergeOpts.fErrBehavior == ENTupleMergeErrBehavior::kSkip) { \
1447 R__LOG_WARNING(NTupleMergeLog()) << "Skipping RNTuple due to: " << (errMsg); \
1450 return R__FAIL(errMsg); \
1455 source->Attach(RNTupleSerializer::EDescriptorDeserializeMode::kForWriting);
1456 auto srcDescriptor = source->GetSharedDescriptorGuard();
1457 mergeData.fSrcDescriptor = &srcDescriptor.GetRef();
1462 <<
"RNTuple '" << mergeData.fSrcDescriptor->GetName()
1463 <<
"' has a higher format version than the latest supported by this version "
1464 "of ROOT. Merging will work but some features may be dropped.";
1466 return R__FAIL(
"RNTuple '" + mergeData.fSrcDescriptor->GetName() +
1467 "' has a higher format version than the latest supported by this version. Refusing to "
1468 "merge, since RNTupleMergeOptions::fVersionBehavior is set to AbortOnHigherVersion.");
1477 for (
const auto &extraTypeInfoDesc : srcDescriptor->GetExtraTypeInfoIterable())
1482 SKIP_OR_ABORT(std::string(
"Source RNTuple has an incompatible schema with the destination:\n") +
1483 descCmpRes.GetError()->GetReport())
1485 auto descCmp = descCmpRes.Unwrap();
1490 std::string msg =
"Source RNTuple is missing the following fields:";
1491 for (
const auto *field : descCmp.fExtraDstFields) {
1492 msg +=
"\n " + field->GetFieldName() +
" : " + field->GetTypeName();
1498 if (!descCmp.fExtraSrcFields.empty()) {
1506 std::string msg =
"Source RNTuple has extra fields that the destination RNTuple doesn't have:";
1507 for (
const auto *field : descCmp.fExtraSrcFields) {
1508 msg +=
"\n " + field->GetFieldName() +
" : " + field->GetTypeName();
1515 if (!descCmp.fColReprExtensions.empty()) {
1516 for (
const auto &field : descCmp.fExtraDstFields) {
1517 if (field->IsProjectedField())
1518 projectionPointees[field->GetProjectionSourceId()].push_back(field);
1527 std::vector<std::pair<const RFieldDescriptor *, std::vector<RColReprExtension>>> colExtensions;
1528 colExtensions.reserve(descCmp.fColReprExtensions.size());
1529 for (
const auto &commonField : descCmp.fCommonFields) {
1530 const auto *field = commonField.fDst;
1532 projectionPointees);
1534 for (
const auto &field : descCmp.fExtraSrcFields) {
1535 if (field->IsProjectedField())
1536 projectionPointees[field->GetProjectionSourceId()].push_back(field);
1539 for (
const auto &[fieldDesc, extensions] : colExtensions) {
1540 auto &mappings = descCmp.fColReprMappings[fieldDesc];
1541 for (
const auto &extension : extensions) {
1542 const auto firstColumnId =
fDestination->AddColumnRepresentation(*fieldDesc, extension.fSourceRepr,
1543 extension.fOrigFirstElementIndex);
1548 if (
auto it = projectionPointees.find(fieldDesc->GetId()); it != projectionPointees.end()) {
1549 for (
const auto &projection : it->second) {
1550 for (
auto colIdx = 0u; colIdx < extension.fSourceRepr.size(); ++colIdx)
1551 fDestination->AddAliasColumn(mergeData.fDstDescriptor, *projection, firstColumnId + colIdx);
1554 mappings.push_back(extension);
1560 auto columnInfos =
GatherColumnInfos(descCmp, srcDescriptor.GetRef(), mergeData);
1561 auto res =
MergeSourceClusters(*source, columnInfos.fCommonColumns, columnInfos.fExtraDstColumns, mergeData);
#define R__FORWARD_ERROR(res)
Short-hand to return an RResult<T> in an error state (i.e. after checking)
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
#define R__LOG_WARNING(...)
#define R__LOG_ERROR(...)
static void MatchColumnRepresentations(const ROOT::RNTupleDescriptor &srcDesc, const ROOT::RNTupleDescriptor &dstDesc, const ROOT::RFieldDescriptor &srcField, const ROOT::RFieldDescriptor &dstField, RDescriptorsComparison &result, std::vector< std::string > &errors)
static std::optional< std::type_index > ColumnInMemoryType(std::string_view fieldType, ENTupleColumnType onDiskType)
static ROOT::RResult< RDescriptorsComparison > CompareDescriptorStructure(const ROOT::RNTupleDescriptor &dst, const ROOT::RNTupleDescriptor &src)
Compares the top level fields of dst and src and determines whether they can be merged or not.
static ROOT::RResult< void > ExtendDestinationModel(RDescriptorsComparison &descCmp, ROOT::RNTupleModel &dstModel, RNTupleMergeData &mergeData)
static ROOT::RResult< void > GenerateZeroPagesForColumns(size_t nEntriesToGenerate, std::span< const RColumnMergeInfo > columns, RSealedPageMergeData &sealedPageData, ROOT::Internal::RPageAllocator &pageAlloc, const ROOT::RNTupleDescriptor &dstDescriptor, const RNTupleMergeData &mergeData)
static void AddColumnsFromField(std::vector< RColumnMergeInfo > &columns, const ROOT::RNTupleDescriptor &srcDesc, const FieldCollectionMap_t< RColReprMapping > &colReprMappings, RNTupleMergeData &mergeData, const ROOT::RFieldDescriptor &srcFieldDesc, const ROOT::RFieldDescriptor &dstFieldDesc, const std::string &prefix="")
static std::optional< ENTupleMergeErrBehavior > ParseOptionErrBehavior(const TString &opts)
static ROOT::RLogChannel & NTupleMergeLog()
#define SKIP_OR_ABORT(errMsg)
static std::optional< T > ParseStringOption(const TString &opts, const char *pattern, std::initializer_list< std::pair< const char *, T > > validValues)
static void AddColumnExtensionsInFieldOrder(const ROOT::RFieldDescriptor &field, const ROOT::RNTupleDescriptor &desc, const FieldCollectionMap_t< RColReprExtension > &extensions, std::vector< std::pair< const ROOT::RFieldDescriptor *, std::vector< RColReprExtension > > > &outExtensions, std::unordered_map< ROOT::DescriptorId_t, std::vector< const ROOT::RFieldDescriptor * > > &outProjectionPointees)
static std::optional< ENTupleMergingMode > ParseOptionMergingMode(const TString &opts)
static void PrefillColumnMap(const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &fieldDesc, ColumnIdMap_t &colIdMap, const std::string &prefix="")
static RColumnInfoGroup GatherColumnInfos(const RDescriptorsComparison &descCmp, const ROOT::RNTupleDescriptor &srcDesc, RNTupleMergeData &mergeData)
static ROOT::RResult< void > RestoreRealFieldParams(ROOT::RFieldBase &field, const ROOT::RFieldDescriptor &fieldDesc, const ROOT::RNTupleDescriptor &srcDesc)
static std::optional< ENTupleMergeVersionBehavior > ParseOptionVersionBehavior(const TString &opts)
static bool BeginsWithDelimitedWord(const TString &str, const char *word)
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
long long Long64_t
Portable signed long integer 8 bytes.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
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 char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
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
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 Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
The available trivial, native content types of a column.
Given a set of RPageSources merge them into an RPagePersistentSink, optionally changing their compres...
std::optional< TTaskGroup > fTaskGroup
ROOT::RResult< void > MergeSourceClusters(ROOT::Internal::RPageSource &source, std::span< RColumnMergeInfo > commonColumns, std::span< const RColumnMergeInfo > extraDstColumns, RNTupleMergeData &mergeData)
std::unique_ptr< ROOT::RNTupleModel > fModel
RNTupleMerger(std::unique_ptr< ROOT::Internal::RPagePersistentSink > destination, std::unique_ptr< ROOT::RNTupleModel > model)
Creates a RNTupleMerger with the given destination.
std::unique_ptr< ROOT::Internal::RPagePersistentSink > fDestination
ROOT::RResult< void > MergeCommonColumns(ROOT::Internal::RClusterPool &clusterPool, const ROOT::RClusterDescriptor &clusterDesc, std::span< RColumnMergeInfo > commonColumns, const ROOT::Internal::RCluster::ColumnSet_t &commonColumnSet, RSealedPageMergeData &sealedPageData, const RNTupleMergeData &mergeData, ROOT::Internal::RPageAllocator &pageAlloc)
RResult< void > Merge(std::span< ROOT::Internal::RPageSource * > sources, const RNTupleMergeOptions &mergeOpts=RNTupleMergeOptions())
Merge a given set of sources into the destination.
A class to manage the asynchronous execution of work items.
Managed a set of clusters containing compressed and packed pages.
RCluster * GetCluster(ROOT::DescriptorId_t clusterId, const RCluster::ColumnSet_t &physicalColumns)
Returns the requested cluster either from the pool or, in case of a cache miss, lets the I/O thread l...
An in-memory subset of the packed and compressed pages of a cluster.
std::unordered_set< ROOT::DescriptorId_t > ColumnSet_t
const ROnDiskPage * GetOnDiskPage(const ROnDiskPage::Key &key) const
A column element encapsulates the translation between basic C++ types and their column representation...
static const char * GetColumnTypeName(ROOT::ENTupleColumnType type)
static std::unique_ptr< RColumnElementBase > Generate(ROOT::ENTupleColumnType type)
If CppT == void, use the default C++ type for the given column type.
std::size_t GetPackedSize(std::size_t nElements=1U) const
The in-memory representation of a 32bit or 64bit on-disk index column.
Holds the index and the tag of a kSwitch column.
static std::size_t Zip(const void *from, std::size_t nbytes, int compression, void *to)
Returns the size of the compressed data, written into the provided output buffer.
static void Unzip(const void *from, size_t nbytes, size_t dataLen, void *to)
The nbytes parameter provides the size ls of the from buffer.
A helper class for serializing and deserialization of the RNTuple binary format.
Uses standard C++ memory allocation for the column data pages.
Abstract interface to allocate and release pages.
virtual RPage NewPage(std::size_t elementSize, std::size_t nElements)=0
Reserves memory large enough to hold nElements of the given size.
Abstract interface to write data into an ntuple.
virtual void CommitSuppressedColumn(ColumnHandle_t columnHandle)=0
Commits a suppressed column for the current cluster.
const ROOT::RNTupleWriteOptions & GetWriteOptions() const
Returns the sink's write options.
virtual std::uint64_t CommitCluster(ROOT::NTupleSize_t nNewEntries)
Finalize the current cluster and create a new one for the following data.
virtual void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry)=0
Incorporate incremental changes to the model into the ntuple descriptor.
RSealedPage SealPage(const ROOT::Internal::RPage &page, const ROOT::Internal::RColumnElementBase &element)
Helper for streaming a page.
virtual void CommitSealedPageV(std::span< RPageStorage::RSealedPageGroup > ranges)=0
Write a vector of preprocessed pages to storage. The corresponding columns must have been added befor...
Storage provider that reads ntuple pages from a file.
static std::unique_ptr< RPageSourceFile > CreateFromAnchor(const RNTuple &anchor, const ROOT::RNTupleReadOptions &options=ROOT::RNTupleReadOptions())
Used from the RNTuple class to build a datasource if the anchor is already available.
Abstract interface to read data from an ntuple.
Common functionality of an ntuple storage for both reading and writing.
static constexpr std::size_t kNBytesPageChecksum
The page checksum is a 64bit xxhash3.
std::deque< RSealedPage > SealedPageSequence_t
RColumnHandle ColumnHandle_t
The column handle identifies a column with the current open page storage.
void * GrowUnchecked(std::uint32_t nElements)
Increases the number elements in the page.
std::unordered_map< const ROOT::RFieldBase *, const ROOT::RFieldBase * > FieldMap_t
The map keys are the projected target fields, the map values are the backing source fields Note that ...
RResult< void > Add(std::unique_ptr< ROOT::RFieldBase > field, const FieldMap_t &fieldMap)
Adds a new projected field.
Metadata for RNTuple clusters.
ROOT::DescriptorId_t GetId() const
const RPageRange & GetPageRange(ROOT::DescriptorId_t physicalId) const
bool ContainsColumn(ROOT::DescriptorId_t physicalId) const
const RColumnRange & GetColumnRange(ROOT::DescriptorId_t physicalId) const
ROOT::NTupleSize_t GetFirstEntryIndex() const
ROOT::DescriptorId_t GetLogicalId() const
Base class for all ROOT issued exceptions.
A field translates read and write calls from/to underlying columns to/from tree values.
std::vector< RFieldBase * > GetMutableSubfields()
std::string GetQualifiedFieldName() const
Returns the field name and parent field names separated by dots (grandparent.parent....
std::vector< ROOT::ENTupleColumnType > ColumnRepresentation_t
Metadata stored for every field of an RNTuple.
const std::vector< ROOT::DescriptorId_t > & GetLogicalColumnIds() const
ROOT::ENTupleStructure GetStructure() const
std::uint32_t GetColumnCardinality() const
const std::vector< ROOT::DescriptorId_t > & GetLinkIds() const
ROOT::DescriptorId_t GetParentId() const
std::uint64_t GetNRepetitions() const
bool IsProjectedField() const
ROOT::DescriptorId_t GetProjectionSourceId() const
const std::string & GetFieldName() const
const std::string & GetTypeName() const
A log configuration for a channel, e.g.
The on-storage metadata of an RNTuple.
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
const RFieldDescriptor & GetFieldDescriptor(ROOT::DescriptorId_t fieldId) const
RClusterDescriptorIterable GetActiveClusterIterable() const
std::size_t GetNActiveClusters() const
std::string GetTypeNameForComparison(const RFieldDescriptor &fieldDesc) const
Adjust the type name of the passed RFieldDescriptor for comparison with another renormalized type nam...
std::size_t GetNClusters() const
ROOT::DescriptorId_t FindFieldId(std::string_view fieldName, ROOT::DescriptorId_t parentId) const
ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindClusterId(ROOT ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindNextClusterId(ROOT ROOT::DescriptorId_t R__DEPRECATED(6, 46, "This function is ill-defined in the descriptor " "as not all cluster descriptors may be present. This interface is no longer exposed.") FindPrevClusterId(ROOT std::string GetQualifiedFieldName(ROOT::DescriptorId_t fieldId) const
Walks up the parents of the field ID and returns a field name of the form a.b.c.d In case of invalid ...
The RNTupleModel encapulates the schema of an RNTuple.
void Unfreeze()
Transitions an RNTupleModel from the frozen state back to the building state, invalidating all previo...
void Freeze()
Transitions an RNTupleModel from the building state to the frozen state, disabling adding additional ...
const ROOT::RFieldBase & GetConstField(std::string_view fieldName) const
Common user-tunable settings for storing RNTuples.
bool GetEnablePageChecksums() const
void SetCompression(std::uint32_t val)
Representation of an RNTuple data set in a ROOT file.
Long64_t Merge(TCollection *input, TFileMergeInfo *mergeInfo)
RNTuple implements the hadd MergeFile interface Merge this NTuple with the input list entries.
static constexpr std::uint64_t GetCurrentVersion()
Returns the RNTuple version in the following form: Epoch: 2 most significant bytes Major: next 2 byte...
void SetQuantized(std::size_t nBits, std::pair< T, T > valueRange)
Sets this field to use a quantized integer representation using nBits per value.
void SetTruncated(std::size_t nBits)
Set the on-disk representation of this field to a single-precision float truncated to nBits.
void ThrowOnError()
Short-hand method to throw an exception in the case of errors.
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Collection abstract base class.
virtual Int_t GetEntries() const
TKey * FindKey(const char *keyname) const override
Find key with name keyname in the current directory.
TObject * Get(const char *namecycle) override
Return pointer to object identified by namecycle.
A class to pass information from the TFileMerger to the objects being merged.
TString fOptions
Additional text based option being passed down to customize the merge.
A file, usually with extension .root, that stores data and code in the form of serialized objects in ...
Int_t GetCompressionSettings() const
Book space in a file, create I/O buffers, to fill them, (un)compress them.
T * ReadObject()
To read an object (non deriving from TObject) from the file.
const char * GetName() const override
Returns name of object.
Mother of all ROOT objects.
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
@ kStrict
The merger will refuse to merge any 2 RNTuples whose schema doesn't match exactly.
@ kUnion
The merger will update the output model to include all columns from all sources.
@ kWarnOnHigherVersion
The merger will emit a warning when merging RNTuples with higher version than the latest supported by...
std::unique_ptr< T[]> MakeUninitArray(std::size_t size)
Make an array of default-initialized elements.
RProjectedFields & GetProjectedFieldsOfModel(RNTupleModel &model)
std::unique_ptr< RColumnElementBase > GenerateColumnElement(std::type_index inMemoryType, ROOT::ENTupleColumnType onDiskType)
std::string GetRenormalizedTypeName(const std::string &metaNormalizedName)
Given a type name normalized by ROOT meta, renormalize it for RNTuple. E.g., insert std::prefix.
Bool_t IsImplicitMTEnabled()
Returns true if the implicit multi-threading in ROOT is enabled.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
if(fPos !=fText.size()) Fail("unexpected trailing content")
ROOT::DescriptorId_t fOutputId
const ROOT::RFieldDescriptor * fParentFieldDescriptor
ROOT::DescriptorId_t fInputId
std::optional< std::type_index > fInMemoryType
const ROOT::RNTupleDescriptor * fParentNTupleDescriptor
std::uint16_t fOutputReprIndex
const ROOT::RNTupleDescriptor * fSrcDescriptor
ColumnIdMap_t fColumnIdMap
RNTupleMergeData(std::span< RPageSource * > sources, RPageSink &destination, const RNTupleMergeOptions &mergeOpts)
std::span< RPageSource * > fSources
ROOT::NTupleSize_t fNumDstEntries
const RNTupleMergeOptions & fMergeOpts
std::vector< RColumnMergeInfo > fColumns
const ROOT::RNTupleDescriptor & fDstDescriptor
Set of merging options to pass to RNTupleMerger.
ENTupleMergingMode fMergingMode
Determines how the merging treats sources with different models (.
ENTupleMergeVersionBehavior fVersionBehavior
Determines how the Merge function behaves depending on the RNTuple sources' version.
ENTupleMergeErrBehavior fErrBehavior
Determines how the Merge function behaves upon merging errors.
std::optional< std::uint32_t > fCompressionSettings
If fCompressionSettings is empty (the default), the merger will not change the compression of any of ...
bool fExtraVerbose
If true, the merger will emit further diagnostics and information.
std::vector< RPageStorage::RSealedPageGroup > fGroups
std::vector< std::unique_ptr< std::byte[]> > fBuffers
std::deque< RPageStorage::SealedPageSequence_t > fPagesV
The incremental changes to a RNTupleModel
On-disk pages within a page source are identified by the column and page number.
Parameters for the SealPage() method.
bool fWriteChecksum
Adds a 8 byte little-endian xxhash3 checksum to the page payload.
std::uint32_t fCompressionSettings
Compression algorithm and level to apply.
void * fBuffer
Location for sealed output. The memory buffer has to be large enough.
const ROOT::Internal::RPage * fPage
Input page to be sealed.
const ROOT::Internal::RColumnElementBase * fElement
Corresponds to the page's elements, for size calculation etc.
A sealed page contains the bytes of a page as written to storage (packed & compressed).
RResult< void > VerifyChecksumIfEnabled() const
void SetBuffer(const void *buffer)
std::uint32_t GetNElements() const
void SetHasChecksum(bool hasChecksum)
const void * GetBuffer() const
void SetNElements(std::uint32_t nElements)
bool GetHasChecksum() const
std::size_t GetBufferSize() const
void SetBufferSize(std::size_t bufferSize)
std::size_t GetDataSize() const
@ kUseGeneralPurpose
Use the new recommended general-purpose setting; it is a best trade-off between compression ratio/dec...