Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RMiniFile.cxx
Go to the documentation of this file.
1/// \file RMiniFile.cxx
2/// \author Jakob Blomer <jblomer@cern.ch>
3/// \date 2019-12-22
4
5/*************************************************************************
6 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
7 * All rights reserved. *
8 * *
9 * For the licensing terms see $ROOTSYS/LICENSE. *
10 * For the list of contributors see $ROOTSYS/README/CREDITS. *
11 *************************************************************************/
12
13#include "Rtypes.h"
14#include <ROOT/RConfig.hxx>
15#include <ROOT/RError.hxx>
16#include <ROOT/RMiniFile.hxx>
17#include <ROOT/RRawFile.hxx>
18#include <ROOT/RNTupleUtils.hxx>
19#include <ROOT/RNTupleZip.hxx>
22#include <ROOT/RFile.hxx>
23
24#include <Byteswap.h>
25#include <TBufferFile.h>
26#include <TDirectory.h>
27#include <TError.h>
28#include <TFile.h>
29#include <TKey.h>
30#include <TObjString.h>
31#include <TUUID.h>
32#include <TStreamerInfo.h>
33
34#include <xxhash.h>
35
36#include <algorithm>
37#include <cassert>
38#include <cerrno>
39#include <cstdio>
40#include <cstring>
41#include <memory>
42#include <string>
43#include <chrono>
44
45#ifdef R__LINUX
46#include <fcntl.h>
47#endif
48
49#ifndef R__LITTLE_ENDIAN
50#ifdef R__BYTESWAP
51// `R__BYTESWAP` is defined in RConfig.hxx for little-endian architectures; undefined otherwise
52#define R__LITTLE_ENDIAN 1
53#else
54#define R__LITTLE_ENDIAN 0
55#endif
56#endif /* R__LITTLE_ENDIAN */
57
61
62namespace {
63
64// The following types are used to read and write the TFile binary format
65
66/// Big-endian 16-bit unsigned integer
67class RUInt16BE {
68private:
69 std::uint16_t fValBE = 0;
70 static std::uint16_t Swap(std::uint16_t val)
71 {
72#if R__LITTLE_ENDIAN == 1
73 return RByteSwap<sizeof(val)>::bswap(val);
74#else
75 return val;
76#endif
77 }
78
79public:
80 RUInt16BE() = default;
81 explicit RUInt16BE(const std::uint16_t val) : fValBE(Swap(val)) {}
82 operator std::uint16_t() const { return Swap(fValBE); }
83 RUInt16BE &operator=(const std::uint16_t val)
84 {
85 fValBE = Swap(val);
86 return *this;
87 }
88};
89
90/// Big-endian 32-bit unsigned integer
91class RUInt32BE {
92private:
93 std::uint32_t fValBE = 0;
94 static std::uint32_t Swap(std::uint32_t val)
95 {
96#if R__LITTLE_ENDIAN == 1
97 return RByteSwap<sizeof(val)>::bswap(val);
98#else
99 return val;
100#endif
101 }
102
103public:
104 RUInt32BE() = default;
105 explicit RUInt32BE(const std::uint32_t val) : fValBE(Swap(val)) {}
106 operator std::uint32_t() const { return Swap(fValBE); }
107 RUInt32BE &operator=(const std::uint32_t val)
108 {
109 fValBE = Swap(val);
110 return *this;
111 }
112};
113
114/// Big-endian 32-bit signed integer
115class RInt32BE {
116private:
117 std::int32_t fValBE = 0;
118 static std::int32_t Swap(std::int32_t val)
119 {
120#if R__LITTLE_ENDIAN == 1
121 return RByteSwap<sizeof(val)>::bswap(val);
122#else
123 return val;
124#endif
125 }
126
127public:
128 RInt32BE() = default;
129 explicit RInt32BE(const std::int32_t val) : fValBE(Swap(val)) {}
130 operator std::int32_t() const { return Swap(fValBE); }
131 RInt32BE &operator=(const std::int32_t val)
132 {
133 fValBE = Swap(val);
134 return *this;
135 }
136};
137
138/// Big-endian 64-bit unsigned integer
139class RUInt64BE {
140private:
141 std::uint64_t fValBE = 0;
142 static std::uint64_t Swap(std::uint64_t val)
143 {
144#if R__LITTLE_ENDIAN == 1
145 return RByteSwap<sizeof(val)>::bswap(val);
146#else
147 return val;
148#endif
149 }
150
151public:
152 RUInt64BE() = default;
153 explicit RUInt64BE(const std::uint64_t val) : fValBE(Swap(val)) {}
154 operator std::uint64_t() const { return Swap(fValBE); }
155 RUInt64BE &operator=(const std::uint64_t val)
156 {
157 fValBE = Swap(val);
158 return *this;
159 }
160};
161
162#pragma pack(push, 1)
163/// A name (type, identifier, ...) in the TFile binary format
164struct RTFString {
165 unsigned char fLName{0};
166 char fData[255];
167 RTFString() = default;
168 RTFString(const std::string &str)
169 {
170 // The length of strings with 255 characters and longer are encoded with a 32-bit integer following the first
171 // byte. This is currently not handled.
172 R__ASSERT(str.length() < 255);
173 fLName = static_cast<unsigned char>(str.length());
174 memcpy(fData, str.data(), fLName);
175 }
176 std::size_t GetSize() const
177 {
178 // A length of 255 is special and means that the first byte is followed by a 32-bit integer with the actual
179 // length.
180 R__ASSERT(fLName != 255);
181 return 1 + fLName;
182 }
183};
184
185/// The timestamp format used in TFile; the default constructor initializes with the current time
186struct RTFDatetime {
187 RUInt32BE fDatetime;
188 RTFDatetime()
189 {
190 auto now = std::chrono::system_clock::now();
191 auto tt = std::chrono::system_clock::to_time_t(now);
192 auto tm = *localtime(&tt);
193 fDatetime = (tm.tm_year + 1900 - 1995) << 26 | (tm.tm_mon + 1) << 22 | tm.tm_mday << 17 | tm.tm_hour << 12 |
194 tm.tm_min << 6 | tm.tm_sec;
195 }
196 explicit RTFDatetime(RUInt32BE val) : fDatetime(val) {}
197};
198
199/// The key part of a TFile record excluding the class, object, and title names
200struct RTFKey {
201 static constexpr unsigned kBigKeyVersion = 1000;
202
203 RInt32BE fNbytes{0};
204 RUInt16BE fVersion{4};
205 RUInt32BE fObjLen{0};
206 RTFDatetime fDatetime;
207 RUInt16BE fKeyLen{0};
208 RUInt16BE fCycle{1};
209 union {
210 struct {
211 RUInt32BE fSeekKey{0};
212 RUInt32BE fSeekPdir{0};
213 } fInfoShort;
214 struct {
215 RUInt64BE fSeekKey{0};
216 RUInt64BE fSeekPdir{0};
217 } fInfoLong;
218 };
219
220 RTFKey() : fInfoLong() {}
221 RTFKey(std::uint64_t seekKey, std::uint64_t seekPdir, const RTFString &clName, const RTFString &objName,
222 const RTFString &titleName, std::size_t szObjInMem, std::size_t szObjOnDisk = 0)
223 {
224 R__ASSERT(szObjInMem <= std::numeric_limits<std::uint32_t>::max());
225 R__ASSERT(szObjOnDisk <= std::numeric_limits<std::uint32_t>::max());
226 // For writing, we always produce "big" keys with 64-bit SeekKey and SeekPdir.
227 fVersion = fVersion + kBigKeyVersion;
228 fObjLen = szObjInMem;
229 fKeyLen = static_cast<RUInt16BE>(GetHeaderSize() + clName.GetSize() + objName.GetSize() + titleName.GetSize());
230 fInfoLong.fSeekKey = seekKey;
231 fInfoLong.fSeekPdir = seekPdir;
232 // Depends on fKeyLen being set
233 fNbytes = fKeyLen + ((szObjOnDisk == 0) ? szObjInMem : szObjOnDisk);
234 }
235
236 std::uint32_t GetSize() const
237 {
238 // Negative size indicates a gap in the file
239 if (fNbytes < 0)
240 return -fNbytes;
241 return fNbytes;
242 }
243
244 std::uint32_t GetHeaderSize() const
245 {
246 if (fVersion >= kBigKeyVersion)
247 return 18 + sizeof(fInfoLong);
248 return 18 + sizeof(fInfoShort);
249 }
250
251 std::uint64_t GetSeekKey() const
252 {
253 if (fVersion >= kBigKeyVersion)
254 return fInfoLong.fSeekKey;
255 return fInfoShort.fSeekKey;
256 }
257};
258
259/// The TFile global header
260struct RTFHeader {
261 static constexpr unsigned kBEGIN = 100;
262 static constexpr unsigned kBigHeaderVersion = 1000000;
263
264 char fMagic[4]{'r', 'o', 'o', 't'};
265 RUInt32BE fVersion{(ROOT_VERSION_CODE >> 16) * 10000 + ((ROOT_VERSION_CODE & 0xFF00) >> 8) * 100 +
266 (ROOT_VERSION_CODE & 0xFF)};
267 RUInt32BE fBEGIN{kBEGIN};
268 union {
269 struct {
270 RUInt32BE fEND{0};
271 RUInt32BE fSeekFree{0};
272 RUInt32BE fNbytesFree{0};
273 RUInt32BE fNfree{1};
274 RUInt32BE fNbytesName{0};
275 unsigned char fUnits{4};
276 RUInt32BE fCompress{0};
277 RUInt32BE fSeekInfo{0};
278 RUInt32BE fNbytesInfo{0};
279 } fInfoShort;
280 struct {
281 RUInt64BE fEND{0};
282 RUInt64BE fSeekFree{0};
283 RUInt32BE fNbytesFree{0};
284 RUInt32BE fNfree{1};
285 RUInt32BE fNbytesName{0};
286 unsigned char fUnits{8};
287 RUInt32BE fCompress{0};
288 RUInt64BE fSeekInfo{0};
289 RUInt32BE fNbytesInfo{0};
290 } fInfoLong;
291 };
292
293 RTFHeader() : fInfoShort() {}
294 RTFHeader(int compression) : fInfoShort() { fInfoShort.fCompress = compression; }
295
296 void SetBigFile()
297 {
298 if (fVersion >= kBigHeaderVersion)
299 return;
300
301 // clang-format off
302 std::uint32_t end = fInfoShort.fEND;
303 std::uint32_t seekFree = fInfoShort.fSeekFree;
304 std::uint32_t nbytesFree = fInfoShort.fNbytesFree;
305 std::uint32_t nFree = fInfoShort.fNfree;
306 std::uint32_t nbytesName = fInfoShort.fNbytesName;
307 std::uint32_t compress = fInfoShort.fCompress;
308 std::uint32_t seekInfo = fInfoShort.fSeekInfo;
309 std::uint32_t nbytesInfo = fInfoShort.fNbytesInfo;
310 fInfoLong.fEND = end;
311 fInfoLong.fSeekFree = seekFree;
312 fInfoLong.fNbytesFree = nbytesFree;
313 fInfoLong.fNfree = nFree;
314 fInfoLong.fNbytesName = nbytesName;
315 fInfoLong.fUnits = 8;
316 fInfoLong.fCompress = compress;
317 fInfoLong.fSeekInfo = seekInfo;
318 fInfoLong.fNbytesInfo = nbytesInfo;
319 fVersion = fVersion + kBigHeaderVersion;
320 // clang-format on
321 }
322
323 bool IsBigFile(std::uint64_t offset = 0) const
324 {
325 return (fVersion >= kBigHeaderVersion) ||
326 (offset > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max()));
327 }
328
329 std::uint32_t GetSize() const
330 {
331 std::uint32_t sizeHead = sizeof(fMagic) + sizeof(fVersion) + sizeof(fBEGIN);
332 if (IsBigFile())
333 return sizeHead + sizeof(fInfoLong);
334 return sizeHead + sizeof(fInfoShort);
335 }
336
337 std::uint64_t GetEnd() const
338 {
339 if (IsBigFile())
340 return fInfoLong.fEND;
341 return fInfoShort.fEND;
342 }
343
344 void SetEnd(std::uint64_t value)
345 {
346 if (IsBigFile(value)) {
347 SetBigFile();
348 fInfoLong.fEND = value;
349 } else {
350 fInfoShort.fEND = value;
351 }
352 }
353
354 std::uint64_t GetSeekFree() const
355 {
356 if (IsBigFile())
357 return fInfoLong.fSeekFree;
358 return fInfoShort.fSeekFree;
359 }
360
361 void SetSeekFree(std::uint64_t value)
362 {
363 if (IsBigFile(value)) {
364 SetBigFile();
365 fInfoLong.fSeekFree = value;
366 } else {
367 fInfoShort.fSeekFree = value;
368 }
369 }
370
371 void SetNbytesFree(std::uint32_t value)
372 {
373 if (IsBigFile()) {
374 fInfoLong.fNbytesFree = value;
375 } else {
376 fInfoShort.fNbytesFree = value;
377 }
378 }
379
380 void SetNbytesName(std::uint32_t value)
381 {
382 if (IsBigFile()) {
383 fInfoLong.fNbytesName = value;
384 } else {
385 fInfoShort.fNbytesName = value;
386 }
387 }
388
389 std::uint64_t GetSeekInfo() const
390 {
391 if (IsBigFile())
392 return fInfoLong.fSeekInfo;
393 return fInfoShort.fSeekInfo;
394 }
395
396 void SetSeekInfo(std::uint64_t value)
397 {
398 if (IsBigFile(value)) {
399 SetBigFile();
400 fInfoLong.fSeekInfo = value;
401 } else {
402 fInfoShort.fSeekInfo = value;
403 }
404 }
405
406 std::uint64_t GetNbytesInfo() const
407 {
408 if (IsBigFile())
409 return fInfoLong.fNbytesInfo;
410 return fInfoShort.fNbytesInfo;
411 }
412
413 void SetNbytesInfo(std::uint32_t value)
414 {
415 if (IsBigFile()) {
416 fInfoLong.fNbytesInfo = value;
417 } else {
418 fInfoShort.fNbytesInfo = value;
419 }
420 }
421
422 void SetCompression(std::uint32_t value)
423 {
424 if (IsBigFile()) {
425 fInfoLong.fCompress = value;
426 } else {
427 fInfoShort.fCompress = value;
428 }
429 }
430};
431
432/// A reference to an unused byte-range in a TFile
433struct RTFFreeEntry {
434 static constexpr unsigned kBigFreeEntryVersion = 1000;
435
436 RUInt16BE fVersion{1};
437 union {
438 struct {
439 RUInt32BE fFirst{0};
440 RUInt32BE fLast{0};
441 } fInfoShort;
442 struct {
443 RUInt64BE fFirst{0};
444 RUInt64BE fLast{0};
445 } fInfoLong;
446 };
447
448 RTFFreeEntry() : fInfoShort() {}
449 void Set(std::uint64_t first, std::uint64_t last)
450 {
451 if (last > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
452 fVersion = fVersion + kBigFreeEntryVersion;
453 fInfoLong.fFirst = first;
454 fInfoLong.fLast = last;
455 } else {
456 fInfoShort.fFirst = first;
457 fInfoShort.fLast = last;
458 }
459 }
460 std::uint32_t GetSize() { return (fVersion >= kBigFreeEntryVersion) ? 18 : 10; }
461};
462
463/// The header of the directory key index
464struct RTFKeyList {
465 RUInt32BE fNKeys;
466 std::uint32_t GetSize() const { return sizeof(RTFKeyList); }
467 explicit RTFKeyList(std::uint32_t nKeys) : fNKeys(nKeys) {}
468};
469
470/// A streamed TDirectory (TFile) object
471struct RTFDirectory {
472 static constexpr unsigned kBigFileVersion = 1000;
473
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{RTFHeader::kBEGIN};
483 RUInt32BE fSeekParent{0};
484 RUInt32BE fSeekKeys{0};
485 } fInfoShort;
486 struct {
487 RUInt64BE fSeekDir{RTFHeader::kBEGIN};
488 RUInt64BE fSeekParent{0};
489 RUInt64BE fSeekKeys{0};
490 } fInfoLong;
491 };
492
493 RTFDirectory() : 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 >= kBigFileVersion)
499 return sizeof(RTFDirectory);
500 return 18 + sizeof(fInfoShort);
501 }
502
503 std::uint64_t GetSeekKeys() const
504 {
505 if (fClassVersion >= kBigFileVersion)
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 + kBigFileVersion;
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];
529
530 RTFUUID()
531 {
532 TUUID uuid{TUUID::UUIDv4()};
533 char *buffer = reinterpret_cast<char *>(this);
534 uuid.FillBuffer(buffer);
535 assert(reinterpret_cast<RTFUUID *>(buffer) <= (this + 1));
536 }
537 std::uint32_t GetSize() const { return sizeof(RTFUUID); }
538};
539
540/// A streamed RNTuple class
541///
542/// NOTE: this must be kept in sync with RNTuple.hxx.
543/// Aside ensuring consistency between the two classes' members, you need to make sure
544/// that fVersionClass matches the class version of RNTuple.
545struct RTFNTuple {
546 RUInt32BE fByteCount{0x40000000 | (sizeof(RTFNTuple) - sizeof(fByteCount))};
547 RUInt16BE fVersionClass{2};
548 RUInt16BE fVersionEpoch{0};
549 RUInt16BE fVersionMajor{0};
550 RUInt16BE fVersionMinor{0};
551 RUInt16BE fVersionPatch{0};
552 RUInt64BE fSeekHeader{0};
553 RUInt64BE fNBytesHeader{0};
554 RUInt64BE fLenHeader{0};
555 RUInt64BE fSeekFooter{0};
556 RUInt64BE fNBytesFooter{0};
557 RUInt64BE fLenFooter{0};
558 RUInt64BE fMaxKeySize{0};
559
560 static constexpr std::uint32_t GetSizePlusChecksum() { return sizeof(RTFNTuple) + sizeof(std::uint64_t); }
561
562 RTFNTuple() = default;
563 explicit RTFNTuple(const ROOT::RNTuple &inMemoryAnchor)
564 {
565 fVersionEpoch = inMemoryAnchor.GetVersionEpoch();
566 fVersionMajor = inMemoryAnchor.GetVersionMajor();
567 fVersionMinor = inMemoryAnchor.GetVersionMinor();
568 fVersionPatch = inMemoryAnchor.GetVersionPatch();
569 fSeekHeader = inMemoryAnchor.GetSeekHeader();
570 fNBytesHeader = inMemoryAnchor.GetNBytesHeader();
571 fLenHeader = inMemoryAnchor.GetLenHeader();
572 fSeekFooter = inMemoryAnchor.GetSeekFooter();
573 fNBytesFooter = inMemoryAnchor.GetNBytesFooter();
574 fLenFooter = inMemoryAnchor.GetLenFooter();
575 fMaxKeySize = inMemoryAnchor.GetMaxKeySize();
576 }
577 std::uint32_t GetSize() const { return sizeof(RTFNTuple); }
578 // The byte count and class version members are not checksummed
579 std::uint32_t GetOffsetCkData() { return sizeof(fByteCount) + sizeof(fVersionClass); }
580 std::uint32_t GetSizeCkData() { return GetSize() - GetOffsetCkData(); }
581 unsigned char *GetPtrCkData() { return reinterpret_cast<unsigned char *>(this) + GetOffsetCkData(); }
582};
583
584/// The bare file global header
585struct RBareFileHeader {
586 char fMagic[7]{'r', 'n', 't', 'u', 'p', 'l', 'e'};
587 RUInt32BE fRootVersion{(ROOT_VERSION_CODE >> 16) * 10000 + ((ROOT_VERSION_CODE & 0xFF00) >> 8) * 100 +
588 (ROOT_VERSION_CODE & 0xFF)};
589 RUInt32BE fFormatVersion{1};
590 RUInt32BE fCompress{0};
591 RTFNTuple fNTuple;
592 // followed by the ntuple name
593};
594#pragma pack(pop)
595
596/// The artificial class name shown for opaque RNTuple keys (see TBasket)
597constexpr char const *kBlobClassName = "RBlob";
598/// The class name of the RNTuple anchor
599constexpr char const *kNTupleClassName = "ROOT::RNTuple";
600
601} // anonymous namespace
602
603namespace ROOT {
604namespace Internal {
605/// If a TFile container is written by a C stream (simple file), on dataset commit, the file header
606/// and the TFile record need to be updated
608 RTFHeader fHeader;
609 RTFDirectory fFileRecord;
610 std::uint64_t fSeekNTuple{0}; // Remember the offset for the keys list
611 std::uint64_t fSeekFileRecord{0};
612};
613
614/// The RKeyBlob writes an invisible key into a TFile. That is, a key that is not indexed in the list of keys,
615/// like a TBasket.
616/// NOTE: out of anonymous namespace because otherwise ClassDefInline fails to compile
617/// on some platforms.
618class RKeyBlob : public TKey { // NOLINT(misc-use-internal-linkage)
619public:
620 RKeyBlob() = default;
621
622 explicit RKeyBlob(TFile *file) : TKey(file)
623 {
625 fVersion += RTFKey::kBigKeyVersion;
626 fKeylen = Sizeof();
627 }
628
629 /// Register a new key for a data record of size nbytes
630 void Reserve(size_t nbytes, std::uint64_t *seekKey)
631 {
632 Create(nbytes);
633 *seekKey = fSeekKey;
634 }
635
636 bool WasAllocatedInAFreeSlot() const { return fLeft > 0; }
637
639};
640
641} // namespace Internal
642} // namespace ROOT
643
644// Computes how many chunks do we need to fit `nbytes` of payload, considering that the
645// first chunk also needs to house the offsets of the other chunks and no chunk can
646// be bigger than `maxChunkSize`. When saved to a TFile, each chunk is part of a separate TKey.
647static size_t ComputeNumChunks(size_t nbytes, size_t maxChunkSize)
648{
649 constexpr size_t kChunkOffsetSize = sizeof(std::uint64_t);
650
652 size_t nChunks = (nbytes + maxChunkSize - 1) / maxChunkSize;
653 assert(nChunks > 1);
654 size_t nbytesTail = nbytes % maxChunkSize;
655 size_t nbytesExtra = (nbytesTail > 0) * (maxChunkSize - nbytesTail);
658 ++nChunks;
660 }
661
662 // We don't support having more chunkOffsets than what fits in one chunk.
663 // For a reasonable-sized maxKeySize it looks very unlikely that we can have more chunks
664 // than we can fit in the first `maxKeySize` bytes. E.g. for maxKeySize = 1GiB we can fit
665 // 134217728 chunk offsets, making our multi-key blob's capacity exactly 128 PiB.
667
668 return nChunks;
669}
670
672
674{
675 char ident[4];
676 ReadBuffer(ident, 4, 0);
677 if (std::string(ident, 4) == "root")
678 return GetNTupleProper(ntupleName);
679 fIsBare = true;
680 return GetNTupleBare(ntupleName);
681}
682
683/// Searches for a key with the given name and type in the key index of the given directory.
684/// Return 0 if the key was not found.
685std::uint64_t ROOT::Internal::RMiniFileReader::SearchInDirectory(std::uint64_t &offsetDir, std::string_view keyName,
686 std::string_view typeName)
687{
688 RTFDirectory directory;
690
691 RTFKey key;
692 RUInt32BE nKeys;
693 std::uint64_t offset = directory.GetSeekKeys();
694 ReadBuffer(&key, sizeof(key), offset);
695 offset += key.fKeyLen;
696 ReadBuffer(&nKeys, sizeof(nKeys), offset);
697 offset += sizeof(nKeys);
698
699 for (unsigned int i = 0; i < nKeys; ++i) {
700 ReadBuffer(&key, sizeof(key), offset);
701 auto offsetNextKey = offset + key.fKeyLen;
702
703 offset += key.GetHeaderSize();
704 RTFString name;
705 ReadBuffer(&name, 1, offset);
706 ReadBuffer(&name, name.GetSize(), offset);
707 if (std::string_view(name.fData, name.fLName) != typeName) {
709 continue;
710 }
711 offset += name.GetSize();
712 ReadBuffer(&name, 1, offset);
713 ReadBuffer(&name, name.GetSize(), offset);
714 if (std::string_view(name.fData, name.fLName) == keyName) {
715 return key.GetSeekKey();
716 }
718 }
719
720 // Not found
721 return 0;
722}
723
725{
726 RTFHeader fileHeader;
727 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
728
729 const std::uint64_t seekKeyInfo = fileHeader.GetSeekInfo();
730
731 RTFKey key;
732 ReadBuffer(&key, sizeof(key), seekKeyInfo);
733
734 const std::uint64_t nbytesInfo = fileHeader.GetNbytesInfo() - key.fKeyLen;
735 const std::uint64_t seekInfo = seekKeyInfo + key.fKeyLen;
736 const std::uint32_t uncompLenInfo = key.fObjLen;
738 if (nbytesInfo == uncompLenInfo) {
739 // Uncompressed
741 } else {
743 ReadBuffer(buffer.get(), nbytesInfo, seekInfo);
745 }
746
748 // This is necessary to allow the "class tags" inside the StreamerInfo list to refer to the proper offset into
749 // the buffer. Normally TFile loads the StreamerInfo via TKey::ReadObjWithBuffer, whose buffer also includes the
750 // key itself. Since we dealt with the key above already, we are only passing the payload to TBufferFile so offsets
751 // need to be patched up.
752 buffer.SetBufferDisplacement(key.fKeyLen);
754 streamerInfoList.Streamer(buffer);
755 TObjLink *lnk = streamerInfoList.FirstLink();
756 while (lnk) {
757 auto obj = lnk->GetObject();
758 // NOTE: the last element of the streamer info list may be a TList with the IO customization rules, so we need
759 // to check before static casting.
760 if (obj->IsA() == TStreamerInfo::Class()) {
761 auto info = static_cast<TStreamerInfo *>(obj);
762 info->BuildCheck();
763 }
764 lnk = lnk->Next();
765 }
766}
767
769{
770 RTFHeader fileHeader;
771 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
772
773 RTFKey key;
774 RTFString name;
775 ReadBuffer(&key, sizeof(key), fileHeader.fBEGIN);
776 // Skip over the entire key length, including the class name, object name, and title stored in it.
777 std::uint64_t offset = fileHeader.fBEGIN + key.fKeyLen;
778 // Skip over the name and title of the TNamed preceding the TFile (root TDirectory) entry.
779 ReadBuffer(&name, 1, offset);
780 offset += name.GetSize();
781 ReadBuffer(&name, 1, offset);
782 offset += name.GetSize();
783
784 // split ntupleName by '/' character to open datasets in subdirectories.
785 std::string ntuplePathTail(ntuplePath);
786 if (!ntuplePathTail.empty() && ntuplePathTail[0] == '/')
787 ntuplePathTail = ntuplePathTail.substr(1);
788 auto pos = std::string::npos;
789 while ((pos = ntuplePathTail.find('/')) != std::string::npos) {
790 auto directoryName = ntuplePathTail.substr(0, pos);
791 ntuplePathTail.erase(0, pos + 1);
792
793 offset = SearchInDirectory(offset, directoryName, "TDirectory");
794 if (offset == 0) {
795 return R__FAIL("no directory named '" + std::string(directoryName) + "' in file '" + fRawFile->GetUrl() + "'");
796 }
797 ReadBuffer(&key, sizeof(key), offset);
798 offset = key.GetSeekKey() + key.fKeyLen;
799 }
800 // no more '/' delimiter in ntuplePath
802
803 offset = SearchInDirectory(offset, ntupleName, kNTupleClassName);
804 if (offset == 0) {
805 return R__FAIL("no RNTuple named '" + std::string(ntupleName) + "' in file '" + fRawFile->GetUrl() + "'");
806 }
807
808 ReadBuffer(&key, sizeof(key), offset);
809 offset = key.GetSeekKey() + key.fKeyLen;
810
811 // size of a RTFNTuple version 2 (min supported version); future anchor versions can grow.
812 constexpr size_t kMinNTupleSize = 78;
813 static_assert(kMinNTupleSize == RTFNTuple::GetSizePlusChecksum());
814 if (key.fObjLen < kMinNTupleSize) {
815 return R__FAIL("invalid anchor size: " + std::to_string(key.fObjLen) + " < " + std::to_string(sizeof(RTFNTuple)));
816 }
817
818 const auto objNbytes = key.GetSize() - key.fKeyLen;
819 auto res = GetNTupleProperAtOffset(offset, objNbytes, key.fObjLen);
820 return res;
821}
822
824 std::uint64_t compSize,
825 std::uint64_t uncompLen)
826{
827 // The object length can be smaller than the size of RTFNTuple if it comes from a past RNTuple class version,
828 // or larger than it if it comes from a future RNTuple class version.
829 auto bufAnchor = MakeUninitArray<unsigned char>(std::max<size_t>(uncompLen, sizeof(RTFNTuple)));
830 RTFNTuple *ntuple = new (bufAnchor.get()) RTFNTuple;
831
832 if (compSize != uncompLen) {
833 // Read into a temporary buffer
834 auto unzipBuf = MakeUninitArray<unsigned char>(std::max<size_t>(uncompLen, sizeof(RTFNTuple)));
836 // Unzip into the final buffer
838 } else {
840 }
841
842 // We require that future class versions only append members and store the checksum in the last 8 bytes
843 // Checksum calculation: strip byte count, class version, fChecksum member
844 const auto lenCkData = uncompLen - ntuple->GetOffsetCkData() - sizeof(uint64_t);
845 const auto ckCalc = XXH3_64bits(ntuple->GetPtrCkData(), lenCkData);
846 uint64_t ckOnDisk;
847
848 RUInt64BE *ckOnDiskPtr = reinterpret_cast<RUInt64BE *>(bufAnchor.get() + uncompLen - sizeof(uint64_t));
849 ckOnDisk = static_cast<uint64_t>(*ckOnDiskPtr);
850 if (ckCalc != ckOnDisk) {
851 return R__FAIL("RNTuple anchor checksum mismatch");
852 }
853
854 return CreateAnchor(ntuple->fVersionEpoch, ntuple->fVersionMajor, ntuple->fVersionMinor, ntuple->fVersionPatch,
855 ntuple->fSeekHeader, ntuple->fNBytesHeader, ntuple->fLenHeader, ntuple->fSeekFooter,
856 ntuple->fNBytesFooter, ntuple->fLenFooter, ntuple->fMaxKeySize);
857}
858
860{
861 RBareFileHeader fileHeader;
862 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
863 RTFString name;
864 auto offset = sizeof(fileHeader);
865 ReadBuffer(&name, 1, offset);
866 ReadBuffer(&name, name.GetSize(), offset);
867 std::string_view foundName(name.fData, name.fLName);
868 if (foundName != ntupleName) {
869 return R__FAIL("expected RNTuple named '" + std::string(ntupleName) + "' but instead found '" +
870 std::string(foundName) + "' in file '" + fRawFile->GetUrl() + "'");
871 }
872 offset += name.GetSize();
873
874 RTFNTuple ntuple;
875 ReadBuffer(&ntuple, sizeof(ntuple), offset);
876 std::uint64_t onDiskChecksum;
878 auto checksum = XXH3_64bits(ntuple.GetPtrCkData(), ntuple.GetSizeCkData());
879 if (checksum != static_cast<uint64_t>(onDiskChecksum))
880 return R__FAIL("RNTuple bare file: anchor checksum mismatch");
881
882 return CreateAnchor(ntuple.fVersionEpoch, ntuple.fVersionMajor, ntuple.fVersionMinor, ntuple.fVersionPatch,
883 ntuple.fSeekHeader, ntuple.fNBytesHeader, ntuple.fLenHeader, ntuple.fSeekFooter,
884 ntuple.fNBytesFooter, ntuple.fLenFooter, ntuple.fMaxKeySize);
885}
886
887void ROOT::Internal::RMiniFileReader::ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
888{
889 TryReadBuffer(buffer, nbytes, offset).ThrowOnError();
890}
891
893{
894 const auto ByteReadErr = [](std::size_t expected, std::size_t nread) {
895 return R__FAIL("invalid read (expected bytes: " + std::to_string(expected) + ", read: " + std::to_string(nread) +
896 ")");
897 };
898
899 size_t nread;
900 if (fMaxKeySize == 0 || nbytes <= fMaxKeySize) {
901 // Fast path: read single blob
902 nread = fRawFile->ReadAt(buffer, nbytes, offset);
903 } else {
904 // Read chunked blob. See RNTupleFileWriter::WriteBlob() for details.
905 const size_t nChunks = ComputeNumChunks(nbytes, fMaxKeySize);
906 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
907 const size_t nbytesFirstChunk = fMaxKeySize - nbytesChunkOffsets;
908 uint8_t *bufCur = reinterpret_cast<uint8_t *>(buffer);
909
910 // Read first chunk
911 nread = fRawFile->ReadAt(bufCur, fMaxKeySize, offset);
912 if (nread != fMaxKeySize)
913 return ByteReadErr(fMaxKeySize, nread);
914
915 // NOTE: we read the entire chunk in `bufCur`, but we only advance the pointer by `nbytesFirstChunk`,
916 // since the last part of `bufCur` will later be overwritten by the next chunk's payload.
917 // We do this to avoid a second ReadAt to read in the chunk offsets.
920
923
925 std::uint64_t *curChunkOffset = &chunkOffsets[0];
926
927 do {
928 std::uint64_t chunkOffset;
931
932 const size_t bytesToRead = std::min<size_t>(fMaxKeySize, remainingBytes);
933 // Ensure we don't read outside of the buffer
934 R__ASSERT(static_cast<size_t>(bufCur - reinterpret_cast<uint8_t *>(buffer)) <= nbytes - bytesToRead);
935
936 auto nbytesRead = fRawFile->ReadAt(bufCur, bytesToRead, chunkOffset);
937 if (nbytesRead != bytesToRead)
939
943 } while (remainingBytes > 0);
944 }
945
946 if (nread != nbytes)
947 return ByteReadErr(nbytes, nread);
948
949 return RResult<void>::Success();
950}
951
952////////////////////////////////////////////////////////////////////////////////
953
954/// Prepare a blob key in the provided buffer, which must provide space for kBlobKeyLen bytes. Note that the array type
955/// is purely documentation, the argument is actually just a pointer.
957 unsigned char buffer[kBlobKeyLen])
958{
959 RTFString strClass{kBlobClassName};
960 RTFString strObject;
961 RTFString strTitle;
962 RTFKey keyHeader(offset, RTFHeader::kBEGIN, strClass, strObject, strTitle, len, nbytes);
963 R__ASSERT(keyHeader.fKeyLen == kBlobKeyLen);
964
965 // Copy structures into the buffer.
966 unsigned char *writeBuffer = buffer;
967 memcpy(writeBuffer, &keyHeader, keyHeader.GetHeaderSize());
968 writeBuffer += keyHeader.GetHeaderSize();
969 memcpy(writeBuffer, &strClass, strClass.GetSize());
970 writeBuffer += strClass.GetSize();
972 writeBuffer += strObject.GetSize();
973 memcpy(writeBuffer, &strTitle, strTitle.GetSize());
974 writeBuffer += strTitle.GetSize();
975 R__ASSERT(writeBuffer == buffer + kBlobKeyLen);
976}
977
978////////////////////////////////////////////////////////////////////////////////
979
981
983{
984 static_assert(kHeaderBlockSize % kBlockAlign == 0, "invalid header block size");
985 if (bufferSize % kBlockAlign != 0)
986 throw RException(R__FAIL("Buffer size not a multiple of alignment: " + std::to_string(bufferSize)));
987
988 assert(!fShared);
989
990 fShared = std::make_shared<RSharedData>(nullptr);
991
992 fShared->fBlockSize = bufferSize;
993 fShared->fControlBlock = std::make_unique<ROOT::Internal::RTFileControlBlock>();
994
995 std::align_val_t blockAlign{kBlockAlign};
996 fShared->fHeaderBlock = static_cast<unsigned char *>(::operator new[](kHeaderBlockSize, blockAlign));
997 memset(fShared->fHeaderBlock, 0, kHeaderBlockSize);
998 fShared->fBlock = static_cast<unsigned char *>(::operator new[](fShared->fBlockSize, blockAlign));
999 memset(fShared->fBlock, 0, fShared->fBlockSize);
1000}
1001
1003
1005{
1006 if (fFile)
1007 fclose(fFile);
1008
1009 std::align_val_t blockAlign{kBlockAlign};
1010 if (fHeaderBlock)
1011 ::operator delete[](fHeaderBlock, blockAlign);
1012 if (fBlock)
1013 ::operator delete[](fBlock, blockAlign);
1014}
1015
1016namespace {
1017int FSeek64(FILE *stream, std::int64_t offset, int origin)
1018{
1019#ifdef R__SEEK64
1020 return fseeko64(stream, offset, origin);
1021#else
1022 return fseek(stream, offset, origin);
1023#endif
1024}
1025} // namespace
1026
1028{
1029 auto &shared = *fShared;
1030
1031 // Write the last partially filled block, which may still need appropriate alignment for Direct I/O.
1032 // If it is the first block, get the updated header block.
1033 if (shared.fBlockOffset == 0) {
1034 std::size_t headerBlockSize = kHeaderBlockSize;
1035 if (headerBlockSize > shared.fFilePos) {
1036 headerBlockSize = shared.fFilePos;
1037 }
1038 memcpy(shared.fBlock, shared.fHeaderBlock, headerBlockSize);
1039 }
1040
1041 std::size_t retval = FSeek64(shared.fFile, shared.fBlockOffset, SEEK_SET);
1042 if (retval)
1043 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1044
1045 std::size_t lastBlockSize = shared.fFilePos - shared.fBlockOffset;
1046 R__ASSERT(lastBlockSize <= shared.fBlockSize);
1047 if (shared.fDirectIO) {
1048 // Round up to a multiple of kBlockAlign.
1051 R__ASSERT(lastBlockSize <= shared.fBlockSize);
1052 }
1053 retval = fwrite(shared.fBlock, 1, lastBlockSize, shared.fFile);
1054 if (retval != lastBlockSize)
1055 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1056
1057 // Write the (updated) header block, unless it was part of the write above.
1058 if (shared.fBlockOffset > 0) {
1059 retval = FSeek64(shared.fFile, 0, SEEK_SET);
1060 if (retval)
1061 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1062
1063 retval = fwrite(shared.fHeaderBlock, 1, kHeaderBlockSize, shared.fFile);
1065 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1066 }
1067
1068 retval = fflush(shared.fFile);
1069 if (retval)
1070 throw RException(R__FAIL(std::string("Flush failed: ") + strerror(errno)));
1071}
1072
1073void ROOT::Internal::RNTupleFileWriter::RImplSimple::Write(const void *buffer, size_t nbytes, std::int64_t offset)
1074{
1075 auto &shared = *fShared;
1076
1077 R__ASSERT(shared.fFile);
1078 size_t retval;
1079 if ((offset >= 0) && (static_cast<std::uint64_t>(offset) != shared.fFilePos)) {
1080 shared.fFilePos = offset;
1081 }
1082
1083 // Keep header block to overwrite on commit.
1084 if (shared.fFilePos < kHeaderBlockSize) {
1085 std::size_t headerBytes = nbytes;
1086 if (shared.fFilePos + headerBytes > kHeaderBlockSize) {
1087 headerBytes = kHeaderBlockSize - shared.fFilePos;
1088 }
1089 memcpy(shared.fHeaderBlock + shared.fFilePos, buffer, headerBytes);
1090 }
1091
1092 R__ASSERT(shared.fFilePos >= shared.fBlockOffset);
1093
1094 while (nbytes > 0) {
1095 std::uint64_t posInBlock = shared.fFilePos % shared.fBlockSize;
1096 std::uint64_t blockOffset = shared.fFilePos - posInBlock;
1097 if (blockOffset != shared.fBlockOffset) {
1098 // Write the block.
1099 retval = FSeek64(shared.fFile, shared.fBlockOffset, SEEK_SET);
1100 if (retval)
1101 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1102
1103 retval = fwrite(shared.fBlock, 1, shared.fBlockSize, shared.fFile);
1104 if (retval != shared.fBlockSize)
1105 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1106
1107 // Null the buffer contents for good measure.
1108 memset(shared.fBlock, 0, shared.fBlockSize);
1109 }
1110
1111 shared.fBlockOffset = blockOffset;
1112 std::size_t blockSize = nbytes;
1113 if (blockSize > shared.fBlockSize - posInBlock) {
1114 blockSize = shared.fBlockSize - posInBlock;
1115 }
1116 memcpy(shared.fBlock + posInBlock, buffer, blockSize);
1117 buffer = static_cast<const unsigned char *>(buffer) + blockSize;
1118 nbytes -= blockSize;
1119 shared.fFilePos += blockSize;
1120 }
1121}
1122
1123std::uint64_t
1124ROOT::Internal::RNTupleFileWriter::RImplSimple::WriteKey(const void *buffer, std::size_t nbytes, std::size_t len,
1125 std::int64_t offset, std::uint64_t directoryOffset,
1126 const std::string &className, const std::string &objectName,
1127 const std::string &title)
1128{
1129 auto &shared = *fShared;
1130
1131 if (offset > 0)
1132 shared.fKeyOffset = offset;
1133 RTFString strClass{className};
1134 RTFString strObject{objectName};
1135 RTFString strTitle{title};
1136
1137 RTFKey key(shared.fKeyOffset, directoryOffset, strClass, strObject, strTitle, len, nbytes);
1138 Write(&key, key.GetHeaderSize(), shared.fKeyOffset);
1139 Write(&strClass, strClass.GetSize());
1140 Write(&strObject, strObject.GetSize());
1141 Write(&strTitle, strTitle.GetSize());
1142 auto offsetData = shared.fFilePos;
1143 // The next key starts after the data.
1144 shared.fKeyOffset = offsetData + nbytes;
1145 if (buffer)
1146 Write(buffer, nbytes);
1147
1148 return offsetData;
1149}
1150
1152 unsigned char keyBuffer[kBlobKeyLen])
1153{
1154 auto &shared = *fShared;
1155
1156 if (keyBuffer) {
1157 PrepareBlobKey(shared.fKeyOffset, nbytes, len, keyBuffer);
1158 } else {
1159 unsigned char localKeyBuffer[kBlobKeyLen];
1160 PrepareBlobKey(shared.fKeyOffset, nbytes, len, localKeyBuffer);
1161 Write(localKeyBuffer, kBlobKeyLen, shared.fKeyOffset);
1162 }
1163
1164 auto offsetData = shared.fKeyOffset + kBlobKeyLen;
1165 // The next key starts after the data.
1166 shared.fKeyOffset = offsetData + nbytes;
1167
1168 return offsetData;
1169}
1170
1171////////////////////////////////////////////////////////////////////////////////
1172
1173template <typename T>
1175 std::size_t len, unsigned char keyBuffer[kBlobKeyLen])
1176{
1177 std::uint64_t offsetKey;
1179 // Since it is unknown beforehand if offsetKey is beyond the 2GB limit or not,
1180 // RKeyBlob will always reserve space for a big key (version >= 1000)
1181 keyBlob.Reserve(nbytes, &offsetKey);
1182
1183 if (keyBuffer) {
1185 } else {
1186 unsigned char localKeyBuffer[kBlobKeyLen];
1189 }
1190
1191 if (keyBlob.WasAllocatedInAFreeSlot()) {
1192 // If the key was allocated in a free slot, the last 4 bytes of its buffer contain the new size
1193 // of the remaining free slot and we need to write it to disk before the key gets destroyed at the end of the
1194 // function.
1195 caller.Write(keyBlob.GetBuffer() + nbytes, sizeof(Int_t), offsetKey + kBlobKeyLen + nbytes);
1196 }
1197
1199
1200 return offsetData;
1201}
1202
1203void ROOT::Internal::RNTupleFileWriter::RImplTFile::Write(const void *buffer, size_t nbytes, std::int64_t offset)
1204{
1205 fDirectory->GetFile()->Seek(offset);
1206 bool rv = fDirectory->GetFile()->WriteBuffer((char *)(buffer), nbytes);
1207 if (rv)
1208 throw RException(R__FAIL("WriteBuffer failed."));
1209}
1210
1212 unsigned char keyBuffer[kBlobKeyLen])
1213{
1214 auto offsetData = RNTupleFileWriter::ReserveBlobKey(*this, *fDirectory->GetFile(), nbytes, len, keyBuffer);
1215 return offsetData;
1216}
1217
1218////////////////////////////////////////////////////////////////////////////////
1219
1220void ROOT::Internal::RNTupleFileWriter::RImplRFile::Write(const void *buffer, size_t nbytes, std::int64_t offset)
1221{
1223 file->Seek(offset);
1224 bool rv = file->WriteBuffer((char *)(buffer), nbytes);
1225 if (rv)
1226 throw RException(R__FAIL("WriteBuffer failed."));
1227}
1228
1236
1237////////////////////////////////////////////////////////////////////////////////
1238
1239ROOT::Internal::RNTupleFileWriter::RNTupleFileWriter(std::string_view name, std::uint64_t maxKeySize, bool isHidden)
1240 : fIsHidden(isHidden), fNTupleName(name)
1241{
1242 fFile.emplace<RImplSimple>();
1244 auto infoRNTuple = RNTuple::Class()->GetStreamerInfo();
1246}
1247
1249
1250std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1251ROOT::Internal::RNTupleFileWriter::Recreate(std::string_view ntupleName, std::string_view path,
1253{
1254 std::string fileName(path);
1255 size_t idxDirSep = fileName.find_last_of("\\/");
1256 if (idxDirSep != std::string::npos) {
1257 fileName.erase(0, idxDirSep + 1);
1258 }
1259#ifdef R__LINUX
1260 int flags = O_WRONLY | O_CREAT | O_TRUNC;
1261#ifdef O_LARGEFILE
1262 // Add the equivalent flag that is passed by fopen64.
1263 flags |= O_LARGEFILE;
1264#endif
1265 if (options.GetUseDirectIO()) {
1266 flags |= O_DIRECT;
1267 }
1268 int fd = open(std::string(path).c_str(), flags, 0666);
1269 if (fd == -1) {
1270 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1271 }
1272 FILE *fileStream = fdopen(fd, "wb");
1273#else
1274#ifdef R__SEEK64
1275 FILE *fileStream = fopen64(std::string(path.data(), path.size()).c_str(), "wb");
1276#else
1277 FILE *fileStream = fopen(std::string(path.data(), path.size()).c_str(), "wb");
1278#endif
1279#endif
1280 if (!fileStream) {
1281 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1282 }
1283 // RNTupleFileWriter::RImplSimple does its own buffering, turn off additional buffering from C stdio.
1284 std::setvbuf(fileStream, nullptr, _IONBF, 0);
1285
1286 auto writer = std::unique_ptr<RNTupleFileWriter>(
1287 new RNTupleFileWriter(ntupleName, options.GetMaxKeySize(), /*isHidden=*/false));
1288 RImplSimple &fileSimple = std::get<RImplSimple>(writer->fFile);
1289 fileSimple.AllocateBuffers(options.GetWriteBufferSize());
1290 fileSimple.fShared->fFile = fileStream;
1291 fileSimple.fShared->fDirectIO = options.GetUseDirectIO();
1292 writer->fFileName = fileName;
1293
1294 int defaultCompression = options.GetCompression();
1295 switch (containerFormat) {
1296 case EContainerFormat::kTFile: writer->WriteTFileSkeleton(defaultCompression); break;
1297 case EContainerFormat::kBare:
1298 writer->fIsBare = true;
1299 writer->WriteBareFileSkeleton(defaultCompression);
1300 break;
1301 default: R__ASSERT(false && "Internal error: unhandled container format");
1302 }
1303
1304 return writer;
1305}
1306
1307std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1309 std::uint64_t maxKeySize, bool hidden)
1310{
1311 TFile *file = fileOrDirectory.GetFile();
1312 if (!file)
1313 throw RException(R__FAIL("invalid attempt to add an RNTuple to a directory that is not backed by a file"));
1314 assert(file->IsBinary());
1315
1316 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, maxKeySize, hidden));
1317 auto &fileProper = writer->fFile.emplace<RImplTFile>();
1318 fileProper.fDirectory = &fileOrDirectory;
1319 return writer;
1320}
1321
1322std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1324 std::string_view ntupleDir, std::uint64_t maxKeySize)
1325{
1326 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, maxKeySize, /*isHidden=*/false));
1327 auto &rfile = writer->fFile.emplace<RImplRFile>();
1328 rfile.fFile = &file;
1329 R__ASSERT(ntupleDir.empty() || ntupleDir[ntupleDir.size() - 1] == '/');
1330 rfile.fDir = ntupleDir;
1331 return writer;
1332}
1333
1334std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1336{
1337 if (auto *tfile = std::get_if<RImplTFile>(&fFile)) {
1338 return Append(ntupleName, *tfile->fDirectory, fNTupleAnchor.fMaxKeySize, /* isHidden= */ true);
1339 } else if (auto *file = std::get_if<RImplSimple>(&fFile)) {
1340 if (fIsBare)
1341 throw ROOT::RException(R__FAIL("cloning a bare file is currently unsupported"));
1342
1343 auto writer = std::unique_ptr<RNTupleFileWriter>(
1344 new RNTupleFileWriter(ntupleName, fNTupleAnchor.GetMaxKeySize(), /* isHidden= */ true));
1345 auto &clonedFile = std::get<RImplSimple>(writer->fFile);
1346 clonedFile.fShared = file->fShared;
1347 return writer;
1348 }
1349 // TODO: support also RFile-based writers
1350 throw ROOT::RException(R__FAIL("cannot clone an RFile-based RNTupleFileWriter."));
1351}
1352
1354{
1355 RImplSimple *fileSimple = std::get_if<RImplSimple>(&fFile);
1356 if (!fileSimple)
1357 throw RException(R__FAIL("invalid attempt to seek non-simple writer"));
1358
1359 fileSimple->fShared->fFilePos = offset;
1360 fileSimple->fShared->fKeyOffset = offset;
1361 // The next Write() will Flush() if necessary.
1362}
1363
1368
1370{
1371 const auto WriteStreamerInfoToFile = [&](TFile *file) {
1372 // Make sure the streamer info records used in the RNTuple are written to the file
1374 buf.SetParent(file);
1375 for (auto [_, info] : fStreamerInfoMap)
1376 buf.TagStreamerInfo(info);
1377 };
1378
1380 // NOTE: checksum length is included in the uncompressed len
1381 anchorInfo.fLength = RTFNTuple{}.GetSize() + sizeof(std::uint64_t);
1382 anchorInfo.fLocator.SetType(RNTupleLocator::kTypeFile);
1383
1384 if (auto fileProper = std::get_if<RImplTFile>(&fFile)) {
1385 // Easy case, the ROOT file header and the RNTuple streaming is taken care of by TFile
1386 fileProper->fDirectory->WriteObject(&fNTupleAnchor, fNTupleName.c_str());
1387 WriteStreamerInfoToFile(fileProper->fDirectory->GetFile());
1388 auto key = static_cast<TKey *>(fileProper->fDirectory->GetListOfKeys()->FindObject(fNTupleName.c_str()));
1389 R__ASSERT(key);
1390 anchorInfo.fLocator.SetPosition(key->GetSeekKey() + key->GetKeylen());
1391 anchorInfo.fLocator.SetNBytesOnStorage(key->GetNbytes() - key->GetKeylen());
1392 // NOTE: this must happen after FindObject(), otherwise some TFile implementations, such as TBufferMergerFile,
1393 // may reset the keys list upon write.
1394 fileProper->fDirectory->GetFile()->Write();
1395
1396 if (fIsHidden) {
1397 // Remove the anchor's key from the directory's KeysList to disallow retrieving directly the
1398 // attribute RNTuple from the TFile.
1399 fileProper->fDirectory->GetListOfKeys()->Remove(key);
1400 }
1401 } else if (auto fileRFile = std::get_if<RImplRFile>(&fFile)) {
1402 // Same as the case above but handled via RFile
1403 fileRFile->fFile->Put(fileRFile->fDir + fNTupleName, fNTupleAnchor);
1405 auto key = fileRFile->fFile->GetKeyInfo(fNTupleName);
1406 R__ASSERT(key);
1407 anchorInfo.fLocator.SetPosition(key->GetSeekKey() + key->GetNBytesKey());
1408 anchorInfo.fLocator.SetNBytesOnStorage(key->GetNBytesObj());
1409 fileRFile->fFile->Flush();
1410 } else {
1411 // Writing by C file stream: prepare the container format header and stream the RNTuple anchor object
1412 auto &fileSimple = std::get<RImplSimple>(fFile);
1413 auto &shared = *fileSimple.fShared;
1414
1415 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1416 anchorInfo.fLocator.SetPosition(shared.fControlBlock->fSeekNTuple);
1417
1418 if (fIsBare) {
1419 // Compute the checksum
1420 std::uint64_t checksum = XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData());
1421 memcpy(shared.fHeaderBlock + shared.fControlBlock->fSeekNTuple, &ntupleOnDisk, ntupleOnDisk.GetSize());
1422 memcpy(shared.fHeaderBlock + shared.fControlBlock->fSeekNTuple + ntupleOnDisk.GetSize(), &checksum,
1423 sizeof(checksum));
1424 fileSimple.Flush();
1425
1426 anchorInfo.fLocator.SetNBytesOnStorage(ntupleOnDisk.GetSize());
1427 } else {
1428 auto anchorSize = WriteTFileNTupleKey(compression);
1429 WriteTFileKeysList(anchorSize); // NOTE: this is written uncompressed
1430 WriteTFileStreamerInfo(compression);
1431 WriteTFileFreeList(); // NOTE: this is written uncompressed
1432
1433 // Update header and TFile record
1434 memcpy(shared.fHeaderBlock, &shared.fControlBlock->fHeader, shared.fControlBlock->fHeader.GetSize());
1435 R__ASSERT(shared.fControlBlock->fSeekFileRecord + shared.fControlBlock->fFileRecord.GetSize() <
1436 RImplSimple::kHeaderBlockSize);
1437 memcpy(shared.fHeaderBlock + shared.fControlBlock->fSeekFileRecord, &shared.fControlBlock->fFileRecord,
1438 shared.fControlBlock->fFileRecord.GetSize());
1439
1440 fileSimple.Flush();
1441
1442 anchorInfo.fLocator.SetNBytesOnStorage(anchorSize);
1443 }
1444 }
1445
1446 return anchorInfo;
1447}
1448
1449std::uint64_t ROOT::Internal::RNTupleFileWriter::WriteBlob(const void *data, size_t nbytes, size_t len)
1450{
1451 auto writeKey = [this](const void *payload, size_t nBytes, size_t length) {
1452 std::uint64_t offset = ReserveBlob(nBytes, length);
1453 WriteIntoReservedBlob(payload, nBytes, offset);
1454 return offset;
1455 };
1456
1457 const std::uint64_t maxKeySize = fNTupleAnchor.fMaxKeySize;
1458 R__ASSERT(maxKeySize > 0);
1459 // We don't need the object length except for seeing compression ratios in TFile::Map()
1460 // Make sure that the on-disk object length fits into the TKey header.
1461 if (static_cast<std::uint64_t>(len) > static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max()))
1462 len = nbytes;
1463
1464 if (nbytes <= maxKeySize) {
1465 // Fast path: only write 1 key.
1466 return writeKey(data, nbytes, len);
1467 }
1468
1469 /**
1470 * Writing a key bigger than the max allowed size. In this case we split the payload
1471 * into multiple keys, reserving the end of the first key payload for pointers to the
1472 * next ones. E.g. if a key needs to be split into 3 chunks, the first chunk will have
1473 * the format:
1474 * +--------------------+
1475 * | |
1476 * | Data |
1477 * |--------------------|
1478 * | pointer to chunk 2 |
1479 * | pointer to chunk 3 |
1480 * +--------------------+
1481 */
1482 const size_t nChunks = ComputeNumChunks(nbytes, maxKeySize);
1483 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
1485 // Skip writing the first chunk, it will be written last (in the file) below.
1486
1487 const uint8_t *chunkData = reinterpret_cast<const uint8_t *>(data) + nbytesFirstChunk;
1489
1491 std::uint64_t chunkOffsetIdx = 0;
1492
1493 do {
1494 const size_t bytesNextChunk = std::min<size_t>(remainingBytes, maxKeySize);
1495 const std::uint64_t offset = writeKey(chunkData, bytesNextChunk, bytesNextChunk);
1496
1499
1502
1503 } while (remainingBytes > 0);
1504
1505 // Write the first key, with part of the data and the pointers to (logically) following keys appended.
1506 const std::uint64_t firstOffset = ReserveBlob(maxKeySize, maxKeySize);
1507 WriteIntoReservedBlob(data, nbytesFirstChunk, firstOffset);
1508 const std::uint64_t chunkOffsetsOffset = firstOffset + nbytesFirstChunk;
1509 WriteIntoReservedBlob(chunkOffsetsToWrite.get(), nbytesChunkOffsets, chunkOffsetsOffset);
1510
1511 return firstOffset;
1512}
1513
1514std::uint64_t
1515ROOT::Internal::RNTupleFileWriter::ReserveBlob(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen])
1516{
1517 // ReserveBlob cannot be used to reserve a multi-key blob
1518 R__ASSERT(nbytes <= fNTupleAnchor.GetMaxKeySize());
1519
1520 std::uint64_t offset;
1521 if (auto *fileSimple = std::get_if<RImplSimple>(&fFile)) {
1522 if (fIsBare) {
1523 offset = fileSimple->fShared->fKeyOffset;
1524 fileSimple->fShared->fKeyOffset += nbytes;
1525 } else {
1526 offset = fileSimple->ReserveBlobKey(nbytes, len, keyBuffer);
1527 }
1528 } else if (auto *fileProper = std::get_if<RImplTFile>(&fFile)) {
1529 offset = fileProper->ReserveBlobKey(nbytes, len, keyBuffer);
1530 } else {
1531 auto &fileRFile = std::get<RImplRFile>(fFile);
1532 offset = fileRFile.ReserveBlobKey(nbytes, len, keyBuffer);
1533 }
1534 return offset;
1535}
1536
1537void ROOT::Internal::RNTupleFileWriter::WriteIntoReservedBlob(const void *buffer, size_t nbytes, std::int64_t offset)
1538{
1539 if (auto *fileSimple = std::get_if<RImplSimple>(&fFile)) {
1540 fileSimple->Write(buffer, nbytes, offset);
1541 } else if (auto *fileProper = std::get_if<RImplTFile>(&fFile)) {
1542 fileProper->Write(buffer, nbytes, offset);
1543 } else {
1544 auto &fileRFile = std::get<RImplRFile>(fFile);
1545 fileRFile.Write(buffer, nbytes, offset);
1546 }
1547}
1548
1550{
1551 auto offset = WriteBlob(data, nbytes, lenHeader);
1552 fNTupleAnchor.fLenHeader = lenHeader;
1553 fNTupleAnchor.fNBytesHeader = nbytes;
1554 fNTupleAnchor.fSeekHeader = offset;
1555 return offset;
1556}
1557
1559{
1560 auto offset = WriteBlob(data, nbytes, lenFooter);
1561 fNTupleAnchor.fLenFooter = lenFooter;
1562 fNTupleAnchor.fNBytesFooter = nbytes;
1563 fNTupleAnchor.fSeekFooter = offset;
1564 return offset;
1565}
1566
1568{
1569 RBareFileHeader bareHeader;
1570 bareHeader.fCompress = defaultCompression;
1571 auto &fileSimple = std::get<RImplSimple>(fFile);
1572 fileSimple.Write(&bareHeader, sizeof(bareHeader), 0);
1573 RTFString ntupleName{fNTupleName};
1574 fileSimple.Write(&ntupleName, ntupleName.GetSize());
1575
1576 // Write zero-initialized ntuple to reserve the space; will be overwritten on commit
1577 RTFNTuple ntupleOnDisk;
1578 fileSimple.fShared->fControlBlock->fSeekNTuple = fileSimple.fShared->fFilePos;
1579 fileSimple.Write(&ntupleOnDisk, ntupleOnDisk.GetSize());
1580 std::uint64_t checksum = 0;
1581 fileSimple.Write(&checksum, sizeof(checksum));
1582 fileSimple.fShared->fKeyOffset = fileSimple.fShared->fFilePos;
1583}
1584
1586{
1587 // The streamer info record is a TList of TStreamerInfo object. We cannot use
1588 // RNTupleSerializer::SerializeStreamerInfos because that uses TBufferIO::WriteObject.
1589 // This would prepend the streamed TList with self-decription information.
1590 // The streamer info record is just the streamed TList.
1591
1593 for (auto [_, info] : fStreamerInfoMap) {
1595 }
1596
1597 // We will stream the list with a TBufferFile. When reading the streamer info records back,
1598 // the read buffer includes the key and the streamed list. Therefore, we need to start streaming
1599 // with an offset of the key length. Otherwise, the offset for referencing duplicate objects in the
1600 // buffer will point to the wrong places.
1601
1602 // Figure out key length
1603 RTFString strTList{"TList"};
1604 RTFString strStreamerInfo{"StreamerInfo"};
1605 RTFString strStreamerTitle{"Doubly linked list"};
1606 auto &fileSimple = std::get<RImplSimple>(fFile);
1607 fileSimple.fShared->fControlBlock->fHeader.SetSeekInfo(fileSimple.fShared->fKeyOffset);
1608 auto keyLen = RTFKey(fileSimple.fShared->fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, strTList,
1610 .fKeyLen;
1611
1612 TBufferFile buffer(TBuffer::kWrite, keyLen + 1);
1613 buffer.SetBufferOffset(keyLen);
1614 streamerInfoList.Streamer(buffer);
1615 assert(buffer.Length() > keyLen);
1616 const auto bufPayload = buffer.Buffer() + keyLen;
1617 const auto lenPayload = buffer.Length() - keyLen;
1618
1621
1623 fileSimple.fShared->fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, "TList",
1624 "StreamerInfo", "Doubly linked list");
1625 fileSimple.fShared->fControlBlock->fHeader.SetNbytesInfo(fileSimple.fShared->fFilePos -
1626 fileSimple.fShared->fControlBlock->fHeader.GetSeekInfo());
1627}
1628
1630{
1631 RTFString strEmpty;
1632 RTFString strRNTupleClass{"ROOT::RNTuple"};
1633 RTFString strRNTupleName{fNTupleName};
1634 RTFString strFileName{fFileName};
1635
1636 auto &fileSimple = std::get<RImplSimple>(fFile);
1637 RTFKey keyRNTuple(fileSimple.fShared->fControlBlock->fSeekNTuple, RTFHeader::kBEGIN, strRNTupleClass, strRNTupleName,
1638 strEmpty, RTFNTuple::GetSizePlusChecksum(), anchorSize);
1639
1640 auto &fileShared = *fileSimple.fShared;
1641 fileSimple.fShared->fControlBlock->fFileRecord.SetSeekKeys(fileShared.fKeyOffset);
1642 RTFKeyList keyList{1};
1643 RTFKey keyKeyList(fileSimple.fShared->fControlBlock->fFileRecord.GetSeekKeys(), RTFHeader::kBEGIN, strEmpty,
1644 strFileName, strEmpty, keyList.GetSize() + keyRNTuple.fKeyLen);
1645 fileSimple.Write(&keyKeyList, keyKeyList.GetHeaderSize(),
1646 fileSimple.fShared->fControlBlock->fFileRecord.GetSeekKeys());
1647 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1648 fileSimple.Write(&strFileName, strFileName.GetSize());
1649 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1650 fileSimple.Write(&keyList, keyList.GetSize());
1651 fileSimple.Write(&keyRNTuple, keyRNTuple.GetHeaderSize());
1652 // Write class name, object name, and title for this key.
1653 fileSimple.Write(&strRNTupleClass, strRNTupleClass.GetSize());
1654 fileSimple.Write(&strRNTupleName, strRNTupleName.GetSize());
1655 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1656 fileSimple.fShared->fControlBlock->fFileRecord.fNBytesKeys =
1657 fileShared.fFilePos - fileSimple.fShared->fControlBlock->fFileRecord.GetSeekKeys();
1658 fileShared.fKeyOffset = fileShared.fFilePos;
1659}
1660
1662{
1663 auto &fileSimple = std::get<RImplSimple>(fFile);
1664 auto &fileShared = *fileSimple.fShared;
1665 fileSimple.fShared->fControlBlock->fHeader.SetSeekFree(fileShared.fKeyOffset);
1666 RTFString strEmpty;
1667 RTFString strFileName{fFileName};
1668 RTFFreeEntry freeEntry;
1669 RTFKey keyFreeList(fileSimple.fShared->fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, strEmpty,
1670 strFileName, strEmpty, freeEntry.GetSize());
1671 std::uint64_t firstFree = fileSimple.fShared->fControlBlock->fHeader.GetSeekFree() + keyFreeList.GetSize();
1672 freeEntry.Set(firstFree, std::max(2000000000ULL, ((firstFree / 1000000000ULL) + 1) * 1000000000ULL));
1673 fileSimple.WriteKey(&freeEntry, freeEntry.GetSize(), freeEntry.GetSize(),
1674 fileSimple.fShared->fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, "", fFileName, "");
1675 fileSimple.fShared->fControlBlock->fHeader.SetNbytesFree(fileShared.fFilePos -
1676 fileSimple.fShared->fControlBlock->fHeader.GetSeekFree());
1677 fileSimple.fShared->fControlBlock->fHeader.SetEnd(fileShared.fFilePos);
1678}
1679
1681{
1682 RTFString strRNTupleClass{"ROOT::RNTuple"};
1683 RTFString strRNTupleName{fNTupleName};
1684 RTFString strEmpty;
1685
1686 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1687 RUInt64BE checksum{XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData())};
1688 auto &fileSimple = std::get<RImplSimple>(fFile);
1689 fileSimple.fShared->fControlBlock->fSeekNTuple = fileSimple.fShared->fKeyOffset;
1690
1691 char keyBuf[RTFNTuple::GetSizePlusChecksum()];
1692
1693 // concatenate the RNTuple anchor with its checksum
1694 memcpy(keyBuf, &ntupleOnDisk, sizeof(RTFNTuple));
1695 memcpy(keyBuf + sizeof(RTFNTuple), &checksum, sizeof(checksum));
1696
1697 const auto sizeAnchor = sizeof(RTFNTuple) + sizeof(checksum);
1698 char zipAnchor[RTFNTuple::GetSizePlusChecksum()];
1700
1701 fileSimple.WriteKey(zipAnchor, szZipAnchor, sizeof(keyBuf), fileSimple.fShared->fControlBlock->fSeekNTuple,
1702 RTFHeader::kBEGIN, "ROOT::RNTuple", fNTupleName, "");
1703 return szZipAnchor;
1704}
1705
1707{
1708 RTFString strTFile{"TFile"};
1709 RTFString strFileName{fFileName};
1710 RTFString strEmpty;
1711
1712 auto &fileSimple = std::get<RImplSimple>(fFile);
1713 fileSimple.fShared->fControlBlock->fHeader = RTFHeader(defaultCompression);
1714
1715 RTFUUID uuid;
1716
1717 // First record of the file: the TFile object at offset kBEGIN (= 100)
1718 RTFKey keyRoot(RTFHeader::kBEGIN, 0, strTFile, strFileName, strEmpty,
1719 sizeof(RTFDirectory) + strFileName.GetSize() + strEmpty.GetSize() + uuid.GetSize());
1720 std::uint32_t nbytesName = keyRoot.fKeyLen + strFileName.GetSize() + 1;
1721 fileSimple.fShared->fControlBlock->fFileRecord.fNBytesName = nbytesName;
1722 fileSimple.fShared->fControlBlock->fHeader.SetNbytesName(nbytesName);
1723
1724 fileSimple.Write(&keyRoot, keyRoot.GetHeaderSize(), RTFHeader::kBEGIN);
1725 // Write class name, object name, and title for the TFile key.
1726 fileSimple.Write(&strTFile, strTFile.GetSize());
1727 fileSimple.Write(&strFileName, strFileName.GetSize());
1728 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1729 // Write the name and title of the TNamed preceding the TFile entry.
1730 fileSimple.Write(&strFileName, strFileName.GetSize());
1731 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1732 // Will be overwritten on commit
1733 fileSimple.fShared->fControlBlock->fSeekFileRecord = fileSimple.fShared->fFilePos;
1734 fileSimple.Write(&fileSimple.fShared->fControlBlock->fFileRecord,
1735 fileSimple.fShared->fControlBlock->fFileRecord.GetSize());
1736 fileSimple.Write(&uuid, uuid.GetSize());
1737
1738 // Padding bytes to allow the TFile record to grow for a big file
1739 RUInt32BE padding{0};
1740 for (int i = 0; i < 3; ++i)
1741 fileSimple.Write(&padding, sizeof(padding));
1742 fileSimple.fShared->fKeyOffset = fileSimple.fShared->fFilePos;
1743}
T ReadBuffer(TBufferFile *buf)
One of the template functions used to read objects from messages.
Definition MPSendRecv.h:158
#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:322
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:360
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
Definition TError.h:125
const Int_t kBEGIN
Definition TFile.cxx:204
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:148
T1 fFirst
Definition X11Events.mm:86
#define _(A, B)
Definition cfortran.h:108
An interface to read from, or write to, a ROOT file, as well as performing other common operations.
Definition RFile.hxx:252
The RKeyBlob writes an invisible key into a TFile.
bool WasAllocatedInAFreeSlot() const
void Reserve(size_t nbytes, std::uint64_t *seekKey)
Register a new key for a data record of size nbytes.
void ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
Reads a given byte range from the file into the provided memory buffer.
void LoadStreamerInfo()
Attempts to load the streamer info from the file.
RResult< RNTuple > GetNTupleProperAtOffset(std::uint64_t payloadOffset, std::uint64_t compSize, std::uint64_t uncompLen)
Loads an RNTuple anchor from a TFile at the given file offset (unzipping it if necessary).
RResult< RNTuple > GetNTupleBare(std::string_view ntupleName)
Used when the file container turns out to be a bare file.
ROOT::RResult< void > TryReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
Like ReadBuffer but returns a RResult instead of throwing.
std::uint64_t SearchInDirectory(std::uint64_t &offsetDir, std::string_view keyName, std::string_view typeName)
Searches for a key with the given name and type in the key index of the directory starting at offsetD...
RResult< RNTuple > GetNTuple(std::string_view ntupleName)
Extracts header and footer location for the RNTuple identified by ntupleName.
RResult< RNTuple > GetNTupleProper(std::string_view ntuplePath)
Used when the file turns out to be a TFile container.
Helper class to compress data blocks in the ROOT compression frame format.
static std::size_t Zip(const void *from, std::size_t nbytes, int compression, void *to)
Returns the size of the compressed data, written into the provided output buffer.
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.
std::uint64_t ReserveBlob(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen]=nullptr)
Reserves a new record as an RBlob key in the file.
void WriteTFileStreamerInfo(int compression)
Write the compressed streamer info record with the description of the RNTuple class.
std::string fNTupleName
The identifier of the RNTuple; A single writer object can only write a single RNTuple but multiple wr...
void WriteTFileKeysList(std::uint64_t anchorSize)
Write the TList with the RNTuple key.
std::uint64_t WriteTFileNTupleKey(int compression)
The only key that will be visible in file->ls() Returns the size on disk of the anchor object.
void WriteBareFileSkeleton(int defaultCompression)
For a bare file, which is necessarily written by a C file stream, write file header.
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...
static std::uint64_t ReserveBlobKey(T &caller, TFile &file, std::size_t nbytes, std::size_t len, unsigned char keyBuffer[kBlobKeyLen])
static std::unique_ptr< RNTupleFileWriter > Append(std::string_view ntupleName, TDirectory &fileOrDirectory, std::uint64_t maxKeySize, bool isHidden)
The directory parameter can also be a TFile object (TFile inherits from TDirectory).
void WriteTFileFreeList()
Last record in the file.
std::unique_ptr< RNTupleFileWriter > CloneAsHidden(std::string_view ntupleName) const
Creates a new RNTupleFileWriter with the same underlying TDirectory as this but writing to a differen...
void WriteTFileSkeleton(int defaultCompression)
For a TFile container written by a C file stream, write the header and TFile object.
RNTupleFileWriter(std::string_view name, std::uint64_t maxKeySize, bool isHidden)
void Seek(std::uint64_t offset)
Seek a simple writer to offset.
ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t fStreamerInfoMap
Set of streamer info records that should be written to the file.
std::uint64_t WriteBlob(const void *data, size_t nbytes, size_t len)
Writes a new record as an RBlob key into the file.
static std::unique_ptr< RNTupleFileWriter > Recreate(std::string_view ntupleName, std::string_view path, EContainerFormat containerFormat, const ROOT::RNTupleWriteOptions &options)
Create or truncate the local file given by path with the new empty RNTuple identified by ntupleName.
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...
static constexpr std::size_t kBlobKeyLen
The key length of a blob. It is always a big key (version > 1000) with class name RBlob.
RNTupleLink Commit(int compression=RCompressionSetting::EDefaults::kUseGeneralPurpose)
Writes the RNTuple key to the file so that the header and footer keys can be found.
static void PrepareBlobKey(std::int64_t offset, size_t nbytes, size_t len, unsigned char buffer[kBlobKeyLen])
Prepares buffer for a new record as an RBlob key at offset.
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...
bool fIsHidden
True if this RNTuple's anchor must be stored as a hidden key (this is the case e.g....
void UpdateStreamerInfos(const ROOT::Internal::RNTupleSerializer::StreamerInfoMap_t &streamerInfos)
Ensures that the streamer info records passed as argument are written to the file.
RNTuple fNTupleAnchor
Header and footer location of the ntuple, written on Commit()
EContainerFormat
For testing purposes, RNTuple data can be written into a bare file container instead of a ROOT file.
A helper class for serializing and deserialization of the RNTuple binary format.
std::map< Int_t, TVirtualStreamerInfo * > StreamerInfoMap_t
static std::uint32_t DeserializeUInt64(const void *buffer, std::uint64_t &val)
static std::uint32_t SerializeUInt64(std::uint64_t val, void *buffer)
The RRawFile provides read-only access to local and remote files.
Definition RRawFile.hxx:43
Base class for all ROOT issued exceptions.
Definition RError.hxx:78
Common user-tunable settings for storing RNTuples.
std::size_t GetWriteBufferSize() const
std::uint64_t GetMaxKeySize() const
std::uint32_t GetCompression() const
Representation of an RNTuple data set in a ROOT file.
Definition RNTuple.hxx:67
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:120
static TClass * Class()
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:222
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 SetBufferDisplacement() override
Definition TBufferIO.h:82
void SetParent(TObject *parent)
Set parent owning this buffer.
Definition TBuffer.cxx:269
@ kWrite
Definition TBuffer.h:73
@ kRead
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
Describe directory structure in memory.
Definition TDirectory.h:45
A file, usually with extension .root, that stores data and code in the form of serialized objects in ...
Definition TFile.h:130
Bool_t IsBinary() const
Definition TFile.h:347
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:1496
Int_t fVersion
Key version identifier.
Definition TKey.h:41
Int_t fLeft
Number of bytes left in current segment.
Definition TKey.h:50
Short_t fKeylen
Number of bytes for the key itself.
Definition TKey.h:45
Long64_t fSeekKey
Location of object on file.
Definition TKey.h:47
virtual void Create(Int_t nbytes, TFile *f=nullptr)
Create a TKey object of specified size.
Definition TKey.cxx:505
TString fClassName
Object Class name.
Definition TKey.h:49
A doubly linked list.
Definition TList.h:38
Describes a persistent version of a class.
static TClass * Class()
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
static TUUID UUIDv4()
Create a UUID version 4 (variant 1) UUID according to RFC 4122.
Definition TUUID.cxx:157
TFile * GetRFileTFile(RFile &rfile)
Definition RFile.cxx:577
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)
Definition RNTuple.cxx:51
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 ReserveBlobKey(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen]=nullptr)
Reserves an RBlob opaque key as data record and returns the offset of the record.
void AllocateBuffers(std::size_t bufferSize)
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 ...
static constexpr int kBlockAlign
Direct I/O requires that all buffers and write lengths are aligned.
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.
static constexpr std::size_t kHeaderBlockSize
During commit, WriteTFileKeysList() updates fNBytesKeys and fSeekKeys of the RTFFile located at fSeek...
std::shared_ptr< RSharedData > fShared
std::uint64_t ReserveBlobKey(std::size_t nbytes, std::size_t len, unsigned char keyBuffer[kBlobKeyLen]=nullptr)
Reserves an RBlob opaque key as data record and returns the offset of the record.
void Write(const void *buffer, size_t nbytes, std::int64_t offset)
Low-level writing using a TFile.
std::uint64_t ReserveBlobKey(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen]=nullptr)
Reserves an RBlob opaque key as data record and returns the offset of the record.
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