Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
RMiniFile.cxx
Go to the documentation of this file.
1/// \file RMiniFile.cxx
2/// \ingroup NTuple
3/// \author Jakob Blomer <jblomer@cern.ch>
4/// \date 2019-12-22
5
6/*************************************************************************
7 * Copyright (C) 1995-2019, Rene Brun and Fons Rademakers. *
8 * All rights reserved. *
9 * *
10 * For the licensing terms see $ROOTSYS/LICENSE. *
11 * For the list of contributors see $ROOTSYS/README/CREDITS. *
12 *************************************************************************/
13
14#include "Rtypes.h"
15#include <ROOT/RConfig.hxx>
16#include <ROOT/RError.hxx>
17#include <ROOT/RMiniFile.hxx>
18#include <ROOT/RRawFile.hxx>
19#include <ROOT/RNTupleUtils.hxx>
20#include <ROOT/RNTupleZip.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
62
63namespace {
64
65// The following types are used to read and write the TFile binary format
66
67/// Big-endian 16-bit unsigned integer
68class RUInt16BE {
69private:
70 std::uint16_t fValBE = 0;
71 static std::uint16_t Swap(std::uint16_t val)
72 {
73#if R__LITTLE_ENDIAN == 1
74 return RByteSwap<sizeof(val)>::bswap(val);
75#else
76 return val;
77#endif
78 }
79
80public:
81 RUInt16BE() = default;
82 explicit RUInt16BE(const std::uint16_t val) : fValBE(Swap(val)) {}
83 operator std::uint16_t() const { return Swap(fValBE); }
84 RUInt16BE &operator=(const std::uint16_t val)
85 {
86 fValBE = Swap(val);
87 return *this;
88 }
89};
90
91/// Big-endian 32-bit unsigned integer
92class RUInt32BE {
93private:
94 std::uint32_t fValBE = 0;
95 static std::uint32_t Swap(std::uint32_t val)
96 {
97#if R__LITTLE_ENDIAN == 1
98 return RByteSwap<sizeof(val)>::bswap(val);
99#else
100 return val;
101#endif
102 }
103
104public:
105 RUInt32BE() = default;
106 explicit RUInt32BE(const std::uint32_t val) : fValBE(Swap(val)) {}
107 operator std::uint32_t() const { return Swap(fValBE); }
108 RUInt32BE &operator=(const std::uint32_t val)
109 {
110 fValBE = Swap(val);
111 return *this;
112 }
113};
114
115/// Big-endian 32-bit signed integer
116class RInt32BE {
117private:
118 std::int32_t fValBE = 0;
119 static std::int32_t Swap(std::int32_t val)
120 {
121#if R__LITTLE_ENDIAN == 1
122 return RByteSwap<sizeof(val)>::bswap(val);
123#else
124 return val;
125#endif
126 }
127
128public:
129 RInt32BE() = default;
130 explicit RInt32BE(const std::int32_t val) : fValBE(Swap(val)) {}
131 operator std::int32_t() const { return Swap(fValBE); }
132 RInt32BE &operator=(const std::int32_t val)
133 {
134 fValBE = Swap(val);
135 return *this;
136 }
137};
138
139/// Big-endian 64-bit unsigned integer
140class RUInt64BE {
141private:
142 std::uint64_t fValBE = 0;
143 static std::uint64_t Swap(std::uint64_t val)
144 {
145#if R__LITTLE_ENDIAN == 1
146 return RByteSwap<sizeof(val)>::bswap(val);
147#else
148 return val;
149#endif
150 }
151
152public:
153 RUInt64BE() = default;
154 explicit RUInt64BE(const std::uint64_t val) : fValBE(Swap(val)) {}
155 operator std::uint64_t() const { return Swap(fValBE); }
156 RUInt64BE &operator=(const std::uint64_t val)
157 {
158 fValBE = Swap(val);
159 return *this;
160 }
161};
162
163#pragma pack(push, 1)
164/// A name (type, identifies, ...) in the TFile binary format
165struct RTFString {
166 unsigned char fLName{0};
167 char fData[255];
168 RTFString() = default;
169 RTFString(const std::string &str)
170 {
171 // The length of strings with 255 characters and longer are encoded with a 32-bit integer following the first
172 // byte. This is currently not handled.
173 R__ASSERT(str.length() < 255);
174 fLName = str.length();
175 memcpy(fData, str.data(), fLName);
176 }
177 std::size_t GetSize() const
178 {
179 // A length of 255 is special and means that the first byte is followed by a 32-bit integer with the actual
180 // length.
181 R__ASSERT(fLName != 255);
182 return 1 + fLName;
183 }
184};
185
186/// The timestamp format used in TFile; the default constructor initializes with the current time
187struct RTFDatetime {
188 RUInt32BE fDatetime;
189 RTFDatetime()
190 {
191 auto now = std::chrono::system_clock::now();
192 auto tt = std::chrono::system_clock::to_time_t(now);
193 auto tm = *localtime(&tt);
194 fDatetime = (tm.tm_year + 1900 - 1995) << 26 | (tm.tm_mon + 1) << 22 | tm.tm_mday << 17 | tm.tm_hour << 12 |
195 tm.tm_min << 6 | tm.tm_sec;
196 }
197 explicit RTFDatetime(RUInt32BE val) : fDatetime(val) {}
198};
199
200/// The key part of a TFile record excluding the class, object, and title names
201struct RTFKey {
202 static constexpr unsigned kBigKeyVersion = 1000;
203
204 RInt32BE fNbytes{0};
205 RUInt16BE fVersion{4};
206 RUInt32BE fObjLen{0};
207 RTFDatetime fDatetime;
208 RUInt16BE fKeyLen{0};
209 RUInt16BE fCycle{1};
210 union {
211 struct {
212 RUInt32BE fSeekKey{0};
213 RUInt32BE fSeekPdir{0};
214 } fInfoShort;
215 struct {
216 RUInt64BE fSeekKey{0};
217 RUInt64BE fSeekPdir{0};
218 } fInfoLong;
219 };
220
221 RTFKey() : fInfoLong() {}
222 RTFKey(std::uint64_t seekKey, std::uint64_t seekPdir, const RTFString &clName, const RTFString &objName,
223 const RTFString &titleName, std::size_t szObjInMem, std::size_t szObjOnDisk = 0)
224 {
225 R__ASSERT(szObjInMem <= std::numeric_limits<std::uint32_t>::max());
226 R__ASSERT(szObjOnDisk <= std::numeric_limits<std::uint32_t>::max());
227 // For writing, we alywas produce "big" keys with 64-bit SeekKey and SeekPdir.
228 fVersion = fVersion + kBigKeyVersion;
229 fObjLen = szObjInMem;
230 fKeyLen = GetHeaderSize() + clName.GetSize() + objName.GetSize() + titleName.GetSize();
231 fInfoLong.fSeekKey = seekKey;
232 fInfoLong.fSeekPdir = seekPdir;
233 // Depends on fKeyLen being set
234 fNbytes = fKeyLen + ((szObjOnDisk == 0) ? szObjInMem : szObjOnDisk);
235 }
236
237 std::uint32_t GetSize() const
238 {
239 // Negative size indicates a gap in the file
240 if (fNbytes < 0)
241 return -fNbytes;
242 return fNbytes;
243 }
244
245 std::uint32_t GetHeaderSize() const
246 {
247 if (fVersion >= kBigKeyVersion)
248 return 18 + sizeof(fInfoLong);
249 return 18 + sizeof(fInfoShort);
250 }
251
252 std::uint64_t GetSeekKey() const
253 {
254 if (fVersion >= kBigKeyVersion)
255 return fInfoLong.fSeekKey;
256 return fInfoShort.fSeekKey;
257 }
258};
259
260/// The TFile global header
261struct RTFHeader {
262 static constexpr unsigned kBEGIN = 100;
263 static constexpr unsigned kBigHeaderVersion = 1000000;
264
265 char fMagic[4]{'r', 'o', 'o', 't'};
266 RUInt32BE fVersion{(ROOT_VERSION_CODE >> 16) * 10000 + ((ROOT_VERSION_CODE & 0xFF00) >> 8) * 100 +
267 (ROOT_VERSION_CODE & 0xFF)};
268 RUInt32BE fBEGIN{kBEGIN};
269 union {
270 struct {
271 RUInt32BE fEND{0};
272 RUInt32BE fSeekFree{0};
273 RUInt32BE fNbytesFree{0};
274 RUInt32BE fNfree{1};
275 RUInt32BE fNbytesName{0};
276 unsigned char fUnits{4};
277 RUInt32BE fCompress{0};
278 RUInt32BE fSeekInfo{0};
279 RUInt32BE fNbytesInfo{0};
280 } fInfoShort;
281 struct {
282 RUInt64BE fEND{0};
283 RUInt64BE fSeekFree{0};
284 RUInt32BE fNbytesFree{0};
285 RUInt32BE fNfree{1};
286 RUInt32BE fNbytesName{0};
287 unsigned char fUnits{8};
288 RUInt32BE fCompress{0};
289 RUInt64BE fSeekInfo{0};
290 RUInt32BE fNbytesInfo{0};
291 } fInfoLong;
292 };
293
294 RTFHeader() : fInfoShort() {}
295 RTFHeader(int compression) : fInfoShort() { fInfoShort.fCompress = compression; }
296
297 void SetBigFile()
298 {
299 if (fVersion >= kBigHeaderVersion)
300 return;
301
302 // clang-format off
303 std::uint32_t end = fInfoShort.fEND;
304 std::uint32_t seekFree = fInfoShort.fSeekFree;
305 std::uint32_t nbytesFree = fInfoShort.fNbytesFree;
306 std::uint32_t nFree = fInfoShort.fNfree;
307 std::uint32_t nbytesName = fInfoShort.fNbytesName;
308 std::uint32_t compress = fInfoShort.fCompress;
309 std::uint32_t seekInfo = fInfoShort.fSeekInfo;
310 std::uint32_t nbytesInfo = fInfoShort.fNbytesInfo;
311 fInfoLong.fEND = end;
312 fInfoLong.fSeekFree = seekFree;
313 fInfoLong.fNbytesFree = nbytesFree;
314 fInfoLong.fNfree = nFree;
315 fInfoLong.fNbytesName = nbytesName;
316 fInfoLong.fUnits = 8;
317 fInfoLong.fCompress = compress;
318 fInfoLong.fSeekInfo = seekInfo;
319 fInfoLong.fNbytesInfo = nbytesInfo;
320 fVersion = fVersion + kBigHeaderVersion;
321 // clang-format on
322 }
323
324 bool IsBigFile(std::uint64_t offset = 0) const
325 {
326 return (fVersion >= kBigHeaderVersion) ||
327 (offset > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max()));
328 }
329
330 std::uint32_t GetSize() const
331 {
332 std::uint32_t sizeHead = sizeof(fMagic) + sizeof(fVersion) + sizeof(fBEGIN);
333 if (IsBigFile())
334 return sizeHead + sizeof(fInfoLong);
335 return sizeHead + sizeof(fInfoShort);
336 }
337
338 std::uint64_t GetEnd() const
339 {
340 if (IsBigFile())
341 return fInfoLong.fEND;
342 return fInfoShort.fEND;
343 }
344
345 void SetEnd(std::uint64_t value)
346 {
347 if (IsBigFile(value)) {
348 SetBigFile();
349 fInfoLong.fEND = value;
350 } else {
351 fInfoShort.fEND = value;
352 }
353 }
354
355 std::uint64_t GetSeekFree() const
356 {
357 if (IsBigFile())
358 return fInfoLong.fSeekFree;
359 return fInfoShort.fSeekFree;
360 }
361
362 void SetSeekFree(std::uint64_t value)
363 {
364 if (IsBigFile(value)) {
365 SetBigFile();
366 fInfoLong.fSeekFree = value;
367 } else {
368 fInfoShort.fSeekFree = value;
369 }
370 }
371
372 void SetNbytesFree(std::uint32_t value)
373 {
374 if (IsBigFile()) {
375 fInfoLong.fNbytesFree = value;
376 } else {
377 fInfoShort.fNbytesFree = value;
378 }
379 }
380
381 void SetNbytesName(std::uint32_t value)
382 {
383 if (IsBigFile()) {
384 fInfoLong.fNbytesName = value;
385 } else {
386 fInfoShort.fNbytesName = value;
387 }
388 }
389
390 std::uint64_t GetSeekInfo() const
391 {
392 if (IsBigFile())
393 return fInfoLong.fSeekInfo;
394 return fInfoShort.fSeekInfo;
395 }
396
397 void SetSeekInfo(std::uint64_t value)
398 {
399 if (IsBigFile(value)) {
400 SetBigFile();
401 fInfoLong.fSeekInfo = value;
402 } else {
403 fInfoShort.fSeekInfo = value;
404 }
405 }
406
407 std::uint64_t GetNbytesInfo() const
408 {
409 if (IsBigFile())
410 return fInfoLong.fNbytesInfo;
411 return fInfoShort.fNbytesInfo;
412 }
413
414 void SetNbytesInfo(std::uint32_t value)
415 {
416 if (IsBigFile()) {
417 fInfoLong.fNbytesInfo = value;
418 } else {
419 fInfoShort.fNbytesInfo = value;
420 }
421 }
422
423 void SetCompression(std::uint32_t value)
424 {
425 if (IsBigFile()) {
426 fInfoLong.fCompress = value;
427 } else {
428 fInfoShort.fCompress = value;
429 }
430 }
431};
432
433/// A reference to an unused byte-range in a TFile
434struct RTFFreeEntry {
435 static constexpr unsigned kBigFreeEntryVersion = 1000;
436
437 RUInt16BE fVersion{1};
438 union {
439 struct {
440 RUInt32BE fFirst{0};
441 RUInt32BE fLast{0};
442 } fInfoShort;
443 struct {
444 RUInt64BE fFirst{0};
445 RUInt64BE fLast{0};
446 } fInfoLong;
447 };
448
449 RTFFreeEntry() : fInfoShort() {}
450 void Set(std::uint64_t first, std::uint64_t last)
451 {
452 if (last > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
453 fVersion = fVersion + kBigFreeEntryVersion;
454 fInfoLong.fFirst = first;
455 fInfoLong.fLast = last;
456 } else {
457 fInfoShort.fFirst = first;
458 fInfoShort.fLast = last;
459 }
460 }
461 std::uint32_t GetSize() { return (fVersion >= kBigFreeEntryVersion) ? 18 : 10; }
462};
463
464/// The header of the directory key index
465struct RTFKeyList {
466 RUInt32BE fNKeys;
467 std::uint32_t GetSize() const { return sizeof(RTFKeyList); }
468 explicit RTFKeyList(std::uint32_t nKeys) : fNKeys(nKeys) {}
469};
470
471/// A streamed TDirectory (TFile) object
472struct RTFDirectory {
473 static constexpr unsigned kBigFileVersion = 1000;
474
475 RUInt16BE fClassVersion{5};
476 RTFDatetime fDateC;
477 RTFDatetime fDateM;
478 RUInt32BE fNBytesKeys{0};
479 RUInt32BE fNBytesName{0};
480 // The version of the key has to tell whether offsets are 32bit or 64bit long
481 union {
482 struct {
483 RUInt32BE fSeekDir{RTFHeader::kBEGIN};
484 RUInt32BE fSeekParent{0};
485 RUInt32BE fSeekKeys{0};
486 } fInfoShort;
487 struct {
488 RUInt64BE fSeekDir{RTFHeader::kBEGIN};
489 RUInt64BE fSeekParent{0};
490 RUInt64BE fSeekKeys{0};
491 } fInfoLong;
492 };
493
494 RTFDirectory() : fInfoShort() {}
495
496 // In case of a short TFile record (<2G), 3 padding ints are written after the UUID
497 std::uint32_t GetSize() const
498 {
499 if (fClassVersion >= kBigFileVersion)
500 return sizeof(RTFDirectory);
501 return 18 + sizeof(fInfoShort);
502 }
503
504 std::uint64_t GetSeekKeys() const
505 {
506 if (fClassVersion >= kBigFileVersion)
507 return fInfoLong.fSeekKeys;
508 return fInfoShort.fSeekKeys;
509 }
510
511 void SetSeekKeys(std::uint64_t seekKeys)
512 {
513 if (seekKeys > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
514 std::uint32_t seekDir = fInfoShort.fSeekDir;
515 std::uint32_t seekParent = fInfoShort.fSeekParent;
516 fInfoLong.fSeekDir = seekDir;
517 fInfoLong.fSeekParent = seekParent;
518 fInfoLong.fSeekKeys = seekKeys;
519 fClassVersion = fClassVersion + kBigFileVersion;
520 } else {
521 fInfoShort.fSeekKeys = seekKeys;
522 }
523 }
524};
525
526/// A zero UUID stored at the end of the TFile record
527struct RTFUUID {
528 RUInt16BE fVersionClass{1};
529 unsigned char fUUID[16];
530
531 RTFUUID()
532 {
533 TUUID uuid;
534 char *buffer = reinterpret_cast<char *>(this);
535 uuid.FillBuffer(buffer);
536 assert(reinterpret_cast<RTFUUID *>(buffer) <= (this + 1));
537 }
538 std::uint32_t GetSize() const { return sizeof(RTFUUID); }
539};
540
541/// A streamed RNTuple class
542///
543/// NOTE: this must be kept in sync with RNTuple.hxx.
544/// Aside ensuring consistency between the two classes' members, you need to make sure
545/// that fVersionClass matches the class version of RNTuple.
546struct RTFNTuple {
547 RUInt32BE fByteCount{0x40000000 | (sizeof(RTFNTuple) - sizeof(fByteCount))};
548 RUInt16BE fVersionClass{2};
549 RUInt16BE fVersionEpoch{0};
550 RUInt16BE fVersionMajor{0};
551 RUInt16BE fVersionMinor{0};
552 RUInt16BE fVersionPatch{0};
553 RUInt64BE fSeekHeader{0};
554 RUInt64BE fNBytesHeader{0};
555 RUInt64BE fLenHeader{0};
556 RUInt64BE fSeekFooter{0};
557 RUInt64BE fNBytesFooter{0};
558 RUInt64BE fLenFooter{0};
559 RUInt64BE fMaxKeySize{0};
560
561 static constexpr std::uint32_t GetSizePlusChecksum() { return sizeof(RTFNTuple) + sizeof(std::uint64_t); }
562
563 RTFNTuple() = default;
564 explicit RTFNTuple(const ROOT::RNTuple &inMemoryAnchor)
565 {
566 fVersionEpoch = inMemoryAnchor.GetVersionEpoch();
567 fVersionMajor = inMemoryAnchor.GetVersionMajor();
568 fVersionMinor = inMemoryAnchor.GetVersionMinor();
569 fVersionPatch = inMemoryAnchor.GetVersionPatch();
570 fSeekHeader = inMemoryAnchor.GetSeekHeader();
571 fNBytesHeader = inMemoryAnchor.GetNBytesHeader();
572 fLenHeader = inMemoryAnchor.GetLenHeader();
573 fSeekFooter = inMemoryAnchor.GetSeekFooter();
574 fNBytesFooter = inMemoryAnchor.GetNBytesFooter();
575 fLenFooter = inMemoryAnchor.GetLenFooter();
576 fMaxKeySize = inMemoryAnchor.GetMaxKeySize();
577 }
578 std::uint32_t GetSize() const { return sizeof(RTFNTuple); }
579 // The byte count and class version members are not checksummed
580 std::uint32_t GetOffsetCkData() { return sizeof(fByteCount) + sizeof(fVersionClass); }
581 std::uint32_t GetSizeCkData() { return GetSize() - GetOffsetCkData(); }
582 unsigned char *GetPtrCkData() { return reinterpret_cast<unsigned char *>(this) + GetOffsetCkData(); }
583};
584
585/// The bare file global header
586struct RBareFileHeader {
587 char fMagic[7]{'r', 'n', 't', 'u', 'p', 'l', 'e'};
588 RUInt32BE fRootVersion{(ROOT_VERSION_CODE >> 16) * 10000 + ((ROOT_VERSION_CODE & 0xFF00) >> 8) * 100 +
589 (ROOT_VERSION_CODE & 0xFF)};
590 RUInt32BE fFormatVersion{1};
591 RUInt32BE fCompress{0};
592 RTFNTuple fNTuple;
593 // followed by the ntuple name
594};
595#pragma pack(pop)
596
597/// The artifical class name shown for opaque RNTuple keys (see TBasket)
598constexpr char const *kBlobClassName = "RBlob";
599/// The class name of the RNTuple anchor
600constexpr char const *kNTupleClassName = "ROOT::RNTuple";
601
602} // anonymous namespace
603
604namespace ROOT {
605namespace Internal {
606/// If a TFile container is written by a C stream (simple file), on dataset commit, the file header
607/// and the TFile record need to be updated
609 RTFHeader fHeader;
610 RTFDirectory fFileRecord;
611 std::uint64_t fSeekNTuple{0}; // Remember the offset for the keys list
612 std::uint64_t fSeekFileRecord{0};
613};
614
615/// The RKeyBlob writes an invisible key into a TFile. That is, a key that is not indexed in the list of keys,
616/// like a TBasket.
617/// NOTE: out of anonymous namespace because otherwise ClassDefInline fails to compile
618/// on some platforms.
619class RKeyBlob : public TKey {
620public:
621 RKeyBlob() = default;
622
623 explicit RKeyBlob(TFile *file) : TKey(file)
624 {
626 fVersion += RTFKey::kBigKeyVersion;
627 fKeylen = Sizeof();
628 }
629
630 /// Register a new key for a data record of size nbytes
631 void Reserve(size_t nbytes, std::uint64_t *seekKey)
632 {
633 Create(nbytes);
634 *seekKey = fSeekKey;
635 }
636
637 bool WasAllocatedInAFreeSlot() const { return fLeft > 0; }
638
640};
641
642} // namespace Internal
643} // namespace ROOT
644
645// Computes how many chunks do we need to fit `nbytes` of payload, considering that the
646// first chunk also needs to house the offsets of the other chunks and no chunk can
647// be bigger than `maxChunkSize`. When saved to a TFile, each chunk is part of a separate TKey.
648static size_t ComputeNumChunks(size_t nbytes, size_t maxChunkSize)
649{
650 constexpr size_t kChunkOffsetSize = sizeof(std::uint64_t);
651
653 size_t nChunks = (nbytes + maxChunkSize - 1) / maxChunkSize;
654 assert(nChunks > 1);
655 size_t nbytesTail = nbytes % maxChunkSize;
656 size_t nbytesExtra = (nbytesTail > 0) * (maxChunkSize - nbytesTail);
659 ++nChunks;
661 }
662
663 // We don't support having more chunkOffsets than what fits in one chunk.
664 // For a reasonable-sized maxKeySize it looks very unlikely that we can have more chunks
665 // than we can fit in the first `maxKeySize` bytes. E.g. for maxKeySize = 1GiB we can fit
666 // 134217728 chunk offsets, making our multi-key blob's capacity exactly 128 PiB.
668
669 return nChunks;
670}
671
673
675{
676 char ident[4];
677 ReadBuffer(ident, 4, 0);
678 if (std::string(ident, 4) == "root")
679 return GetNTupleProper(ntupleName);
680 fIsBare = true;
681 return GetNTupleBare(ntupleName);
682}
683
684/// Searches for a key with the given name and type in the key index of the given directory.
685/// Return 0 if the key was not found.
686std::uint64_t ROOT::Internal::RMiniFileReader::SearchInDirectory(std::uint64_t &offsetDir, std::string_view keyName,
687 std::string_view typeName)
688{
689 RTFDirectory directory;
691
692 RTFKey key;
693 RUInt32BE nKeys;
694 std::uint64_t offset = directory.GetSeekKeys();
695 ReadBuffer(&key, sizeof(key), offset);
696 offset += key.fKeyLen;
697 ReadBuffer(&nKeys, sizeof(nKeys), offset);
698 offset += sizeof(nKeys);
699
700 for (unsigned int i = 0; i < nKeys; ++i) {
701 ReadBuffer(&key, sizeof(key), offset);
702 auto offsetNextKey = offset + key.fKeyLen;
703
704 offset += key.GetHeaderSize();
705 RTFString name;
706 ReadBuffer(&name, 1, offset);
707 ReadBuffer(&name, name.GetSize(), offset);
708 if (std::string_view(name.fData, name.fLName) != typeName) {
710 continue;
711 }
712 offset += name.GetSize();
713 ReadBuffer(&name, 1, offset);
714 ReadBuffer(&name, name.GetSize(), offset);
715 if (std::string_view(name.fData, name.fLName) == keyName) {
716 return key.GetSeekKey();
717 }
719 }
720
721 // Not found
722 return 0;
723}
724
726{
727 RTFHeader fileHeader;
728 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
729
730 const std::uint64_t seekKeyInfo = fileHeader.GetSeekInfo();
731
732 RTFKey key;
733 ReadBuffer(&key, sizeof(key), seekKeyInfo);
734
735 const std::uint64_t nbytesInfo = fileHeader.GetNbytesInfo() - key.fKeyLen;
736 const std::uint64_t seekInfo = seekKeyInfo + key.fKeyLen;
737 const std::uint32_t uncompLenInfo = key.fObjLen;
739 if (nbytesInfo == uncompLenInfo) {
740 // Uncompressed
742 } else {
744 ReadBuffer(buffer.get(), nbytesInfo, seekInfo);
746 }
747
749 // This is necessary to allow the "class tags" inside the StreamerInfo list to refer to the proper offset into
750 // the buffer. Normally TFile loads the StreamerInfo via TKey::ReadObjWithBuffer, whose buffer also includes the
751 // key itself. Since we dealt with the key above already, we are only passing the payload to TBufferFile so offsets
752 // need to be patched up.
753 buffer.SetBufferDisplacement(key.fKeyLen);
755 streamerInfoList.Streamer(buffer);
756 TObjLink *lnk = streamerInfoList.FirstLink();
757 while (lnk) {
758 auto obj = lnk->GetObject();
759 // NOTE: the last element of the streamer info list may be a TList with the IO customization rules, so we need
760 // to check before static casting.
761 if (obj->IsA() == TStreamerInfo::Class()) {
762 auto info = static_cast<TStreamerInfo *>(obj);
763 info->BuildCheck();
764 }
765 lnk = lnk->Next();
766 }
767}
768
770{
771 RTFHeader fileHeader;
772 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
773
774 RTFKey key;
775 RTFString name;
776 ReadBuffer(&key, sizeof(key), fileHeader.fBEGIN);
777 // Skip over the entire key length, including the class name, object name, and title stored in it.
778 std::uint64_t offset = fileHeader.fBEGIN + key.fKeyLen;
779 // Skip over the name and title of the TNamed preceding the TFile (root TDirectory) entry.
780 ReadBuffer(&name, 1, offset);
781 offset += name.GetSize();
782 ReadBuffer(&name, 1, offset);
783 offset += name.GetSize();
784
785 // split ntupleName by '/' character to open datasets in subdirectories.
786 std::string ntuplePathTail(ntuplePath);
787 if (!ntuplePathTail.empty() && ntuplePathTail[0] == '/')
788 ntuplePathTail = ntuplePathTail.substr(1);
789 auto pos = std::string::npos;
790 while ((pos = ntuplePathTail.find('/')) != std::string::npos) {
791 auto directoryName = ntuplePathTail.substr(0, pos);
792 ntuplePathTail.erase(0, pos + 1);
793
794 offset = SearchInDirectory(offset, directoryName, "TDirectory");
795 if (offset == 0) {
796 return R__FAIL("no directory named '" + std::string(directoryName) + "' in file '" + fRawFile->GetUrl() + "'");
797 }
798 ReadBuffer(&key, sizeof(key), offset);
799 offset = key.GetSeekKey() + key.fKeyLen;
800 }
801 // no more '/' delimiter in ntuplePath
803
804 offset = SearchInDirectory(offset, ntupleName, kNTupleClassName);
805 if (offset == 0) {
806 return R__FAIL("no RNTuple named '" + std::string(ntupleName) + "' in file '" + fRawFile->GetUrl() + "'");
807 }
808
809 ReadBuffer(&key, sizeof(key), offset);
810 offset = key.GetSeekKey() + key.fKeyLen;
811
812 // size of a RTFNTuple version 2 (min supported version); future anchor versions can grow.
813 constexpr size_t kMinNTupleSize = 78;
814 static_assert(kMinNTupleSize == RTFNTuple::GetSizePlusChecksum());
815 if (key.fObjLen < kMinNTupleSize) {
816 return R__FAIL("invalid anchor size: " + std::to_string(key.fObjLen) + " < " + std::to_string(sizeof(RTFNTuple)));
817 }
818
819 const auto objNbytes = key.GetSize() - key.fKeyLen;
820 auto res = GetNTupleProperAtOffset(offset, objNbytes, key.fObjLen);
821 return res;
822}
823
825 std::uint64_t compSize,
826 std::uint64_t uncompLen)
827{
828 // The object length can be smaller than the size of RTFNTuple if it comes from a past RNTuple class version,
829 // or larger than it if it comes from a future RNTuple class version.
830 auto bufAnchor = MakeUninitArray<unsigned char>(std::max<size_t>(uncompLen, sizeof(RTFNTuple)));
831 RTFNTuple *ntuple = new (bufAnchor.get()) RTFNTuple;
832
833 if (compSize != uncompLen) {
834 // Read into a temporary buffer
835 auto unzipBuf = MakeUninitArray<unsigned char>(std::max<size_t>(uncompLen, sizeof(RTFNTuple)));
837 // Unzip into the final buffer
839 } else {
841 }
842
843 // We require that future class versions only append members and store the checksum in the last 8 bytes
844 // Checksum calculation: strip byte count, class version, fChecksum member
845 const auto lenCkData = uncompLen - ntuple->GetOffsetCkData() - sizeof(uint64_t);
846 const auto ckCalc = XXH3_64bits(ntuple->GetPtrCkData(), lenCkData);
847 uint64_t ckOnDisk;
848
849 RUInt64BE *ckOnDiskPtr = reinterpret_cast<RUInt64BE *>(bufAnchor.get() + uncompLen - sizeof(uint64_t));
850 ckOnDisk = static_cast<uint64_t>(*ckOnDiskPtr);
851 if (ckCalc != ckOnDisk) {
852 return R__FAIL("RNTuple anchor checksum mismatch");
853 }
854
855 return CreateAnchor(ntuple->fVersionEpoch, ntuple->fVersionMajor, ntuple->fVersionMinor, ntuple->fVersionPatch,
856 ntuple->fSeekHeader, ntuple->fNBytesHeader, ntuple->fLenHeader, ntuple->fSeekFooter,
857 ntuple->fNBytesFooter, ntuple->fLenFooter, ntuple->fMaxKeySize);
858}
859
861{
862 RBareFileHeader fileHeader;
863 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
864 RTFString name;
865 auto offset = sizeof(fileHeader);
866 ReadBuffer(&name, 1, offset);
867 ReadBuffer(&name, name.GetSize(), offset);
868 std::string_view foundName(name.fData, name.fLName);
869 if (foundName != ntupleName) {
870 return R__FAIL("expected RNTuple named '" + std::string(ntupleName) + "' but instead found '" +
871 std::string(foundName) + "' in file '" + fRawFile->GetUrl() + "'");
872 }
873 offset += name.GetSize();
874
875 RTFNTuple ntuple;
876 ReadBuffer(&ntuple, sizeof(ntuple), offset);
877 std::uint64_t onDiskChecksum;
879 auto checksum = XXH3_64bits(ntuple.GetPtrCkData(), ntuple.GetSizeCkData());
880 if (checksum != static_cast<uint64_t>(onDiskChecksum))
881 return R__FAIL("RNTuple bare file: anchor checksum mismatch");
882
883 return CreateAnchor(ntuple.fVersionEpoch, ntuple.fVersionMajor, ntuple.fVersionMinor, ntuple.fVersionPatch,
884 ntuple.fSeekHeader, ntuple.fNBytesHeader, ntuple.fLenHeader, ntuple.fSeekFooter,
885 ntuple.fNBytesFooter, ntuple.fLenFooter, ntuple.fMaxKeySize);
886}
887
888void ROOT::Internal::RMiniFileReader::ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
889{
890 size_t nread;
891 if (fMaxKeySize == 0 || nbytes <= fMaxKeySize) {
892 // Fast path: read single blob
893 nread = fRawFile->ReadAt(buffer, nbytes, offset);
894 } else {
895 // Read chunked blob. See RNTupleFileWriter::WriteBlob() for details.
896 const size_t nChunks = ComputeNumChunks(nbytes, fMaxKeySize);
897 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
898 const size_t nbytesFirstChunk = fMaxKeySize - nbytesChunkOffsets;
899 uint8_t *bufCur = reinterpret_cast<uint8_t *>(buffer);
900
901 // Read first chunk
902 nread = fRawFile->ReadAt(bufCur, fMaxKeySize, offset);
903 R__ASSERT(nread == fMaxKeySize);
904 // NOTE: we read the entire chunk in `bufCur`, but we only advance the pointer by `nbytesFirstChunk`,
905 // since the last part of `bufCur` will later be overwritten by the next chunk's payload.
906 // We do this to avoid a second ReadAt to read in the chunk offsets.
909
912
914 std::uint64_t *curChunkOffset = &chunkOffsets[0];
915
916 do {
917 std::uint64_t chunkOffset;
920
921 const size_t bytesToRead = std::min<size_t>(fMaxKeySize, remainingBytes);
922 // Ensure we don't read outside of the buffer
923 R__ASSERT(static_cast<size_t>(bufCur - reinterpret_cast<uint8_t *>(buffer)) <= nbytes - bytesToRead);
924
925 auto nbytesRead = fRawFile->ReadAt(bufCur, bytesToRead, chunkOffset);
927
931 } while (remainingBytes > 0);
932 }
934}
935
936////////////////////////////////////////////////////////////////////////////////
937
938/// Prepare a blob key in the provided buffer, which must provide space for kBlobKeyLen bytes. Note that the array type
939/// is purely documentation, the argument is actually just a pointer.
941 unsigned char buffer[kBlobKeyLen])
942{
943 RTFString strClass{kBlobClassName};
944 RTFString strObject;
945 RTFString strTitle;
946 RTFKey keyHeader(offset, RTFHeader::kBEGIN, strClass, strObject, strTitle, len, nbytes);
947 R__ASSERT(keyHeader.fKeyLen == kBlobKeyLen);
948
949 // Copy structures into the buffer.
950 unsigned char *writeBuffer = buffer;
951 memcpy(writeBuffer, &keyHeader, keyHeader.GetHeaderSize());
952 writeBuffer += keyHeader.GetHeaderSize();
953 memcpy(writeBuffer, &strClass, strClass.GetSize());
954 writeBuffer += strClass.GetSize();
956 writeBuffer += strObject.GetSize();
957 memcpy(writeBuffer, &strTitle, strTitle.GetSize());
958 writeBuffer += strTitle.GetSize();
959 R__ASSERT(writeBuffer == buffer + kBlobKeyLen);
960}
961
962////////////////////////////////////////////////////////////////////////////////
963
965
967{
968 static_assert(kHeaderBlockSize % kBlockAlign == 0, "invalid header block size");
969 if (bufferSize % kBlockAlign != 0)
970 throw RException(R__FAIL("Buffer size not a multiple of alignment: " + std::to_string(bufferSize)));
971 fBlockSize = bufferSize;
972
973 std::align_val_t blockAlign{kBlockAlign};
974 fHeaderBlock = static_cast<unsigned char *>(::operator new[](kHeaderBlockSize, blockAlign));
975 memset(fHeaderBlock, 0, kHeaderBlockSize);
976 fBlock = static_cast<unsigned char *>(::operator new[](fBlockSize, blockAlign));
977 memset(fBlock, 0, fBlockSize);
978}
979
981{
982 if (fFile)
983 fclose(fFile);
984
985 std::align_val_t blockAlign{kBlockAlign};
986 if (fHeaderBlock)
987 ::operator delete[](fHeaderBlock, blockAlign);
988 if (fBlock)
989 ::operator delete[](fBlock, blockAlign);
990}
991
992namespace {
993int FSeek64(FILE *stream, std::int64_t offset, int origin)
994{
995#ifdef R__SEEK64
996 return fseeko64(stream, offset, origin);
997#else
998 return fseek(stream, offset, origin);
999#endif
1000}
1001} // namespace
1002
1004{
1005 // Write the last partially filled block, which may still need appropriate alignment for Direct I/O.
1006 // If it is the first block, get the updated header block.
1007 if (fBlockOffset == 0) {
1008 std::size_t headerBlockSize = kHeaderBlockSize;
1009 if (headerBlockSize > fFilePos) {
1010 headerBlockSize = fFilePos;
1011 }
1012 memcpy(fBlock, fHeaderBlock, headerBlockSize);
1013 }
1014
1015 std::size_t retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
1016 if (retval)
1017 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1018
1019 std::size_t lastBlockSize = fFilePos - fBlockOffset;
1020 R__ASSERT(lastBlockSize <= fBlockSize);
1021 if (fDirectIO) {
1022 // Round up to a multiple of kBlockAlign.
1023 lastBlockSize += kBlockAlign - 1;
1024 lastBlockSize = (lastBlockSize / kBlockAlign) * kBlockAlign;
1025 R__ASSERT(lastBlockSize <= fBlockSize);
1026 }
1027 retval = fwrite(fBlock, 1, lastBlockSize, fFile);
1028 if (retval != lastBlockSize)
1029 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1030
1031 // Write the (updated) header block, unless it was part of the write above.
1032 if (fBlockOffset > 0) {
1033 retval = FSeek64(fFile, 0, SEEK_SET);
1034 if (retval)
1035 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1036
1037 retval = fwrite(fHeaderBlock, 1, kHeaderBlockSize, fFile);
1038 if (retval != RFileSimple::kHeaderBlockSize)
1039 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1040 }
1041
1042 retval = fflush(fFile);
1043 if (retval)
1044 throw RException(R__FAIL(std::string("Flush failed: ") + strerror(errno)));
1045}
1046
1047void ROOT::Internal::RNTupleFileWriter::RFileSimple::Write(const void *buffer, size_t nbytes, std::int64_t offset)
1048{
1049 R__ASSERT(fFile);
1050 size_t retval;
1051 if ((offset >= 0) && (static_cast<std::uint64_t>(offset) != fFilePos)) {
1052 fFilePos = offset;
1053 }
1054
1055 // Keep header block to overwrite on commit.
1056 if (fFilePos < kHeaderBlockSize) {
1057 std::size_t headerBytes = nbytes;
1058 if (fFilePos + headerBytes > kHeaderBlockSize) {
1059 headerBytes = kHeaderBlockSize - fFilePos;
1060 }
1061 memcpy(fHeaderBlock + fFilePos, buffer, headerBytes);
1062 }
1063
1064 R__ASSERT(fFilePos >= fBlockOffset);
1065
1066 while (nbytes > 0) {
1067 std::uint64_t posInBlock = fFilePos % fBlockSize;
1068 std::uint64_t blockOffset = fFilePos - posInBlock;
1069 if (blockOffset != fBlockOffset) {
1070 // Write the block.
1071 retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
1072 if (retval)
1073 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1074
1075 retval = fwrite(fBlock, 1, fBlockSize, fFile);
1076 if (retval != fBlockSize)
1077 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1078
1079 // Null the buffer contents for good measure.
1080 memset(fBlock, 0, fBlockSize);
1081 }
1082
1083 fBlockOffset = blockOffset;
1084 std::size_t blockSize = nbytes;
1085 if (blockSize > fBlockSize - posInBlock) {
1086 blockSize = fBlockSize - posInBlock;
1087 }
1088 memcpy(fBlock + posInBlock, buffer, blockSize);
1089 buffer = static_cast<const unsigned char *>(buffer) + blockSize;
1090 nbytes -= blockSize;
1091 fFilePos += blockSize;
1092 }
1093}
1094
1095std::uint64_t
1096ROOT::Internal::RNTupleFileWriter::RFileSimple::WriteKey(const void *buffer, std::size_t nbytes, std::size_t len,
1097 std::int64_t offset, std::uint64_t directoryOffset,
1098 const std::string &className, const std::string &objectName,
1099 const std::string &title)
1100{
1101 if (offset > 0)
1102 fKeyOffset = offset;
1103 RTFString strClass{className};
1104 RTFString strObject{objectName};
1105 RTFString strTitle{title};
1106
1107 RTFKey key(fKeyOffset, directoryOffset, strClass, strObject, strTitle, len, nbytes);
1108 Write(&key, key.GetHeaderSize(), fKeyOffset);
1109 Write(&strClass, strClass.GetSize());
1110 Write(&strObject, strObject.GetSize());
1111 Write(&strTitle, strTitle.GetSize());
1112 auto offsetData = fFilePos;
1113 // The next key starts after the data.
1114 fKeyOffset = offsetData + nbytes;
1115 if (buffer)
1116 Write(buffer, nbytes);
1117
1118 return offsetData;
1119}
1120
1122 unsigned char keyBuffer[kBlobKeyLen])
1123{
1124 if (keyBuffer) {
1125 PrepareBlobKey(fKeyOffset, nbytes, len, keyBuffer);
1126 } else {
1127 unsigned char localKeyBuffer[kBlobKeyLen];
1128 PrepareBlobKey(fKeyOffset, nbytes, len, localKeyBuffer);
1129 Write(localKeyBuffer, kBlobKeyLen, fKeyOffset);
1130 }
1131
1132 auto offsetData = fKeyOffset + kBlobKeyLen;
1133 // The next key starts after the data.
1134 fKeyOffset = offsetData + nbytes;
1135
1136 return offsetData;
1137}
1138
1139////////////////////////////////////////////////////////////////////////////////
1140
1141void ROOT::Internal::RNTupleFileWriter::RFileProper::Write(const void *buffer, size_t nbytes, std::int64_t offset)
1142{
1143 fDirectory->GetFile()->Seek(offset);
1144 bool rv = fDirectory->GetFile()->WriteBuffer((char *)(buffer), nbytes);
1145 if (rv)
1146 throw RException(R__FAIL("WriteBuffer failed."));
1147}
1148
1150 unsigned char keyBuffer[kBlobKeyLen])
1151{
1152 std::uint64_t offsetKey;
1153 RKeyBlob keyBlob(fDirectory->GetFile());
1154 // Since it is unknown beforehand if offsetKey is beyond the 2GB limit or not,
1155 // RKeyBlob will always reserve space for a big key (version >= 1000)
1156 keyBlob.Reserve(nbytes, &offsetKey);
1157
1158 if (keyBuffer) {
1159 PrepareBlobKey(offsetKey, nbytes, len, keyBuffer);
1160 } else {
1161 unsigned char localKeyBuffer[kBlobKeyLen];
1162 PrepareBlobKey(offsetKey, nbytes, len, localKeyBuffer);
1163 Write(localKeyBuffer, kBlobKeyLen, offsetKey);
1164 }
1165
1166 if (keyBlob.WasAllocatedInAFreeSlot()) {
1167 // If the key was allocated in a free slot, the last 4 bytes of its buffer contain the new size
1168 // of the remaining free slot and we need to write it to disk before the key gets destroyed at the end of the
1169 // function.
1170 Write(keyBlob.GetBuffer() + nbytes, sizeof(Int_t), offsetKey + kBlobKeyLen + nbytes);
1171 }
1172
1173 auto offsetData = offsetKey + kBlobKeyLen;
1174
1175 return offsetData;
1176}
1177
1178////////////////////////////////////////////////////////////////////////////////
1179
1181 : fNTupleName(name)
1182{
1183 auto &fileSimple = fFile.emplace<RFileSimple>();
1184 fileSimple.fControlBlock = std::make_unique<ROOT::Internal::RTFileControlBlock>();
1186 auto infoRNTuple = RNTuple::Class()->GetStreamerInfo();
1188}
1189
1191
1192std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1193ROOT::Internal::RNTupleFileWriter::Recreate(std::string_view ntupleName, std::string_view path,
1195{
1196 std::string fileName(path);
1197 size_t idxDirSep = fileName.find_last_of("\\/");
1198 if (idxDirSep != std::string::npos) {
1199 fileName.erase(0, idxDirSep + 1);
1200 }
1201#ifdef R__LINUX
1202 int flags = O_WRONLY | O_CREAT | O_TRUNC;
1203#ifdef O_LARGEFILE
1204 // Add the equivalent flag that is passed by fopen64.
1205 flags |= O_LARGEFILE;
1206#endif
1207 if (options.GetUseDirectIO()) {
1208 flags |= O_DIRECT;
1209 }
1210 int fd = open(std::string(path).c_str(), flags, 0666);
1211 if (fd == -1) {
1212 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1213 }
1214 FILE *fileStream = fdopen(fd, "wb");
1215#else
1216#ifdef R__SEEK64
1217 FILE *fileStream = fopen64(std::string(path.data(), path.size()).c_str(), "wb");
1218#else
1219 FILE *fileStream = fopen(std::string(path.data(), path.size()).c_str(), "wb");
1220#endif
1221#endif
1222 if (!fileStream) {
1223 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1224 }
1225 // RNTupleFileWriter::RFileSimple does its own buffering, turn off additional buffering from C stdio.
1226 std::setvbuf(fileStream, nullptr, _IONBF, 0);
1227
1228 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, options.GetMaxKeySize()));
1229 RFileSimple &fileSimple = std::get<RFileSimple>(writer->fFile);
1230 fileSimple.fFile = fileStream;
1231 fileSimple.fDirectIO = options.GetUseDirectIO();
1232 fileSimple.AllocateBuffers(options.GetWriteBufferSize());
1233 writer->fFileName = fileName;
1234
1235 int defaultCompression = options.GetCompression();
1236 switch (containerFormat) {
1237 case EContainerFormat::kTFile: writer->WriteTFileSkeleton(defaultCompression); break;
1238 case EContainerFormat::kBare:
1239 writer->fIsBare = true;
1240 writer->WriteBareFileSkeleton(defaultCompression);
1241 break;
1242 default: R__ASSERT(false && "Internal error: unhandled container format");
1243 }
1244
1245 return writer;
1246}
1247
1248std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1250 std::uint64_t maxKeySize)
1251{
1252 TFile *file = fileOrDirectory.GetFile();
1253 if (!file)
1254 throw RException(R__FAIL("invalid attempt to add an RNTuple to a directory that is not backed by a file"));
1255 assert(file->IsBinary());
1256
1257 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, maxKeySize));
1258 auto &fileProper = writer->fFile.emplace<RFileProper>();
1259 fileProper.fDirectory = &fileOrDirectory;
1260 return writer;
1261}
1262
1264{
1265 RFileSimple *fileSimple = std::get_if<RFileSimple>(&fFile);
1266 if (!fileSimple)
1267 throw RException(R__FAIL("invalid attempt to seek non-simple writer"));
1268
1269 fileSimple->fFilePos = offset;
1270 fileSimple->fKeyOffset = offset;
1271 // The next Write() will Flush() if necessary.
1272}
1273
1278
1280{
1281 if (auto fileProper = std::get_if<RFileProper>(&fFile)) {
1282 // Easy case, the ROOT file header and the RNTuple streaming is taken care of by TFile
1283 fileProper->fDirectory->WriteObject(&fNTupleAnchor, fNTupleName.c_str());
1284
1285 // Make sure the streamer info records used in the RNTuple are written to the file
1287 buf.SetParent(fileProper->fDirectory->GetFile());
1288 for (auto [_, info] : fStreamerInfoMap)
1289 buf.TagStreamerInfo(info);
1290
1291 fileProper->fDirectory->GetFile()->Write();
1292 return;
1293 }
1294
1295 // Writing by C file stream: prepare the container format header and stream the RNTuple anchor object
1296 auto &fileSimple = std::get<RFileSimple>(fFile);
1297
1298 if (fIsBare) {
1299 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1300 // Compute the checksum
1301 std::uint64_t checksum = XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData());
1302 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekNTuple, &ntupleOnDisk, ntupleOnDisk.GetSize());
1303 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekNTuple + ntupleOnDisk.GetSize(), &checksum,
1304 sizeof(checksum));
1305 fileSimple.Flush();
1306 return;
1307 }
1308
1309 auto anchorSize = WriteTFileNTupleKey(compression);
1310 WriteTFileKeysList(anchorSize); // NOTE: this is written uncompressed
1311 WriteTFileStreamerInfo(compression);
1312 WriteTFileFreeList(); // NOTE: this is written uncompressed
1313
1314 // Update header and TFile record
1315 memcpy(fileSimple.fHeaderBlock, &fileSimple.fControlBlock->fHeader, fileSimple.fControlBlock->fHeader.GetSize());
1316 R__ASSERT(fileSimple.fControlBlock->fSeekFileRecord + fileSimple.fControlBlock->fFileRecord.GetSize() <
1317 RFileSimple::kHeaderBlockSize);
1318 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekFileRecord, &fileSimple.fControlBlock->fFileRecord,
1319 fileSimple.fControlBlock->fFileRecord.GetSize());
1320
1321 fileSimple.Flush();
1322}
1323
1324std::uint64_t ROOT::Internal::RNTupleFileWriter::WriteBlob(const void *data, size_t nbytes, size_t len)
1325{
1326 auto writeKey = [this](const void *payload, size_t nBytes, size_t length) {
1327 std::uint64_t offset = ReserveBlob(nBytes, length);
1328 WriteIntoReservedBlob(payload, nBytes, offset);
1329 return offset;
1330 };
1331
1332 const std::uint64_t maxKeySize = fNTupleAnchor.fMaxKeySize;
1333 R__ASSERT(maxKeySize > 0);
1334 // We don't need the object length except for seeing compression ratios in TFile::Map()
1335 // Make sure that the on-disk object length fits into the TKey header.
1336 if (static_cast<std::uint64_t>(len) > static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max()))
1337 len = nbytes;
1338
1339 if (nbytes <= maxKeySize) {
1340 // Fast path: only write 1 key.
1341 return writeKey(data, nbytes, len);
1342 }
1343
1344 /**
1345 * Writing a key bigger than the max allowed size. In this case we split the payload
1346 * into multiple keys, reserving the end of the first key payload for pointers to the
1347 * next ones. E.g. if a key needs to be split into 3 chunks, the first chunk will have
1348 * the format:
1349 * +--------------------+
1350 * | |
1351 * | Data |
1352 * |--------------------|
1353 * | pointer to chunk 2 |
1354 * | pointer to chunk 3 |
1355 * +--------------------+
1356 */
1357 const size_t nChunks = ComputeNumChunks(nbytes, maxKeySize);
1358 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
1360 // Skip writing the first chunk, it will be written last (in the file) below.
1361
1362 const uint8_t *chunkData = reinterpret_cast<const uint8_t *>(data) + nbytesFirstChunk;
1364
1366 std::uint64_t chunkOffsetIdx = 0;
1367
1368 do {
1369 const size_t bytesNextChunk = std::min<size_t>(remainingBytes, maxKeySize);
1370 const std::uint64_t offset = writeKey(chunkData, bytesNextChunk, bytesNextChunk);
1371
1374
1377
1378 } while (remainingBytes > 0);
1379
1380 // Write the first key, with part of the data and the pointers to (logically) following keys appended.
1381 const std::uint64_t firstOffset = ReserveBlob(maxKeySize, maxKeySize);
1382 WriteIntoReservedBlob(data, nbytesFirstChunk, firstOffset);
1383 const std::uint64_t chunkOffsetsOffset = firstOffset + nbytesFirstChunk;
1384 WriteIntoReservedBlob(chunkOffsetsToWrite.get(), nbytesChunkOffsets, chunkOffsetsOffset);
1385
1386 return firstOffset;
1387}
1388
1389std::uint64_t
1390ROOT::Internal::RNTupleFileWriter::ReserveBlob(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen])
1391{
1392 // ReserveBlob cannot be used to reserve a multi-key blob
1393 R__ASSERT(nbytes <= fNTupleAnchor.GetMaxKeySize());
1394
1395 std::uint64_t offset;
1396 if (auto *fileSimple = std::get_if<RFileSimple>(&fFile)) {
1397 if (fIsBare) {
1398 offset = fileSimple->fKeyOffset;
1399 fileSimple->fKeyOffset += nbytes;
1400 } else {
1401 offset = fileSimple->ReserveBlobKey(nbytes, len, keyBuffer);
1402 }
1403 } else {
1404 auto &fileProper = std::get<RFileProper>(fFile);
1405 offset = fileProper.ReserveBlobKey(nbytes, len, keyBuffer);
1406 }
1407 return offset;
1408}
1409
1410void ROOT::Internal::RNTupleFileWriter::WriteIntoReservedBlob(const void *buffer, size_t nbytes, std::int64_t offset)
1411{
1412 if (auto *fileSimple = std::get_if<RFileSimple>(&fFile)) {
1413 fileSimple->Write(buffer, nbytes, offset);
1414 } else {
1415 auto &fileProper = std::get<RFileProper>(fFile);
1416 fileProper.Write(buffer, nbytes, offset);
1417 }
1418}
1419
1421{
1422 auto offset = WriteBlob(data, nbytes, lenHeader);
1423 fNTupleAnchor.fLenHeader = lenHeader;
1424 fNTupleAnchor.fNBytesHeader = nbytes;
1425 fNTupleAnchor.fSeekHeader = offset;
1426 return offset;
1427}
1428
1430{
1431 auto offset = WriteBlob(data, nbytes, lenFooter);
1432 fNTupleAnchor.fLenFooter = lenFooter;
1433 fNTupleAnchor.fNBytesFooter = nbytes;
1434 fNTupleAnchor.fSeekFooter = offset;
1435 return offset;
1436}
1437
1439{
1440 RBareFileHeader bareHeader;
1441 bareHeader.fCompress = defaultCompression;
1442 auto &fileSimple = std::get<RFileSimple>(fFile);
1443 fileSimple.Write(&bareHeader, sizeof(bareHeader), 0);
1444 RTFString ntupleName{fNTupleName};
1445 fileSimple.Write(&ntupleName, ntupleName.GetSize());
1446
1447 // Write zero-initialized ntuple to reserve the space; will be overwritten on commit
1448 RTFNTuple ntupleOnDisk;
1449 fileSimple.fControlBlock->fSeekNTuple = fileSimple.fFilePos;
1450 fileSimple.Write(&ntupleOnDisk, ntupleOnDisk.GetSize());
1451 std::uint64_t checksum = 0;
1452 fileSimple.Write(&checksum, sizeof(checksum));
1453 fileSimple.fKeyOffset = fileSimple.fFilePos;
1454}
1455
1457{
1458 // The streamer info record is a TList of TStreamerInfo object. We cannot use
1459 // RNTupleSerializer::SerializeStreamerInfos because that uses TBufferIO::WriteObject.
1460 // This would prepend the streamed TList with self-decription information.
1461 // The streamer info record is just the streamed TList.
1462
1464 for (auto [_, info] : fStreamerInfoMap) {
1466 }
1467
1468 // We will stream the list with a TBufferFile. When reading the streamer info records back,
1469 // the read buffer includes the key and the streamed list. Therefore, we need to start streaming
1470 // with an offset of the key length. Otherwise, the offset for referencing duplicate objects in the
1471 // buffer will point to the wrong places.
1472
1473 // Figure out key length
1474 RTFString strTList{"TList"};
1475 RTFString strStreamerInfo{"StreamerInfo"};
1476 RTFString strStreamerTitle{"Doubly linked list"};
1477 auto &fileSimple = std::get<RFileSimple>(fFile);
1478 fileSimple.fControlBlock->fHeader.SetSeekInfo(fileSimple.fKeyOffset);
1479 auto keyLen = RTFKey(fileSimple.fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, strTList, strStreamerInfo,
1481 .fKeyLen;
1482
1483 TBufferFile buffer(TBuffer::kWrite, keyLen + 1);
1484 buffer.SetBufferOffset(keyLen);
1485 streamerInfoList.Streamer(buffer);
1486 assert(buffer.Length() > keyLen);
1487 const auto bufPayload = buffer.Buffer() + keyLen;
1488 const auto lenPayload = buffer.Length() - keyLen;
1489
1492
1494 fileSimple.fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, "TList", "StreamerInfo",
1495 "Doubly linked list");
1496 fileSimple.fControlBlock->fHeader.SetNbytesInfo(fileSimple.fFilePos -
1497 fileSimple.fControlBlock->fHeader.GetSeekInfo());
1498}
1499
1501{
1502 RTFString strEmpty;
1503 RTFString strRNTupleClass{"ROOT::RNTuple"};
1504 RTFString strRNTupleName{fNTupleName};
1505 RTFString strFileName{fFileName};
1506
1507 auto &fileSimple = std::get<RFileSimple>(fFile);
1508 RTFKey keyRNTuple(fileSimple.fControlBlock->fSeekNTuple, RTFHeader::kBEGIN, strRNTupleClass, strRNTupleName,
1509 strEmpty, RTFNTuple::GetSizePlusChecksum(), anchorSize);
1510
1511 fileSimple.fControlBlock->fFileRecord.SetSeekKeys(fileSimple.fKeyOffset);
1512 RTFKeyList keyList{1};
1513 RTFKey keyKeyList(fileSimple.fControlBlock->fFileRecord.GetSeekKeys(), RTFHeader::kBEGIN, strEmpty, strFileName,
1514 strEmpty, keyList.GetSize() + keyRNTuple.fKeyLen);
1515 fileSimple.Write(&keyKeyList, keyKeyList.GetHeaderSize(), fileSimple.fControlBlock->fFileRecord.GetSeekKeys());
1516 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1517 fileSimple.Write(&strFileName, strFileName.GetSize());
1518 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1519 fileSimple.Write(&keyList, keyList.GetSize());
1520 fileSimple.Write(&keyRNTuple, keyRNTuple.GetHeaderSize());
1521 // Write class name, object name, and title for this key.
1522 fileSimple.Write(&strRNTupleClass, strRNTupleClass.GetSize());
1523 fileSimple.Write(&strRNTupleName, strRNTupleName.GetSize());
1524 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1525 fileSimple.fControlBlock->fFileRecord.fNBytesKeys =
1526 fileSimple.fFilePos - fileSimple.fControlBlock->fFileRecord.GetSeekKeys();
1527 fileSimple.fKeyOffset = fileSimple.fFilePos;
1528}
1529
1531{
1532 auto &fileSimple = std::get<RFileSimple>(fFile);
1533 fileSimple.fControlBlock->fHeader.SetSeekFree(fileSimple.fKeyOffset);
1534 RTFString strEmpty;
1535 RTFString strFileName{fFileName};
1536 RTFFreeEntry freeEntry;
1537 RTFKey keyFreeList(fileSimple.fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, strEmpty, strFileName,
1538 strEmpty, freeEntry.GetSize());
1539 std::uint64_t firstFree = fileSimple.fControlBlock->fHeader.GetSeekFree() + keyFreeList.GetSize();
1540 freeEntry.Set(firstFree, std::max(2000000000ULL, ((firstFree / 1000000000ULL) + 1) * 1000000000ULL));
1541 fileSimple.WriteKey(&freeEntry, freeEntry.GetSize(), freeEntry.GetSize(),
1542 fileSimple.fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, "", fFileName, "");
1543 fileSimple.fControlBlock->fHeader.SetNbytesFree(fileSimple.fFilePos -
1544 fileSimple.fControlBlock->fHeader.GetSeekFree());
1545 fileSimple.fControlBlock->fHeader.SetEnd(fileSimple.fFilePos);
1546}
1547
1549{
1550 RTFString strRNTupleClass{"ROOT::RNTuple"};
1551 RTFString strRNTupleName{fNTupleName};
1552 RTFString strEmpty;
1553
1554 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1555 RUInt64BE checksum{XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData())};
1556 auto &fileSimple = std::get<RFileSimple>(fFile);
1557 fileSimple.fControlBlock->fSeekNTuple = fileSimple.fKeyOffset;
1558
1559 char keyBuf[RTFNTuple::GetSizePlusChecksum()];
1560
1561 // concatenate the RNTuple anchor with its checksum
1562 memcpy(keyBuf, &ntupleOnDisk, sizeof(RTFNTuple));
1563 memcpy(keyBuf + sizeof(RTFNTuple), &checksum, sizeof(checksum));
1564
1565 const auto sizeAnchor = sizeof(RTFNTuple) + sizeof(checksum);
1566 char zipAnchor[RTFNTuple::GetSizePlusChecksum()];
1568
1569 fileSimple.WriteKey(zipAnchor, szZipAnchor, sizeof(keyBuf), fileSimple.fControlBlock->fSeekNTuple, RTFHeader::kBEGIN,
1570 "ROOT::RNTuple", fNTupleName, "");
1571 return szZipAnchor;
1572}
1573
1575{
1576 RTFString strTFile{"TFile"};
1577 RTFString strFileName{fFileName};
1578 RTFString strEmpty;
1579
1580 auto &fileSimple = std::get<RFileSimple>(fFile);
1581 fileSimple.fControlBlock->fHeader = RTFHeader(defaultCompression);
1582
1583 RTFUUID uuid;
1584
1585 // First record of the file: the TFile object at offset kBEGIN (= 100)
1586 RTFKey keyRoot(RTFHeader::kBEGIN, 0, strTFile, strFileName, strEmpty,
1587 sizeof(RTFDirectory) + strFileName.GetSize() + strEmpty.GetSize() + uuid.GetSize());
1588 std::uint32_t nbytesName = keyRoot.fKeyLen + strFileName.GetSize() + 1;
1589 fileSimple.fControlBlock->fFileRecord.fNBytesName = nbytesName;
1590 fileSimple.fControlBlock->fHeader.SetNbytesName(nbytesName);
1591
1592 fileSimple.Write(&keyRoot, keyRoot.GetHeaderSize(), RTFHeader::kBEGIN);
1593 // Write class name, object name, and title for the TFile key.
1594 fileSimple.Write(&strTFile, strTFile.GetSize());
1595 fileSimple.Write(&strFileName, strFileName.GetSize());
1596 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1597 // Write the name and title of the TNamed preceding the TFile entry.
1598 fileSimple.Write(&strFileName, strFileName.GetSize());
1599 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1600 // Will be overwritten on commit
1601 fileSimple.fControlBlock->fSeekFileRecord = fileSimple.fFilePos;
1602 fileSimple.Write(&fileSimple.fControlBlock->fFileRecord, fileSimple.fControlBlock->fFileRecord.GetSize());
1603 fileSimple.Write(&uuid, uuid.GetSize());
1604
1605 // Padding bytes to allow the TFile record to grow for a big file
1606 RUInt32BE padding{0};
1607 for (int i = 0; i < 3; ++i)
1608 fileSimple.Write(&padding, sizeof(padding));
1609 fileSimple.fKeyOffset = fileSimple.fFilePos;
1610}
#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:300
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:183
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.
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.
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.
RNTupleFileWriter(std::string_view name, std::uint64_t maxKeySize)
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.
void Commit(int compression=RCompressionSetting::EDefaults::kUseGeneralPurpose)
Writes the RNTuple key to the file so that the header and footer keys can be found.
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...
void WriteTFileFreeList()
Last record in the file.
void WriteTFileSkeleton(int defaultCompression)
For a TFile container written by a C file stream, write the header and TFile object.
void Seek(std::uint64_t offset)
Seek a simple writer to offset.
std::variant< RFileSimple, RFileProper > fFile
RFileSimple: for simple use cases, survives without libRIO dependency RFileProper: for updating exist...
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 std::unique_ptr< RNTupleFileWriter > Append(std::string_view ntupleName, TDirectory &fileOrDirectory, std::uint64_t maxKeySize)
The directory parameter can also be a TFile object (TFile inherits from TDirectory).
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...
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:79
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:108
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:198
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 ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
Definition TFile.h:131
Bool_t IsBinary() const
Definition TFile.h:338
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:1345
Int_t fVersion
Key version identifier.
Definition TKey.h:39
Int_t fLeft
Number of bytes left in current segment.
Definition TKey.h:48
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:461
TString fClassName
Object Class name.
Definition TKey.h:47
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
void FillBuffer(char *&buffer)
Stream UUID into output buffer.
Definition TUUID.cxx:274
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:52
std::unique_ptr< T[]> MakeUninitArray(std::size_t size)
Make an array of default-initialized elements.
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 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.
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 ...
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.
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