Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RMiniFile.cxx
Go to the documentation of this file.
1/// \file RMiniFile.cxx
2/// \ingroup NTuple ROOT7
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2019-12-22
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#include "Rtypes.h"
17#include <ROOT/RConfig.hxx>
18#include <ROOT/RError.hxx>
19#include <ROOT/RMiniFile.hxx>
20#include <ROOT/RRawFile.hxx>
21#include <ROOT/RNTupleZip.hxx>
24
25#include <Byteswap.h>
26#include <TBufferFile.h>
27#include <TError.h>
28#include <TFile.h>
29#include <TKey.h>
30#include <TObjString.h>
32
33#include <xxhash.h>
34
35#include <algorithm>
36#include <cerrno>
37#include <cstdio>
38#include <cstring>
39#include <memory>
40#include <string>
41#include <chrono>
42
43#ifdef R__LINUX
44#include <fcntl.h>
45#endif
46
47#ifndef R__LITTLE_ENDIAN
48#ifdef R__BYTESWAP
49// `R__BYTESWAP` is defined in RConfig.hxx for little-endian architectures; undefined otherwise
50#define R__LITTLE_ENDIAN 1
51#else
52#define R__LITTLE_ENDIAN 0
53#endif
54#endif /* R__LITTLE_ENDIAN */
55
56namespace {
57
58// The following types are used to read and write the TFile binary format
59
60/// Big-endian 16-bit unsigned integer
61class RUInt16BE {
62private:
63 std::uint16_t fValBE = 0;
64 static std::uint16_t Swap(std::uint16_t val)
65 {
66#if R__LITTLE_ENDIAN == 1
67 return RByteSwap<sizeof(val)>::bswap(val);
68#else
69 return val;
70#endif
71 }
72
73public:
74 RUInt16BE() = default;
75 explicit RUInt16BE(const std::uint16_t val) : fValBE(Swap(val)) {}
76 operator std::uint16_t() const { return Swap(fValBE); }
77 RUInt16BE &operator=(const std::uint16_t val)
78 {
79 fValBE = Swap(val);
80 return *this;
81 }
82};
83
84/// Big-endian 32-bit unsigned integer
85class RUInt32BE {
86private:
87 std::uint32_t fValBE = 0;
88 static std::uint32_t Swap(std::uint32_t val)
89 {
90#if R__LITTLE_ENDIAN == 1
91 return RByteSwap<sizeof(val)>::bswap(val);
92#else
93 return val;
94#endif
95 }
96
97public:
98 RUInt32BE() = default;
99 explicit RUInt32BE(const std::uint32_t val) : fValBE(Swap(val)) {}
100 operator std::uint32_t() const { return Swap(fValBE); }
101 RUInt32BE &operator=(const std::uint32_t val)
102 {
103 fValBE = Swap(val);
104 return *this;
105 }
106};
107
108/// Big-endian 32-bit signed integer
109class RInt32BE {
110private:
111 std::int32_t fValBE = 0;
112 static std::int32_t Swap(std::int32_t val)
113 {
114#if R__LITTLE_ENDIAN == 1
115 return RByteSwap<sizeof(val)>::bswap(val);
116#else
117 return val;
118#endif
119 }
120
121public:
122 RInt32BE() = default;
123 explicit RInt32BE(const std::int32_t val) : fValBE(Swap(val)) {}
124 operator std::int32_t() const { return Swap(fValBE); }
125 RInt32BE &operator=(const std::int32_t val)
126 {
127 fValBE = Swap(val);
128 return *this;
129 }
130};
131
132/// Big-endian 64-bit unsigned integer
133class RUInt64BE {
134private:
135 std::uint64_t fValBE = 0;
136 static std::uint64_t Swap(std::uint64_t val)
137 {
138#if R__LITTLE_ENDIAN == 1
139 return RByteSwap<sizeof(val)>::bswap(val);
140#else
141 return val;
142#endif
143 }
144
145public:
146 RUInt64BE() = default;
147 explicit RUInt64BE(const std::uint64_t val) : fValBE(Swap(val)) {}
148 operator std::uint64_t() const { return Swap(fValBE); }
149 RUInt64BE &operator=(const std::uint64_t val)
150 {
151 fValBE = Swap(val);
152 return *this;
153 }
154};
155
156#pragma pack(push, 1)
157/// A name (type, identifies, ...) in the TFile binary format
158struct RTFString {
159 unsigned char fLName{0};
160 char fData[255];
161 RTFString() = default;
162 RTFString(const std::string &str)
163 {
164 // The length of strings with 255 characters and longer are encoded with a 32-bit integer following the first
165 // byte. This is currently not handled.
166 R__ASSERT(str.length() < 255);
167 fLName = str.length();
168 memcpy(fData, str.data(), fLName);
169 }
170 std::size_t GetSize() const
171 {
172 // A length of 255 is special and means that the first byte is followed by a 32-bit integer with the actual
173 // length.
174 R__ASSERT(fLName != 255);
175 return 1 + fLName;
176 }
177};
178
179/// The timestamp format used in TFile; the default constructor initializes with the current time
180struct RTFDatetime {
181 RUInt32BE fDatetime;
182 RTFDatetime()
183 {
184 auto now = std::chrono::system_clock::now();
185 auto tt = std::chrono::system_clock::to_time_t(now);
186 auto tm = *localtime(&tt);
187 fDatetime = (tm.tm_year + 1900 - 1995) << 26 | (tm.tm_mon + 1) << 22 | tm.tm_mday << 17 | tm.tm_hour << 12 |
188 tm.tm_min << 6 | tm.tm_sec;
189 }
190 explicit RTFDatetime(RUInt32BE val) : fDatetime(val) {}
191};
192
193/// The key part of a TFile record excluding the class, object, and title names
194struct RTFKey {
195 RInt32BE fNbytes{0};
196 RUInt16BE fVersion{4};
197 RUInt32BE fObjLen{0};
198 RTFDatetime fDatetime;
199 RUInt16BE fKeyLen{0};
200 RUInt16BE fCycle{1};
201 union {
202 struct {
203 RUInt32BE fSeekKey{0};
204 RUInt32BE fSeekPdir{0};
205 } fInfoShort;
206 struct {
207 RUInt64BE fSeekKey{0};
208 RUInt64BE fSeekPdir{0};
209 } fInfoLong;
210 };
211
212 std::uint32_t fKeyHeaderSize{18 + sizeof(fInfoShort)}; // not part of serialization
213
214 RTFKey() : fInfoShort() {}
215 RTFKey(std::uint64_t seekKey, std::uint64_t seekPdir, const RTFString &clName, const RTFString &objName,
216 const RTFString &titleName, std::size_t szObjInMem, std::size_t szObjOnDisk = 0)
217 {
218 R__ASSERT(szObjInMem < std::numeric_limits<std::int32_t>::max());
219 R__ASSERT(szObjOnDisk < std::numeric_limits<std::int32_t>::max());
220 fObjLen = szObjInMem;
221 if ((seekKey > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) ||
222 (seekPdir > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max()))) {
223 fKeyHeaderSize = 18 + sizeof(fInfoLong);
224 fKeyLen = fKeyHeaderSize + clName.GetSize() + objName.GetSize() + titleName.GetSize();
225 fInfoLong.fSeekKey = seekKey;
226 fInfoLong.fSeekPdir = seekPdir;
227 fVersion = fVersion + 1000;
228 } else {
229 fKeyHeaderSize = 18 + sizeof(fInfoShort);
230 fKeyLen = fKeyHeaderSize + clName.GetSize() + objName.GetSize() + titleName.GetSize();
231 fInfoShort.fSeekKey = seekKey;
232 fInfoShort.fSeekPdir = seekPdir;
233 }
234 fNbytes = fKeyLen + ((szObjOnDisk == 0) ? szObjInMem : szObjOnDisk);
235 }
236
237 void MakeBigKey()
238 {
239 if (fVersion >= 1000)
240 return;
241 std::uint32_t seekKey = fInfoShort.fSeekKey;
242 std::uint32_t seekPdir = fInfoShort.fSeekPdir;
243 fInfoLong.fSeekKey = seekKey;
244 fInfoLong.fSeekPdir = seekPdir;
245 fKeyHeaderSize = fKeyHeaderSize + sizeof(fInfoLong) - sizeof(fInfoShort);
246 fKeyLen = fKeyLen + sizeof(fInfoLong) - sizeof(fInfoShort);
247 fNbytes = fNbytes + sizeof(fInfoLong) - sizeof(fInfoShort);
248 fVersion = fVersion + 1000;
249 }
250
251 std::uint32_t GetSize() const
252 {
253 // Negative size indicates a gap in the file
254 if (fNbytes < 0)
255 return -fNbytes;
256 return fNbytes;
257 }
258
259 std::uint32_t GetHeaderSize() const
260 {
261 if (fVersion >= 1000)
262 return 18 + sizeof(fInfoLong);
263 return 18 + sizeof(fInfoShort);
264 }
265
266 std::uint64_t GetSeekKey() const
267 {
268 if (fVersion >= 1000)
269 return fInfoLong.fSeekKey;
270 return fInfoShort.fSeekKey;
271 }
272};
273
274/// The TFile global header
275struct RTFHeader {
276 char fMagic[4]{'r', 'o', 'o', 't'};
277 RUInt32BE fVersion{(ROOT_VERSION_CODE >> 16) * 10000 + ((ROOT_VERSION_CODE & 0xFF00) >> 8) * 100 +
278 (ROOT_VERSION_CODE & 0xFF)};
279 RUInt32BE fBEGIN{100};
280 union {
281 struct {
282 RUInt32BE fEND{0};
283 RUInt32BE fSeekFree{0};
284 RUInt32BE fNbytesFree{0};
285 RUInt32BE fNfree{1};
286 RUInt32BE fNbytesName{0};
287 unsigned char fUnits{4};
288 RUInt32BE fCompress{0};
289 RUInt32BE fSeekInfo{0};
290 RUInt32BE fNbytesInfo{0};
291 } fInfoShort;
292 struct {
293 RUInt64BE fEND{0};
294 RUInt64BE fSeekFree{0};
295 RUInt32BE fNbytesFree{0};
296 RUInt32BE fNfree{1};
297 RUInt32BE fNbytesName{0};
298 unsigned char fUnits{8};
299 RUInt32BE fCompress{0};
300 RUInt64BE fSeekInfo{0};
301 RUInt32BE fNbytesInfo{0};
302 } fInfoLong;
303 };
304
305 RTFHeader() : fInfoShort() {}
306 RTFHeader(int compression) : fInfoShort() { fInfoShort.fCompress = compression; }
307
308 void SetBigFile()
309 {
310 if (fVersion >= 1000000)
311 return;
312
313 // clang-format off
314 std::uint32_t end = fInfoShort.fEND;
315 std::uint32_t seekFree = fInfoShort.fSeekFree;
316 std::uint32_t nbytesFree = fInfoShort.fNbytesFree;
317 std::uint32_t nFree = fInfoShort.fNfree;
318 std::uint32_t nbytesName = fInfoShort.fNbytesName;
319 std::uint32_t compress = fInfoShort.fCompress;
320 std::uint32_t seekInfo = fInfoShort.fSeekInfo;
321 std::uint32_t nbytesInfo = fInfoShort.fNbytesInfo;
322 fInfoLong.fEND = end;
323 fInfoLong.fSeekFree = seekFree;
324 fInfoLong.fNbytesFree = nbytesFree;
325 fInfoLong.fNfree = nFree;
326 fInfoLong.fNbytesName = nbytesName;
327 fInfoLong.fUnits = 8;
328 fInfoLong.fCompress = compress;
329 fInfoLong.fSeekInfo = seekInfo;
330 fInfoLong.fNbytesInfo = nbytesInfo;
331 fVersion = fVersion + 1000000;
332 // clang-format on
333 }
334
335 bool IsBigFile(std::uint64_t offset = 0) const
336 {
337 return (fVersion >= 1000000) || (offset > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max()));
338 }
339
340 std::uint32_t GetSize() const
341 {
342 std::uint32_t sizeHead = 4 + sizeof(fVersion) + sizeof(fBEGIN);
343 if (IsBigFile())
344 return sizeHead + sizeof(fInfoLong);
345 return sizeHead + sizeof(fInfoShort);
346 }
347
348 std::uint64_t GetEnd() const
349 {
350 if (IsBigFile())
351 return fInfoLong.fEND;
352 return fInfoShort.fEND;
353 }
354
355 void SetEnd(std::uint64_t value)
356 {
357 if (IsBigFile(value)) {
358 SetBigFile();
359 fInfoLong.fEND = value;
360 } else {
361 fInfoShort.fEND = value;
362 }
363 }
364
365 std::uint64_t GetSeekFree() const
366 {
367 if (IsBigFile())
368 return fInfoLong.fSeekFree;
369 return fInfoShort.fSeekFree;
370 }
371
372 void SetSeekFree(std::uint64_t value)
373 {
374 if (IsBigFile(value)) {
375 SetBigFile();
376 fInfoLong.fSeekFree = value;
377 } else {
378 fInfoShort.fSeekFree = value;
379 }
380 }
381
382 void SetNbytesFree(std::uint32_t value)
383 {
384 if (IsBigFile()) {
385 fInfoLong.fNbytesFree = value;
386 } else {
387 fInfoShort.fNbytesFree = value;
388 }
389 }
390
391 void SetNbytesName(std::uint32_t value)
392 {
393 if (IsBigFile()) {
394 fInfoLong.fNbytesName = value;
395 } else {
396 fInfoShort.fNbytesName = value;
397 }
398 }
399
400 std::uint64_t GetSeekInfo() const
401 {
402 if (IsBigFile())
403 return fInfoLong.fSeekInfo;
404 return fInfoShort.fSeekInfo;
405 }
406
407 void SetSeekInfo(std::uint64_t value)
408 {
409 if (IsBigFile(value)) {
410 SetBigFile();
411 fInfoLong.fSeekInfo = value;
412 } else {
413 fInfoShort.fSeekInfo = value;
414 }
415 }
416
417 void SetNbytesInfo(std::uint32_t value)
418 {
419 if (IsBigFile()) {
420 fInfoLong.fNbytesInfo = value;
421 } else {
422 fInfoShort.fNbytesInfo = value;
423 }
424 }
425
426 void SetCompression(std::uint32_t value)
427 {
428 if (IsBigFile()) {
429 fInfoLong.fCompress = value;
430 } else {
431 fInfoShort.fCompress = value;
432 }
433 }
434};
435
436/// A reference to an unused byte-range in a TFile
437struct RTFFreeEntry {
438 RUInt16BE fVersion{1};
439 union {
440 struct {
441 RUInt32BE fFirst{0};
442 RUInt32BE fLast{0};
443 } fInfoShort;
444 struct {
445 RUInt64BE fFirst{0};
446 RUInt64BE fLast{0};
447 } fInfoLong;
448 };
449
450 RTFFreeEntry() : fInfoShort() {}
451 void Set(std::uint64_t first, std::uint64_t last)
452 {
453 if (last > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
454 fVersion = fVersion + 1000;
455 fInfoLong.fFirst = first;
456 fInfoLong.fLast = last;
457 } else {
458 fInfoShort.fFirst = first;
459 fInfoShort.fLast = last;
460 }
461 }
462 std::uint32_t GetSize() { return (fVersion >= 1000) ? 18 : 10; }
463};
464
465/// The header of the directory key index
466struct RTFKeyList {
467 RUInt32BE fNKeys;
468 std::uint32_t GetSize() const { return sizeof(RTFKeyList); }
469 explicit RTFKeyList(std::uint32_t nKeys) : fNKeys(nKeys) {}
470};
471
472/// A streamed TFile object
473struct RTFFile {
474 RUInt16BE fClassVersion{5};
475 RTFDatetime fDateC;
476 RTFDatetime fDateM;
477 RUInt32BE fNBytesKeys{0};
478 RUInt32BE fNBytesName{0};
479 // The version of the key has to tell whether offsets are 32bit or 64bit long
480 union {
481 struct {
482 RUInt32BE fSeekDir{100};
483 RUInt32BE fSeekParent{0};
484 RUInt32BE fSeekKeys{0};
485 } fInfoShort;
486 struct {
487 RUInt64BE fSeekDir{100};
488 RUInt64BE fSeekParent{0};
489 RUInt64BE fSeekKeys{0};
490 } fInfoLong;
491 };
492
493 RTFFile() : fInfoShort() {}
494
495 // In case of a short TFile record (<2G), 3 padding ints are written after the UUID
496 std::uint32_t GetSize() const
497 {
498 if (fClassVersion >= 1000)
499 return sizeof(RTFFile);
500 return 18 + sizeof(fInfoShort);
501 }
502
503 std::uint64_t GetSeekKeys() const
504 {
505 if (fClassVersion >= 1000)
506 return fInfoLong.fSeekKeys;
507 return fInfoShort.fSeekKeys;
508 }
509
510 void SetSeekKeys(std::uint64_t seekKeys)
511 {
512 if (seekKeys > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
513 std::uint32_t seekDir = fInfoShort.fSeekDir;
514 std::uint32_t seekParent = fInfoShort.fSeekParent;
515 fInfoLong.fSeekDir = seekDir;
516 fInfoLong.fSeekParent = seekParent;
517 fInfoLong.fSeekKeys = seekKeys;
518 fClassVersion = fClassVersion + 1000;
519 } else {
520 fInfoShort.fSeekKeys = seekKeys;
521 }
522 }
523};
524
525/// A zero UUID stored at the end of the TFile record
526struct RTFUUID {
527 RUInt16BE fVersionClass{1};
528 unsigned char fUUID[16] = {0};
529
530 RTFUUID() = default;
531 std::uint32_t GetSize() const { return sizeof(RTFUUID); }
532};
533
534/// A streamed RNTuple class
535///
536/// NOTE: this must be kept in sync with RNTuple.hxx.
537/// Aside ensuring consistency between the two classes' members, you need to make sure
538/// that fVersionClass matches the class version of RNTuple.
539struct RTFNTuple {
540 RUInt32BE fByteCount{0x40000000 | (sizeof(RTFNTuple) - sizeof(fByteCount))};
541 RUInt16BE fVersionClass{6};
542 RUInt16BE fVersionEpoch{0};
543 RUInt16BE fVersionMajor{0};
544 RUInt16BE fVersionMinor{0};
545 RUInt16BE fVersionPatch{0};
546 RUInt64BE fSeekHeader{0};
547 RUInt64BE fNBytesHeader{0};
548 RUInt64BE fLenHeader{0};
549 RUInt64BE fSeekFooter{0};
550 RUInt64BE fNBytesFooter{0};
551 RUInt64BE fLenFooter{0};
552 RUInt64BE fMaxKeySize{0};
553
554 static constexpr std::uint32_t GetSizePlusChecksum() { return sizeof(RTFNTuple) + sizeof(std::uint64_t); }
555
556 RTFNTuple() = default;
557 explicit RTFNTuple(const ROOT::Experimental::RNTuple &inMemoryAnchor)
558 {
559 fVersionEpoch = inMemoryAnchor.GetVersionEpoch();
560 fVersionMajor = inMemoryAnchor.GetVersionMajor();
561 fVersionMinor = inMemoryAnchor.GetVersionMinor();
562 fVersionPatch = inMemoryAnchor.GetVersionPatch();
563 fSeekHeader = inMemoryAnchor.GetSeekHeader();
564 fNBytesHeader = inMemoryAnchor.GetNBytesHeader();
565 fLenHeader = inMemoryAnchor.GetLenHeader();
566 fSeekFooter = inMemoryAnchor.GetSeekFooter();
567 fNBytesFooter = inMemoryAnchor.GetNBytesFooter();
568 fLenFooter = inMemoryAnchor.GetLenFooter();
569 fMaxKeySize = inMemoryAnchor.GetMaxKeySize();
570 }
571 std::uint32_t GetSize() const { return sizeof(RTFNTuple); }
572 // The byte count and class version members are not checksummed
573 std::uint32_t GetOffsetCkData() { return sizeof(fByteCount) + sizeof(fVersionClass); }
574 std::uint32_t GetSizeCkData() { return GetSize() - GetOffsetCkData(); }
575 unsigned char *GetPtrCkData() { return reinterpret_cast<unsigned char *>(this) + GetOffsetCkData(); }
576};
577
578/// The bare file global header
579struct RBareFileHeader {
580 char fMagic[7]{'r', 'n', 't', 'u', 'p', 'l', 'e'};
581 RUInt32BE fRootVersion{(ROOT_VERSION_CODE >> 16) * 10000 + ((ROOT_VERSION_CODE & 0xFF00) >> 8) * 100 +
582 (ROOT_VERSION_CODE & 0xFF)};
583 RUInt32BE fFormatVersion{1};
584 RUInt32BE fCompress{0};
585 RTFNTuple fNTuple;
586 // followed by the ntuple name
587};
588#pragma pack(pop)
589
590/// The artifical class name shown for opaque RNTuple keys (see TBasket)
591constexpr char const *kBlobClassName = "RBlob";
592/// The class name of the RNTuple anchor
593constexpr char const *kNTupleClassName = "ROOT::Experimental::RNTuple";
594
595} // anonymous namespace
596
597namespace ROOT {
598namespace Experimental {
599namespace Internal {
600/// If a TFile container is written by a C stream (simple file), on dataset commit, the file header
601/// and the TFile record need to be updated
603 RTFHeader fHeader;
604 RTFFile fFileRecord;
605 std::uint64_t fSeekNTuple{0}; // Remember the offset for the keys list
606 std::uint64_t fSeekFileRecord{0};
607};
608
609/// The RKeyBlob writes an invisible key into a TFile. That is, a key that is not indexed in the list of keys,
610/// like a TBasket.
611/// NOTE: out of anonymous namespace because otherwise ClassDefInline fails to compile
612/// on some platforms.
613class RKeyBlob : public TKey {
614public:
615 RKeyBlob() = default;
616
617 explicit RKeyBlob(TFile *file) : TKey(file)
618 {
619 fClassName = kBlobClassName;
620 fVersion += 1000;
621 fKeylen = Sizeof();
622 }
623
624 /// Register a new key for a data record of size nbytes
625 void Reserve(size_t nbytes, std::uint64_t *seekKey)
626 {
627 Create(nbytes);
628 *seekKey = fSeekKey;
629 }
630
632};
633
634} // namespace Internal
635} // namespace Experimental
636} // namespace ROOT
637
638// Computes how many chunks do we need to fit `nbytes` of payload, considering that the
639// first chunk also needs to house the offsets of the other chunks and no chunk can
640// be bigger than `maxChunkSize`. When saved to a TFile, each chunk is part of a separate TKey.
641static size_t ComputeNumChunks(size_t nbytes, size_t maxChunkSize)
642{
643 constexpr size_t kChunkOffsetSize = sizeof(std::uint64_t);
644
645 assert(nbytes > maxChunkSize);
646 size_t nChunks = (nbytes + maxChunkSize - 1) / maxChunkSize;
647 assert(nChunks > 1);
648 size_t nbytesTail = nbytes % maxChunkSize;
649 size_t nbytesExtra = (nbytesTail > 0) * (maxChunkSize - nbytesTail);
650 size_t nbytesChunkOffsets = (nChunks - 1) * kChunkOffsetSize;
651 if (nbytesChunkOffsets > nbytesExtra) {
652 ++nChunks;
653 nbytesChunkOffsets += kChunkOffsetSize;
654 }
655
656 // We don't support having more chunkOffsets than what fits in one chunk.
657 // For a reasonable-sized maxKeySize it looks very unlikely that we can have more chunks
658 // than we can fit in the first `maxKeySize` bytes. E.g. for maxKeySize = 1GiB we can fit
659 // 134217728 chunk offsets, making our multi-key blob's capacity exactly 128 PiB.
660 R__ASSERT(nbytesChunkOffsets <= maxChunkSize);
661
662 return nChunks;
663}
664
666
668 std::uint16_t versionEpoch, std::uint16_t versionMajor, std::uint16_t versionMinor, std::uint16_t versionPatch,
669 std::uint64_t seekHeader, std::uint64_t nbytesHeader, std::uint64_t lenHeader, std::uint64_t seekFooter,
670 std::uint64_t nbytesFooter, std::uint64_t lenFooter, std::uint64_t maxKeySize)
671{
672 RNTuple ntuple;
673 ntuple.fVersionEpoch = versionEpoch;
674 ntuple.fVersionMajor = versionMajor;
675 ntuple.fVersionMinor = versionMinor;
676 ntuple.fVersionPatch = versionPatch;
677 ntuple.fSeekHeader = seekHeader;
678 ntuple.fNBytesHeader = nbytesHeader;
679 ntuple.fLenHeader = lenHeader;
680 ntuple.fSeekFooter = seekFooter;
681 ntuple.fNBytesFooter = nbytesFooter;
682 ntuple.fLenFooter = lenFooter;
683 ntuple.fMaxKeySize = maxKeySize;
684 return ntuple;
685}
686
689{
690 char ident[4];
691 ReadBuffer(ident, 4, 0);
692 if (std::string(ident, 4) == "root")
693 return GetNTupleProper(ntupleName);
694 fIsBare = true;
695 return GetNTupleBare(ntupleName);
696}
697
700{
701 RTFHeader fileHeader;
702 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
703
704 RTFKey key;
705 RTFString name;
706 ReadBuffer(&key, sizeof(key), fileHeader.fBEGIN);
707 // Skip over the entire key length, including the class name, object name, and title stored in it.
708 std::uint64_t offset = fileHeader.fBEGIN + key.fKeyLen;
709 // Skip over the name and title of the TNamed preceding the TFile entry.
710 ReadBuffer(&name, 1, offset);
711 offset += name.GetSize();
712 ReadBuffer(&name, 1, offset);
713 offset += name.GetSize();
714 RTFFile file;
715 ReadBuffer(&file, sizeof(file), offset);
716
717 RUInt32BE nKeys;
718 offset = file.GetSeekKeys();
719 ReadBuffer(&key, sizeof(key), offset);
720 offset += key.fKeyLen;
721 ReadBuffer(&nKeys, sizeof(nKeys), offset);
722 offset += sizeof(nKeys);
723 bool found = false;
724 for (unsigned int i = 0; i < nKeys; ++i) {
725 ReadBuffer(&key, sizeof(key), offset);
726 auto offsetNextKey = offset + key.fKeyLen;
727
728 offset += key.GetHeaderSize();
729 ReadBuffer(&name, 1, offset);
730 ReadBuffer(&name, name.GetSize(), offset);
731 if (std::string_view(name.fData, name.fLName) != kNTupleClassName) {
732 offset = offsetNextKey;
733 continue;
734 }
735 offset += name.GetSize();
736 ReadBuffer(&name, 1, offset);
737 ReadBuffer(&name, name.GetSize(), offset);
738 if (std::string_view(name.fData, name.fLName) == ntupleName) {
739 found = true;
740 break;
741 }
742 offset = offsetNextKey;
743 }
744 if (!found) {
745 return R__FAIL("no RNTuple named '" + std::string(ntupleName) + "' in file '" + fRawFile->GetUrl() + "'");
746 }
747
748 offset = key.GetSeekKey() + key.fKeyLen;
749
750 constexpr size_t kMinNTupleSize = 70; // size of a RTFNTuple version 4 (min supported version)
751 if (key.fObjLen < kMinNTupleSize) {
752 return R__FAIL("invalid anchor size: " + std::to_string(key.fObjLen) + " < " + std::to_string(sizeof(RTFNTuple)));
753 }
754 // The object length can be smaller than the size of RTFNTuple if it comes from a past RNTuple class version,
755 // or larger than it if it comes from a future RNTuple class version.
756 auto bufAnchor = std::make_unique<unsigned char[]>(std::max<size_t>(key.fObjLen, sizeof(RTFNTuple)));
757 RTFNTuple *ntuple = new (bufAnchor.get()) RTFNTuple;
758
759 auto objNbytes = key.GetSize() - key.fKeyLen;
760 ReadBuffer(ntuple, objNbytes, offset);
761 if (objNbytes != key.fObjLen) {
762 RNTupleDecompressor decompressor;
763 decompressor.Unzip(bufAnchor.get(), objNbytes, key.fObjLen);
764 }
765
766 if (ntuple->fVersionClass < 4) {
767 return R__FAIL("invalid anchor, unsupported pre-release of RNTuple");
768 }
769
770 // We require that future class versions only append members and store the checksum in the last 8 bytes
771 // Checksum calculation: strip byte count, class version, fChecksum member
772 auto lenCkData = key.fObjLen - ntuple->GetOffsetCkData() - sizeof(uint64_t);
773 auto ckCalc = XXH3_64bits(ntuple->GetPtrCkData(), lenCkData);
774 uint64_t ckOnDisk;
775
776 // For version 4 there is no maxKeySize (there is the checksum instead)
777 if (ntuple->fVersionClass == 4) {
778 ckOnDisk = ntuple->fMaxKeySize;
779 ntuple->fMaxKeySize = 0;
780 } else {
781 RUInt64BE *ckOnDiskPtr = reinterpret_cast<RUInt64BE *>(bufAnchor.get() + key.fObjLen - sizeof(uint64_t));
782 ckOnDisk = static_cast<uint64_t>(*ckOnDiskPtr);
783 }
784 if (ckCalc != ckOnDisk) {
785 return R__FAIL("RNTuple anchor checksum mismatch");
786 }
787
788 fMaxKeySize = ntuple->fMaxKeySize;
789
790 return CreateAnchor(ntuple->fVersionEpoch, ntuple->fVersionMajor, ntuple->fVersionMinor, ntuple->fVersionPatch,
791 ntuple->fSeekHeader, ntuple->fNBytesHeader, ntuple->fLenHeader, ntuple->fSeekFooter,
792 ntuple->fNBytesFooter, ntuple->fLenFooter, ntuple->fMaxKeySize);
793}
794
797{
798 RBareFileHeader fileHeader;
799 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
800 RTFString name;
801 auto offset = sizeof(fileHeader);
802 ReadBuffer(&name, 1, offset);
803 ReadBuffer(&name, name.GetSize(), offset);
804 std::string_view foundName(name.fData, name.fLName);
805 if (foundName != ntupleName) {
806 return R__FAIL("expected RNTuple named '" + std::string(ntupleName) + "' but instead found '" +
807 std::string(foundName) + "' in file '" + fRawFile->GetUrl() + "'");
808 }
809 offset += name.GetSize();
810
811 RTFNTuple ntuple;
812 ReadBuffer(&ntuple, sizeof(ntuple), offset);
813 std::uint64_t onDiskChecksum;
814 ReadBuffer(&onDiskChecksum, sizeof(onDiskChecksum), offset + sizeof(ntuple));
815 auto checksum = XXH3_64bits(ntuple.GetPtrCkData(), ntuple.GetSizeCkData());
816 if (checksum != static_cast<uint64_t>(onDiskChecksum))
817 return R__FAIL("RNTuple bare file: anchor checksum mismatch");
818
819 fMaxKeySize = ntuple.fMaxKeySize;
820
821 return CreateAnchor(ntuple.fVersionEpoch, ntuple.fVersionMajor, ntuple.fVersionMinor, ntuple.fVersionPatch,
822 ntuple.fSeekHeader, ntuple.fNBytesHeader, ntuple.fLenHeader, ntuple.fSeekFooter,
823 ntuple.fNBytesFooter, ntuple.fLenFooter, ntuple.fMaxKeySize);
824}
825
826void ROOT::Experimental::Internal::RMiniFileReader::ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
827{
828 size_t nread;
829 if (fMaxKeySize == 0 || nbytes <= fMaxKeySize) {
830 // Fast path: read single blob
831 nread = fRawFile->ReadAt(buffer, nbytes, offset);
832 } else {
833 // Read chunked blob. See RNTupleFileWriter::WriteBlob() for details.
834 const size_t nChunks = ComputeNumChunks(nbytes, fMaxKeySize);
835 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
836 const size_t nbytesFirstChunk = fMaxKeySize - nbytesChunkOffsets;
837 uint8_t *bufCur = reinterpret_cast<uint8_t *>(buffer);
838
839 // Read first chunk
840 nread = fRawFile->ReadAt(bufCur, fMaxKeySize, offset);
841 R__ASSERT(nread == fMaxKeySize);
842 // NOTE: we read the entire chunk in `bufCur`, but we only advance the pointer by `nbytesFirstChunk`,
843 // since the last part of `bufCur` will later be overwritten by the next chunk's payload.
844 // We do this to avoid a second ReadAt to read in the chunk offsets.
845 bufCur += nbytesFirstChunk;
846 nread -= nbytesChunkOffsets;
847
848 const auto chunkOffsets = std::make_unique<std::uint64_t[]>(nChunks - 1);
849 memcpy(chunkOffsets.get(), bufCur, nbytesChunkOffsets);
850
851 size_t remainingBytes = nbytes - nbytesFirstChunk;
852 std::uint64_t *curChunkOffset = &chunkOffsets[0];
853
854 do {
855 std::uint64_t chunkOffset;
856 RNTupleSerializer::DeserializeUInt64(curChunkOffset, chunkOffset);
857 ++curChunkOffset;
858
859 const size_t bytesToRead = std::min<size_t>(fMaxKeySize, remainingBytes);
860 // Ensure we don't read outside of the buffer
861 R__ASSERT(static_cast<size_t>(bufCur - reinterpret_cast<uint8_t *>(buffer)) <= nbytes - bytesToRead);
862
863 auto nbytesRead = fRawFile->ReadAt(bufCur, bytesToRead, chunkOffset);
864 R__ASSERT(nbytesRead == bytesToRead);
865
866 nread += bytesToRead;
867 bufCur += bytesToRead;
868 remainingBytes -= bytesToRead;
869 } while (remainingBytes > 0);
870 }
871 R__ASSERT(nread == nbytes);
872}
873
874////////////////////////////////////////////////////////////////////////////////
875
877{
878 static_assert(kHeaderBlockSize % kBlockAlign == 0, "invalid header block size");
879 static_assert(kBlockSize % kBlockAlign == 0, "invalid block size");
880 std::align_val_t blockAlign{kBlockAlign};
881 fHeaderBlock = static_cast<unsigned char *>(::operator new[](kHeaderBlockSize, blockAlign));
882 memset(fHeaderBlock, 0, kHeaderBlockSize);
883 fBlock = static_cast<unsigned char *>(::operator new[](kBlockSize, blockAlign));
884 memset(fBlock, 0, kBlockSize);
885}
886
888{
889 if (fFile)
890 fclose(fFile);
891
892 std::align_val_t blockAlign{kBlockAlign};
893 ::operator delete[](fHeaderBlock, blockAlign);
894 ::operator delete[](fBlock, blockAlign);
895}
896
897namespace {
898int FSeek64(FILE *stream, std::int64_t offset, int origin)
899{
900#ifdef R__SEEK64
901 return fseeko64(stream, offset, origin);
902#else
903 return fseek(stream, offset, origin);
904#endif
905}
906} // namespace
907
909{
910 // Write the last partially filled block, which may still need appropriate alignment for Direct I/O.
911 // If it is the first block, get the updated header block.
912 if (fBlockOffset == 0) {
913 std::size_t headerBlockSize = kHeaderBlockSize;
914 if (headerBlockSize > fFilePos) {
915 headerBlockSize = fFilePos;
916 }
917 memcpy(fBlock, fHeaderBlock, headerBlockSize);
918 }
919
920 std::size_t retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
921 if (retval)
922 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
923
924 std::size_t lastBlockSize = fFilePos - fBlockOffset;
925 R__ASSERT(lastBlockSize <= kBlockSize);
926 if (fDirectIO) {
927 // Round up to a multiple of kBlockAlign.
928 lastBlockSize += kBlockAlign - 1;
929 lastBlockSize = (lastBlockSize / kBlockAlign) * kBlockAlign;
930 R__ASSERT(lastBlockSize <= kBlockSize);
931 }
932 retval = fwrite(fBlock, 1, lastBlockSize, fFile);
933 if (retval != lastBlockSize)
934 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
935
936 // Write the (updated) header block, unless it was part of the write above.
937 if (fBlockOffset > 0) {
938 retval = FSeek64(fFile, 0, SEEK_SET);
939 if (retval)
940 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
941
942 retval = fwrite(fHeaderBlock, 1, kHeaderBlockSize, fFile);
943 if (retval != RFileSimple::kHeaderBlockSize)
944 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
945 }
946
947 retval = fflush(fFile);
948 if (retval)
949 throw RException(R__FAIL(std::string("Flush failed: ") + strerror(errno)));
950}
951
953 std::int64_t offset)
954{
955 R__ASSERT(fFile);
956 size_t retval;
957 if ((offset >= 0) && (static_cast<std::uint64_t>(offset) != fFilePos)) {
958 fFilePos = offset;
959 }
960
961 // Keep header block to overwrite on commit.
962 if (fFilePos < kHeaderBlockSize) {
963 std::size_t headerBytes = nbytes;
964 if (fFilePos + headerBytes > kHeaderBlockSize) {
965 headerBytes = kHeaderBlockSize - fFilePos;
966 }
967 memcpy(fHeaderBlock + fFilePos, buffer, headerBytes);
968 }
969
970 R__ASSERT(fFilePos >= fBlockOffset);
971
972 while (nbytes > 0) {
973 std::uint64_t posInBlock = fFilePos % kBlockSize;
974 std::uint64_t blockOffset = fFilePos - posInBlock;
975 if (blockOffset != fBlockOffset) {
976 // Write the block.
977 retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
978 if (retval)
979 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
980
981 retval = fwrite(fBlock, 1, kBlockSize, fFile);
982 if (retval != kBlockSize)
983 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
984
985 // Null the buffer contents for good measure.
986 memset(fBlock, 0, kBlockSize);
987 }
988
989 fBlockOffset = blockOffset;
990 std::size_t blockSize = nbytes;
991 if (blockSize > kBlockSize - posInBlock) {
992 blockSize = kBlockSize - posInBlock;
993 }
994 memcpy(fBlock + posInBlock, buffer, blockSize);
995 buffer = static_cast<const unsigned char *>(buffer) + blockSize;
996 nbytes -= blockSize;
997 fFilePos += blockSize;
998 }
999}
1000
1002 const void *buffer, std::size_t nbytes, std::size_t len, std::int64_t offset, std::uint64_t directoryOffset,
1003 const std::string &className, const std::string &objectName, const std::string &title)
1004{
1005 if (offset > 0)
1006 fKeyOffset = offset;
1007 RTFString strClass{className};
1008 RTFString strObject{objectName};
1009 RTFString strTitle{title};
1010
1011 RTFKey key(fKeyOffset, directoryOffset, strClass, strObject, strTitle, len, nbytes);
1012 Write(&key, key.fKeyHeaderSize, fKeyOffset);
1013 Write(&strClass, strClass.GetSize());
1014 Write(&strObject, strObject.GetSize());
1015 Write(&strTitle, strTitle.GetSize());
1016 auto offsetData = fFilePos;
1017 // The next key starts after the data.
1018 fKeyOffset = offsetData + nbytes;
1019 if (buffer)
1020 Write(buffer, nbytes);
1021
1022 return offsetData;
1023}
1024
1025////////////////////////////////////////////////////////////////////////////////
1026
1028 std::int64_t offset)
1029{
1030 R__ASSERT(fFile);
1031 fFile->Seek(offset);
1032 bool rv = fFile->WriteBuffer((char *)(buffer), nbytes);
1033 if (rv)
1034 throw RException(R__FAIL("WriteBuffer failed."));
1035}
1036
1037std::uint64_t
1039{
1040 std::uint64_t offsetKey;
1041 RKeyBlob keyBlob(fFile);
1042 // Since it is unknown beforehand if offsetKey is beyond the 2GB limit or not,
1043 // RKeyBlob will always reserve space for a big key (version >= 1000)
1044 keyBlob.Reserve(nbytes, &offsetKey);
1045
1046 auto offset = offsetKey;
1047 RTFString strClass{kBlobClassName};
1048 RTFString strObject;
1049 RTFString strTitle;
1050 RTFKey keyHeader(offset, offset, strClass, strObject, strTitle, len, nbytes);
1051 // Follow the fact that RKeyBlob is a big key unconditionally (see above)
1052 keyHeader.MakeBigKey();
1053
1054 Write(&keyHeader, keyHeader.fKeyHeaderSize, offset);
1055 offset += keyHeader.fKeyHeaderSize;
1056 Write(&strClass, strClass.GetSize(), offset);
1057 offset += strClass.GetSize();
1058 Write(&strObject, strObject.GetSize(), offset);
1059 offset += strObject.GetSize();
1060 Write(&strTitle, strTitle.GetSize(), offset);
1061 offset += strTitle.GetSize();
1062 auto offsetData = offset;
1063 if (buffer)
1064 Write(buffer, nbytes, offset);
1065
1066 return offsetData;
1067}
1068
1069////////////////////////////////////////////////////////////////////////////////
1070
1072 : fNTupleName(name)
1073{
1074 fFileSimple.fControlBlock = std::make_unique<ROOT::Experimental::Internal::RTFileControlBlock>();
1075 fNTupleAnchor.fMaxKeySize = maxKeySize;
1076 auto infoRNTuple = RNTuple::Class()->GetStreamerInfo();
1077 fStreamerInfoMap[infoRNTuple->GetNumber()] = infoRNTuple;
1078}
1079
1081
1082std::unique_ptr<ROOT::Experimental::Internal::RNTupleFileWriter>
1083ROOT::Experimental::Internal::RNTupleFileWriter::Recreate(std::string_view ntupleName, std::string_view path,
1084 EContainerFormat containerFormat,
1085 const RNTupleWriteOptions &options)
1086{
1087 std::string fileName(path);
1088 size_t idxDirSep = fileName.find_last_of("\\/");
1089 if (idxDirSep != std::string::npos) {
1090 fileName.erase(0, idxDirSep + 1);
1091 }
1092#ifdef R__LINUX
1093 int flags = O_WRONLY | O_CREAT | O_TRUNC;
1094#ifdef O_LARGEFILE
1095 // Add the equivalent flag that is passed by fopen64.
1096 flags |= O_LARGEFILE;
1097#endif
1098 if (options.GetUseDirectIO()) {
1099 flags |= O_DIRECT;
1100 }
1101 int fd = open(std::string(path).c_str(), flags, 0666);
1102 FILE *fileStream = fdopen(fd, "wb");
1103#else
1104#ifdef R__SEEK64
1105 FILE *fileStream = fopen64(std::string(path.data(), path.size()).c_str(), "wb");
1106#else
1107 FILE *fileStream = fopen(std::string(path.data(), path.size()).c_str(), "wb");
1108#endif
1109#endif
1110 R__ASSERT(fileStream);
1111 // RNTupleFileWriter::RFileSimple does its own buffering, turn off additional buffering from C stdio.
1112 std::setvbuf(fileStream, nullptr, _IONBF, 0);
1113
1114 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, options.GetMaxKeySize()));
1115 writer->fFileSimple.fFile = fileStream;
1116 writer->fFileSimple.fDirectIO = options.GetUseDirectIO();
1117 writer->fFileName = fileName;
1118
1119 int defaultCompression = options.GetCompression();
1120 switch (containerFormat) {
1121 case EContainerFormat::kTFile: writer->WriteTFileSkeleton(defaultCompression); break;
1122 case EContainerFormat::kBare:
1123 writer->fIsBare = true;
1124 writer->WriteBareFileSkeleton(defaultCompression);
1125 break;
1126 default: R__ASSERT(false && "Internal error: unhandled container format");
1127 }
1128
1129 return writer;
1130}
1131
1132std::unique_ptr<ROOT::Experimental::Internal::RNTupleFileWriter>
1134 std::uint64_t maxKeySize)
1135{
1136 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, maxKeySize));
1137 writer->fFileProper.fFile = &file;
1138 return writer;
1139}
1140
1142 const RNTupleSerializer::StreamerInfoMap_t &streamerInfos)
1143{
1144 fStreamerInfoMap.insert(streamerInfos.cbegin(), streamerInfos.cend());
1145}
1146
1148{
1149 if (fFileProper) {
1150 // Easy case, the ROOT file header and the RNTuple streaming is taken care of by TFile
1151 fFileProper.fFile->WriteObject(&fNTupleAnchor, fNTupleName.c_str());
1152
1153 // Make sure the streamer info records used in the RNTuple are written to the file
1155 buf.SetParent(fFileProper.fFile);
1156 for (auto [_, info] : fStreamerInfoMap)
1157 buf.TagStreamerInfo(info);
1158
1159 fFileProper.fFile->Write();
1160 return;
1161 }
1162
1163 // Writing by C file stream: prepare the container format header and stream the RNTuple anchor object
1164 R__ASSERT(fFileSimple);
1165
1166 if (fIsBare) {
1167 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1168 // Compute the checksum
1169 std::uint64_t checksum = XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData());
1170 memcpy(fFileSimple.fHeaderBlock + fFileSimple.fControlBlock->fSeekNTuple, &ntupleOnDisk, ntupleOnDisk.GetSize());
1171 memcpy(fFileSimple.fHeaderBlock + fFileSimple.fControlBlock->fSeekNTuple + ntupleOnDisk.GetSize(), &checksum,
1172 sizeof(checksum));
1173 fFileSimple.Flush();
1174 return;
1175 }
1176
1177 WriteTFileNTupleKey();
1178 WriteTFileKeysList();
1179 WriteTFileStreamerInfo();
1180 WriteTFileFreeList();
1181
1182 // Update header and TFile record
1183 memcpy(fFileSimple.fHeaderBlock, &fFileSimple.fControlBlock->fHeader, fFileSimple.fControlBlock->fHeader.GetSize());
1184 R__ASSERT(fFileSimple.fControlBlock->fSeekFileRecord + fFileSimple.fControlBlock->fFileRecord.GetSize() <
1185 RFileSimple::kHeaderBlockSize);
1186 memcpy(fFileSimple.fHeaderBlock + fFileSimple.fControlBlock->fSeekFileRecord,
1187 &fFileSimple.fControlBlock->fFileRecord, fFileSimple.fControlBlock->fFileRecord.GetSize());
1188
1189 fFileSimple.Flush();
1190}
1191
1192std::uint64_t ROOT::Experimental::Internal::RNTupleFileWriter::WriteBlob(const void *data, size_t nbytes, size_t len)
1193{
1194 auto writeKey = [this](const void *payload, size_t nBytes, size_t length) {
1195 std::uint64_t offset;
1196 if (fFileSimple) {
1197 if (fIsBare) {
1198 offset = fFileSimple.fKeyOffset;
1199 fFileSimple.Write(payload, nBytes);
1200 fFileSimple.fKeyOffset += nBytes;
1201 } else {
1202 offset = fFileSimple.WriteKey(payload, nBytes, length, -1, 100, kBlobClassName);
1203 }
1204 } else {
1205 offset = fFileProper.WriteKey(payload, nBytes, length);
1206 }
1207 return offset;
1208 };
1209
1210 const std::uint64_t maxKeySize = fNTupleAnchor.fMaxKeySize;
1211 R__ASSERT(maxKeySize > 0);
1212
1213 if (nbytes <= maxKeySize) {
1214 // Fast path: only write 1 key.
1215 return writeKey(data, nbytes, len);
1216 }
1217
1218 /**
1219 * Writing a key bigger than the max allowed size. In this case we split the payload
1220 * into multiple keys, reserving the end of the first key payload for pointers to the
1221 * next ones. E.g. if a key needs to be split into 3 chunks, the first chunk will have
1222 * the format:
1223 * +--------------------+
1224 * | |
1225 * | Data |
1226 * |--------------------|
1227 * | pointer to chunk 2 |
1228 * | pointer to chunk 3 |
1229 * +--------------------+
1230 */
1231 const size_t nChunks = ComputeNumChunks(nbytes, maxKeySize);
1232 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
1233 const size_t nbytesFirstChunk = maxKeySize - nbytesChunkOffsets;
1234 // Skip writing the first chunk, it will be written last (in the file) below.
1235
1236 const uint8_t *chunkData = reinterpret_cast<const uint8_t *>(data) + nbytesFirstChunk;
1237 size_t remainingBytes = nbytes - nbytesFirstChunk;
1238
1239 const auto chunkOffsetsToWrite = std::make_unique<std::uint64_t[]>(nChunks - 1);
1240 std::uint64_t chunkOffsetIdx = 0;
1241
1242 do {
1243 const size_t bytesNextChunk = std::min<size_t>(remainingBytes, maxKeySize);
1244 const std::uint64_t offset = writeKey(chunkData, bytesNextChunk, bytesNextChunk);
1245
1246 RNTupleSerializer::SerializeUInt64(offset, &chunkOffsetsToWrite[chunkOffsetIdx]);
1247 ++chunkOffsetIdx;
1248
1249 remainingBytes -= bytesNextChunk;
1250 chunkData += bytesNextChunk;
1251
1252 } while (remainingBytes > 0);
1253
1254 // Write the first key, with part of the data and the pointers to (logically) following keys appended.
1255 const std::uint64_t firstOffset = ReserveBlob(maxKeySize, maxKeySize);
1256 WriteIntoReservedBlob(data, nbytesFirstChunk, firstOffset);
1257 const std::uint64_t chunkOffsetsOffset = firstOffset + nbytesFirstChunk;
1258 WriteIntoReservedBlob(chunkOffsetsToWrite.get(), nbytesChunkOffsets, chunkOffsetsOffset);
1259
1260 return firstOffset;
1261}
1262
1264{
1265 // ReserveBlob cannot be used to reserve a multi-key blob
1266 R__ASSERT(nbytes <= fNTupleAnchor.GetMaxKeySize());
1267
1268 std::uint64_t offset;
1269 if (fFileSimple) {
1270 if (fIsBare) {
1271 offset = fFileSimple.fKeyOffset;
1272 fFileSimple.fKeyOffset += nbytes;
1273 } else {
1274 offset = fFileSimple.WriteKey(/*buffer=*/nullptr, nbytes, len, -1, 100, kBlobClassName);
1275 }
1276 } else {
1277 offset = fFileProper.WriteKey(/*buffer=*/nullptr, nbytes, len);
1278 }
1279 return offset;
1280}
1281
1283 std::int64_t offset)
1284{
1285 if (fFileSimple) {
1286 fFileSimple.Write(buffer, nbytes, offset);
1287 } else {
1288 fFileProper.Write(buffer, nbytes, offset);
1289 }
1290}
1291
1292std::uint64_t
1294{
1295 auto offset = WriteBlob(data, nbytes, lenHeader);
1296 fNTupleAnchor.fLenHeader = lenHeader;
1297 fNTupleAnchor.fNBytesHeader = nbytes;
1298 fNTupleAnchor.fSeekHeader = offset;
1299 return offset;
1300}
1301
1302std::uint64_t
1304{
1305 auto offset = WriteBlob(data, nbytes, lenFooter);
1306 fNTupleAnchor.fLenFooter = lenFooter;
1307 fNTupleAnchor.fNBytesFooter = nbytes;
1308 fNTupleAnchor.fSeekFooter = offset;
1309 return offset;
1310}
1311
1313{
1314 RBareFileHeader bareHeader;
1315 bareHeader.fCompress = defaultCompression;
1316 fFileSimple.Write(&bareHeader, sizeof(bareHeader), 0);
1317 RTFString ntupleName{fNTupleName};
1318 fFileSimple.Write(&ntupleName, ntupleName.GetSize());
1319
1320 // Write zero-initialized ntuple to reserve the space; will be overwritten on commit
1321 RTFNTuple ntupleOnDisk;
1322 fFileSimple.fControlBlock->fSeekNTuple = fFileSimple.fFilePos;
1323 fFileSimple.Write(&ntupleOnDisk, ntupleOnDisk.GetSize());
1324 std::uint64_t checksum = 0;
1325 fFileSimple.Write(&checksum, sizeof(checksum));
1326 fFileSimple.fKeyOffset = fFileSimple.fFilePos;
1327}
1328
1330{
1331 // The streamer info record is a TList of TStreamerInfo object. We cannot use
1332 // RNTupleSerializer::SerializeStreamerInfos because that uses TBufferIO::WriteObject.
1333 // This would prepend the streamed TList with self-decription information.
1334 // The streamer info record is just the streamed TList.
1335
1336 TList streamerInfoList;
1337 for (auto [_, info] : fStreamerInfoMap) {
1338 streamerInfoList.Add(info);
1339 }
1340
1341 // We will stream the list with a TBufferFile. When reading the streamer info records back,
1342 // the read buffer includes the key and the streamed list. Therefore, we need to start streaming
1343 // with an offset of the key length. Otherwise, the offset for referencing duplicate objects in the
1344 // buffer will point to the wrong places.
1345
1346 // Figure out key length
1347 RTFString strTList{"TList"};
1348 RTFString strStreamerInfo{"StreamerInfo"};
1349 RTFString strStreamerTitle{"Doubly linked list"};
1350 fFileSimple.fControlBlock->fHeader.SetSeekInfo(fFileSimple.fKeyOffset);
1351 auto keyLen =
1352 RTFKey(fFileSimple.fControlBlock->fHeader.GetSeekInfo(), 100, strTList, strStreamerInfo, strStreamerTitle, 0)
1353 .fKeyLen;
1354
1355 TBufferFile buffer(TBuffer::kWrite, keyLen + 1);
1356 buffer.SetBufferOffset(keyLen);
1357 streamerInfoList.Streamer(buffer);
1358 assert(buffer.Length() > keyLen);
1359 const auto bufPayload = buffer.Buffer() + keyLen;
1360 const auto lenPayload = buffer.Length() - keyLen;
1361
1362 RNTupleCompressor compressor;
1363 auto zipStreamerInfos = std::make_unique<unsigned char[]>(lenPayload);
1364 auto szZipStreamerInfos = compressor.Zip(bufPayload, lenPayload, 1, zipStreamerInfos.get());
1365
1366 fFileSimple.WriteKey(zipStreamerInfos.get(), szZipStreamerInfos, lenPayload,
1367 fFileSimple.fControlBlock->fHeader.GetSeekInfo(), 100, "TList", "StreamerInfo",
1368 "Doubly linked list");
1369 fFileSimple.fControlBlock->fHeader.SetNbytesInfo(fFileSimple.fFilePos -
1370 fFileSimple.fControlBlock->fHeader.GetSeekInfo());
1371}
1372
1374{
1375 RTFString strEmpty;
1376 RTFString strRNTupleClass{"ROOT::Experimental::RNTuple"};
1377 RTFString strRNTupleName{fNTupleName};
1378 RTFString strFileName{fFileName};
1379
1380 RTFKey keyRNTuple(fFileSimple.fControlBlock->fSeekNTuple, 100, strRNTupleClass, strRNTupleName, strEmpty,
1381 RTFNTuple::GetSizePlusChecksum());
1382
1383 fFileSimple.fControlBlock->fFileRecord.SetSeekKeys(fFileSimple.fKeyOffset);
1384 RTFKeyList keyList{1};
1385 RTFKey keyKeyList(fFileSimple.fControlBlock->fFileRecord.GetSeekKeys(), 100, strEmpty, strFileName, strEmpty,
1386 keyList.GetSize() + keyRNTuple.fKeyLen);
1387 fFileSimple.Write(&keyKeyList, keyKeyList.fKeyHeaderSize, fFileSimple.fControlBlock->fFileRecord.GetSeekKeys());
1388 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1389 fFileSimple.Write(&strFileName, strFileName.GetSize());
1390 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1391 fFileSimple.Write(&keyList, keyList.GetSize());
1392 fFileSimple.Write(&keyRNTuple, keyRNTuple.fKeyHeaderSize);
1393 // Write class name, object name, and title for this key.
1394 fFileSimple.Write(&strRNTupleClass, strRNTupleClass.GetSize());
1395 fFileSimple.Write(&strRNTupleName, strRNTupleName.GetSize());
1396 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1397 fFileSimple.fControlBlock->fFileRecord.fNBytesKeys =
1398 fFileSimple.fFilePos - fFileSimple.fControlBlock->fFileRecord.GetSeekKeys();
1399 fFileSimple.fKeyOffset = fFileSimple.fFilePos;
1400}
1401
1403{
1404 fFileSimple.fControlBlock->fHeader.SetSeekFree(fFileSimple.fKeyOffset);
1405 RTFString strEmpty;
1406 RTFString strFileName{fFileName};
1407 RTFFreeEntry freeEntry;
1408 RTFKey keyFreeList(fFileSimple.fControlBlock->fHeader.GetSeekFree(), 100, strEmpty, strFileName, strEmpty,
1409 freeEntry.GetSize());
1410 std::uint64_t firstFree = fFileSimple.fControlBlock->fHeader.GetSeekFree() + keyFreeList.GetSize();
1411 freeEntry.Set(firstFree, std::max(2000000000ULL, ((firstFree / 1000000000ULL) + 1) * 1000000000ULL));
1412 fFileSimple.WriteKey(&freeEntry, freeEntry.GetSize(), freeEntry.GetSize(),
1413 fFileSimple.fControlBlock->fHeader.GetSeekFree(), 100, "", fFileName, "");
1414 fFileSimple.fControlBlock->fHeader.SetNbytesFree(fFileSimple.fFilePos -
1415 fFileSimple.fControlBlock->fHeader.GetSeekFree());
1416 fFileSimple.fControlBlock->fHeader.SetEnd(fFileSimple.fFilePos);
1417}
1418
1420{
1421 RTFString strRNTupleClass{"ROOT::Experimental::RNTuple"};
1422 RTFString strRNTupleName{fNTupleName};
1423 RTFString strEmpty;
1424
1425 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1426 RUInt64BE checksum{XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData())};
1427 fFileSimple.fControlBlock->fSeekNTuple = fFileSimple.fKeyOffset;
1428
1429 char keyBuf[RTFNTuple::GetSizePlusChecksum()];
1430
1431 // concatenate the RNTuple anchor with its checksum
1432 memcpy(keyBuf, &ntupleOnDisk, sizeof(RTFNTuple));
1433 memcpy(keyBuf + sizeof(RTFNTuple), &checksum, sizeof(checksum));
1434
1435 fFileSimple.WriteKey(keyBuf, sizeof(keyBuf), sizeof(keyBuf), fFileSimple.fControlBlock->fSeekNTuple, 100,
1436 "ROOT::Experimental::RNTuple", fNTupleName, "");
1437}
1438
1440{
1441 RTFString strTFile{"TFile"};
1442 RTFString strFileName{fFileName};
1443 RTFString strEmpty;
1444
1445 fFileSimple.fControlBlock->fHeader = RTFHeader(defaultCompression);
1446
1447 RTFUUID uuid;
1448
1449 // First record of the file: the TFile object at offset 100
1450 RTFKey keyRoot(100, 0, strTFile, strFileName, strEmpty,
1451 sizeof(RTFFile) + strFileName.GetSize() + strEmpty.GetSize() + uuid.GetSize());
1452 std::uint32_t nbytesName = keyRoot.fKeyLen + strFileName.GetSize() + 1;
1453 fFileSimple.fControlBlock->fFileRecord.fNBytesName = nbytesName;
1454 fFileSimple.fControlBlock->fHeader.SetNbytesName(nbytesName);
1455
1456 fFileSimple.Write(&keyRoot, keyRoot.fKeyHeaderSize, 100);
1457 // Write class name, object name, and title for the TFile key.
1458 fFileSimple.Write(&strTFile, strTFile.GetSize());
1459 fFileSimple.Write(&strFileName, strFileName.GetSize());
1460 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1461 // Write the name and title of the TNamed preceding the TFile entry.
1462 fFileSimple.Write(&strFileName, strFileName.GetSize());
1463 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1464 // Will be overwritten on commit
1465 fFileSimple.fControlBlock->fSeekFileRecord = fFileSimple.fFilePos;
1466 fFileSimple.Write(&fFileSimple.fControlBlock->fFileRecord, fFileSimple.fControlBlock->fFileRecord.GetSize());
1467 fFileSimple.Write(&uuid, uuid.GetSize());
1468
1469 // Padding bytes to allow the TFile record to grow for a big file
1470 RUInt32BE padding{0};
1471 for (int i = 0; i < 3; ++i)
1472 fFileSimple.Write(&padding, sizeof(padding));
1473 fFileSimple.fKeyOffset = fFileSimple.fFilePos;
1474}
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
Definition RError.hxx:290
static size_t ComputeNumChunks(size_t nbytes, size_t maxChunkSize)
#define ROOT_VERSION_CODE
Definition RVersion.hxx:24
#define ClassDefInlineOverride(name, id)
Definition Rtypes.h:353
#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 data
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 offset
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 length
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t 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 UChar_t len
char name[80]
Definition TGX11.cxx:110
Binding & operator=(OUT(*fun)(void))
void ReadBuffer(char *&buffer) override
T1 fFirst
Definition X11Events.mm:86
#define _(A, B)
Definition cfortran.h:108
The RKeyBlob writes an invisible key into a TFile.
void Reserve(size_t nbytes, std::uint64_t *seekKey)
Register a new key for a data record of size nbytes.
RResult< RNTuple > GetNTuple(std::string_view ntupleName)
Extracts header and footer location for the RNTuple identified by ntupleName.
RResult< RNTuple > GetNTupleProper(std::string_view ntupleName)
Used when the file turns out to be a TFile container.
RNTuple CreateAnchor(std::uint16_t versionEpoch, std::uint16_t versionMajor, std::uint16_t versionMinor, std::uint16_t versionPatch, std::uint64_t seekHeader, std::uint64_t nbytesHeader, std::uint64_t lenHeader, std::uint64_t seekFooter, std::uint64_t nbytesFooter, std::uint64_t lenFooter, std::uint64_t maxKeySize)
RResult< RNTuple > GetNTupleBare(std::string_view ntupleName)
Used when the file container turns out to be a bare file.
void ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
Reads a given byte range from the file into the provided memory buffer.
Helper class to compress data blocks in the ROOT compression frame format.
size_t Zip(const void *from, size_t nbytes, int compression, Writer_t fnWriter)
Returns the size of the compressed data.
Helper class to uncompress data blocks in the ROOT compression frame format.
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.
Write RNTuple data blocks in a TFile or a bare file container.
RNTupleFileWriter(std::string_view name, std::uint64_t maxKeySize)
std::uint64_t WriteBlob(const void *data, size_t nbytes, size_t len)
Writes a new record as an RBlob key into the file.
std::uint64_t WriteNTupleFooter(const void *data, size_t nbytes, size_t lenFooter)
Writes the compressed footer and registeres its location; lenFooter is the size of the uncompressed f...
static std::unique_ptr< RNTupleFileWriter > Append(std::string_view ntupleName, TFile &file, std::uint64_t maxKeySize)
Add a new RNTuple identified by ntupleName to the existing TFile.
void WriteTFileKeysList()
Write the TList with the RNTuple key.
void UpdateStreamerInfos(const RNTupleSerializer::StreamerInfoMap_t &streamerInfos)
Ensures that the pass streamer info is written to the file.
void Commit()
Writes the RNTuple key to the file so that the header and footer keys can be found.
std::uint64_t ReserveBlob(size_t nbytes, size_t len)
Reserves a new record as an RBlob key in the file.
RFileSimple fFileSimple
For simple use cases, survives without libRIO dependency.
void WriteTFileNTupleKey()
The only key that will be visible in file->ls()
void WriteTFileFreeList()
Last record in the file.
EContainerFormat
For testing purposes, RNTuple data can be written into a bare file container instead of a ROOT file.
static std::unique_ptr< RNTupleFileWriter > Recreate(std::string_view ntupleName, std::string_view path, EContainerFormat containerFormat, const RNTupleWriteOptions &options)
Create or truncate the local file given by path with the new empty RNTuple identified by ntupleName.
void WriteTFileSkeleton(int defaultCompression)
For a TFile container written by a C file stream, write the header and TFile object.
void WriteBareFileSkeleton(int defaultCompression)
For a bare file, which is necessarily written by a C file stream, write file header.
void WriteTFileStreamerInfo()
Write the compressed streamer info record with the description of the RNTuple class.
std::uint64_t WriteNTupleHeader(const void *data, size_t nbytes, size_t lenHeader)
Writes the compressed header and registeres its location; lenHeader is the size of the uncompressed h...
RNTuple fNTupleAnchor
Header and footer location of the ntuple, written on Commit()
void WriteIntoReservedBlob(const void *buffer, size_t nbytes, std::int64_t offset)
Write into a reserved record; the caller is responsible for making sure that the written byte range i...
RNTupleSerializer::StreamerInfoMap_t fStreamerInfoMap
Set of streamer info records that should be written to the file.
static std::uint32_t SerializeUInt64(std::uint64_t val, void *buffer)
std::map< Int_t, TVirtualStreamerInfo * > StreamerInfoMap_t
static std::uint32_t DeserializeUInt64(const void *buffer, std::uint64_t &val)
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Common user-tunable settings for storing ntuples.
Representation of an RNTuple data set in a ROOT file.
Definition RNTuple.hxx:61
std::uint16_t fVersionMajor
Changing the major version indicates forward incompatible changes; such changes should correspond to ...
Definition RNTuple.hxx:79
std::uint64_t fSeekFooter
The file offset of the footer excluding the TKey part.
Definition RNTuple.hxx:91
std::uint64_t GetLenFooter() const
Definition RNTuple.hxx:116
std::uint64_t fMaxKeySize
The maximum size for a TKey payload. Payloads bigger than this size will be written as multiple blobs...
Definition RNTuple.hxx:97
std::uint64_t GetNBytesHeader() const
Definition RNTuple.hxx:111
std::uint64_t GetLenHeader() const
Definition RNTuple.hxx:112
std::uint16_t fVersionMinor
Changing the minor version indicates new optional fields added to the RNTuple meta-data.
Definition RNTuple.hxx:81
std::uint16_t fVersionEpoch
Version of the RNTuple binary format that the writer supports (see specification).
Definition RNTuple.hxx:75
std::uint16_t GetVersionEpoch() const
Definition RNTuple.hxx:105
std::uint64_t fNBytesFooter
The size of the compressed ntuple footer.
Definition RNTuple.hxx:93
std::uint64_t fLenFooter
The size of the uncompressed ntuple footer.
Definition RNTuple.hxx:95
std::uint64_t GetNBytesFooter() const
Definition RNTuple.hxx:115
std::uint64_t GetMaxKeySize() const
Definition RNTuple.hxx:117
std::uint64_t GetSeekHeader() const
Definition RNTuple.hxx:110
std::uint64_t fLenHeader
The size of the uncompressed ntuple header.
Definition RNTuple.hxx:89
std::uint64_t fNBytesHeader
The size of the compressed ntuple header.
Definition RNTuple.hxx:87
std::uint16_t GetVersionMinor() const
Definition RNTuple.hxx:107
std::uint16_t fVersionPatch
Changing the patch version indicates new backported features from newer binary format versions.
Definition RNTuple.hxx:83
std::uint16_t GetVersionMajor() const
Definition RNTuple.hxx:106
static TClass * Class()
std::uint16_t GetVersionPatch() const
Definition RNTuple.hxx:108
std::uint64_t GetSeekFooter() const
Definition RNTuple.hxx:114
std::uint64_t fSeekHeader
The file offset of the header excluding the TKey part.
Definition RNTuple.hxx:85
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
Definition RError.hxx:194
The RRawFile provides read-only access to local and remote files.
Definition RRawFile.hxx:43
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
Definition TBufferFile.h:47
void TagStreamerInfo(TVirtualStreamerInfo *info) override
Mark the classindex of the current file as using this TStreamerInfo.
void SetParent(TObject *parent)
Set parent owning this buffer.
Definition TBuffer.cxx:270
@ kWrite
Definition TBuffer.h:73
void SetBufferOffset(Int_t offset=0)
Definition TBuffer.h:93
Int_t Length() const
Definition TBuffer.h:100
char * Buffer() const
Definition TBuffer.h:96
TVirtualStreamerInfo * GetStreamerInfo(Int_t version=0, Bool_t isTransient=kFALSE) const
returns a pointer to the TVirtualStreamerInfo object for version If the object does not exist,...
Definition TClass.cxx:4599
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
Definition TFile.h:53
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
Int_t Sizeof() const override
Return the size in bytes of the key header structure.
Definition TKey.cxx:1342
Int_t fVersion
Key version identifier.
Definition TKey.h:39
Short_t fKeylen
Number of bytes for the key itself.
Definition TKey.h:43
Long64_t fSeekKey
Location of object on file.
Definition TKey.h:45
virtual void Create(Int_t nbytes, TFile *f=nullptr)
Create a TKey object of specified size.
Definition TKey.cxx:460
TString fClassName
Object Class name.
Definition TKey.h:47
A doubly linked list.
Definition TList.h:38
void Streamer(TBuffer &) override
Stream all objects in the collection to or from the I/O buffer.
Definition TList.cxx:1189
void Add(TObject *obj) override
Definition TList.h:83
#define Swap(a, b)
Definition geom.c:201
tbb::task_arena is an alias of tbb::interface7::task_arena, which doesn't allow to forward declare tb...
Helper templated class for swapping bytes; specializations for N={2,4,8} are provided below.
Definition Byteswap.h:124
void Write(const void *buffer, size_t nbytes, std::int64_t offset)
Low-level writing using a TFile.
std::uint64_t WriteKey(const void *buffer, size_t nbytes, size_t len)
Writes an RBlob opaque key with the provided buffer as data record and returns the offset of the reco...
std::unique_ptr< ROOT::Experimental::Internal::RTFileControlBlock > fControlBlock
Keeps track of TFile control structures, which need to be updated on committing the data set.
void Write(const void *buffer, size_t nbytes, std::int64_t offset=-1)
Writes bytes in the open stream, either at fFilePos or at the given offset.
std::uint64_t WriteKey(const void *buffer, std::size_t nbytes, std::size_t len, std::int64_t offset=-1, std::uint64_t directoryOffset=100, const std::string &className="", const std::string &objectName="", const std::string &title="")
Writes a TKey including the data record, given by buffer, into fFile; returns the file offset to the ...
If a TFile container is written by a C stream (simple file), on dataset commit, the file header and t...
auto * tt
Definition textangle.C:16