Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RColumn.hxx
Go to the documentation of this file.
1/// \file ROOT/RColumn.hxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2018-10-09
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_RColumn
17#define ROOT7_RColumn
18
19#include <ROOT/RConfig.hxx> // for R__likely
21#include <ROOT/RNTupleUtil.hxx>
22#include <ROOT/RPage.hxx>
23#include <ROOT/RPageStorage.hxx>
24
25#include <TError.h>
26
27#include <cstring> // for memcpy
28#include <memory>
29#include <utility>
30
32
33// clang-format off
34/**
35\class ROOT::Experimental::Internal::RColumn
36\ingroup NTuple
37\brief A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into memory.
38*/
39// clang-format on
40class RColumn {
41private:
43 /// Columns belonging to the same field are distinguished by their order. E.g. for an std::string field, there is
44 /// the offset column with index 0 and the character value column with index 1.
45 std::uint32_t fIndex;
46 /// Fields can have multiple column representations, distinguished by representation index
47 std::uint16_t fRepresentationIndex;
48 RPageSink *fPageSink = nullptr;
52 /// The page into which new elements are being written. The page will initially be small
53 /// (RNTupleWriteOptions::fInitialUnzippedPageSize, which corresponds to fInitialElements) and expand as needed and
54 /// as memory for page buffers is still available (RNTupleWriteOptions::fPageBufferBudget) or the maximum page
55 /// size is reached (RNTupleWriteOptions::fMaxUnzippedPageSize).
57 /// The initial number of elements in a page
59 /// The number of elements written resp. available in the column
61 /// The currently mapped page for reading
63 /// The column id in the column descriptor, once connected to a sink or source
65 /// Global index of the first element in this column; usually == 0, unless it is a deferred column
67 /// Used to pack and unpack pages on writing/reading
68 std::unique_ptr<RColumnElementBase> fElement;
69 /// The column team is a set of columns that serve the same column index for different representation IDs.
70 /// Initially, the team has only one member, the very column it belongs to. Through MergeTeams(), two columns
71 /// can join forces. The team is used to react on suppressed columns: if the current team member has a suppressed
72 /// column for a MapPage() call, it get the page from the active column in the corresponding cluster.
73 std::vector<RColumn *> fTeam;
74 /// Points into fTeam to the column that successfully returned the last page.
75 std::size_t fLastGoodTeamIdx = 0;
76
77 RColumn(EColumnType type, std::uint32_t columnIndex, std::uint16_t representationIndex);
78
79 /// Used when trying to append to a full write page. If possible, expand the page. Otherwise, flush and reset
80 /// to the minimal size.
82 {
83 auto newMaxElements = fWritePage.GetMaxElements() * 2;
84 if (newMaxElements * fElement->GetSize() > fPageSink->GetWriteOptions().GetMaxUnzippedPageSize()) {
85 newMaxElements = fPageSink->GetWriteOptions().GetMaxUnzippedPageSize() / fElement->GetSize();
86 }
87
88 if (newMaxElements == fWritePage.GetMaxElements()) {
89 // Maximum page size reached, flush and reset
90 Flush();
91 } else {
92 auto expandedPage = fPageSink->ReservePage(fHandleSink, newMaxElements);
93 if (expandedPage.IsNull()) {
94 Flush();
95 } else {
96 memcpy(expandedPage.GetBuffer(), fWritePage.GetBuffer(), fWritePage.GetNBytes());
97 expandedPage.Reset(fNElements);
98 expandedPage.GrowUnchecked(fWritePage.GetNElements());
99 fWritePage = std::move(expandedPage);
100 }
101 }
102
104 }
105
106public:
107 template <typename CppT>
108 static std::unique_ptr<RColumn> Create(EColumnType type, std::uint32_t columnIdx, std::uint16_t representationIdx)
109 {
110 auto column = std::unique_ptr<RColumn>(new RColumn(type, columnIdx, representationIdx));
111 column->fElement = RColumnElementBase::Generate<CppT>(type);
112 return column;
113 }
114
115 RColumn(const RColumn &) = delete;
116 RColumn &operator=(const RColumn &) = delete;
117 ~RColumn();
118
119 /// Connect the column to a page sink. `firstElementIndex` can be used to specify the first column element index
120 /// with backing storage for this column. On read back, elements before `firstElementIndex` will cause the zero page
121 /// to be mapped.
122 void ConnectPageSink(DescriptorId_t fieldId, RPageSink &pageSink, NTupleSize_t firstElementIndex = 0U);
123 /// Connect the column to a page source.
124 void ConnectPageSource(DescriptorId_t fieldId, RPageSource &pageSource);
125
126 void Append(const void *from)
127 {
130 }
131
132 void *dst = fWritePage.GrowUnchecked(1);
133
134 std::memcpy(dst, from, fElement->GetSize());
135 fNElements++;
136 }
137
138 void AppendV(const void *from, std::size_t count)
139 {
140 auto src = reinterpret_cast<const unsigned char *>(from);
141 // TODO(jblomer): A future optimization should grow the page in one go, up to the maximum unzipped page size
142 while (count > 0) {
143 std::size_t nElementsRemaining = fWritePage.GetMaxElements() - fWritePage.GetNElements();
144 if (nElementsRemaining == 0) {
146 nElementsRemaining = fWritePage.GetMaxElements() - fWritePage.GetNElements();
147 }
148
149 assert(nElementsRemaining > 0);
150 auto nBatch = std::min(count, nElementsRemaining);
151
152 void *dst = fWritePage.GrowUnchecked(nBatch);
153 std::memcpy(dst, src, nBatch * fElement->GetSize());
154 src += nBatch * fElement->GetSize();
155 count -= nBatch;
156 fNElements += nBatch;
157 }
158 }
159
160 void Read(const NTupleSize_t globalIndex, void *to)
161 {
162 if (!fReadPageRef.Get().Contains(globalIndex)) {
163 MapPage(globalIndex);
164 }
165 const auto elemSize = fElement->GetSize();
166 void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
167 (globalIndex - fReadPageRef.Get().GetGlobalRangeFirst()) * elemSize;
168 std::memcpy(to, from, elemSize);
169 }
170
171 void Read(RClusterIndex clusterIndex, void *to)
172 {
173 if (!fReadPageRef.Get().Contains(clusterIndex)) {
174 MapPage(clusterIndex);
175 }
176 const auto elemSize = fElement->GetSize();
177 void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
178 (clusterIndex.GetIndex() - fReadPageRef.Get().GetClusterRangeFirst()) * elemSize;
179 std::memcpy(to, from, elemSize);
180 }
181
182 void ReadV(const NTupleSize_t globalIndex, const ClusterSize_t::ValueType count, void *to)
183 {
184 if (!fReadPageRef.Get().Contains(globalIndex)) {
185 MapPage(globalIndex);
186 }
187 NTupleSize_t idxInPage = globalIndex - fReadPageRef.Get().GetGlobalRangeFirst();
188
189 const auto elemSize = fElement->GetSize();
190 const void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) + idxInPage * elemSize;
191 if (globalIndex + count <= fReadPageRef.Get().GetGlobalRangeLast() + 1) {
192 std::memcpy(to, from, elemSize * count);
193 } else {
194 ClusterSize_t::ValueType nBatch = fReadPageRef.Get().GetNElements() - idxInPage;
195 std::memcpy(to, from, elemSize * nBatch);
196 auto tail = static_cast<unsigned char *>(to) + nBatch * elemSize;
197 ReadV(globalIndex + nBatch, count - nBatch, tail);
198 }
199 }
200
201 void ReadV(RClusterIndex clusterIndex, const ClusterSize_t::ValueType count, void *to)
202 {
203 if (!fReadPageRef.Get().Contains(clusterIndex)) {
204 MapPage(clusterIndex);
205 }
206 NTupleSize_t idxInPage = clusterIndex.GetIndex() - fReadPageRef.Get().GetClusterRangeFirst();
207
208 const auto elemSize = fElement->GetSize();
209 const void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) + idxInPage * elemSize;
210 if (clusterIndex.GetIndex() + count <= fReadPageRef.Get().GetClusterRangeLast() + 1) {
211 std::memcpy(to, from, elemSize * count);
212 } else {
213 ClusterSize_t::ValueType nBatch = fReadPageRef.Get().GetNElements() - idxInPage;
214 std::memcpy(to, from, elemSize * nBatch);
215 auto tail = static_cast<unsigned char *>(to) + nBatch * elemSize;
216 ReadV(RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() + nBatch), count - nBatch, tail);
217 }
218 }
219
220 template <typename CppT>
221 CppT *Map(const NTupleSize_t globalIndex)
222 {
223 NTupleSize_t nItems;
224 return MapV<CppT>(globalIndex, nItems);
225 }
226
227 template <typename CppT>
228 CppT *Map(RClusterIndex clusterIndex)
229 {
230 NTupleSize_t nItems;
231 return MapV<CppT>(clusterIndex, nItems);
232 }
233
234 template <typename CppT>
235 CppT *MapV(const NTupleSize_t globalIndex, NTupleSize_t &nItems)
236 {
237 if (R__unlikely(!fReadPageRef.Get().Contains(globalIndex))) {
238 MapPage(globalIndex);
239 }
240 // +1 to go from 0-based indexing to 1-based number of items
241 nItems = fReadPageRef.Get().GetGlobalRangeLast() - globalIndex + 1;
242 return reinterpret_cast<CppT *>(static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
243 (globalIndex - fReadPageRef.Get().GetGlobalRangeFirst()) * sizeof(CppT));
244 }
245
246 template <typename CppT>
247 CppT *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
248 {
249 if (!fReadPageRef.Get().Contains(clusterIndex)) {
250 MapPage(clusterIndex);
251 }
252 // +1 to go from 0-based indexing to 1-based number of items
253 nItems = fReadPageRef.Get().GetClusterRangeLast() - clusterIndex.GetIndex() + 1;
254 return reinterpret_cast<CppT *>(static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
255 (clusterIndex.GetIndex() - fReadPageRef.Get().GetClusterRangeFirst()) *
256 sizeof(CppT));
257 }
258
260 {
261 if (!fReadPageRef.Get().Contains(clusterIndex)) {
262 MapPage(clusterIndex);
263 }
264 return fReadPageRef.Get().GetClusterInfo().GetIndexOffset() + clusterIndex.GetIndex();
265 }
266
268 {
269 if (!fReadPageRef.Get().Contains(globalIndex)) {
270 MapPage(globalIndex);
271 }
273 globalIndex - fReadPageRef.Get().GetClusterInfo().GetIndexOffset());
274 }
275
276 /// For offset columns only, look at the two adjacent values that define a collection's coordinates
277 void GetCollectionInfo(const NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
278 {
279 NTupleSize_t idxStart = 0;
280 NTupleSize_t idxEnd;
281 // Try to avoid jumping back to the previous page and jumping back to the previous cluster
282 if (R__likely(globalIndex > 0)) {
283 if (R__likely(fReadPageRef.Get().Contains(globalIndex - 1))) {
284 idxStart = *Map<ClusterSize_t>(globalIndex - 1);
285 idxEnd = *Map<ClusterSize_t>(globalIndex);
286 if (R__unlikely(fReadPageRef.Get().GetClusterInfo().GetIndexOffset() == globalIndex))
287 idxStart = 0;
288 } else {
289 idxEnd = *Map<ClusterSize_t>(globalIndex);
290 auto selfOffset = fReadPageRef.Get().GetClusterInfo().GetIndexOffset();
291 idxStart = (globalIndex == selfOffset) ? 0 : *Map<ClusterSize_t>(globalIndex - 1);
292 }
293 } else {
294 idxEnd = *Map<ClusterSize_t>(globalIndex);
295 }
296 *collectionSize = idxEnd - idxStart;
297 *collectionStart = RClusterIndex(fReadPageRef.Get().GetClusterInfo().GetId(), idxStart);
298 }
299
300 void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
301 {
302 auto index = clusterIndex.GetIndex();
303 auto idxStart = (index == 0) ? 0 : *Map<ClusterSize_t>(clusterIndex - 1);
304 auto idxEnd = *Map<ClusterSize_t>(clusterIndex);
305 *collectionSize = idxEnd - idxStart;
306 *collectionStart = RClusterIndex(clusterIndex.GetClusterId(), idxStart);
307 }
308
309 /// Get the currently active cluster id
310 void GetSwitchInfo(NTupleSize_t globalIndex, RClusterIndex *varIndex, std::uint32_t *tag)
311 {
312 auto varSwitch = Map<RColumnSwitch>(globalIndex);
313 *varIndex = RClusterIndex(fReadPageRef.Get().GetClusterInfo().GetId(), varSwitch->GetIndex());
314 *tag = varSwitch->GetTag();
315 }
316
317 void Flush();
318 void CommitSuppressed();
319
320 void MapPage(NTupleSize_t globalIndex) { R__ASSERT(TryMapPage(globalIndex)); }
321 void MapPage(RClusterIndex clusterIndex) { R__ASSERT(TryMapPage(clusterIndex)); }
322 bool TryMapPage(NTupleSize_t globalIndex);
323 bool TryMapPage(RClusterIndex clusterIndex);
324
325 bool ReadPageContains(NTupleSize_t globalIndex) const { return fReadPageRef.Get().Contains(globalIndex); }
326 bool ReadPageContains(RClusterIndex clusterIndex) const { return fReadPageRef.Get().Contains(clusterIndex); }
327
328 void MergeTeams(RColumn &other);
329
331 RColumnElementBase *GetElement() const { return fElement.get(); }
332 EColumnType GetType() const { return fType; }
333 std::uint16_t GetBitsOnStorage() const
334 {
335 assert(fElement);
336 return fElement->GetBitsOnStorage();
337 }
338 std::optional<std::pair<double, double>> GetValueRange() const
339 {
340 assert(fElement);
341 return fElement->GetValueRange();
342 }
343 std::uint32_t GetIndex() const { return fIndex; }
344 std::uint16_t GetRepresentationIndex() const { return fRepresentationIndex; }
348 RPageSink *GetPageSink() const { return fPageSink; }
351
352 void SetBitsOnStorage(std::size_t bits) { fElement->SetBitsOnStorage(bits); }
353 std::size_t GetWritePageCapacity() const { return fWritePage.GetCapacity(); }
354 void SetValueRange(double min, double max) { fElement->SetValueRange(min, max); }
355}; // class RColumn
356
357} // namespace ROOT::Experimental::Internal
358
359#endif
#define R__likely(expr)
Definition RConfig.hxx:595
#define R__unlikely(expr)
Definition RConfig.hxx:594
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t index
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
A column element encapsulates the translation between basic C++ types and their column representation...
A column is a storage-backed array of a simple, fixed-size type, from which pages can be mapped into ...
Definition RColumn.hxx:40
RColumn(const RColumn &)=delete
std::size_t fLastGoodTeamIdx
Points into fTeam to the column that successfully returned the last page.
Definition RColumn.hxx:75
RPageStorage::ColumnHandle_t GetHandleSink() const
Definition RColumn.hxx:350
void Read(RClusterIndex clusterIndex, void *to)
Definition RColumn.hxx:171
void ConnectPageSink(DescriptorId_t fieldId, RPageSink &pageSink, NTupleSize_t firstElementIndex=0U)
Connect the column to a page sink.
Definition RColumn.cxx:40
std::unique_ptr< RColumnElementBase > fElement
Used to pack and unpack pages on writing/reading.
Definition RColumn.hxx:68
void HandleWritePageIfFull()
Used when trying to append to a full write page.
Definition RColumn.hxx:81
NTupleSize_t GetGlobalIndex(RClusterIndex clusterIndex)
Definition RColumn.hxx:259
RColumnElementBase * GetElement() const
Definition RColumn.hxx:331
RPageRef fReadPageRef
The currently mapped page for reading.
Definition RColumn.hxx:62
bool TryMapPage(NTupleSize_t globalIndex)
Definition RColumn.cxx:85
void SetBitsOnStorage(std::size_t bits)
Definition RColumn.hxx:352
void MapPage(RClusterIndex clusterIndex)
Definition RColumn.hxx:321
DescriptorId_t GetOnDiskId() const
Definition RColumn.hxx:345
void MapPage(NTupleSize_t globalIndex)
Definition RColumn.hxx:320
void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
Definition RColumn.hxx:300
void ReadV(RClusterIndex clusterIndex, const ClusterSize_t::ValueType count, void *to)
Definition RColumn.hxx:201
CppT * Map(const NTupleSize_t globalIndex)
Definition RColumn.hxx:221
RPageSource * GetPageSource() const
Definition RColumn.hxx:347
static std::unique_ptr< RColumn > Create(EColumnType type, std::uint32_t columnIdx, std::uint16_t representationIdx)
Definition RColumn.hxx:108
void AppendV(const void *from, std::size_t count)
Definition RColumn.hxx:138
std::optional< std::pair< double, double > > GetValueRange() const
Definition RColumn.hxx:338
void Append(const void *from)
Definition RColumn.hxx:126
CppT * Map(RClusterIndex clusterIndex)
Definition RColumn.hxx:228
CppT * MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
Definition RColumn.hxx:247
RColumn & operator=(const RColumn &)=delete
bool ReadPageContains(RClusterIndex clusterIndex) const
Definition RColumn.hxx:326
RPageStorage::ColumnHandle_t fHandleSource
Definition RColumn.hxx:51
NTupleSize_t fNElements
The number of elements written resp. available in the column.
Definition RColumn.hxx:60
std::uint16_t GetRepresentationIndex() const
Definition RColumn.hxx:344
void ReadV(const NTupleSize_t globalIndex, const ClusterSize_t::ValueType count, void *to)
Definition RColumn.hxx:182
std::vector< RColumn * > fTeam
The column team is a set of columns that serve the same column index for different representation IDs...
Definition RColumn.hxx:73
void Read(const NTupleSize_t globalIndex, void *to)
Definition RColumn.hxx:160
std::uint32_t fIndex
Columns belonging to the same field are distinguished by their order.
Definition RColumn.hxx:45
std::size_t GetWritePageCapacity() const
Definition RColumn.hxx:353
void ConnectPageSource(DescriptorId_t fieldId, RPageSource &pageSource)
Connect the column to a page source.
Definition RColumn.cxx:57
void GetCollectionInfo(const NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
For offset columns only, look at the two adjacent values that define a collection's coordinates.
Definition RColumn.hxx:277
CppT * MapV(const NTupleSize_t globalIndex, NTupleSize_t &nItems)
Definition RColumn.hxx:235
std::uint16_t fRepresentationIndex
Fields can have multiple column representations, distinguished by representation index.
Definition RColumn.hxx:47
void GetSwitchInfo(NTupleSize_t globalIndex, RClusterIndex *varIndex, std::uint32_t *tag)
Get the currently active cluster id.
Definition RColumn.hxx:310
RPageStorage::ColumnHandle_t fHandleSink
Definition RColumn.hxx:50
void SetValueRange(double min, double max)
Definition RColumn.hxx:354
NTupleSize_t fInitialNElements
The initial number of elements in a page.
Definition RColumn.hxx:58
DescriptorId_t fOnDiskId
The column id in the column descriptor, once connected to a sink or source.
Definition RColumn.hxx:64
NTupleSize_t GetNElements() const
Definition RColumn.hxx:330
RPage fWritePage
The page into which new elements are being written.
Definition RColumn.hxx:56
bool ReadPageContains(NTupleSize_t globalIndex) const
Definition RColumn.hxx:325
NTupleSize_t GetFirstElementIndex() const
Definition RColumn.hxx:346
RPageStorage::ColumnHandle_t GetHandleSource() const
Definition RColumn.hxx:349
RClusterIndex GetClusterIndex(NTupleSize_t globalIndex)
Definition RColumn.hxx:267
NTupleSize_t fFirstElementIndex
Global index of the first element in this column; usually == 0, unless it is a deferred column.
Definition RColumn.hxx:66
std::uint16_t GetBitsOnStorage() const
Definition RColumn.hxx:333
Reference to a page stored in the page pool.
Abstract interface to write data into an ntuple.
virtual 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...
const RNTupleWriteOptions & GetWriteOptions() const
Returns the sink's write options.
Abstract interface to read data from an ntuple.
A page is a slice of a column that is mapped into memory.
Definition RPage.hxx:47
void * GrowUnchecked(ClusterSize_t::ValueType nElements)
Increases the number elements in the page.
Definition RPage.hxx:154
std::size_t GetCapacity() const
Definition RPage.hxx:119
ClusterSize_t::ValueType GetClusterRangeFirst() const
Definition RPage.hxx:128
std::size_t GetNBytes() const
The space taken by column elements in the buffer.
Definition RPage.hxx:115
NTupleSize_t GetGlobalRangeFirst() const
Definition RPage.hxx:126
const RClusterInfo & GetClusterInfo() const
Definition RPage.hxx:132
std::uint32_t GetNElements() const
Definition RPage.hxx:124
bool Contains(NTupleSize_t globalIndex) const
Definition RPage.hxx:134
ClusterSize_t::ValueType GetClusterRangeLast() const
Definition RPage.hxx:129
NTupleSize_t GetGlobalRangeLast() const
Definition RPage.hxx:127
std::uint32_t GetMaxElements() const
Definition RPage.hxx:125
Addresses a column element or field item relative to a particular cluster, instead of a global NTuple...
DescriptorId_t GetClusterId() const
ClusterSize_t::ValueType GetIndex() const
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.
constexpr DescriptorId_t kInvalidDescriptorId
Wrap the integer in a struct in order to avoid template specialization clash with std::uint64_t.