44#include <unordered_map>
78 auto charBuf =
reinterpret_cast<const unsigned char *
>(
fBuffer);
79 auto checksumBuf =
const_cast<unsigned char *
>(charBuf) + GetDataSize();
80 std::uint64_t xxhash3;
91 return R__FAIL(
"page checksum verification failed, data corruption detected");
98 return R__FAIL(
"invalid attempt to extract non-existing page checksum");
100 assert(fBufferSize >= kNBytesPageChecksum);
101 std::uint64_t checksum;
103 reinterpret_cast<const unsigned char *
>(
fBuffer) + fBufferSize - kNBytesPageChecksum, checksum);
112 auto [itr,
_] = fColumnInfos.emplace(physicalColumnId, std::vector<RColumnInfo>());
113 for (
auto &columnInfo : itr->second) {
114 if (columnInfo.fElementId == elementId) {
115 columnInfo.fRefCounter++;
119 itr->second.emplace_back(
RColumnInfo{elementId, 1});
125 auto itr = fColumnInfos.find(physicalColumnId);
127 for (std::size_t i = 0; i < itr->second.size(); ++i) {
128 if (itr->second[i].fElementId != elementId)
131 itr->second[i].fRefCounter--;
132 if (itr->second[i].fRefCounter == 0) {
133 itr->second.erase(itr->second.begin() + i);
134 if (itr->second.empty()) {
135 fColumnInfos.erase(itr);
145 for (
const auto &[physicalColumnId,
_] : fColumnInfos)
146 result.insert(physicalColumnId);
176std::unique_ptr<ROOT::Internal::RPageSource>
180 if (ntupleName.empty()) {
183 if (location.empty()) {
186 if (location.find(
"daos://") == 0)
188 return std::make_unique<ROOT::Experimental::Internal::RPageSourceDaos>(ntupleName, location, options);
195 return std::make_unique<ROOT::Experimental::Internal::RPageSourceS3>(ntupleName, location, options);
197 throw RException(
R__FAIL(
"This RNTuple build does not support S3. Rebuild ROOT with the 'curl' "
198 "cmake option enabled (-Dcurl=ON) to enable the S3 backend."));
202 return std::make_unique<ROOT::Internal::RPageSourceFile>(ntupleName, location, options);
233 fHasStructure =
true;
241 fDeserializationMode =
mode;
246 auto descGuard = GetExclDescriptorGuard();
247 descGuard.MoveIn(AttachImpl());
248 fStructureBuffer.Reset();
251 fCumulativeClusterCounts.reserve(descGuard->GetNClusterGroups());
252 for (
const auto &cgDesc : descGuard->GetClusterGroupIterable()) {
253 sumClusterCount += cgDesc.GetNClusters();
254 fCumulativeClusterCounts.emplace_back(sumClusterCount);
266 auto descGuard = GetExclDescriptorGuard();
268 for (
const auto &cgDesc : descGuard->GetClusterGroupIterable()) {
269 LoadPageList(cgDesc.GetId(), descGuard);
276 if (cgDesc.HasClusterDetails())
280 MakeUninitArray<unsigned char>(cgDesc.GetPageListLength() + cgDesc.GetPageListLocator().GetNBytesOnStorage());
281 auto zipBuffer = buffer.get() + cgDesc.GetPageListLength();
282 LoadPageListImpl(cgDesc.GetPageListLocator(), zipBuffer);
286 fDeserializationMode);
291 auto clone = CloneImpl();
293 clone->GetExclDescriptorGuard().MoveIn(GetSharedDescriptorGuard()->Clone());
294 clone->fHasStructure =
true;
295 clone->fIsAttached =
true;
296 clone->fDeserializationMode = fDeserializationMode;
297 clone->fCumulativeClusterCounts = fCumulativeClusterCounts;
311 auto exclGuard = GetExclDescriptorGuard();
313 if (exclGuard->GetClusterGroupDescriptor(cgId).HasClusterDetails()) {
319 LoadPageList(cgId, exclGuard);
328 auto iter = std::lower_bound(fCumulativeClusterCounts.begin(), fCumulativeClusterCounts.end(), clusterId + 1);
329 if (iter == fCumulativeClusterCounts.end())
331 return std::distance(fCumulativeClusterCounts.begin(), iter);
336 return GetSharedDescriptorGuard()->GetNEntries();
341 auto sharedGuard = GetSharedDescriptorGuard();
342 if (sharedGuard->GetNClusters() == 0)
345 const auto lastClusterGroupId = sharedGuard->GetNClusterGroups() - 1;
346 auto descGuard = EnsureClusterDetails(lastClusterGroupId, std::move(sharedGuard));
348 const auto &cgDesc = descGuard->GetClusterGroupDescriptor(lastClusterGroupId);
349 const auto &cd = descGuard->GetClusterDescriptor(cgDesc.GetClusterIds().back());
350 R__ASSERT(cd.ContainsColumn(physicalColumnId));
352 const auto &columnRange = cd.GetColumnRange(physicalColumnId);
353 return columnRange.GetFirstElementIndex() + columnRange.GetNElements();
362 auto sharedGuard = GetSharedDescriptorGuard();
363 assert(clusterId < sharedGuard->GetNClusters());
364 nextId = clusterId + 1;
365 if (nextId == sharedGuard->GetNClusters()) {
369 return EnsureClusterDetails(FindClusterGroupId(nextId), std::move(sharedGuard));
384 std::size_t cgLeft = 0;
386 while (cgLeft <= cgRight) {
387 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
388 const auto &cgDesc = *(cgIter + cgMidpoint);
390 if (cgDesc.GetMinEntry() > entryIdx) {
392 cgRight = cgMidpoint - 1;
396 if (cgDesc.GetMinEntry() + cgDesc.GetEntrySpan() <= entryIdx) {
397 cgLeft = cgMidpoint + 1;
403 const auto tgtClusterGroupId = cgDesc.GetId();
404 descGuard = EnsureClusterDetails(tgtClusterGroupId, std::move(descGuard));
407 std::size_t clusterLeft = 0;
408 std::size_t clusterRight = clusterIds.size() - 1;
409 while (clusterLeft <= clusterRight) {
410 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
413 if (clusterDesc.GetFirstEntryIndex() > entryIdx) {
415 clusterRight = clusterMidpoint - 1;
419 if (clusterDesc.GetFirstEntryIndex() + clusterDesc.GetNEntries() <= entryIdx) {
420 clusterLeft = clusterMidpoint + 1;
424 cid = clusterIds[clusterMidpoint];
443 std::size_t cgLeft = 0;
445 while (cgLeft <= cgRight) {
446 const std::size_t cgMidpoint = (cgLeft + cgRight) / 2;
447 descGuard = EnsureClusterDetails(cgMidpoint, std::move(descGuard));
450 const auto &clusterIds = (cgIter + cgMidpoint)->GetClusterIds();
453 const auto &firstColumnRange =
455 if (firstColumnRange.GetFirstElementIndex() >
index) {
458 cgRight = cgMidpoint - 1;
463 if ((lastColumnRange.GetFirstElementIndex() + lastColumnRange.GetNElements()) <=
index) {
465 cgLeft = cgMidpoint + 1;
471 std::size_t clusterLeft = 0;
472 std::size_t clusterRight = clusterIds.size() - 1;
473 while (clusterLeft <= clusterRight) {
474 const std::size_t clusterMidpoint = (clusterLeft + clusterRight) / 2;
475 const auto clusterId = clusterIds[clusterMidpoint];
478 if (columnRange.Contains(
index)) {
483 if (columnRange.GetFirstElementIndex() >
index) {
485 clusterRight = clusterMidpoint - 1;
489 if (columnRange.GetFirstElementIndex() + columnRange.GetNElements() <=
index) {
490 clusterLeft = clusterMidpoint + 1;
504 RNTupleAtomicTimer timer(fCounters->fTimeWallUnzip, fCounters->fTimeCpuUnzip);
506 const auto clusterId = cluster->
GetId();
507 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
508 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(clusterId);
510 fPreloadedClusters[clusterDescriptor.GetFirstEntryIndex()] = clusterId;
512 std::atomic<bool> foundChecksumFailure{
false};
516 std::vector<std::pair<Internal::RPage, Internal::RPagePool::RKey>> allPages;
520 std::size_t nPages = 0;
522 for (
const auto columnId : columnsInCluster) {
523 if (fActivePhysicalColumns.HasColumnInfos(columnId)) {
524 nPages += fActivePhysicalColumns.GetColumnInfos(columnId).size() *
525 clusterDescriptor.GetPageRange(columnId).GetPageInfos().size();
528 allPages.resize(nPages);
529 std::size_t iPage = 0;
531 std::vector<std::unique_ptr<RColumnElementBase>> allElements;
532 for (
const auto columnId : columnsInCluster) {
536 if (!fActivePhysicalColumns.HasColumnInfos(columnId))
538 const auto &columnInfos = fActivePhysicalColumns.GetColumnInfos(columnId);
539 const auto indexOffset = clusterDescriptor.GetColumnRange(columnId).GetFirstElementIndex();
541 allElements.reserve(allElements.size() + columnInfos.size());
542 for (
const auto &info : columnInfos) {
544 const auto element = allElements.back().get();
546 const auto &pageRange = clusterDescriptor.GetPageRange(columnId);
547 std::uint64_t pageNo = 0;
548 std::uint64_t firstInPage = 0;
549 for (
const auto &pi : pageRange.GetPageInfos()) {
551 RSealedPage sealedPage;
552 sealedPage.SetNElements(pi.GetNElements());
553 sealedPage.SetHasChecksum(pi.HasChecksum());
554 sealedPage.SetBufferSize(pi.GetLocator().GetNBytesOnStorage() + pi.HasChecksum() * kNBytesPageChecksum);
555 sealedPage.SetBuffer(onDiskPage->GetAddress());
556 R__ASSERT(onDiskPage->GetSize() == sealedPage.GetBufferSize());
558 auto taskFunc = [
this, columnId, clusterId, firstInPage, sealedPage, element, iPage, indexOffset,
559 &foundChecksumFailure, &allPages]() {
560 auto rv = UnsealPage(sealedPage, *element);
562 foundChecksumFailure =
true;
565 auto newPage = rv.Unwrap();
566 fCounters->fSzUnzip.Add(element->GetSize() * sealedPage.GetNElements());
568 newPage.SetWindow(indexOffset + firstInPage,
571 allPages[iPage] = std::make_pair(std::move(newPage), keyPagePool);
574 if (sealedPage.GetNElements() * element->GetSize() < kInlineDecompressionThreshold) {
577 fTaskScheduler->AddTask(taskFunc);
580 firstInPage += pi.GetNElements();
587 fTaskScheduler->Wait();
588 fCounters->fNPageUnsealed.Add(nPages);
590 if (foundChecksumFailure) {
591 throw RException(
R__FAIL(
"page checksum verification failed, data corruption detected"));
594 fPagePool.PreloadPageV(allPages);
602 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterKey.
fClusterId), GetSharedDescriptorGuard());
603 const auto &clusterDesc = descriptorGuard->GetClusterDescriptor(clusterKey.
fClusterId);
606 if (clusterDesc.GetColumnRange(physicalColumnId).IsSuppressed())
609 const auto &pageRange = clusterDesc.GetPageRange(physicalColumnId);
611 for (
const auto &pageInfo : pageRange.GetPageInfos()) {
615 pageInfo.GetLocator().GetNBytesOnStorage()));
617 perPageFunc(physicalColumnId, pageNo, pageInfo);
626 if (fLastUsedCluster == clusterId)
631 auto descGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
632 firstEntryIndex = descGuard->GetClusterDescriptor(clusterId).GetFirstEntryIndex();
634 auto itr = fPreloadedClusters.begin();
635 while ((itr != fPreloadedClusters.end()) && (itr->first < firstEntryIndex)) {
636 if (fPinnedClusters.count(itr->second) > 0) {
639 fPagePool.Evict(itr->second);
640 itr = fPreloadedClusters.erase(itr);
643 std::size_t poolWindow = 0;
644 while ((itr != fPreloadedClusters.end()) &&
649 while (itr != fPreloadedClusters.end()) {
650 if (fPinnedClusters.count(itr->second) > 0) {
653 fPagePool.Evict(itr->second);
654 itr = fPreloadedClusters.erase(itr);
658 fLastUsedCluster = clusterId;
668 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
669 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(clusterId);
670 pageInfo = clusterDescriptor.GetPageRange(physicalColumnId).Find(localIndex.
GetIndexInCluster());
682 LoadSealedPageImpl(pageInfo.
GetLocator(), sealedPage);
689 const auto &pageInfo = pageSummary.
fPageInfo;
693 const auto elementSize = element->
GetSize();
694 const auto elementInMemoryType = element->GetIdentifier().fInMemoryType;
696 auto pageZero = fPageAllocator->NewPage(elementSize, pageInfo.GetNElements());
697 pageZero.GrowUnchecked(pageInfo.GetNElements());
698 std::memset(pageZero.GetBuffer(), 0, pageZero.GetNBytes());
699 pageZero.SetWindow(pageSummary.
fColumnOffset + pageInfo.GetFirstElementIndex(),
710 return LoadZeroPage(columnHandle, pageSummary);
714 const auto &clusterId = pageSummary.
fClusterId;
715 const auto &pageInfo = pageSummary.
fPageInfo;
718 const auto elementSize = element->
GetSize();
719 const auto elementInMemoryType = element->GetIdentifier().fInMemoryType;
721 UpdateLastUsedCluster(clusterId);
726 sealedPage.
SetBufferSize(pageInfo.GetLocator().GetNBytesOnStorage() + pageInfo.HasChecksum() * kNBytesPageChecksum);
727 std::unique_ptr<unsigned char[]> directReadBuffer;
730 directReadBuffer = MakeUninitArray<unsigned char>(sealedPage.
GetBufferSize());
731 sealedPage.
SetBuffer(directReadBuffer.get());
732 LoadSealedPageImpl(pageInfo.GetLocator(), sealedPage);
734 fCounters->fNPageRead.Inc();
735 fCounters->fNRead.Inc();
738 if (!fCurrentCluster || (fCurrentCluster->GetId() != clusterId) || !fCurrentCluster->ContainsColumn(columnId))
739 fCurrentCluster = fClusterPool.GetCluster(clusterId, fActivePhysicalColumns.ToColumnSet());
740 R__ASSERT(fCurrentCluster->ContainsColumn(columnId));
745 if (!cachedPageRef.Get().IsNull())
746 return cachedPageRef;
749 auto onDiskPage = fCurrentCluster->GetOnDiskPage(key);
751 sealedPage.
SetBuffer(onDiskPage->GetAddress());
756 RNTupleAtomicTimer timer(fCounters->fTimeWallUnzip, fCounters->fTimeCpuUnzip);
757 newPage = UnsealPage(sealedPage, *element).Unwrap();
758 fCounters->fSzUnzip.Add(elementSize * pageInfo.GetNElements());
763 fCounters->fNPageUnsealed.Inc();
765 return fPagePool.RegisterPage(std::move(newPage),
RPagePool::RKey{columnId, elementInMemoryType});
775 if (!cachedPageRef.Get().IsNull()) {
776 UpdateLastUsedCluster(cachedPageRef.Get().GetClusterInfo().GetId());
777 return cachedPageRef;
782 auto descriptorGuard = FindClusterId(columnId, globalIndex, pageSummary.
fClusterId);
785 throw RException(
R__FAIL(
"entry with index " + std::to_string(globalIndex) +
" out of bounds"));
787 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(pageSummary.
fClusterId);
788 const auto &columnRange = clusterDescriptor.GetColumnRange(columnId);
789 if (columnRange.IsSuppressed())
792 pageSummary.
fColumnOffset = columnRange.GetFirstElementIndex();
794 pageSummary.
fPageInfo = clusterDescriptor.GetPageRange(columnId).Find(globalIndex - pageSummary.
fColumnOffset);
797 return LoadPageFromSummary(columnHandle, pageSummary);
808 if (!cachedPageRef.Get().IsNull()) {
809 UpdateLastUsedCluster(clusterId);
810 return cachedPageRef;
818 auto descriptorGuard = EnsureClusterDetails(FindClusterGroupId(clusterId), GetSharedDescriptorGuard());
819 const auto &clusterDescriptor = descriptorGuard->GetClusterDescriptor(clusterId);
820 const auto &columnRange = clusterDescriptor.GetColumnRange(columnId);
821 if (columnRange.IsSuppressed())
825 pageSummary.
fColumnOffset = columnRange.GetFirstElementIndex();
829 return LoadPageFromSummary(columnHandle, pageSummary);
835 fMetrics.ObserveMetrics(fClusterPool.GetMetrics());
836 fMetrics.ObserveMetrics(fPagePool.GetMetrics());
837 fCounters = std::make_unique<RCounters>(
RCounters{
840 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"szReadPayload",
"B",
"volume read from storage (required)"),
841 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"szReadOverhead",
"B",
"volume read from storage (overhead)"),
844 "number of partial clusters preloaded from storage"),
845 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"nPageRead",
"",
"number of pages read from storage"),
846 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"nPageUnsealed",
"",
"number of pages unzipped and decoded"),
847 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallRead",
"ns",
"wall clock time spent reading"),
848 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallUnzip",
"ns",
"wall clock time spent decompressing"),
851 "CPU time spent decompressing"),
853 "bwRead",
"MB/s",
"bandwidth compressed bytes read per second", fMetrics,
855 if (
const auto szReadPayload = metrics.GetLocalCounter(
"szReadPayload")) {
856 if (
const auto szReadOverhead = metrics.GetLocalCounter(
"szReadOverhead")) {
857 if (
const auto timeWallRead = metrics.GetLocalCounter(
"timeWallRead")) {
858 if (
auto walltime = timeWallRead->GetValueAsInt()) {
859 double payload = szReadPayload->GetValueAsInt();
860 double overhead = szReadOverhead->GetValueAsInt();
862 return {
true, (1000. * (payload + overhead) / walltime)};
870 "bwReadUnzip",
"MB/s",
"bandwidth uncompressed bytes read per second", fMetrics,
873 if (
const auto timeWallRead = metrics.
GetLocalCounter(
"timeWallRead")) {
874 if (
auto walltime = timeWallRead->GetValueAsInt()) {
877 return {
true, 1000. * unzip / walltime};
884 "bwUnzip",
"MB/s",
"decompression bandwidth of uncompressed bytes per second", fMetrics,
887 if (
const auto timeWallUnzip = metrics.
GetLocalCounter(
"timeWallUnzip")) {
888 if (
auto walltime = timeWallUnzip->GetValueAsInt()) {
891 return {
true, 1000. * unzip / walltime};
898 "rtReadEfficiency",
"",
"ratio of payload over all bytes read", fMetrics,
900 if (
const auto szReadPayload = metrics.
GetLocalCounter(
"szReadPayload")) {
901 if (
const auto szReadOverhead = metrics.
GetLocalCounter(
"szReadOverhead")) {
902 if (
auto payload = szReadPayload->GetValueAsInt()) {
904 return {
true, 1. / (1. + (1. * szReadOverhead->GetValueAsInt()) / payload)};
910 *fMetrics.MakeCounter<
RNTupleCalcPerf *>(
"rtCompression",
"",
"ratio of compressed bytes / uncompressed bytes",
911 fMetrics, [](
const RNTupleMetrics &metrics) -> std::pair<bool, double> {
912 if (
const auto szReadPayload =
915 if (
auto unzip = szUnzip->GetValueAsInt()) {
916 return {
true, (1. * szReadPayload->GetValueAsInt()) / unzip};
927 return UnsealPage(sealedPage, element, *fPageAllocator);
939 memset(page.GetBuffer(), 0, page.GetNBytes());
956 memcpy(page.GetBuffer(), sealedPage.
GetBuffer(), bytesPacked);
962 page = std::move(tmp);
971 if (fHasStreamerInfosRegistered)
974 for (
const auto &extraTypeInfo : fDescriptor.GetExtraTypeInfoIterable()) {
982 fHasStreamerInfosRegistered =
true;
997 if (fMaxAllocatedBytes - fCurrentAllocatedBytes >= targetAvailableSize)
1000 auto itr = fColumnsSortedByPageSize.begin();
1001 while (itr != fColumnsSortedByPageSize.end()) {
1002 if (itr->fCurrentPageSize <= pageSizeLimit)
1004 if (itr->fCurrentPageSize == itr->fInitialPageSize) {
1010 auto itrFlush = itr++;
1013 if (itr != fColumnsSortedByPageSize.end())
1017 if (fMaxAllocatedBytes - fCurrentAllocatedBytes >= targetAvailableSize)
1022 itr = fColumnsSortedByPageSize.find(next);
1031 auto itr = fColumnsSortedByPageSize.find(key);
1032 if (itr == fColumnsSortedByPageSize.end()) {
1033 if (!TryEvict(newWritePageSize, 0))
1035 fColumnsSortedByPageSize.insert({&column, newWritePageSize, newWritePageSize});
1036 fCurrentAllocatedBytes += newWritePageSize;
1041 assert(newWritePageSize >= elem.fInitialPageSize);
1043 if (newWritePageSize == elem.fCurrentPageSize)
1046 fColumnsSortedByPageSize.erase(itr);
1048 if (newWritePageSize < elem.fCurrentPageSize) {
1050 fCurrentAllocatedBytes -= elem.fCurrentPageSize - newWritePageSize;
1051 elem.fCurrentPageSize = newWritePageSize;
1052 fColumnsSortedByPageSize.insert(elem);
1057 const auto diffBytes = newWritePageSize - elem.fCurrentPageSize;
1058 if (!TryEvict(diffBytes, elem.fCurrentPageSize)) {
1061 fColumnsSortedByPageSize.insert(elem);
1064 fCurrentAllocatedBytes += diffBytes;
1065 elem.fCurrentPageSize = newWritePageSize;
1066 fColumnsSortedByPageSize.insert(elem);
1073 :
RPageStorage(
name), fOptions(options.Clone()), fWritePageMemoryManager(options.GetPageBufferBudget())
1082 assert(config.
fPage);
1086 unsigned char *pageBuf =
reinterpret_cast<unsigned char *
>(config.
fPage->
GetBuffer());
1087 bool isAdoptedBuffer =
true;
1089 auto nBytesChecksum = config.
fWriteChecksum * kNBytesPageChecksum;
1093 pageBuf =
new unsigned char[nBytesPacked];
1094 isAdoptedBuffer =
false;
1097 auto nBytesZipped = nBytesPacked;
1103 if (!isAdoptedBuffer)
1105 pageBuf =
reinterpret_cast<unsigned char *
>(config.
fBuffer);
1106 isAdoptedBuffer =
true;
1112 sealedPage.ChecksumIfEnabled();
1120 const auto nBytes = page.
GetNBytes() + GetWriteOptions().GetEnablePageChecksums() * kNBytesPageChecksum;
1121 if (fSealPageBuffer.size() < nBytes)
1122 fSealPageBuffer.resize(nBytes);
1125 config.
fPage = &page;
1128 config.
fWriteChecksum = GetWriteOptions().GetEnablePageChecksums();
1130 config.
fBuffer = fSealPageBuffer.data();
1132 return SealPage(config);
1137 for (
const auto &cb : fOnDatasetCommitCallbacks)
1139 return CommitDatasetImpl();
1146 const auto nBytes = elementSize * nElements;
1147 if (!fWritePageMemoryManager.TryUpdate(*columnHandle.
fColumn, nBytes))
1149 return fPageAllocator->NewPage(elementSize, nElements);
1154std::unique_ptr<ROOT::Internal::RPageSink>
1158 if (ntupleName.empty()) {
1161 if (location.empty()) {
1164 if (location.find(
"daos://") == 0) {
1165#ifdef R__ENABLE_DAOS
1166 return std::make_unique<ROOT::Experimental::Internal::RPageSinkDaos>(ntupleName, location, options);
1174 return std::make_unique<ROOT::Experimental::Internal::RPageSinkS3>(ntupleName, location, options);
1176 throw RException(
R__FAIL(
"This RNTuple build does not support S3. Rebuild ROOT with the 'curl' "
1177 "cmake option enabled (-Dcurl=ON) to enable the S3 backend."));
1182 return std::make_unique<ROOT::Internal::RPageSinkFile>(ntupleName, location, options);
1196 auto columnId = fDescriptorBuilder.GetDescriptor().GetNPhysicalColumns();
1211 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1218 if (fIsInitialized) {
1226 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1228 if (descriptor.GetNLogicalColumns() > descriptor.GetNPhysicalColumns()) {
1232 const auto &reps =
f.GetColumnRepresentatives();
1235 return reps.size() * reps[0].size();
1237 std::uint32_t nNewPhysicalColumns = 0;
1239 nNewPhysicalColumns += getNColumns(*
f);
1240 for (
const auto &descendant : *
f)
1241 nNewPhysicalColumns += getNColumns(descendant);
1243 fDescriptorBuilder.ShiftAliasColumns(nNewPhysicalColumns);
1247 auto fieldId = descriptor.GetNFields();
1248 fDescriptorBuilder.AddField(
f, fieldId);
1249 fDescriptorBuilder.AddFieldLink(
f.GetParent()->GetOnDiskId(), fieldId);
1250 f.SetOnDiskId(fieldId);
1254 auto fieldId = descriptor.GetNFields();
1255 auto sourceFieldId =
1257 fDescriptorBuilder.AddField(
f, fieldId);
1258 fDescriptorBuilder.AddFieldLink(
f.GetParent()->GetOnDiskId(), fieldId);
1259 fDescriptorBuilder.AddFieldProjection(sourceFieldId, fieldId);
1260 f.SetOnDiskId(fieldId);
1261 for (
const auto &source : descriptor.GetColumnIterable(sourceFieldId)) {
1262 auto targetId = descriptor.GetNLogicalColumns();
1268 .ValueRange(source.GetValueRange())
1269 .Type(source.GetType())
1270 .Index(source.GetIndex())
1271 .RepresentationIndex(source.GetRepresentationIndex());
1272 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1276 R__ASSERT(firstEntry >= fPrevClusterNEntries);
1277 const auto nColumnsBeforeUpdate = descriptor.GetNPhysicalColumns();
1280 for (
auto &descendant : *
f)
1281 addField(descendant);
1284 addProjectedField(*
f);
1285 for (
auto &descendant : *
f)
1286 addProjectedField(descendant);
1289 const auto nColumns = descriptor.GetNPhysicalColumns();
1290 fOpenColumnRanges.reserve(fOpenColumnRanges.size() + (nColumns - nColumnsBeforeUpdate));
1291 fOpenPageRanges.reserve(fOpenPageRanges.size() + (nColumns - nColumnsBeforeUpdate));
1301 fOpenColumnRanges.emplace_back(columnRange);
1304 fOpenPageRanges.emplace_back(std::move(pageRange));
1309 if (fSerializationContext.GetHeaderSize() > 0)
1310 fSerializationContext.MapSchema(descriptor,
true);
1316 throw RException(
R__FAIL(
"ROOT bug: unexpected type extra info in UpdateExtraTypeInfo()"));
1323 fDescriptorBuilder.SetNTuple(fNTupleName, model.GetDescription());
1324 fDescriptorBuilder.SetVersionForWriting();
1325 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1328 fDescriptorBuilder.AddField(fieldZero, 0);
1329 fieldZero.SetOnDiskId(0);
1331 projectedFields.GetFieldZero().SetOnDiskId(0);
1334 initialChangeset.fAddedFields.reserve(fieldZero.GetMutableSubfields().size());
1335 for (
auto f : fieldZero.GetMutableSubfields())
1336 initialChangeset.fAddedFields.emplace_back(
f);
1337 initialChangeset.fAddedProjectedFields.reserve(projectedFields.GetFieldZero().GetMutableSubfields().size());
1338 for (
auto f : projectedFields.GetFieldZero().GetMutableSubfields())
1339 initialChangeset.fAddedProjectedFields.emplace_back(
f);
1340 UpdateSchema(initialChangeset, 0U);
1343 auto buffer = MakeUninitArray<unsigned char>(fSerializationContext.GetHeaderSize());
1345 InitImpl(buffer.get(), fSerializationContext.GetHeaderSize());
1347 fDescriptorBuilder.BeginHeaderExtension();
1350std::unique_ptr<ROOT::RNTupleModel>
1354 fDescriptorBuilder.SetSchemaFromExisting(srcDescriptor);
1356 fDescriptorBuilder.SetVersionForWriting();
1357 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1360 const auto nColumns = descriptor.GetNPhysicalColumns();
1361 R__ASSERT(fOpenColumnRanges.empty() && fOpenPageRanges.empty());
1362 fOpenColumnRanges.reserve(nColumns);
1363 fOpenPageRanges.reserve(nColumns);
1365 const auto &column = descriptor.GetColumnDescriptor(i);
1371 fOpenColumnRanges.emplace_back(columnRange);
1374 fOpenPageRanges.emplace_back(std::move(pageRange));
1381 auto nEntries = cluster.GetNEntries();
1382 for (
unsigned int i = 0; i < fOpenColumnRanges.size(); ++i) {
1383 R__ASSERT(fOpenColumnRanges[i].GetPhysicalColumnId() == i);
1386 const auto &columnRange = cluster.GetColumnRange(i);
1387 R__ASSERT(columnRange.GetPhysicalColumnId() == i);
1389 fOpenColumnRanges[i].IncrementFirstElementIndex(columnRange.GetNElements());
1391 fDescriptorBuilder.AddCluster(cluster.Clone());
1392 fPrevClusterNEntries += nEntries;
1398 modelOpts.SetReconstructProjections(
true);
1400 modelOpts.SetEmulateUnknownTypes(
true);
1401 auto model = descriptor.CreateModel(modelOpts);
1402 if (!copyClusters) {
1404 projectedFields.GetFieldZero().SetOnDiskId(model->GetConstFieldZero().GetOnDiskId());
1409 auto buffer = MakeUninitArray<unsigned char>(fSerializationContext.GetHeaderSize());
1411 InitImpl(buffer.get(), fSerializationContext.GetHeaderSize());
1413 fDescriptorBuilder.BeginHeaderExtension();
1416 fIsInitialized =
true;
1423 std::span<const RColumnFormat> newRepresentation,
1424 std::uint64_t clusterOffset)
1426 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1428 assert(&descriptor.GetFieldDescriptor(field.
GetId()) == &field);
1434 const std::size_t firstPhysicalIndex = fDescriptorBuilder.GetDescriptor().GetNPhysicalColumns();
1437 fDescriptorBuilder.ShiftAliasColumns(newRepresentation.size());
1439 std::uint16_t columnIndex = 0;
1440 for (
auto columnRepr : newRepresentation) {
1441 std::size_t bitsOnStorage = columnRepr.fBitWidth;
1442 if (!bitsOnStorage) {
1444 if (rangeMin != rangeMax) {
1447 bitsOnStorage = rangeMin;
1451 const auto &firstReprColumnRange = fOpenColumnRanges.at(firstReprColumnId);
1454 const std::uint64_t newReprFirstElemIndex = firstReprColumnRange.GetFirstElementIndex() + clusterOffset;
1461 .
Type(columnRepr.fType)
1466 if (newReprFirstElemIndex)
1468 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1470 if (newReprFirstElemIndex != 0) {
1487 fOpenColumnRanges.emplace_back(columnRange);
1491 fOpenPageRanges.emplace_back(std::move(pageRange));
1493 fSerializationContext.MapPhysicalColumnId(columnId);
1498 fDescriptorBuilder.EnsureValidDescriptor().ThrowOnError();
1500 return firstPhysicalIndex;
1508 assert(!pointedColumn.IsAliasColumn());
1511 const auto columnId = fDescriptorBuilder.GetDescriptor().GetNLogicalColumns();
1516 .
Type(pointedColumn.GetType())
1517 .
Index(pointedColumn.GetIndex())
1522 fDescriptorBuilder.AddColumn(columnBuilder.
MoveDescriptor().Unwrap());
1524 fDescriptorBuilder.EnsureValidDescriptor().ThrowOnError();
1529 fOpenColumnRanges.at(columnHandle.
fPhysicalId).SetIsSuppressed(
true);
1539 RNTupleAtomicTimer timer(fCounters->fTimeWallZip, fCounters->fTimeCpuZip);
1540 sealedPage = SealPage(page, *element);
1542 fCounters->fSzZip.Add(page.
GetNBytes());
1547 pageInfo.
SetHasChecksum(GetWriteOptions().GetEnablePageChecksums());
1548 fOpenPageRanges.at(columnHandle.
fPhysicalId).GetPageInfos().emplace_back(pageInfo);
1554 fOpenColumnRanges.at(physicalColumnId).IncrementNElements(sealedPage.
GetNElements());
1558 pageInfo.
SetLocator(CommitSealedPageImpl(physicalColumnId, sealedPage));
1560 fOpenPageRanges.at(physicalColumnId).GetPageInfos().emplace_back(pageInfo);
1563std::vector<ROOT::RNTupleLocator>
1565 const std::vector<bool> &
mask)
1567 std::vector<ROOT::RNTupleLocator> locators;
1568 locators.reserve(
mask.size());
1570 for (
auto &range : ranges) {
1571 for (
auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt) {
1573 locators.push_back(CommitSealedPageImpl(range.fPhysicalColumnId, *sealedPageIt));
1576 locators.shrink_to_fit();
1583 struct RSealedPageLink {
1585 std::size_t fLocatorIdx = 0;
1588 std::vector<bool>
mask;
1590 std::vector<std::size_t> locatorIndexes;
1592 std::unordered_map<std::uint64_t, RSealedPageLink> originalPages;
1593 std::size_t iLocator = 0;
1594 for (
auto &range : ranges) {
1595 const auto rangeSize = std::distance(range.fFirst, range.fLast);
1596 mask.reserve(
mask.size() + rangeSize);
1597 locatorIndexes.reserve(locatorIndexes.size() + rangeSize);
1599 for (
auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt) {
1600 if (!fFeatures.fCanMergePages || !fOptions->GetEnableSamePageMerging()) {
1601 mask.emplace_back(
true);
1602 locatorIndexes.emplace_back(iLocator++);
1606 R__ASSERT(sealedPageIt->GetHasChecksum());
1608 const auto chk = sealedPageIt->GetChecksum().Unwrap();
1609 auto itr = originalPages.find(chk);
1610 if (itr == originalPages.end()) {
1611 originalPages.insert({chk, {&(*sealedPageIt), iLocator}});
1612 mask.emplace_back(
true);
1613 locatorIndexes.emplace_back(iLocator++);
1617 const auto *
p = itr->second.fSealedPage;
1618 if ((sealedPageIt->GetDataSize() !=
p->GetDataSize()) ||
1619 (memcmp(sealedPageIt->GetBuffer(),
p->GetBuffer(),
p->GetDataSize()) != 0)) {
1620 mask.emplace_back(
true);
1621 locatorIndexes.emplace_back(iLocator++);
1625 mask.emplace_back(
false);
1626 locatorIndexes.emplace_back(itr->second.fLocatorIdx);
1629 mask.shrink_to_fit();
1630 locatorIndexes.shrink_to_fit();
1633 auto locators = CommitSealedPageVImpl(ranges,
mask);
1636 for (
auto &range : ranges) {
1637 for (
auto sealedPageIt = range.fFirst; sealedPageIt != range.fLast; ++sealedPageIt) {
1638 fOpenColumnRanges.at(range.fPhysicalColumnId).IncrementNElements(sealedPageIt->GetNElements());
1642 pageInfo.
SetLocator(locators[locatorIndexes[i++]]);
1644 fOpenPageRanges.at(range.fPhysicalColumnId).GetPageInfos().emplace_back(pageInfo);
1656 for (
unsigned int i = 0; i < fOpenColumnRanges.size(); ++i) {
1657 RStagedCluster::RColumnInfo columnInfo;
1658 columnInfo.fCompressionSettings = fOpenColumnRanges[i].GetCompressionSettings().value();
1659 if (fOpenColumnRanges[i].IsSuppressed()) {
1660 assert(fOpenPageRanges[i].GetPageInfos().empty());
1661 columnInfo.fPageRange.SetPhysicalColumnId(i);
1662 columnInfo.fIsSuppressed =
true;
1664 fOpenColumnRanges[i].SetNElements(0);
1665 fOpenColumnRanges[i].SetIsSuppressed(
false);
1667 std::swap(columnInfo.fPageRange, fOpenPageRanges[i]);
1668 fOpenPageRanges[i].SetPhysicalColumnId(i);
1670 columnInfo.fNElements = fOpenColumnRanges[i].GetNElements();
1671 fOpenColumnRanges[i].SetNElements(0);
1673 stagedCluster.
fColumnInfos.push_back(std::move(columnInfo));
1676 return stagedCluster;
1681 for (
const auto &cluster : clusters) {
1683 clusterBuilder.
ClusterId(fDescriptorBuilder.GetDescriptor().GetNActiveClusters())
1684 .FirstEntryIndex(fPrevClusterNEntries)
1686 for (
const auto &columnInfo : cluster.fColumnInfos) {
1687 const auto colId = columnInfo.fPageRange.GetPhysicalColumnId();
1688 if (columnInfo.fIsSuppressed) {
1689 assert(columnInfo.fPageRange.GetPageInfos().empty());
1692 clusterBuilder.
CommitColumnRange(colId, fOpenColumnRanges[colId].GetFirstElementIndex(),
1693 columnInfo.fCompressionSettings, columnInfo.fPageRange);
1694 fOpenColumnRanges[colId].IncrementFirstElementIndex(columnInfo.fNElements);
1699 for (
const auto &columnInfo : cluster.fColumnInfos) {
1700 if (!columnInfo.fIsSuppressed)
1702 const auto colId = columnInfo.fPageRange.GetPhysicalColumnId();
1706 const auto &columnRangeFromDesc = clusterBuilder.
GetColumnRange(colId);
1708 columnRangeFromDesc.GetNElements());
1711 fDescriptorBuilder.AddCluster(clusterBuilder.
MoveDescriptor().Unwrap());
1712 fPrevClusterNEntries += cluster.fNEntries;
1718 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1720 const auto nClusters = descriptor.GetNActiveClusters();
1721 std::vector<ROOT::DescriptorId_t> physClusterIDs;
1722 physClusterIDs.reserve(nClusters);
1723 for (
auto i = fNextClusterInGroup; i < nClusters; ++i) {
1724 physClusterIDs.emplace_back(fSerializationContext.MapClusterId(i));
1729 auto bufPageList = MakeUninitArray<unsigned char>(szPageList);
1732 const auto clusterGroupId = descriptor.GetNClusterGroups();
1733 const auto locator = CommitClusterGroupImpl(bufPageList.get(), szPageList);
1736 if (fNextClusterInGroup == nClusters) {
1739 const auto &firstClusterDesc = descriptor.GetClusterDescriptor(fNextClusterInGroup);
1740 const auto &lastClusterDesc = descriptor.GetClusterDescriptor(nClusters - 1);
1741 cgBuilder.
MinEntry(firstClusterDesc.GetFirstEntryIndex())
1742 .
EntrySpan(lastClusterDesc.GetFirstEntryIndex() + lastClusterDesc.GetNEntries() -
1743 firstClusterDesc.GetFirstEntryIndex())
1744 .
NClusters(nClusters - fNextClusterInGroup);
1746 std::vector<ROOT::DescriptorId_t> clusterIds;
1747 clusterIds.reserve(nClusters);
1748 for (
auto i = fNextClusterInGroup; i < nClusters; ++i) {
1749 clusterIds.emplace_back(i);
1752 fDescriptorBuilder.AddClusterGroup(cgBuilder.
MoveDescriptor().Unwrap());
1753 fSerializationContext.MapClusterGroupId(clusterGroupId);
1755 fNextClusterInGroup = nClusters;
1764 auto attrSetDesc = attrSetDescBuilder.
SchemaVersion(kSchemaVersionMajor, kSchemaVersionMinor)
1770 fDescriptorBuilder.AddAttributeSet(std::move(attrSetDesc)).ThrowOnError();
1775 if (!fInfosOfStreamerFields.empty()) {
1778 for (
const auto &etDesc : fDescriptorBuilder.GetDescriptor().GetExtraTypeInfoIterable()) {
1782 R__ASSERT(etDesc.GetTypeName().empty());
1783 R__ASSERT(etDesc.GetTypeVersion() == 0);
1785 fInfosOfStreamerFields.merge(etInfo);
1792 fDescriptorBuilder.ReplaceExtraTypeInfo(extraInfoBuilder.
MoveDescriptor().Unwrap());
1795 const auto &descriptor = fDescriptorBuilder.GetDescriptor();
1798 auto bufFooter = MakeUninitArray<unsigned char>(szFooter);
1801 return CommitDatasetImpl(bufFooter.get(), szFooter);
1807 fCounters = std::make_unique<RCounters>(
RCounters{
1808 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"nPageCommitted",
"",
"number of pages committed to storage"),
1809 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"szWritePayload",
"B",
"volume written for committed pages"),
1811 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallWrite",
"ns",
"wall clock time spent writing"),
1812 *fMetrics.MakeCounter<
RNTupleAtomicCounter *>(
"timeWallZip",
"ns",
"wall clock time spent compressing"),
1815 "CPU time spent compressing")});
#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__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
winID h TVirtualViewer3D TVirtualGLPainter p
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 mask
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 index
Option_t Option_t TPoint TPoint const char mode
A thread-safe integral performance counter.
A metric element that computes its floating point value from other counters.
std::int64_t GetValueAsInt() const override
A collection of Counter objects with a name, a unit, and a description.
const RNTuplePerfCounter * GetLocalCounter(std::string_view name) const
Searches counters registered in this object only. Returns nullptr if name is not found.
Record wall time and CPU time between construction and destruction.
RNTupleAttrSetDescriptorBuilder & AnchorLocator(const RNTupleLocator &loc)
RNTupleAttrSetDescriptorBuilder & SchemaVersion(std::uint16_t major, std::uint16_t minor)
RResult< ROOT::Experimental::RNTupleAttrSetDescriptor > MoveDescriptor()
Attempt to make an AttributeSet descriptor.
RNTupleAttrSetDescriptorBuilder & Name(std::string_view name)
RNTupleAttrSetDescriptorBuilder & AnchorLength(std::uint32_t length)
A helper class for piece-wise construction of an RClusterDescriptor.
RResult< void > MarkSuppressedColumnRange(ROOT::DescriptorId_t physicalId)
Books the given column ID as being suppressed in this cluster.
RResult< void > CommitColumnRange(ROOT::DescriptorId_t physicalId, std::uint64_t firstElementIndex, std::uint32_t compressionSettings, const RClusterDescriptor::RPageRange &pageRange)
RClusterDescriptorBuilder & NEntries(std::uint64_t nEntries)
RResult< void > CommitSuppressedColumnRanges(const RNTupleDescriptor &desc)
Sets the first element index and number of elements for all the suppressed column ranges.
RResult< RClusterDescriptor > MoveDescriptor()
Move out the full cluster descriptor including page locations.
const RClusterDescriptor::RColumnRange & GetColumnRange(ROOT::DescriptorId_t physicalId)
RClusterDescriptorBuilder & ClusterId(ROOT::DescriptorId_t clusterId)
A helper class for piece-wise construction of an RClusterGroupDescriptor.
RClusterGroupDescriptorBuilder & EntrySpan(std::uint64_t entrySpan)
RClusterGroupDescriptorBuilder & PageListLocator(const RNTupleLocator &pageListLocator)
RClusterGroupDescriptorBuilder & PageListLength(std::uint64_t pageListLength)
RClusterGroupDescriptorBuilder & MinEntry(std::uint64_t minEntry)
void AddSortedClusters(const std::vector< ROOT::DescriptorId_t > &clusterIds)
RResult< RClusterGroupDescriptor > MoveDescriptor()
RClusterGroupDescriptorBuilder & ClusterGroupId(ROOT::DescriptorId_t clusterGroupId)
RClusterGroupDescriptorBuilder & NClusters(std::uint32_t nClusters)
An in-memory subset of the packed and compressed pages of a cluster.
std::unordered_set< ROOT::DescriptorId_t > ColumnSet_t
const ColumnSet_t & GetAvailPhysicalColumns() const
bool ContainsColumn(ROOT::DescriptorId_t colId) const
const ROnDiskPage * GetOnDiskPage(const ROnDiskPage::Key &key) const
ROOT::DescriptorId_t GetId() const
A helper class for piece-wise construction of an RColumnDescriptor.
RColumnDescriptorBuilder & SetSuppressedDeferred()
RColumnDescriptorBuilder & LogicalColumnId(ROOT::DescriptorId_t logicalColumnId)
RColumnDescriptorBuilder & FieldId(ROOT::DescriptorId_t fieldId)
RColumnDescriptorBuilder & BitsOnStorage(std::uint16_t bitsOnStorage)
RColumnDescriptorBuilder & ValueRange(double min, double max)
RColumnDescriptorBuilder & Type(ROOT::ENTupleColumnType type)
RColumnDescriptorBuilder & PhysicalColumnId(ROOT::DescriptorId_t physicalColumnId)
RColumnDescriptorBuilder & FirstElementIndex(std::uint64_t firstElementIdx)
RColumnDescriptorBuilder & Index(std::uint32_t index)
RColumnDescriptorBuilder & RepresentationIndex(std::uint16_t representationIndex)
RResult< RColumnDescriptor > MoveDescriptor()
Attempt to make a column descriptor.
A column element encapsulates the translation between basic C++ types and their column representation...
virtual bool IsMappable() const
Derived, typed classes tell whether the on-storage layout is bitwise identical to the memory layout.
static std::pair< std::uint16_t, std::uint16_t > GetValidBitRange(ROOT::ENTupleColumnType type)
Most types have a fixed on-disk bit width.
virtual void Unpack(void *destination, const void *source, std::size_t count) const
If the on-storage layout and the in-memory layout differ, unpacking creates a memory page from an on-...
std::size_t GetSize() const
virtual RIdentifier GetIdentifier() const =0
virtual void Pack(void *destination, const void *source, std::size_t count) const
If the on-storage layout and the in-memory layout differ, packing creates an on-disk page from an in-...
std::size_t GetPackedSize(std::size_t nElements=1U) const
A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into ...
std::optional< std::pair< double, double > > GetValueRange() const
std::uint16_t GetRepresentationIndex() const
ROOT::Internal::RColumnElementBase * GetElement() const
ROOT::ENTupleColumnType GetType() const
ROOT::NTupleSize_t GetFirstElementIndex() const
std::size_t GetWritePageCapacity() const
ROOT::DescriptorId_t GetOnDiskId() const
std::uint16_t GetBitsOnStorage() const
std::uint32_t GetIndex() const
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.
static unsigned int GetClusterBunchSize(const RNTupleReadOptions &options)
static std::uint32_t SerializeXxHash3(const unsigned char *data, std::uint64_t length, std::uint64_t &xxhash3, void *buffer)
Writes a XxHash-3 64bit checksum of the byte range given by data and length.
static RResult< void > DeserializePageList(const void *buffer, std::uint64_t bufSize, ROOT::DescriptorId_t clusterGroupId, RNTupleDescriptor &desc, EDescriptorDeserializeMode mode)
static RResult< StreamerInfoMap_t > DeserializeStreamerInfos(const std::string &extraTypeInfoContent)
static RResult< void > VerifyXxHash3(const unsigned char *data, std::uint64_t length, std::uint64_t &xxhash3)
Expects an xxhash3 checksum in the 8 bytes following data + length and verifies it.
EDescriptorDeserializeMode
static RResult< std::uint32_t > SerializePageList(void *buffer, const RNTupleDescriptor &desc, std::span< ROOT::DescriptorId_t > physClusterIDs, const RContext &context)
static RResult< std::uint32_t > SerializeFooter(void *buffer, const RNTupleDescriptor &desc, const RContext &context)
static std::uint32_t DeserializeUInt64(const void *buffer, std::uint64_t &val)
static RResult< RContext > SerializeHeader(void *buffer, const RNTupleDescriptor &desc)
static std::string SerializeStreamerInfos(const StreamerInfoMap_t &infos)
A memory region that contains packed and compressed pages.
void Register(const ROnDiskPage::Key &key, const ROnDiskPage &onDiskPage)
Inserts information about a page stored in fMemory.
A page as being stored on disk, that is packed and compressed.
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.
RStagedCluster StageCluster(ROOT::NTupleSize_t nNewEntries) final
Stage the current cluster and create a new one for the following data.
void UpdateSchema(const ROOT::Internal::RNTupleModelChangeset &changeset, ROOT::NTupleSize_t firstEntry) override
Incorporate incremental changes to the model into the ntuple descriptor.
void CommitSealedPage(ROOT::DescriptorId_t physicalColumnId, const RPageStorage::RSealedPage &sealedPage) final
Write a preprocessed page to storage. The column must have been added before.
std::unique_ptr< RNTupleModel > InitFromDescriptor(const ROOT::RNTupleDescriptor &descriptor, bool copyClusters)
Initialize sink based on an existing descriptor and fill into the descriptor builder,...
void UpdateExtraTypeInfo(const ROOT::RExtraTypeInfoDescriptor &extraTypeInfo) final
Adds an extra type information record to schema.
void CommitAttributeSet(std::string_view attrSetName, const RNTupleLink &attrAnchorInfo) final
Adds the given anchor information (name + locator) into the main RNTuple's descriptor as an attribute...
ColumnHandle_t AddColumn(ROOT::DescriptorId_t fieldId, ROOT::Internal::RColumn &column) final
Register a new column.
virtual std::vector< RNTupleLocator > CommitSealedPageVImpl(std::span< RPageStorage::RSealedPageGroup > ranges, const std::vector< bool > &mask)
Vector commit of preprocessed pages.
RPagePersistentSink(std::string_view ntupleName, const ROOT::RNTupleWriteOptions &options)
void CommitSuppressedColumn(ColumnHandle_t columnHandle) final
Commits a suppressed column for the current cluster.
void AddAliasColumn(const ROOT::RNTupleDescriptor &desc, const ROOT::RFieldDescriptor &field, ROOT::DescriptorId_t physicalId)
Adds a new alias column pointing to an existing column with the given physical id to the given field.
void CommitStagedClusters(std::span< RStagedCluster > clusters) final
Commit staged clusters, logically appending them to the ntuple descriptor.
static std::unique_ptr< RPageSink > Create(std::string_view ntupleName, std::string_view location, const ROOT::RNTupleWriteOptions &options=ROOT::RNTupleWriteOptions())
Guess the concrete derived page source from the location.
void CommitPage(ColumnHandle_t columnHandle, const ROOT::Internal::RPage &page) final
Write a page to the storage. The column must have been added before.
RNTupleLink CommitDatasetImpl() final
~RPagePersistentSink() override
virtual void InitImpl(unsigned char *serializedHeader, std::uint32_t length)=0
void CommitClusterGroup() final
Write out the page locations (page list envelope) for all the committed clusters since the last call ...
void CommitSealedPageV(std::span< RPageStorage::RSealedPageGroup > ranges) final
Write a vector of preprocessed pages to storage. The corresponding columns must have been added befor...
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSink.
ROOT::DescriptorId_t AddColumnRepresentation(const ROOT::RFieldDescriptor &field, std::span< const ROOT::Internal::RColumnFormat > newRepresentation, std::uint64_t clusterOffset)
Adds a new column representation to the given field.
Abstract interface to write data into an ntuple.
RNTupleLink CommitDataset()
Run the registered callbacks and finalize the current cluster and the entrire data set.
virtual ROOT::Internal::RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements)
Get a new, empty page for the given column that can be filled with up to nElements; nElements must be...
RSealedPage SealPage(const ROOT::Internal::RPage &page, const ROOT::Internal::RColumnElementBase &element)
Helper for streaming a page.
RPageSink(std::string_view ntupleName, const ROOT::RNTupleWriteOptions &options)
void Insert(ROOT::DescriptorId_t physicalColumnId, ROOT::Internal::RColumnElementBase::RIdentifier elementId)
ROOT::Internal::RCluster::ColumnSet_t ToColumnSet() const
void Erase(ROOT::DescriptorId_t physicalColumnId, ROOT::Internal::RColumnElementBase::RIdentifier elementId)
Either a shared or an exclusive lock guard.
An RAII wrapper used for the writable access to RPageSource::fDescriptor. See GetSharedDescriptorGuar...
RAnyDescriptorGuard FindNextClusterId(ROOT::DescriptorId_t clusterId, ROOT::DescriptorId_t &nextId)
Uses FindClusterId to search for the cluster with the entry index following the last entry index of t...
void LoadStructure()
Loads header and footer without decompressing or deserializing them.
virtual ROOT::Internal::RPageRef LoadPage(ColumnHandle_t columnHandle, ROOT::NTupleSize_t globalIndex)
Allocates and fills a page that contains the index-th element.
void LoadAllPageLists()
Load all remaining cluster details.
void RegisterStreamerInfos()
Builds the streamer info records from the descriptor's extra type info section.
ROOT::NTupleSize_t GetNElements(ROOT::DescriptorId_t physicalColumnId)
RAnyDescriptorGuard EnsureClusterDetails(DescriptorId_t cgId, RAnyDescriptorGuard descGuard)
Ensures that fDescriptor has cluster details loaded for the given cluster group ID.
void Attach(ROOT::Internal::RNTupleSerializer::EDescriptorDeserializeMode mode=ROOT::Internal::RNTupleSerializer::EDescriptorDeserializeMode::kForReading)
Open the physical storage container and deserialize header and footer.
ColumnHandle_t AddColumn(ROOT::DescriptorId_t fieldId, ROOT::Internal::RColumn &column) override
Register a new column.
void UnzipCluster(ROOT::Internal::RCluster *cluster)
Parallel decompression and unpacking of the pages in the given cluster.
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSource.
ROOT::NTupleSize_t GetNEntries()
ROOT::Internal::RPageRef LoadZeroPage(ColumnHandle_t columnHandle, const RPageSummary &pageSummary)
void UpdateLastUsedCluster(ROOT::DescriptorId_t clusterId)
Does nothing if fLastUsedCluster == clusterId.
ROOT::Internal::RPageRef LoadPageFromSummary(ColumnHandle_t columnHandle, const RPageSummary &pageSummary)
void DropColumn(ColumnHandle_t columnHandle) override
Unregisters a column.
RAnyDescriptorGuard FindClusterId(ROOT::DescriptorId_t physicalColumnId, ROOT::NTupleSize_t index, ROOT::DescriptorId_t &cid)
Returns a shared descriptor guard to ensure that the returned cluster id is useable,...
void LoadSealedPage(ROOT::DescriptorId_t physicalColumnId, RNTupleLocalIndex localIndex, RSealedPage &sealedPage)
Read the packed and compressed bytes of a page into the memory buffer provided by sealedPage.
void LoadPageList(DescriptorId_t clusterGroupId, const RExclDescriptorGuard &exclGuard)
RPageSource(std::string_view ntupleName, const ROOT::RNTupleReadOptions &fOptions)
DescriptorId_t FindClusterGroupId(DescriptorId_t clusterId) const
Uses binary search in fCumulativeClusterCounts to determine the cluster group that the cluster ID bel...
void PrepareLoadCluster(const ROOT::Internal::RCluster::RKey &clusterKey, ROOT::Internal::ROnDiskPageMap &pageZeroMap, const std::function< void(ROOT::DescriptorId_t, ROOT::NTupleSize_t, const ROOT::RClusterDescriptor::RPageInfo &)> &perPageFunc)
Prepare a page range read for the column set in clusterKey.
void SetEntryRange(const REntryRange &range)
Promise to only read from the given entry range.
std::unique_ptr< RPageSource > Clone() const
Open the same storage multiple time, e.g.
static std::unique_ptr< RPageSource > Create(std::string_view ntupleName, std::string_view location, const ROOT::RNTupleReadOptions &options=ROOT::RNTupleReadOptions())
Guess the concrete derived page source from the file name (location)
static RResult< ROOT::Internal::RPage > UnsealPage(const RSealedPage &sealedPage, const ROOT::Internal::RColumnElementBase &element, ROOT::Internal::RPageAllocator &pageAlloc)
Helper for unstreaming a page.
Common functionality of an ntuple storage for both reading and writing.
RPageStorage(std::string_view name)
Stores information about the cluster in which this page resides.
A page is a slice of a column that is mapped into memory.
std::uint32_t GetNElements() const
std::size_t GetNBytes() const
The space taken by column elements in the buffer.
void * GrowUnchecked(std::uint32_t nElements)
Increases the number elements in the page.
static const void * GetPageZeroBuffer()
Return a pointer to the page zero buffer used if there is no on-disk data for a particular deferred c...
void SetWindow(const ROOT::NTupleSize_t rangeFirst, const RClusterInfo &clusterInfo)
Seek the page to a certain position of the column.
const ROOT::RFieldBase * GetSourceField(const ROOT::RFieldBase *target) const
bool TryEvict(std::size_t targetAvailableSize, std::size_t pageSizeLimit)
Flush columns in order of allocated write page size until the sum of all write page allocations leave...
bool TryUpdate(ROOT::Internal::RColumn &column, std::size_t newWritePageSize)
Try to register the new write page size for the given column.
The window of element indexes of a particular column in a particular cluster.
void SetCompressionSettings(std::optional< std::uint32_t > comp)
void SetPhysicalColumnId(ROOT::DescriptorId_t id)
void SetFirstElementIndex(ROOT::NTupleSize_t idx)
void SetNElements(ROOT::NTupleSize_t n)
Metadata for RNTuple clusters.
ROOT::NTupleSize_t GetNEntries() const
const RColumnRange & GetColumnRange(ROOT::DescriptorId_t physicalId) const
ROOT::NTupleSize_t GetFirstEntryIndex() const
bool HasClusterDetails() const
A cluster group is loaded in two stages.
const std::vector< ROOT::DescriptorId_t > & GetClusterIds() const
Base class for all ROOT issued exceptions.
A field translates read and write calls from/to underlying columns to/from tree values.
ROOT::DescriptorId_t GetOnDiskId() const
Metadata stored for every field of an RNTuple.
ROOT::DescriptorId_t GetId() const
const std::vector< ROOT::DescriptorId_t > & GetLogicalColumnIds() const
ROOT::ENTupleStructure GetStructure() const
std::uint32_t GetColumnCardinality() const
ROOT::DescriptorId_t GetParentId() const
bool IsProjectedField() const
The on-storage metadata of an RNTuple.
const RClusterGroupDescriptor & GetClusterGroupDescriptor(ROOT::DescriptorId_t clusterGroupId) const
const RColumnDescriptor & GetColumnDescriptor(ROOT::DescriptorId_t columnId) const
RClusterDescriptorIterable GetActiveClusterIterable() const
RClusterGroupDescriptorIterable GetClusterGroupIterable() const
std::size_t GetNActiveClusters() const
std::size_t GetNClusters() const
@ kFeatureFlag_NestedDeferredColumns
Signals that the RNTuple contains at least one deferred column that is part of a collection and was e...
const RClusterDescriptor & GetClusterDescriptor(ROOT::DescriptorId_t clusterId) const
std::size_t GetNClusterGroups() const
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
ROOT::NTupleSize_t GetIndexInCluster() const
ROOT::DescriptorId_t GetClusterId() const
std::uint64_t GetNBytesOnStorage() const
ELocatorType GetType() const
For non-disk locators, the value for the Type field.
The RNTupleModel encapulates the schema of an RNTuple.
Common user-tunable settings for reading RNTuples.
Common user-tunable settings for storing RNTuples.
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...
ROOT::RFieldZero & GetFieldZeroOfModel(RNTupleModel &model)
RResult< void > EnsureValidNameForRNTuple(std::string_view name, std::string_view where)
Check whether a given string is a valid name according to the RNTuple specification.
RProjectedFields & GetProjectedFieldsOfModel(RNTupleModel &model)
std::unique_ptr< RColumnElementBase > GenerateColumnElement(std::type_index inMemoryType, ROOT::ENTupleColumnType onDiskType)
void CallConnectPageSinkOnField(RFieldBase &, ROOT::Internal::RPageSink &, ROOT::NTupleSize_t firstEntry=0)
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
constexpr NTupleSize_t kInvalidNTupleIndex
bool StartsWith(std::string_view string, std::string_view prefix)
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
constexpr DescriptorId_t kInvalidDescriptorId
The identifiers that specifies the content of a (partial) cluster.
ROOT::DescriptorId_t fClusterId
ColumnSet_t fPhysicalColumnSet
Every concrete RColumnElement type is identified by its on-disk type (column type) and the in-memory ...
std::uint32_t fLength
Uncompressed length of the anchor, including the checksum.
The incremental changes to a RNTupleModel
std::vector< ROOT::RFieldBase * > fAddedProjectedFields
Points to the projected fields in fModel that were added as part of an updater transaction.
std::vector< ROOT::RFieldBase * > fAddedFields
Points to the fields in fModel that were added as part of an updater transaction.
On-disk pages within a page source are identified by the column and page number.
Default I/O performance counters that get registered in fMetrics.
std::type_index fInMemoryType
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.
bool fAllowAlias
If false, the output buffer must not point to the input page buffer, which would otherwise be an opti...
const ROOT::Internal::RColumnElementBase * fElement
Corresponds to the page's elements, for size calculation etc.
Cluster that was staged, but not yet logically appended to the RNTuple.
ROOT::NTupleSize_t fNEntries
std::uint64_t fNBytesWritten
std::vector< RColumnInfo > fColumnInfos
Default I/O performance counters that get registered in fMetrics
Used in SetEntryRange / GetEntryRange.
ROOT::NTupleSize_t fNEntries
bool IntersectsWith(const ROOT::RClusterDescriptor &clusterDesc) const
Returns true if the given cluster has entries within the entry range.
ROOT::NTupleSize_t fFirstEntry
Summarizes meta-data necessary to load a certain page. Used by LoadPageFromSummary().
ROOT::RClusterDescriptor::RPageInfoExtended fPageInfo
Location of the page on disk.
std::uint64_t fColumnOffset
The first element number of the page's column in the given cluster.
ROOT::DescriptorId_t fClusterId
ROOT::Internal::RColumn * fColumn
ROOT::DescriptorId_t fPhysicalId
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
RResult< std::uint64_t > GetChecksum() const
Returns a failure if the sealed page has no checksum.
std::size_t GetBufferSize() const
void SetBufferSize(std::size_t bufferSize)
std::size_t GetDataSize() const
ROOT::Internal::RColumn * fColumn
std::size_t fCurrentPageSize
bool operator>(const RColumnInfo &other) const
Information about a single page in the context of a cluster's page range.
void SetLocator(const RNTupleLocator &locator)
std::uint32_t GetNElements() const
const RNTupleLocator & GetLocator() const
void SetHasChecksum(bool hasChecksum)
void SetNElements(std::uint32_t n)
Modifiers passed to CreateModel()