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 /// (just enough to hold RNTupleWriteOptions::fInitialNElementsPerPage elements) 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 number of elements written resp. available in the column
59 /// The currently mapped page for reading
61 /// The column id in the column descriptor, once connected to a sink or source
63 /// Global index of the first element in this column; usually == 0, unless it is a deferred column
65 /// Used to pack and unpack pages on writing/reading
66 std::unique_ptr<RColumnElementBase> fElement;
67 /// The column team is a set of columns that serve the same column index for different representation IDs.
68 /// Initially, the team has only one member, the very column it belongs to. Through MergeTeams(), two columns
69 /// can join forces. The team is used to react on suppressed columns: if the current team member has a suppressed
70 /// column for a MapPage() call, it get the page from the active column in the corresponding cluster.
71 std::vector<RColumn *> fTeam;
72 /// Points into fTeam to the column that successfully returned the last page.
73 std::size_t fLastGoodTeamIdx = 0;
74
75 RColumn(EColumnType type, std::uint32_t columnIndex, std::uint16_t representationIndex);
76
77 /// Used when trying to append to a full write page. If possible, expand the page. Otherwise, flush and reset
78 /// to the minimal size.
80 {
81 auto newMaxElements = fWritePage.GetMaxElements() * 2;
82 if (newMaxElements * fElement->GetSize() > fPageSink->GetWriteOptions().GetMaxUnzippedPageSize()) {
83 newMaxElements = fPageSink->GetWriteOptions().GetMaxUnzippedPageSize() / fElement->GetSize();
84 }
85
86 if (newMaxElements == fWritePage.GetMaxElements()) {
87 // Maximum page size reached, flush and reset
88 Flush();
89 } else {
90 auto expandedPage = fPageSink->ReservePage(fHandleSink, newMaxElements);
91 if (expandedPage.IsNull()) {
92 Flush();
93 } else {
94 memcpy(expandedPage.GetBuffer(), fWritePage.GetBuffer(), fWritePage.GetNBytes());
95 expandedPage.Reset(fNElements);
96 expandedPage.GrowUnchecked(fWritePage.GetNElements());
97 fWritePage = std::move(expandedPage);
98 }
99 }
100
102 }
103
104public:
105 template <typename CppT>
106 static std::unique_ptr<RColumn> Create(EColumnType type, std::uint32_t columnIdx, std::uint16_t representationIdx)
107 {
108 auto column = std::unique_ptr<RColumn>(new RColumn(type, columnIdx, representationIdx));
109 column->fElement = RColumnElementBase::Generate<CppT>(type);
110 return column;
111 }
112
113 RColumn(const RColumn &) = delete;
114 RColumn &operator=(const RColumn &) = delete;
115 ~RColumn();
116
117 /// Connect the column to a page sink. `firstElementIndex` can be used to specify the first column element index
118 /// with backing storage for this column. On read back, elements before `firstElementIndex` will cause the zero page
119 /// to be mapped.
120 void ConnectPageSink(DescriptorId_t fieldId, RPageSink &pageSink, NTupleSize_t firstElementIndex = 0U);
121 /// Connect the column to a page source.
122 void ConnectPageSource(DescriptorId_t fieldId, RPageSource &pageSource);
123
124 void Append(const void *from)
125 {
128 }
129
130 void *dst = fWritePage.GrowUnchecked(1);
131
132 std::memcpy(dst, from, fElement->GetSize());
133 fNElements++;
134 }
135
136 void AppendV(const void *from, std::size_t count)
137 {
138 auto src = reinterpret_cast<const unsigned char *>(from);
139 // TODO(jblomer): A future optimization should grow the page in one go, up to the maximum unzipped page size
140 while (count > 0) {
141 std::size_t nElementsRemaining = fWritePage.GetMaxElements() - fWritePage.GetNElements();
142 if (nElementsRemaining == 0) {
144 nElementsRemaining = fWritePage.GetMaxElements() - fWritePage.GetNElements();
145 }
146
147 assert(nElementsRemaining > 0);
148 auto nBatch = std::min(count, nElementsRemaining);
149
150 void *dst = fWritePage.GrowUnchecked(nBatch);
151 std::memcpy(dst, src, nBatch * fElement->GetSize());
152 src += nBatch * fElement->GetSize();
153 count -= nBatch;
154 fNElements += nBatch;
155 }
156 }
157
158 void Read(const NTupleSize_t globalIndex, void *to)
159 {
160 if (!fReadPageRef.Get().Contains(globalIndex)) {
161 MapPage(globalIndex);
162 }
163 const auto elemSize = fElement->GetSize();
164 void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
165 (globalIndex - fReadPageRef.Get().GetGlobalRangeFirst()) * elemSize;
166 std::memcpy(to, from, elemSize);
167 }
168
169 void Read(RClusterIndex clusterIndex, void *to)
170 {
171 if (!fReadPageRef.Get().Contains(clusterIndex)) {
172 MapPage(clusterIndex);
173 }
174 const auto elemSize = fElement->GetSize();
175 void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
176 (clusterIndex.GetIndex() - fReadPageRef.Get().GetClusterRangeFirst()) * elemSize;
177 std::memcpy(to, from, elemSize);
178 }
179
180 void ReadV(const NTupleSize_t globalIndex, const ClusterSize_t::ValueType count, void *to)
181 {
182 if (!fReadPageRef.Get().Contains(globalIndex)) {
183 MapPage(globalIndex);
184 }
185 NTupleSize_t idxInPage = globalIndex - fReadPageRef.Get().GetGlobalRangeFirst();
186
187 const auto elemSize = fElement->GetSize();
188 const void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) + idxInPage * elemSize;
189 if (globalIndex + count <= fReadPageRef.Get().GetGlobalRangeLast() + 1) {
190 std::memcpy(to, from, elemSize * count);
191 } else {
192 ClusterSize_t::ValueType nBatch = fReadPageRef.Get().GetNElements() - idxInPage;
193 std::memcpy(to, from, elemSize * nBatch);
194 auto tail = static_cast<unsigned char *>(to) + nBatch * elemSize;
195 ReadV(globalIndex + nBatch, count - nBatch, tail);
196 }
197 }
198
199 void ReadV(RClusterIndex clusterIndex, const ClusterSize_t::ValueType count, void *to)
200 {
201 if (!fReadPageRef.Get().Contains(clusterIndex)) {
202 MapPage(clusterIndex);
203 }
204 NTupleSize_t idxInPage = clusterIndex.GetIndex() - fReadPageRef.Get().GetClusterRangeFirst();
205
206 const auto elemSize = fElement->GetSize();
207 const void *from = static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) + idxInPage * elemSize;
208 if (clusterIndex.GetIndex() + count <= fReadPageRef.Get().GetClusterRangeLast() + 1) {
209 std::memcpy(to, from, elemSize * count);
210 } else {
211 ClusterSize_t::ValueType nBatch = fReadPageRef.Get().GetNElements() - idxInPage;
212 std::memcpy(to, from, elemSize * nBatch);
213 auto tail = static_cast<unsigned char *>(to) + nBatch * elemSize;
214 ReadV(RClusterIndex(clusterIndex.GetClusterId(), clusterIndex.GetIndex() + nBatch), count - nBatch, tail);
215 }
216 }
217
218 template <typename CppT>
219 CppT *Map(const NTupleSize_t globalIndex)
220 {
221 NTupleSize_t nItems;
222 return MapV<CppT>(globalIndex, nItems);
223 }
224
225 template <typename CppT>
226 CppT *Map(RClusterIndex clusterIndex)
227 {
228 NTupleSize_t nItems;
229 return MapV<CppT>(clusterIndex, nItems);
230 }
231
232 template <typename CppT>
233 CppT *MapV(const NTupleSize_t globalIndex, NTupleSize_t &nItems)
234 {
235 if (R__unlikely(!fReadPageRef.Get().Contains(globalIndex))) {
236 MapPage(globalIndex);
237 }
238 // +1 to go from 0-based indexing to 1-based number of items
239 nItems = fReadPageRef.Get().GetGlobalRangeLast() - globalIndex + 1;
240 return reinterpret_cast<CppT *>(static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
241 (globalIndex - fReadPageRef.Get().GetGlobalRangeFirst()) * sizeof(CppT));
242 }
243
244 template <typename CppT>
245 CppT *MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
246 {
247 if (!fReadPageRef.Get().Contains(clusterIndex)) {
248 MapPage(clusterIndex);
249 }
250 // +1 to go from 0-based indexing to 1-based number of items
251 nItems = fReadPageRef.Get().GetClusterRangeLast() - clusterIndex.GetIndex() + 1;
252 return reinterpret_cast<CppT *>(static_cast<unsigned char *>(fReadPageRef.Get().GetBuffer()) +
253 (clusterIndex.GetIndex() - fReadPageRef.Get().GetClusterRangeFirst()) *
254 sizeof(CppT));
255 }
256
258 {
259 if (!fReadPageRef.Get().Contains(clusterIndex)) {
260 MapPage(clusterIndex);
261 }
262 return fReadPageRef.Get().GetClusterInfo().GetIndexOffset() + clusterIndex.GetIndex();
263 }
264
266 {
267 if (!fReadPageRef.Get().Contains(globalIndex)) {
268 MapPage(globalIndex);
269 }
271 globalIndex - fReadPageRef.Get().GetClusterInfo().GetIndexOffset());
272 }
273
274 /// For offset columns only, look at the two adjacent values that define a collection's coordinates
275 void GetCollectionInfo(const NTupleSize_t globalIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
276 {
277 NTupleSize_t idxStart = 0;
278 NTupleSize_t idxEnd;
279 // Try to avoid jumping back to the previous page and jumping back to the previous cluster
280 if (R__likely(globalIndex > 0)) {
281 if (R__likely(fReadPageRef.Get().Contains(globalIndex - 1))) {
282 idxStart = *Map<ClusterSize_t>(globalIndex - 1);
283 idxEnd = *Map<ClusterSize_t>(globalIndex);
284 if (R__unlikely(fReadPageRef.Get().GetClusterInfo().GetIndexOffset() == globalIndex))
285 idxStart = 0;
286 } else {
287 idxEnd = *Map<ClusterSize_t>(globalIndex);
288 auto selfOffset = fReadPageRef.Get().GetClusterInfo().GetIndexOffset();
289 idxStart = (globalIndex == selfOffset) ? 0 : *Map<ClusterSize_t>(globalIndex - 1);
290 }
291 } else {
292 idxEnd = *Map<ClusterSize_t>(globalIndex);
293 }
294 *collectionSize = idxEnd - idxStart;
295 *collectionStart = RClusterIndex(fReadPageRef.Get().GetClusterInfo().GetId(), idxStart);
296 }
297
298 void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
299 {
300 auto index = clusterIndex.GetIndex();
301 auto idxStart = (index == 0) ? 0 : *Map<ClusterSize_t>(clusterIndex - 1);
302 auto idxEnd = *Map<ClusterSize_t>(clusterIndex);
303 *collectionSize = idxEnd - idxStart;
304 *collectionStart = RClusterIndex(clusterIndex.GetClusterId(), idxStart);
305 }
306
307 /// Get the currently active cluster id
308 void GetSwitchInfo(NTupleSize_t globalIndex, RClusterIndex *varIndex, std::uint32_t *tag)
309 {
310 auto varSwitch = Map<RColumnSwitch>(globalIndex);
311 *varIndex = RClusterIndex(fReadPageRef.Get().GetClusterInfo().GetId(), varSwitch->GetIndex());
312 *tag = varSwitch->GetTag();
313 }
314
315 void Flush();
316 void CommitSuppressed();
317
318 void MapPage(NTupleSize_t globalIndex) { R__ASSERT(TryMapPage(globalIndex)); }
319 void MapPage(RClusterIndex clusterIndex) { R__ASSERT(TryMapPage(clusterIndex)); }
320 bool TryMapPage(NTupleSize_t globalIndex);
321 bool TryMapPage(RClusterIndex clusterIndex);
322
323 bool ReadPageContains(NTupleSize_t globalIndex) const { return fReadPageRef.Get().Contains(globalIndex); }
324 bool ReadPageContains(RClusterIndex clusterIndex) const { return fReadPageRef.Get().Contains(clusterIndex); }
325
326 void MergeTeams(RColumn &other);
327
329 RColumnElementBase *GetElement() const { return fElement.get(); }
330 EColumnType GetType() const { return fType; }
331 std::uint16_t GetBitsOnStorage() const
332 {
333 assert(fElement);
334 return fElement->GetBitsOnStorage();
335 }
336 std::optional<std::pair<double, double>> GetValueRange() const
337 {
338 assert(fElement);
339 return fElement->GetValueRange();
340 }
341 std::uint32_t GetIndex() const { return fIndex; }
342 std::uint16_t GetRepresentationIndex() const { return fRepresentationIndex; }
346 RPageSink *GetPageSink() const { return fPageSink; }
349
350 void SetBitsOnStorage(std::size_t bits) { fElement->SetBitsOnStorage(bits); }
351 std::size_t GetWritePageCapacity() const { return fWritePage.GetCapacity(); }
352 void SetValueRange(double min, double max) { fElement->SetValueRange(min, max); }
353}; // class RColumn
354
355} // namespace ROOT::Experimental::Internal
356
357#endif
#define R__likely(expr)
Definition RConfig.hxx:587
#define R__unlikely(expr)
Definition RConfig.hxx:586
#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:73
RPageStorage::ColumnHandle_t GetHandleSink() const
Definition RColumn.hxx:348
void Read(RClusterIndex clusterIndex, void *to)
Definition RColumn.hxx:169
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:66
void HandleWritePageIfFull()
Used when trying to append to a full write page.
Definition RColumn.hxx:79
NTupleSize_t GetGlobalIndex(RClusterIndex clusterIndex)
Definition RColumn.hxx:257
RColumnElementBase * GetElement() const
Definition RColumn.hxx:329
RPageRef fReadPageRef
The currently mapped page for reading.
Definition RColumn.hxx:60
bool TryMapPage(NTupleSize_t globalIndex)
Definition RColumn.cxx:85
void SetBitsOnStorage(std::size_t bits)
Definition RColumn.hxx:350
void MapPage(RClusterIndex clusterIndex)
Definition RColumn.hxx:319
DescriptorId_t GetOnDiskId() const
Definition RColumn.hxx:343
void MapPage(NTupleSize_t globalIndex)
Definition RColumn.hxx:318
void GetCollectionInfo(RClusterIndex clusterIndex, RClusterIndex *collectionStart, ClusterSize_t *collectionSize)
Definition RColumn.hxx:298
void ReadV(RClusterIndex clusterIndex, const ClusterSize_t::ValueType count, void *to)
Definition RColumn.hxx:199
CppT * Map(const NTupleSize_t globalIndex)
Definition RColumn.hxx:219
RPageSource * GetPageSource() const
Definition RColumn.hxx:345
static std::unique_ptr< RColumn > Create(EColumnType type, std::uint32_t columnIdx, std::uint16_t representationIdx)
Definition RColumn.hxx:106
void AppendV(const void *from, std::size_t count)
Definition RColumn.hxx:136
std::optional< std::pair< double, double > > GetValueRange() const
Definition RColumn.hxx:336
void Append(const void *from)
Definition RColumn.hxx:124
CppT * Map(RClusterIndex clusterIndex)
Definition RColumn.hxx:226
CppT * MapV(RClusterIndex clusterIndex, NTupleSize_t &nItems)
Definition RColumn.hxx:245
RColumn & operator=(const RColumn &)=delete
bool ReadPageContains(RClusterIndex clusterIndex) const
Definition RColumn.hxx:324
RPageStorage::ColumnHandle_t fHandleSource
Definition RColumn.hxx:51
NTupleSize_t fNElements
The number of elements written resp. available in the column.
Definition RColumn.hxx:58
std::uint16_t GetRepresentationIndex() const
Definition RColumn.hxx:342
void ReadV(const NTupleSize_t globalIndex, const ClusterSize_t::ValueType count, void *to)
Definition RColumn.hxx:180
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:71
void Read(const NTupleSize_t globalIndex, void *to)
Definition RColumn.hxx:158
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:351
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:275
CppT * MapV(const NTupleSize_t globalIndex, NTupleSize_t &nItems)
Definition RColumn.hxx:233
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:308
RPageStorage::ColumnHandle_t fHandleSink
Definition RColumn.hxx:50
void SetValueRange(double min, double max)
Definition RColumn.hxx:352
DescriptorId_t fOnDiskId
The column id in the column descriptor, once connected to a sink or source.
Definition RColumn.hxx:62
NTupleSize_t GetNElements() const
Definition RColumn.hxx:328
RPage fWritePage
The page into which new elements are being written.
Definition RColumn.hxx:56
bool ReadPageContains(NTupleSize_t globalIndex) const
Definition RColumn.hxx:323
NTupleSize_t GetFirstElementIndex() const
Definition RColumn.hxx:344
RPageStorage::ColumnHandle_t GetHandleSource() const
Definition RColumn.hxx:347
RClusterIndex GetClusterIndex(NTupleSize_t globalIndex)
Definition RColumn.hxx:265
NTupleSize_t fFirstElementIndex
Global index of the first element in this column; usually == 0, unless it is a deferred column.
Definition RColumn.hxx:64
std::uint16_t GetBitsOnStorage() const
Definition RColumn.hxx:331
Reference to a page stored in the page pool.
Definition RPagePool.hxx:93
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:46
void * GrowUnchecked(ClusterSize_t::ValueType nElements)
Called during writing: returns a pointer after the last element and increases the element counter in ...
Definition RPage.hxx:158
std::size_t GetCapacity() const
Definition RPage.hxx:126
ClusterSize_t::ValueType GetClusterRangeFirst() const
Definition RPage.hxx:135
std::size_t GetNBytes() const
The space taken by column elements in the buffer.
Definition RPage.hxx:122
NTupleSize_t GetGlobalRangeFirst() const
Definition RPage.hxx:133
const RClusterInfo & GetClusterInfo() const
Definition RPage.hxx:139
std::uint32_t GetNElements() const
Definition RPage.hxx:131
bool Contains(NTupleSize_t globalIndex) const
Definition RPage.hxx:141
ClusterSize_t::ValueType GetClusterRangeLast() const
Definition RPage.hxx:136
NTupleSize_t GetGlobalRangeLast() const
Definition RPage.hxx:134
std::uint32_t GetMaxElements() const
Definition RPage.hxx:132
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.