Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RPageStorage.hxx
Go to the documentation of this file.
1/// \file ROOT/RPageStorage.hxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-07-19
5/// \warning This is part of the ROOT 7 prototype! It will change without notice. It might trigger earthquakes. Feedback
6/// is welcome!
7
8/*************************************************************************
9 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
10 * All rights reserved. *
11 * *
12 * For the licensing terms see $ROOTSYS/LICENSE. *
13 * For the list of contributors see $ROOTSYS/README/CREDITS. *
14 *************************************************************************/
15
16#ifndef ROOT7_RPageStorage
17#define ROOT7_RPageStorage
18
19#include <ROOT/RCluster.hxx>
23#include <ROOT/RNTupleUtil.hxx>
24#include <ROOT/RPage.hxx>
26#include <ROOT/RSpan.hxx>
27#include <ROOT/RStringView.hxx>
28
29#include <atomic>
30#include <cstddef>
31#include <functional>
32#include <memory>
33#include <unordered_set>
34#include <vector>
35
36namespace ROOT {
37namespace Experimental {
38
39class RNTupleModel;
40// TODO(jblomer): factory methods to create tree sinks and sources outside Detail namespace
41
42namespace Detail {
43
44class RColumn;
45class RColumnElementBase;
46class RNTupleCompressor;
47class RNTupleDecompressor;
48class RPagePool;
49class RFieldBase;
50
51enum class EPageStorageType {
52 kSink,
53 kSource,
54};
55
56// clang-format off
57/**
58\class ROOT::Experimental::Detail::RPageStorage
59\ingroup NTuple
60\brief Common functionality of an ntuple storage for both reading and writing
61
62The RPageStore provides access to a storage container that keeps the bits of pages and clusters comprising
63an ntuple. Concrete implementations can use a TFile, a raw file, an object store, and so on.
64*/
65// clang-format on
67public:
68 /// The interface of a task scheduler to schedule page (de)compression tasks
70 public:
71 virtual ~RTaskScheduler() = default;
72 /// Start a new set of tasks
73 virtual void Reset() = 0;
74 /// Take a callable that represents a task
75 virtual void AddTask(const std::function<void(void)> &taskFunc) = 0;
76 /// Blocks until all scheduled tasks finished
77 virtual void Wait() = 0;
78 };
79
80 /// A sealed page contains the bytes of a page as written to storage (packed & compressed). It is used
81 /// as an input to UnsealPages() as well as to transfer pages between different storage media.
82 /// RSealedPage does _not_ own the buffer it is pointing to in order to not interfere with the memory management
83 /// of concrete page sink and page source implementations.
84 struct RSealedPage {
85 const void *fBuffer = nullptr;
86 std::uint32_t fSize = 0;
87 std::uint32_t fNElements = 0;
88
89 RSealedPage() = default;
90 RSealedPage(const void *b, std::uint32_t s, std::uint32_t n) : fBuffer(b), fSize(s), fNElements(n) {}
91 RSealedPage(const RSealedPage &other) = delete;
92 RSealedPage& operator =(const RSealedPage &other) = delete;
93 RSealedPage(RSealedPage &&other) = default;
94 RSealedPage& operator =(RSealedPage &&other) = default;
95 };
96
97protected:
98 std::string fNTupleName;
100
101public:
102 explicit RPageStorage(std::string_view name);
103 RPageStorage(const RPageStorage &other) = delete;
104 RPageStorage& operator =(const RPageStorage &other) = delete;
105 RPageStorage(RPageStorage &&other) = default;
107 virtual ~RPageStorage();
108
109 /// Whether the concrete implementation is a sink or a source
111
114 const RColumn *fColumn = nullptr;
115
116 /// Returns true for a valid column handle; fColumn and fId should always either both
117 /// be valid or both be invalid.
118 explicit operator bool() const { return fId != kInvalidDescriptorId && fColumn; }
119 };
120 /// The column handle identifies a column with the current open page storage
122
123 /// Register a new column. When reading, the column must exist in the ntuple on disk corresponding to the meta-data.
124 /// When writing, every column can only be attached once.
125 virtual ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) = 0;
126 /// Unregisters a column. A page source decreases the reference counter for the corresponding active column.
127 /// For a page sink, dropping columns is currently a no-op.
128 virtual void DropColumn(ColumnHandle_t columnHandle) = 0;
129
130 /// Every page store needs to be able to free pages it handed out. But Sinks and sources have different means
131 /// of allocating pages.
132 virtual void ReleasePage(RPage &page) = 0;
133
134 /// Page storage implementations have their own metrics. The RPageSink and RPageSource classes provide
135 /// a default set of metrics.
137 /// Returns the NTuple name.
138 const std::string &GetNTupleName() const { return fNTupleName; }
139
140 void SetTaskScheduler(RTaskScheduler *taskScheduler) { fTaskScheduler = taskScheduler; }
141};
142
143// clang-format off
144/**
145\class ROOT::Experimental::Detail::RPageSink
146\ingroup NTuple
147\brief Abstract interface to write data into an ntuple
148
149The page sink takes the list of columns and afterwards a series of page commits and cluster commits.
150The user is responsible to commit clusters at a consistent point, i.e. when all pages corresponding to data
151up to the given entry number are committed.
152*/
153// clang-format on
154class RPageSink : public RPageStorage {
155protected:
156 /// Default I/O performance counters that get registered in fMetrics
157 struct RCounters {
165 };
166 std::unique_ptr<RCounters> fCounters;
168
169 std::unique_ptr<RNTupleWriteOptions> fOptions;
170
171 /// Helper to zip pages and header/footer; includes a 16MB (kMAXZIPBUF) zip buffer.
172 /// There could be concrete page sinks that don't need a compressor. Therefore, and in order to stay consistent
173 /// with the page source, we leave it up to the derived class whether or not the compressor gets constructed.
174 std::unique_ptr<RNTupleCompressor> fCompressor;
175
176 /// Building the ntuple descriptor while writing is done in the same way for all the storage sink implementations.
177 /// Field, column, cluster ids and page indexes per cluster are issued sequentially starting with 0
182 /// Keeps track of the number of elements in the currently open cluster. Indexed by column id.
183 std::vector<RClusterDescriptor::RColumnRange> fOpenColumnRanges;
184 /// Keeps track of the written pages in the currently open cluster. Indexed by column id.
185 std::vector<RClusterDescriptor::RPageRange> fOpenPageRanges;
187
188 virtual void CreateImpl(const RNTupleModel &model) = 0;
189 virtual RNTupleLocator CommitPageImpl(ColumnHandle_t columnHandle, const RPage &page) = 0;
191 const RPageStorage::RSealedPage &sealedPage) = 0;
192 /// Returns the number of bytes written to storage (excluding metadata)
193 virtual std::uint64_t CommitClusterImpl(NTupleSize_t nEntries) = 0;
194 virtual void CommitDatasetImpl() = 0;
195
196 /// Helper for streaming a page. This is commonly used in derived, concrete page sinks. Note that if
197 /// compressionSetting is 0 (uncompressed) and the page is mappable, the returned sealed page will
198 /// point directly to the input page buffer. Otherwise, the sealed page references an internal buffer
199 /// of fCompressor. Thus, the buffer pointed to by the RSealedPage should never be freed.
200 /// Usage of this method requires construction of fCompressor.
201 RSealedPage SealPage(const RPage &page, const RColumnElementBase &element, int compressionSetting);
202
203 /// Seal a page using the provided buffer.
204 static RSealedPage SealPage(const RPage &page, const RColumnElementBase &element,
205 int compressionSetting, void *buf);
206
207 /// Enables the default set of metrics provided by RPageSink. `prefix` will be used as the prefix for
208 /// the counters registered in the internal RNTupleMetrics object.
209 /// This set of counters can be extended by a subclass by calling `fMetrics.MakeCounter<...>()`.
210 ///
211 /// A subclass using the default set of metrics is always responsible for updating the counters
212 /// appropriately, e.g. `fCounters->fNPageCommited.Inc()`
213 ///
214 /// Alternatively, a subclass might provide its own RNTupleMetrics object by overriding the
215 /// GetMetrics() member function.
216 void EnableDefaultMetrics(const std::string &prefix);
217
218public:
219 RPageSink(std::string_view ntupleName, const RNTupleWriteOptions &options);
220
221 RPageSink(const RPageSink&) = delete;
222 RPageSink& operator=(const RPageSink&) = delete;
223 RPageSink(RPageSink&&) = default;
225 virtual ~RPageSink();
226
227 /// Guess the concrete derived page source from the file name (location)
228 static std::unique_ptr<RPageSink> Create(std::string_view ntupleName, std::string_view location,
229 const RNTupleWriteOptions &options = RNTupleWriteOptions());
231 /// Returns the sink's write options.
232 const RNTupleWriteOptions &GetWriteOptions() const { return *fOptions; }
233
234 ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) final;
235 void DropColumn(ColumnHandle_t /*columnHandle*/) final {}
236
237 /// Physically creates the storage container to hold the ntuple (e.g., a keys a TFile or an S3 bucket)
238 /// To do so, Create() calls CreateImpl() after updating the descriptor.
239 /// Create() associates column handles to the columns referenced by the model
240 void Create(RNTupleModel &model);
241 /// Write a page to the storage. The column must have been added before.
242 void CommitPage(ColumnHandle_t columnHandle, const RPage &page);
243 /// Write a preprocessed page to storage. The column must have been added before.
244 /// TODO(jblomer): allow for vector commit of sealed pages
245 void CommitSealedPage(DescriptorId_t columnId, const RPageStorage::RSealedPage &sealedPage);
246 /// Finalize the current cluster and create a new one for the following data.
247 /// Returns the number of bytes written to storage (excluding meta-data).
248 std::uint64_t CommitCluster(NTupleSize_t nEntries);
249 /// Finalize the current cluster and the entrire data set.
251
252 /// Get a new, empty page for the given column that can be filled with up to nElements. If nElements is zero,
253 /// the page sink picks an appropriate size.
254 virtual RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements) = 0;
255
256 /// Returns the default metrics object. Subclasses might alternatively provide their own metrics object by overriding this.
257 virtual RNTupleMetrics &GetMetrics() override { return fMetrics; };
258};
259
260// clang-format off
261/**
262\class ROOT::Experimental::Detail::RPageSource
263\ingroup NTuple
264\brief Abstract interface to read data from an ntuple
265
266The page source is initialized with the columns of interest. Pages from those columns can then be
267mapped into memory. The page source also gives access to the ntuple's meta-data.
268*/
269// clang-format on
270class RPageSource : public RPageStorage {
271protected:
272 /// Default I/O performance counters that get registered in fMetrics
273 struct RCounters {
291 };
292 std::unique_ptr<RCounters> fCounters;
293 /// Wraps the I/O counters and is observed by the RNTupleReader metrics
295
298 /// The active columns are implicitly defined by the model fields or views
300
301 /// Helper to unzip pages and header/footer; comprises a 16MB (kMAXZIPBUF) unzip buffer.
302 /// Not all page sources need a decompressor (e.g. virtual ones for chains and friends don't), thus we
303 /// leave it up to the derived class whether or not the decompressor gets constructed.
304 std::unique_ptr<RNTupleDecompressor> fDecompressor;
305
307 // Only called if a task scheduler is set. No-op be default.
308 virtual void UnzipClusterImpl(RCluster * /* cluster */)
309 { }
310
311 /// Helper for unstreaming a page. This is commonly used in derived, concrete page sources. The implementation
312 /// currently always makes a memory copy, even if the sealed page is uncompressed and in the final memory layout.
313 /// The optimization of directly mapping pages is left to the concrete page source implementations.
314 /// Usage of this method requires construction of fDecompressor.
315 std::unique_ptr<unsigned char []> UnsealPage(const RSealedPage &sealedPage, const RColumnElementBase &element);
316
317 /// Enables the default set of metrics provided by RPageSource. `prefix` will be used as the prefix for
318 /// the counters registered in the internal RNTupleMetrics object.
319 /// A subclass using the default set of metrics is responsible for updating the counters
320 /// appropriately, e.g. `fCounters->fNRead.Inc()`
321 /// Alternatively, a subclass might provide its own RNTupleMetrics object by overriding the
322 /// GetMetrics() member function.
323 void EnableDefaultMetrics(const std::string &prefix);
324
325public:
326 RPageSource(std::string_view ntupleName, const RNTupleReadOptions &fOptions);
327 RPageSource(const RPageSource&) = delete;
331 virtual ~RPageSource();
332 /// Guess the concrete derived page source from the file name (location)
333 static std::unique_ptr<RPageSource> Create(std::string_view ntupleName, std::string_view location,
334 const RNTupleReadOptions &options = RNTupleReadOptions());
335 /// Open the same storage multiple time, e.g. for reading in multiple threads
336 virtual std::unique_ptr<RPageSource> Clone() const = 0;
337
339 const RNTupleDescriptor &GetDescriptor() const { return fDescriptor; }
340 const RNTupleReadOptions &GetReadOptions() const { return fOptions; }
341 ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) override;
342 void DropColumn(ColumnHandle_t columnHandle) override;
343
344 /// Open the physical storage container for the tree
349
350 /// Allocates and fills a page that contains the index-th element
351 virtual RPage PopulatePage(ColumnHandle_t columnHandle, NTupleSize_t globalIndex) = 0;
352 /// Another version of PopulatePage that allows to specify cluster-relative indexes
353 virtual RPage PopulatePage(ColumnHandle_t columnHandle, const RClusterIndex &clusterIndex) = 0;
354
355 /// Read the packed and compressed bytes of a page into the memory buffer provided by selaedPage. The sealed page
356 /// can be used subsequently in a call to RPageSink::CommitSealedPage.
357 /// The fSize and fNElements member of the sealedPage parameters are always set. If sealedPage.fBuffer is nullptr,
358 /// no data will be copied but the returned size information can be used by the caller to allocate a large enough
359 /// buffer and call LoadSealedPage again.
360 virtual void LoadSealedPage(DescriptorId_t columnId, const RClusterIndex &clusterIndex, RSealedPage &sealedPage) = 0;
361
362 /// Populates all the pages of the given cluster ids and columns; it is possible that some columns do not
363 /// contain any pages. The page source may load more columns than the minimal necessary set from `columns`.
364 /// To indicate which columns have been loaded, LoadClusters() must mark them with SetColumnAvailable().
365 /// That includes the ones from the `columns` that don't have pages; otherwise subsequent requests
366 /// for the cluster would assume an incomplete cluster and trigger loading again.
367 /// LoadClusters() is typically called from the I/O thread of a cluster pool, i.e. the method runs
368 /// concurrently to other methods of the page source.
369 virtual std::vector<std::unique_ptr<RCluster>> LoadClusters(std::span<RCluster::RKey> clusterKeys) = 0;
370
371 /// Parallel decompression and unpacking of the pages in the given cluster. The unzipped pages are supposed
372 /// to be preloaded in a page pool attached to the source. The method is triggered by the cluster pool's
373 /// unzip thread. It is an optional optimization, the method can safely do nothing. In particular, the
374 /// actual implementation will only run if a task scheduler is set. In practice, a task scheduler is set
375 /// if implicit multi-threading is turned on.
376 void UnzipCluster(RCluster *cluster);
377
378 /// Returns the default metrics object. Subclasses might alternatively override the method and provide their own metrics object.
379 virtual RNTupleMetrics &GetMetrics() override { return fMetrics; };
380};
381
382} // namespace Detail
383
384} // namespace Experimental
385} // namespace ROOT
386
387#endif
#define b(i)
Definition RSha256.hxx:100
char name[80]
Definition TGX11.cxx:110
An in-memory subset of the packed and compressed pages of a cluster.
Definition RCluster.hxx:154
std::unordered_set< DescriptorId_t > ColumnSet_t
Definition RCluster.hxx:156
A thread-safe integral performance counter.
A metric element that computes its floating point value from other counters.
A collection of Counter objects with a name, a unit, and a description.
An either thread-safe or non thread safe counter for CPU ticks.
Abstract interface to write data into an ntuple.
void CommitDataset()
Finalize the current cluster and the entrire data set.
RPageSink(const RPageSink &)=delete
RSealedPage SealPage(const RPage &page, const RColumnElementBase &element, int compressionSetting)
Helper for streaming a page.
std::vector< RClusterDescriptor::RPageRange > fOpenPageRanges
Keeps track of the written pages in the currently open cluster. Indexed by column id.
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSink.
virtual RNTupleMetrics & GetMetrics() override
Returns the default metrics object. Subclasses might alternatively provide their own metrics object b...
RPageSink & operator=(RPageSink &&)=default
virtual RNTupleLocator CommitPageImpl(ColumnHandle_t columnHandle, const RPage &page)=0
virtual std::uint64_t CommitClusterImpl(NTupleSize_t nEntries)=0
Returns the number of bytes written to storage (excluding metadata)
void CommitPage(ColumnHandle_t columnHandle, const RPage &page)
Write a page to the storage. The column must have been added before.
std::unique_ptr< RCounters > fCounters
RNTupleDescriptorBuilder fDescriptorBuilder
RPageSink & operator=(const RPageSink &)=delete
void CommitSealedPage(DescriptorId_t columnId, const RPageStorage::RSealedPage &sealedPage)
Write a preprocessed page to storage.
void DropColumn(ColumnHandle_t) final
Unregisters a column.
std::unique_ptr< RNTupleWriteOptions > fOptions
static std::unique_ptr< RPageSink > Create(std::string_view ntupleName, std::string_view location, const RNTupleWriteOptions &options=RNTupleWriteOptions())
Guess the concrete derived page source from the file name (location)
std::uint64_t CommitCluster(NTupleSize_t nEntries)
Finalize the current cluster and create a new one for the following data.
virtual RNTupleLocator CommitSealedPageImpl(DescriptorId_t columnId, const RPageStorage::RSealedPage &sealedPage)=0
const RNTupleWriteOptions & GetWriteOptions() const
Returns the sink's write options.
DescriptorId_t fLastFieldId
Building the ntuple descriptor while writing is done in the same way for all the storage sink impleme...
virtual void CreateImpl(const RNTupleModel &model)=0
virtual RPage ReservePage(ColumnHandle_t columnHandle, std::size_t nElements)=0
Get a new, empty page for the given column that can be filled with up to nElements.
EPageStorageType GetType() final
Whether the concrete implementation is a sink or a source.
std::unique_ptr< RNTupleCompressor > fCompressor
Helper to zip pages and header/footer; includes a 16MB (kMAXZIPBUF) zip buffer.
std::vector< RClusterDescriptor::RColumnRange > fOpenColumnRanges
Keeps track of the number of elements in the currently open cluster. Indexed by column id.
ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) final
Register a new column.
Abstract interface to read data from an ntuple.
virtual std::vector< std::unique_ptr< RCluster > > LoadClusters(std::span< RCluster::RKey > clusterKeys)=0
Populates all the pages of the given cluster ids and columns; it is possible that some columns do not...
virtual std::unique_ptr< RPageSource > Clone() const =0
Open the same storage multiple time, e.g. for reading in multiple threads.
const RNTupleReadOptions & GetReadOptions() const
void EnableDefaultMetrics(const std::string &prefix)
Enables the default set of metrics provided by RPageSource.
void Attach()
Open the physical storage container for the tree.
virtual RPage PopulatePage(ColumnHandle_t columnHandle, NTupleSize_t globalIndex)=0
Allocates and fills a page that contains the index-th element.
std::unique_ptr< unsigned char[]> UnsealPage(const RSealedPage &sealedPage, const RColumnElementBase &element)
Helper for unstreaming a page.
virtual void LoadSealedPage(DescriptorId_t columnId, const RClusterIndex &clusterIndex, RSealedPage &sealedPage)=0
Read the packed and compressed bytes of a page into the memory buffer provided by selaedPage.
std::unique_ptr< RCounters > fCounters
virtual RPage PopulatePage(ColumnHandle_t columnHandle, const RClusterIndex &clusterIndex)=0
Another version of PopulatePage that allows to specify cluster-relative indexes.
RPageSource & operator=(RPageSource &&)=default
void DropColumn(ColumnHandle_t columnHandle) override
Unregisters a column.
NTupleSize_t GetNElements(ColumnHandle_t columnHandle)
RPageSource(const RPageSource &)=delete
ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column) override
Register a new column.
static std::unique_ptr< RPageSource > Create(std::string_view ntupleName, std::string_view location, const RNTupleReadOptions &options=RNTupleReadOptions())
Guess the concrete derived page source from the file name (location)
virtual RNTupleDescriptor AttachImpl()=0
std::unique_ptr< RNTupleDecompressor > fDecompressor
Helper to unzip pages and header/footer; comprises a 16MB (kMAXZIPBUF) unzip buffer.
RPageSource(RPageSource &&)=default
virtual RNTupleMetrics & GetMetrics() override
Returns the default metrics object. Subclasses might alternatively override the method and provide th...
virtual void UnzipClusterImpl(RCluster *)
EPageStorageType GetType() final
Whether the concrete implementation is a sink or a source.
const RNTupleDescriptor & GetDescriptor() const
RNTupleMetrics fMetrics
Wraps the I/O counters and is observed by the RNTupleReader metrics.
RPageSource & operator=(const RPageSource &)=delete
void UnzipCluster(RCluster *cluster)
Parallel decompression and unpacking of the pages in the given cluster.
ColumnId_t GetColumnId(ColumnHandle_t columnHandle)
RCluster::ColumnSet_t fActiveColumns
The active columns are implicitly defined by the model fields or views.
The interface of a task scheduler to schedule page (de)compression tasks.
virtual void Reset()=0
Start a new set of tasks.
virtual void Wait()=0
Blocks until all scheduled tasks finished.
virtual void AddTask(const std::function< void(void)> &taskFunc)=0
Take a callable that represents a task.
Common functionality of an ntuple storage for both reading and writing.
const std::string & GetNTupleName() const
Returns the NTuple name.
RPageStorage(const RPageStorage &other)=delete
RPageStorage(RPageStorage &&other)=default
RColumnHandle ColumnHandle_t
The column handle identifies a column with the current open page storage.
virtual EPageStorageType GetType()=0
Whether the concrete implementation is a sink or a source.
void SetTaskScheduler(RTaskScheduler *taskScheduler)
virtual ColumnHandle_t AddColumn(DescriptorId_t fieldId, const RColumn &column)=0
Register a new column.
virtual RNTupleMetrics & GetMetrics()=0
Page storage implementations have their own metrics.
virtual void DropColumn(ColumnHandle_t columnHandle)=0
Unregisters a column.
virtual void ReleasePage(RPage &page)=0
Every page store needs to be able to free pages it handed out.
RPageStorage & operator=(const RPageStorage &other)=delete
A page is a slice of a column that is mapped into memory.
Definition RPage.hxx:41
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into ...
A field translates read and write calls from/to underlying columns to/from tree values.
A helper class for piece-wise construction of an RNTupleDescriptor.
The on-storage meta-data of an ntuple.
The RNTupleModel encapulates the schema of an ntuple.
Common user-tunable settings for reading ntuples.
Common user-tunable settings for storing ntuples.
const Int_t n
Definition legend1.C:16
std::uint64_t NTupleSize_t
Integer type long enough to hold the maximum number of entries in a column.
std::uint64_t DescriptorId_t
Distriniguishes elements of the same type within a descriptor, e.g. different fields.
std::int64_t ColumnId_t
Uniquely identifies a physical column within the scope of the current process, used to tag pages.
constexpr DescriptorId_t kInvalidDescriptorId
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Default I/O performance counters that get registered in fMetrics.
RNTupleTickCounter< RNTupleAtomicCounter > & fTimeCpuZip
RNTupleTickCounter< RNTupleAtomicCounter > & fTimeCpuWrite
Default I/O performance counters that get registered in fMetrics.
RNTupleTickCounter< RNTupleAtomicCounter > & fTimeCpuUnzip
RNTupleTickCounter< RNTupleAtomicCounter > & fTimeCpuRead
A sealed page contains the bytes of a page as written to storage (packed & compressed).
RSealedPage & operator=(const RSealedPage &other)=delete
RSealedPage(const void *b, std::uint32_t s, std::uint32_t n)
RSealedPage(const RSealedPage &other)=delete
Generic information about the physical location of data.