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>
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 void SetNbytesInfo(std::uint32_t value)
408 {
409 if (IsBigFile()) {
410 fInfoLong.fNbytesInfo = value;
411 } else {
412 fInfoShort.fNbytesInfo = value;
413 }
414 }
415
416 void SetCompression(std::uint32_t value)
417 {
418 if (IsBigFile()) {
419 fInfoLong.fCompress = value;
420 } else {
421 fInfoShort.fCompress = value;
422 }
423 }
424};
425
426/// A reference to an unused byte-range in a TFile
427struct RTFFreeEntry {
428 static constexpr unsigned kBigFreeEntryVersion = 1000;
429
430 RUInt16BE fVersion{1};
431 union {
432 struct {
433 RUInt32BE fFirst{0};
434 RUInt32BE fLast{0};
435 } fInfoShort;
436 struct {
437 RUInt64BE fFirst{0};
438 RUInt64BE fLast{0};
439 } fInfoLong;
440 };
441
442 RTFFreeEntry() : fInfoShort() {}
443 void Set(std::uint64_t first, std::uint64_t last)
444 {
445 if (last > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
446 fVersion = fVersion + kBigFreeEntryVersion;
447 fInfoLong.fFirst = first;
448 fInfoLong.fLast = last;
449 } else {
450 fInfoShort.fFirst = first;
451 fInfoShort.fLast = last;
452 }
453 }
454 std::uint32_t GetSize() { return (fVersion >= kBigFreeEntryVersion) ? 18 : 10; }
455};
456
457/// The header of the directory key index
458struct RTFKeyList {
459 RUInt32BE fNKeys;
460 std::uint32_t GetSize() const { return sizeof(RTFKeyList); }
461 explicit RTFKeyList(std::uint32_t nKeys) : fNKeys(nKeys) {}
462};
463
464/// A streamed TDirectory (TFile) object
465struct RTFDirectory {
466 static constexpr unsigned kBigFileVersion = 1000;
467
468 RUInt16BE fClassVersion{5};
469 RTFDatetime fDateC;
470 RTFDatetime fDateM;
471 RUInt32BE fNBytesKeys{0};
472 RUInt32BE fNBytesName{0};
473 // The version of the key has to tell whether offsets are 32bit or 64bit long
474 union {
475 struct {
476 RUInt32BE fSeekDir{RTFHeader::kBEGIN};
477 RUInt32BE fSeekParent{0};
478 RUInt32BE fSeekKeys{0};
479 } fInfoShort;
480 struct {
481 RUInt64BE fSeekDir{RTFHeader::kBEGIN};
482 RUInt64BE fSeekParent{0};
483 RUInt64BE fSeekKeys{0};
484 } fInfoLong;
485 };
486
487 RTFDirectory() : fInfoShort() {}
488
489 // In case of a short TFile record (<2G), 3 padding ints are written after the UUID
490 std::uint32_t GetSize() const
491 {
492 if (fClassVersion >= kBigFileVersion)
493 return sizeof(RTFDirectory);
494 return 18 + sizeof(fInfoShort);
495 }
496
497 std::uint64_t GetSeekKeys() const
498 {
499 if (fClassVersion >= kBigFileVersion)
500 return fInfoLong.fSeekKeys;
501 return fInfoShort.fSeekKeys;
502 }
503
504 void SetSeekKeys(std::uint64_t seekKeys)
505 {
506 if (seekKeys > static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
507 std::uint32_t seekDir = fInfoShort.fSeekDir;
508 std::uint32_t seekParent = fInfoShort.fSeekParent;
509 fInfoLong.fSeekDir = seekDir;
510 fInfoLong.fSeekParent = seekParent;
511 fInfoLong.fSeekKeys = seekKeys;
512 fClassVersion = fClassVersion + kBigFileVersion;
513 } else {
514 fInfoShort.fSeekKeys = seekKeys;
515 }
516 }
517};
518
519/// A zero UUID stored at the end of the TFile record
520struct RTFUUID {
521 RUInt16BE fVersionClass{1};
522 unsigned char fUUID[16];
523
524 RTFUUID()
525 {
526 TUUID uuid;
527 char *buffer = reinterpret_cast<char *>(this);
528 uuid.FillBuffer(buffer);
529 assert(reinterpret_cast<RTFUUID *>(buffer) <= (this + 1));
530 }
531 std::uint32_t GetSize() const { return sizeof(RTFUUID); }
532};
533
534/// A streamed RNTuple class
535///
536/// NOTE: this must be kept in sync with RNTuple.hxx.
537/// Aside ensuring consistency between the two classes' members, you need to make sure
538/// that fVersionClass matches the class version of RNTuple.
539struct RTFNTuple {
540 RUInt32BE fByteCount{0x40000000 | (sizeof(RTFNTuple) - sizeof(fByteCount))};
541 RUInt16BE fVersionClass{2};
542 RUInt16BE fVersionEpoch{0};
543 RUInt16BE fVersionMajor{0};
544 RUInt16BE fVersionMinor{0};
545 RUInt16BE fVersionPatch{0};
546 RUInt64BE fSeekHeader{0};
547 RUInt64BE fNBytesHeader{0};
548 RUInt64BE fLenHeader{0};
549 RUInt64BE fSeekFooter{0};
550 RUInt64BE fNBytesFooter{0};
551 RUInt64BE fLenFooter{0};
552 RUInt64BE fMaxKeySize{0};
553
554 static constexpr std::uint32_t GetSizePlusChecksum() { return sizeof(RTFNTuple) + sizeof(std::uint64_t); }
555
556 RTFNTuple() = default;
557 explicit RTFNTuple(const ROOT::RNTuple &inMemoryAnchor)
558 {
559 fVersionEpoch = inMemoryAnchor.GetVersionEpoch();
560 fVersionMajor = inMemoryAnchor.GetVersionMajor();
561 fVersionMinor = inMemoryAnchor.GetVersionMinor();
562 fVersionPatch = inMemoryAnchor.GetVersionPatch();
563 fSeekHeader = inMemoryAnchor.GetSeekHeader();
564 fNBytesHeader = inMemoryAnchor.GetNBytesHeader();
565 fLenHeader = inMemoryAnchor.GetLenHeader();
566 fSeekFooter = inMemoryAnchor.GetSeekFooter();
567 fNBytesFooter = inMemoryAnchor.GetNBytesFooter();
568 fLenFooter = inMemoryAnchor.GetLenFooter();
569 fMaxKeySize = inMemoryAnchor.GetMaxKeySize();
570 }
571 std::uint32_t GetSize() const { return sizeof(RTFNTuple); }
572 // The byte count and class version members are not checksummed
573 std::uint32_t GetOffsetCkData() { return sizeof(fByteCount) + sizeof(fVersionClass); }
574 std::uint32_t GetSizeCkData() { return GetSize() - GetOffsetCkData(); }
575 unsigned char *GetPtrCkData() { return reinterpret_cast<unsigned char *>(this) + GetOffsetCkData(); }
576};
577
578/// The bare file global header
579struct RBareFileHeader {
580 char fMagic[7]{'r', 'n', 't', 'u', 'p', 'l', 'e'};
581 RUInt32BE fRootVersion{(ROOT_VERSION_CODE >> 16) * 10000 + ((ROOT_VERSION_CODE & 0xFF00) >> 8) * 100 +
582 (ROOT_VERSION_CODE & 0xFF)};
583 RUInt32BE fFormatVersion{1};
584 RUInt32BE fCompress{0};
585 RTFNTuple fNTuple;
586 // followed by the ntuple name
587};
588#pragma pack(pop)
589
590/// The artifical class name shown for opaque RNTuple keys (see TBasket)
591constexpr char const *kBlobClassName = "RBlob";
592/// The class name of the RNTuple anchor
593constexpr char const *kNTupleClassName = "ROOT::RNTuple";
594
595} // anonymous namespace
596
597namespace ROOT {
598namespace Internal {
599/// If a TFile container is written by a C stream (simple file), on dataset commit, the file header
600/// and the TFile record need to be updated
602 RTFHeader fHeader;
603 RTFDirectory fFileRecord;
604 std::uint64_t fSeekNTuple{0}; // Remember the offset for the keys list
605 std::uint64_t fSeekFileRecord{0};
606};
607
608/// The RKeyBlob writes an invisible key into a TFile. That is, a key that is not indexed in the list of keys,
609/// like a TBasket.
610/// NOTE: out of anonymous namespace because otherwise ClassDefInline fails to compile
611/// on some platforms.
612class RKeyBlob : public TKey {
613public:
614 RKeyBlob() = default;
615
616 explicit RKeyBlob(TFile *file) : TKey(file)
617 {
619 fVersion += RTFKey::kBigKeyVersion;
620 fKeylen = Sizeof();
621 }
622
623 /// Register a new key for a data record of size nbytes
624 void Reserve(size_t nbytes, std::uint64_t *seekKey)
625 {
626 Create(nbytes);
627 *seekKey = fSeekKey;
628 }
629
630 bool WasAllocatedInAFreeSlot() const { return fLeft > 0; }
631
633};
634
635} // namespace Internal
636} // namespace ROOT
637
638// Computes how many chunks do we need to fit `nbytes` of payload, considering that the
639// first chunk also needs to house the offsets of the other chunks and no chunk can
640// be bigger than `maxChunkSize`. When saved to a TFile, each chunk is part of a separate TKey.
641static size_t ComputeNumChunks(size_t nbytes, size_t maxChunkSize)
642{
643 constexpr size_t kChunkOffsetSize = sizeof(std::uint64_t);
644
646 size_t nChunks = (nbytes + maxChunkSize - 1) / maxChunkSize;
647 assert(nChunks > 1);
648 size_t nbytesTail = nbytes % maxChunkSize;
649 size_t nbytesExtra = (nbytesTail > 0) * (maxChunkSize - nbytesTail);
652 ++nChunks;
654 }
655
656 // We don't support having more chunkOffsets than what fits in one chunk.
657 // For a reasonable-sized maxKeySize it looks very unlikely that we can have more chunks
658 // than we can fit in the first `maxKeySize` bytes. E.g. for maxKeySize = 1GiB we can fit
659 // 134217728 chunk offsets, making our multi-key blob's capacity exactly 128 PiB.
661
662 return nChunks;
663}
664
666
668{
669 char ident[4];
670 ReadBuffer(ident, 4, 0);
671 if (std::string(ident, 4) == "root")
672 return GetNTupleProper(ntupleName);
673 fIsBare = true;
674 return GetNTupleBare(ntupleName);
675}
676
677/// Searches for a key with the given name and type in the key index of the given directory.
678/// Return 0 if the key was not found.
679std::uint64_t ROOT::Internal::RMiniFileReader::SearchInDirectory(std::uint64_t &offsetDir, std::string_view keyName,
680 std::string_view typeName)
681{
682 RTFDirectory directory;
684
685 RTFKey key;
686 RUInt32BE nKeys;
687 std::uint64_t offset = directory.GetSeekKeys();
688 ReadBuffer(&key, sizeof(key), offset);
689 offset += key.fKeyLen;
690 ReadBuffer(&nKeys, sizeof(nKeys), offset);
691 offset += sizeof(nKeys);
692
693 for (unsigned int i = 0; i < nKeys; ++i) {
694 ReadBuffer(&key, sizeof(key), offset);
695 auto offsetNextKey = offset + key.fKeyLen;
696
697 offset += key.GetHeaderSize();
698 RTFString name;
699 ReadBuffer(&name, 1, offset);
700 ReadBuffer(&name, name.GetSize(), offset);
701 if (std::string_view(name.fData, name.fLName) != typeName) {
703 continue;
704 }
705 offset += name.GetSize();
706 ReadBuffer(&name, 1, offset);
707 ReadBuffer(&name, name.GetSize(), offset);
708 if (std::string_view(name.fData, name.fLName) == keyName) {
709 return key.GetSeekKey();
710 }
712 }
713
714 // Not found
715 return 0;
716}
717
719{
720 RTFHeader fileHeader;
721 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
722
723 RTFKey key;
724 RTFString name;
725 ReadBuffer(&key, sizeof(key), fileHeader.fBEGIN);
726 // Skip over the entire key length, including the class name, object name, and title stored in it.
727 std::uint64_t offset = fileHeader.fBEGIN + key.fKeyLen;
728 // Skip over the name and title of the TNamed preceding the TFile (root TDirectory) entry.
729 ReadBuffer(&name, 1, offset);
730 offset += name.GetSize();
731 ReadBuffer(&name, 1, offset);
732 offset += name.GetSize();
733
734 // split ntupleName by '/' character to open datasets in subdirectories.
735 std::string ntuplePathTail(ntuplePath);
736 if (!ntuplePathTail.empty() && ntuplePathTail[0] == '/')
737 ntuplePathTail = ntuplePathTail.substr(1);
738 auto pos = std::string::npos;
739 while ((pos = ntuplePathTail.find('/')) != std::string::npos) {
740 auto directoryName = ntuplePathTail.substr(0, pos);
741 ntuplePathTail.erase(0, pos + 1);
742
743 offset = SearchInDirectory(offset, directoryName, "TDirectory");
744 if (offset == 0) {
745 return R__FAIL("no directory named '" + std::string(directoryName) + "' in file '" + fRawFile->GetUrl() + "'");
746 }
747 ReadBuffer(&key, sizeof(key), offset);
748 offset = key.GetSeekKey() + key.fKeyLen;
749 }
750 // no more '/' delimiter in ntuplePath
752
753 offset = SearchInDirectory(offset, ntupleName, kNTupleClassName);
754 if (offset == 0) {
755 return R__FAIL("no RNTuple named '" + std::string(ntupleName) + "' in file '" + fRawFile->GetUrl() + "'");
756 }
757
758 ReadBuffer(&key, sizeof(key), offset);
759 offset = key.GetSeekKey() + key.fKeyLen;
760
761 // size of a RTFNTuple version 2 (min supported version); future anchor versions can grow.
762 constexpr size_t kMinNTupleSize = 78;
763 static_assert(kMinNTupleSize == RTFNTuple::GetSizePlusChecksum());
764 if (key.fObjLen < kMinNTupleSize) {
765 return R__FAIL("invalid anchor size: " + std::to_string(key.fObjLen) + " < " + std::to_string(sizeof(RTFNTuple)));
766 }
767
768 const auto objNbytes = key.GetSize() - key.fKeyLen;
769 auto res = GetNTupleProperAtOffset(offset, objNbytes, key.fObjLen);
770 return res;
771}
772
774 std::uint64_t compSize,
775 std::uint64_t uncompLen)
776{
777 // The object length can be smaller than the size of RTFNTuple if it comes from a past RNTuple class version,
778 // or larger than it if it comes from a future RNTuple class version.
779 auto bufAnchor = MakeUninitArray<unsigned char>(std::max<size_t>(uncompLen, sizeof(RTFNTuple)));
780 RTFNTuple *ntuple = new (bufAnchor.get()) RTFNTuple;
781
782 if (compSize != uncompLen) {
783 // Read into a temporary buffer
784 auto unzipBuf = MakeUninitArray<unsigned char>(std::max<size_t>(uncompLen, sizeof(RTFNTuple)));
786 // Unzip into the final buffer
788 } else {
790 }
791
792 // We require that future class versions only append members and store the checksum in the last 8 bytes
793 // Checksum calculation: strip byte count, class version, fChecksum member
794 const auto lenCkData = uncompLen - ntuple->GetOffsetCkData() - sizeof(uint64_t);
795 const auto ckCalc = XXH3_64bits(ntuple->GetPtrCkData(), lenCkData);
796 uint64_t ckOnDisk;
797
798 RUInt64BE *ckOnDiskPtr = reinterpret_cast<RUInt64BE *>(bufAnchor.get() + uncompLen - sizeof(uint64_t));
799 ckOnDisk = static_cast<uint64_t>(*ckOnDiskPtr);
800 if (ckCalc != ckOnDisk) {
801 return R__FAIL("RNTuple anchor checksum mismatch");
802 }
803
804 return CreateAnchor(ntuple->fVersionEpoch, ntuple->fVersionMajor, ntuple->fVersionMinor, ntuple->fVersionPatch,
805 ntuple->fSeekHeader, ntuple->fNBytesHeader, ntuple->fLenHeader, ntuple->fSeekFooter,
806 ntuple->fNBytesFooter, ntuple->fLenFooter, ntuple->fMaxKeySize);
807}
808
810{
811 RBareFileHeader fileHeader;
812 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
813 RTFString name;
814 auto offset = sizeof(fileHeader);
815 ReadBuffer(&name, 1, offset);
816 ReadBuffer(&name, name.GetSize(), offset);
817 std::string_view foundName(name.fData, name.fLName);
818 if (foundName != ntupleName) {
819 return R__FAIL("expected RNTuple named '" + std::string(ntupleName) + "' but instead found '" +
820 std::string(foundName) + "' in file '" + fRawFile->GetUrl() + "'");
821 }
822 offset += name.GetSize();
823
824 RTFNTuple ntuple;
825 ReadBuffer(&ntuple, sizeof(ntuple), offset);
826 std::uint64_t onDiskChecksum;
828 auto checksum = XXH3_64bits(ntuple.GetPtrCkData(), ntuple.GetSizeCkData());
829 if (checksum != static_cast<uint64_t>(onDiskChecksum))
830 return R__FAIL("RNTuple bare file: anchor checksum mismatch");
831
832 return CreateAnchor(ntuple.fVersionEpoch, ntuple.fVersionMajor, ntuple.fVersionMinor, ntuple.fVersionPatch,
833 ntuple.fSeekHeader, ntuple.fNBytesHeader, ntuple.fLenHeader, ntuple.fSeekFooter,
834 ntuple.fNBytesFooter, ntuple.fLenFooter, ntuple.fMaxKeySize);
835}
836
837void ROOT::Internal::RMiniFileReader::ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
838{
839 size_t nread;
840 if (fMaxKeySize == 0 || nbytes <= fMaxKeySize) {
841 // Fast path: read single blob
842 nread = fRawFile->ReadAt(buffer, nbytes, offset);
843 } else {
844 // Read chunked blob. See RNTupleFileWriter::WriteBlob() for details.
845 const size_t nChunks = ComputeNumChunks(nbytes, fMaxKeySize);
846 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
847 const size_t nbytesFirstChunk = fMaxKeySize - nbytesChunkOffsets;
848 uint8_t *bufCur = reinterpret_cast<uint8_t *>(buffer);
849
850 // Read first chunk
851 nread = fRawFile->ReadAt(bufCur, fMaxKeySize, offset);
852 R__ASSERT(nread == fMaxKeySize);
853 // NOTE: we read the entire chunk in `bufCur`, but we only advance the pointer by `nbytesFirstChunk`,
854 // since the last part of `bufCur` will later be overwritten by the next chunk's payload.
855 // We do this to avoid a second ReadAt to read in the chunk offsets.
858
861
863 std::uint64_t *curChunkOffset = &chunkOffsets[0];
864
865 do {
866 std::uint64_t chunkOffset;
869
870 const size_t bytesToRead = std::min<size_t>(fMaxKeySize, remainingBytes);
871 // Ensure we don't read outside of the buffer
872 R__ASSERT(static_cast<size_t>(bufCur - reinterpret_cast<uint8_t *>(buffer)) <= nbytes - bytesToRead);
873
874 auto nbytesRead = fRawFile->ReadAt(bufCur, bytesToRead, chunkOffset);
876
880 } while (remainingBytes > 0);
881 }
883}
884
885////////////////////////////////////////////////////////////////////////////////
886
887/// Prepare a blob key in the provided buffer, which must provide space for kBlobKeyLen bytes. Note that the array type
888/// is purely documentation, the argument is actually just a pointer.
890 unsigned char buffer[kBlobKeyLen])
891{
892 RTFString strClass{kBlobClassName};
893 RTFString strObject;
894 RTFString strTitle;
895 RTFKey keyHeader(offset, RTFHeader::kBEGIN, strClass, strObject, strTitle, len, nbytes);
896 R__ASSERT(keyHeader.fKeyLen == kBlobKeyLen);
897
898 // Copy structures into the buffer.
899 unsigned char *writeBuffer = buffer;
900 memcpy(writeBuffer, &keyHeader, keyHeader.GetHeaderSize());
901 writeBuffer += keyHeader.GetHeaderSize();
902 memcpy(writeBuffer, &strClass, strClass.GetSize());
903 writeBuffer += strClass.GetSize();
905 writeBuffer += strObject.GetSize();
906 memcpy(writeBuffer, &strTitle, strTitle.GetSize());
907 writeBuffer += strTitle.GetSize();
908 R__ASSERT(writeBuffer == buffer + kBlobKeyLen);
909}
910
911////////////////////////////////////////////////////////////////////////////////
912
914
916{
917 static_assert(kHeaderBlockSize % kBlockAlign == 0, "invalid header block size");
918 if (bufferSize % kBlockAlign != 0)
919 throw RException(R__FAIL("Buffer size not a multiple of alignment: " + std::to_string(bufferSize)));
920 fBlockSize = bufferSize;
921
922 std::align_val_t blockAlign{kBlockAlign};
923 fHeaderBlock = static_cast<unsigned char *>(::operator new[](kHeaderBlockSize, blockAlign));
924 memset(fHeaderBlock, 0, kHeaderBlockSize);
925 fBlock = static_cast<unsigned char *>(::operator new[](fBlockSize, blockAlign));
926 memset(fBlock, 0, fBlockSize);
927}
928
930{
931 if (fFile)
932 fclose(fFile);
933
934 std::align_val_t blockAlign{kBlockAlign};
935 if (fHeaderBlock)
936 ::operator delete[](fHeaderBlock, blockAlign);
937 if (fBlock)
938 ::operator delete[](fBlock, blockAlign);
939}
940
941namespace {
942int FSeek64(FILE *stream, std::int64_t offset, int origin)
943{
944#ifdef R__SEEK64
945 return fseeko64(stream, offset, origin);
946#else
947 return fseek(stream, offset, origin);
948#endif
949}
950} // namespace
951
953{
954 // Write the last partially filled block, which may still need appropriate alignment for Direct I/O.
955 // If it is the first block, get the updated header block.
956 if (fBlockOffset == 0) {
957 std::size_t headerBlockSize = kHeaderBlockSize;
958 if (headerBlockSize > fFilePos) {
959 headerBlockSize = fFilePos;
960 }
961 memcpy(fBlock, fHeaderBlock, headerBlockSize);
962 }
963
964 std::size_t retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
965 if (retval)
966 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
967
968 std::size_t lastBlockSize = fFilePos - fBlockOffset;
969 R__ASSERT(lastBlockSize <= fBlockSize);
970 if (fDirectIO) {
971 // Round up to a multiple of kBlockAlign.
972 lastBlockSize += kBlockAlign - 1;
973 lastBlockSize = (lastBlockSize / kBlockAlign) * kBlockAlign;
974 R__ASSERT(lastBlockSize <= fBlockSize);
975 }
976 retval = fwrite(fBlock, 1, lastBlockSize, fFile);
977 if (retval != lastBlockSize)
978 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
979
980 // Write the (updated) header block, unless it was part of the write above.
981 if (fBlockOffset > 0) {
982 retval = FSeek64(fFile, 0, SEEK_SET);
983 if (retval)
984 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
985
986 retval = fwrite(fHeaderBlock, 1, kHeaderBlockSize, fFile);
987 if (retval != RFileSimple::kHeaderBlockSize)
988 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
989 }
990
991 retval = fflush(fFile);
992 if (retval)
993 throw RException(R__FAIL(std::string("Flush failed: ") + strerror(errno)));
994}
995
996void ROOT::Internal::RNTupleFileWriter::RFileSimple::Write(const void *buffer, size_t nbytes, std::int64_t offset)
997{
998 R__ASSERT(fFile);
999 size_t retval;
1000 if ((offset >= 0) && (static_cast<std::uint64_t>(offset) != fFilePos)) {
1001 fFilePos = offset;
1002 }
1003
1004 // Keep header block to overwrite on commit.
1005 if (fFilePos < kHeaderBlockSize) {
1006 std::size_t headerBytes = nbytes;
1007 if (fFilePos + headerBytes > kHeaderBlockSize) {
1008 headerBytes = kHeaderBlockSize - fFilePos;
1009 }
1010 memcpy(fHeaderBlock + fFilePos, buffer, headerBytes);
1011 }
1012
1013 R__ASSERT(fFilePos >= fBlockOffset);
1014
1015 while (nbytes > 0) {
1016 std::uint64_t posInBlock = fFilePos % fBlockSize;
1017 std::uint64_t blockOffset = fFilePos - posInBlock;
1018 if (blockOffset != fBlockOffset) {
1019 // Write the block.
1020 retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
1021 if (retval)
1022 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1023
1024 retval = fwrite(fBlock, 1, fBlockSize, fFile);
1025 if (retval != fBlockSize)
1026 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1027
1028 // Null the buffer contents for good measure.
1029 memset(fBlock, 0, fBlockSize);
1030 }
1031
1032 fBlockOffset = blockOffset;
1033 std::size_t blockSize = nbytes;
1034 if (blockSize > fBlockSize - posInBlock) {
1035 blockSize = fBlockSize - posInBlock;
1036 }
1037 memcpy(fBlock + posInBlock, buffer, blockSize);
1038 buffer = static_cast<const unsigned char *>(buffer) + blockSize;
1039 nbytes -= blockSize;
1040 fFilePos += blockSize;
1041 }
1042}
1043
1044std::uint64_t
1045ROOT::Internal::RNTupleFileWriter::RFileSimple::WriteKey(const void *buffer, std::size_t nbytes, std::size_t len,
1046 std::int64_t offset, std::uint64_t directoryOffset,
1047 const std::string &className, const std::string &objectName,
1048 const std::string &title)
1049{
1050 if (offset > 0)
1051 fKeyOffset = offset;
1052 RTFString strClass{className};
1053 RTFString strObject{objectName};
1054 RTFString strTitle{title};
1055
1056 RTFKey key(fKeyOffset, directoryOffset, strClass, strObject, strTitle, len, nbytes);
1057 Write(&key, key.GetHeaderSize(), fKeyOffset);
1058 Write(&strClass, strClass.GetSize());
1059 Write(&strObject, strObject.GetSize());
1060 Write(&strTitle, strTitle.GetSize());
1061 auto offsetData = fFilePos;
1062 // The next key starts after the data.
1063 fKeyOffset = offsetData + nbytes;
1064 if (buffer)
1065 Write(buffer, nbytes);
1066
1067 return offsetData;
1068}
1069
1071 unsigned char keyBuffer[kBlobKeyLen])
1072{
1073 if (keyBuffer) {
1074 PrepareBlobKey(fKeyOffset, nbytes, len, keyBuffer);
1075 } else {
1076 unsigned char localKeyBuffer[kBlobKeyLen];
1077 PrepareBlobKey(fKeyOffset, nbytes, len, localKeyBuffer);
1078 Write(localKeyBuffer, kBlobKeyLen, fKeyOffset);
1079 }
1080
1081 auto offsetData = fKeyOffset + kBlobKeyLen;
1082 // The next key starts after the data.
1083 fKeyOffset = offsetData + nbytes;
1084
1085 return offsetData;
1086}
1087
1088////////////////////////////////////////////////////////////////////////////////
1089
1090void ROOT::Internal::RNTupleFileWriter::RFileProper::Write(const void *buffer, size_t nbytes, std::int64_t offset)
1091{
1092 fDirectory->GetFile()->Seek(offset);
1093 bool rv = fDirectory->GetFile()->WriteBuffer((char *)(buffer), nbytes);
1094 if (rv)
1095 throw RException(R__FAIL("WriteBuffer failed."));
1096}
1097
1099 unsigned char keyBuffer[kBlobKeyLen])
1100{
1101 std::uint64_t offsetKey;
1102 RKeyBlob keyBlob(fDirectory->GetFile());
1103 // Since it is unknown beforehand if offsetKey is beyond the 2GB limit or not,
1104 // RKeyBlob will always reserve space for a big key (version >= 1000)
1105 keyBlob.Reserve(nbytes, &offsetKey);
1106
1107 if (keyBuffer) {
1108 PrepareBlobKey(offsetKey, nbytes, len, keyBuffer);
1109 } else {
1110 unsigned char localKeyBuffer[kBlobKeyLen];
1111 PrepareBlobKey(offsetKey, nbytes, len, localKeyBuffer);
1112 Write(localKeyBuffer, kBlobKeyLen, offsetKey);
1113 }
1114
1115 if (keyBlob.WasAllocatedInAFreeSlot()) {
1116 // If the key was allocated in a free slot, the last 4 bytes of its buffer contain the new size
1117 // of the remaining free slot and we need to write it to disk before the key gets destroyed at the end of the
1118 // function.
1119 Write(keyBlob.GetBuffer() + nbytes, sizeof(Int_t), offsetKey + kBlobKeyLen + nbytes);
1120 }
1121
1122 auto offsetData = offsetKey + kBlobKeyLen;
1123
1124 return offsetData;
1125}
1126
1127////////////////////////////////////////////////////////////////////////////////
1128
1130 : fNTupleName(name)
1131{
1132 auto &fileSimple = fFile.emplace<RFileSimple>();
1133 fileSimple.fControlBlock = std::make_unique<ROOT::Internal::RTFileControlBlock>();
1135 auto infoRNTuple = RNTuple::Class()->GetStreamerInfo();
1137}
1138
1140
1141std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1142ROOT::Internal::RNTupleFileWriter::Recreate(std::string_view ntupleName, std::string_view path,
1144{
1145 std::string fileName(path);
1146 size_t idxDirSep = fileName.find_last_of("\\/");
1147 if (idxDirSep != std::string::npos) {
1148 fileName.erase(0, idxDirSep + 1);
1149 }
1150#ifdef R__LINUX
1151 int flags = O_WRONLY | O_CREAT | O_TRUNC;
1152#ifdef O_LARGEFILE
1153 // Add the equivalent flag that is passed by fopen64.
1154 flags |= O_LARGEFILE;
1155#endif
1156 if (options.GetUseDirectIO()) {
1157 flags |= O_DIRECT;
1158 }
1159 int fd = open(std::string(path).c_str(), flags, 0666);
1160 if (fd == -1) {
1161 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1162 }
1163 FILE *fileStream = fdopen(fd, "wb");
1164#else
1165#ifdef R__SEEK64
1166 FILE *fileStream = fopen64(std::string(path.data(), path.size()).c_str(), "wb");
1167#else
1168 FILE *fileStream = fopen(std::string(path.data(), path.size()).c_str(), "wb");
1169#endif
1170#endif
1171 if (!fileStream) {
1172 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1173 }
1174 // RNTupleFileWriter::RFileSimple does its own buffering, turn off additional buffering from C stdio.
1175 std::setvbuf(fileStream, nullptr, _IONBF, 0);
1176
1177 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, options.GetMaxKeySize()));
1178 RFileSimple &fileSimple = std::get<RFileSimple>(writer->fFile);
1179 fileSimple.fFile = fileStream;
1180 fileSimple.fDirectIO = options.GetUseDirectIO();
1181 fileSimple.AllocateBuffers(options.GetWriteBufferSize());
1182 writer->fFileName = fileName;
1183
1184 int defaultCompression = options.GetCompression();
1185 switch (containerFormat) {
1186 case EContainerFormat::kTFile: writer->WriteTFileSkeleton(defaultCompression); break;
1187 case EContainerFormat::kBare:
1188 writer->fIsBare = true;
1189 writer->WriteBareFileSkeleton(defaultCompression);
1190 break;
1191 default: R__ASSERT(false && "Internal error: unhandled container format");
1192 }
1193
1194 return writer;
1195}
1196
1197std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1199 std::uint64_t maxKeySize)
1200{
1201 TFile *file = fileOrDirectory.GetFile();
1202 if (!file)
1203 throw RException(R__FAIL("invalid attempt to add an RNTuple to a directory that is not backed by a file"));
1204 assert(file->IsBinary());
1205
1206 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, maxKeySize));
1207 auto &fileProper = writer->fFile.emplace<RFileProper>();
1208 fileProper.fDirectory = &fileOrDirectory;
1209 return writer;
1210}
1211
1213{
1214 RFileSimple *fileSimple = std::get_if<RFileSimple>(&fFile);
1215 if (!fileSimple)
1216 throw RException(R__FAIL("invalid attempt to seek non-simple writer"));
1217
1218 fileSimple->fFilePos = offset;
1219 fileSimple->fKeyOffset = offset;
1220 // The next Write() will Flush() if necessary.
1221}
1222
1227
1229{
1230 if (auto fileProper = std::get_if<RFileProper>(&fFile)) {
1231 // Easy case, the ROOT file header and the RNTuple streaming is taken care of by TFile
1232 fileProper->fDirectory->WriteObject(&fNTupleAnchor, fNTupleName.c_str());
1233
1234 // Make sure the streamer info records used in the RNTuple are written to the file
1236 buf.SetParent(fileProper->fDirectory->GetFile());
1237 for (auto [_, info] : fStreamerInfoMap)
1238 buf.TagStreamerInfo(info);
1239
1240 fileProper->fDirectory->GetFile()->Write();
1241 return;
1242 }
1243
1244 // Writing by C file stream: prepare the container format header and stream the RNTuple anchor object
1245 auto &fileSimple = std::get<RFileSimple>(fFile);
1246
1247 if (fIsBare) {
1248 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1249 // Compute the checksum
1250 std::uint64_t checksum = XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData());
1251 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekNTuple, &ntupleOnDisk, ntupleOnDisk.GetSize());
1252 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekNTuple + ntupleOnDisk.GetSize(), &checksum,
1253 sizeof(checksum));
1254 fileSimple.Flush();
1255 return;
1256 }
1257
1258 auto anchorSize = WriteTFileNTupleKey(compression);
1259 WriteTFileKeysList(anchorSize); // NOTE: this is written uncompressed
1260 WriteTFileStreamerInfo(compression);
1261 WriteTFileFreeList(); // NOTE: this is written uncompressed
1262
1263 // Update header and TFile record
1264 memcpy(fileSimple.fHeaderBlock, &fileSimple.fControlBlock->fHeader, fileSimple.fControlBlock->fHeader.GetSize());
1265 R__ASSERT(fileSimple.fControlBlock->fSeekFileRecord + fileSimple.fControlBlock->fFileRecord.GetSize() <
1266 RFileSimple::kHeaderBlockSize);
1267 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekFileRecord, &fileSimple.fControlBlock->fFileRecord,
1268 fileSimple.fControlBlock->fFileRecord.GetSize());
1269
1270 fileSimple.Flush();
1271}
1272
1273std::uint64_t ROOT::Internal::RNTupleFileWriter::WriteBlob(const void *data, size_t nbytes, size_t len)
1274{
1275 auto writeKey = [this](const void *payload, size_t nBytes, size_t length) {
1276 std::uint64_t offset = ReserveBlob(nBytes, length);
1277 WriteIntoReservedBlob(payload, nBytes, offset);
1278 return offset;
1279 };
1280
1281 const std::uint64_t maxKeySize = fNTupleAnchor.fMaxKeySize;
1282 R__ASSERT(maxKeySize > 0);
1283 // We don't need the object length except for seeing compression ratios in TFile::Map()
1284 // Make sure that the on-disk object length fits into the TKey header.
1285 if (static_cast<std::uint64_t>(len) > static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max()))
1286 len = nbytes;
1287
1288 if (nbytes <= maxKeySize) {
1289 // Fast path: only write 1 key.
1290 return writeKey(data, nbytes, len);
1291 }
1292
1293 /**
1294 * Writing a key bigger than the max allowed size. In this case we split the payload
1295 * into multiple keys, reserving the end of the first key payload for pointers to the
1296 * next ones. E.g. if a key needs to be split into 3 chunks, the first chunk will have
1297 * the format:
1298 * +--------------------+
1299 * | |
1300 * | Data |
1301 * |--------------------|
1302 * | pointer to chunk 2 |
1303 * | pointer to chunk 3 |
1304 * +--------------------+
1305 */
1306 const size_t nChunks = ComputeNumChunks(nbytes, maxKeySize);
1307 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
1309 // Skip writing the first chunk, it will be written last (in the file) below.
1310
1311 const uint8_t *chunkData = reinterpret_cast<const uint8_t *>(data) + nbytesFirstChunk;
1313
1315 std::uint64_t chunkOffsetIdx = 0;
1316
1317 do {
1318 const size_t bytesNextChunk = std::min<size_t>(remainingBytes, maxKeySize);
1319 const std::uint64_t offset = writeKey(chunkData, bytesNextChunk, bytesNextChunk);
1320
1323
1326
1327 } while (remainingBytes > 0);
1328
1329 // Write the first key, with part of the data and the pointers to (logically) following keys appended.
1330 const std::uint64_t firstOffset = ReserveBlob(maxKeySize, maxKeySize);
1331 WriteIntoReservedBlob(data, nbytesFirstChunk, firstOffset);
1332 const std::uint64_t chunkOffsetsOffset = firstOffset + nbytesFirstChunk;
1333 WriteIntoReservedBlob(chunkOffsetsToWrite.get(), nbytesChunkOffsets, chunkOffsetsOffset);
1334
1335 return firstOffset;
1336}
1337
1338std::uint64_t
1339ROOT::Internal::RNTupleFileWriter::ReserveBlob(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen])
1340{
1341 // ReserveBlob cannot be used to reserve a multi-key blob
1342 R__ASSERT(nbytes <= fNTupleAnchor.GetMaxKeySize());
1343
1344 std::uint64_t offset;
1345 if (auto *fileSimple = std::get_if<RFileSimple>(&fFile)) {
1346 if (fIsBare) {
1347 offset = fileSimple->fKeyOffset;
1348 fileSimple->fKeyOffset += nbytes;
1349 } else {
1350 offset = fileSimple->ReserveBlobKey(nbytes, len, keyBuffer);
1351 }
1352 } else {
1353 auto &fileProper = std::get<RFileProper>(fFile);
1354 offset = fileProper.ReserveBlobKey(nbytes, len, keyBuffer);
1355 }
1356 return offset;
1357}
1358
1359void ROOT::Internal::RNTupleFileWriter::WriteIntoReservedBlob(const void *buffer, size_t nbytes, std::int64_t offset)
1360{
1361 if (auto *fileSimple = std::get_if<RFileSimple>(&fFile)) {
1362 fileSimple->Write(buffer, nbytes, offset);
1363 } else {
1364 auto &fileProper = std::get<RFileProper>(fFile);
1365 fileProper.Write(buffer, nbytes, offset);
1366 }
1367}
1368
1370{
1371 auto offset = WriteBlob(data, nbytes, lenHeader);
1372 fNTupleAnchor.fLenHeader = lenHeader;
1373 fNTupleAnchor.fNBytesHeader = nbytes;
1374 fNTupleAnchor.fSeekHeader = offset;
1375 return offset;
1376}
1377
1379{
1380 auto offset = WriteBlob(data, nbytes, lenFooter);
1381 fNTupleAnchor.fLenFooter = lenFooter;
1382 fNTupleAnchor.fNBytesFooter = nbytes;
1383 fNTupleAnchor.fSeekFooter = offset;
1384 return offset;
1385}
1386
1388{
1389 RBareFileHeader bareHeader;
1390 bareHeader.fCompress = defaultCompression;
1391 auto &fileSimple = std::get<RFileSimple>(fFile);
1392 fileSimple.Write(&bareHeader, sizeof(bareHeader), 0);
1393 RTFString ntupleName{fNTupleName};
1394 fileSimple.Write(&ntupleName, ntupleName.GetSize());
1395
1396 // Write zero-initialized ntuple to reserve the space; will be overwritten on commit
1397 RTFNTuple ntupleOnDisk;
1398 fileSimple.fControlBlock->fSeekNTuple = fileSimple.fFilePos;
1399 fileSimple.Write(&ntupleOnDisk, ntupleOnDisk.GetSize());
1400 std::uint64_t checksum = 0;
1401 fileSimple.Write(&checksum, sizeof(checksum));
1402 fileSimple.fKeyOffset = fileSimple.fFilePos;
1403}
1404
1406{
1407 // The streamer info record is a TList of TStreamerInfo object. We cannot use
1408 // RNTupleSerializer::SerializeStreamerInfos because that uses TBufferIO::WriteObject.
1409 // This would prepend the streamed TList with self-decription information.
1410 // The streamer info record is just the streamed TList.
1411
1413 for (auto [_, info] : fStreamerInfoMap) {
1415 }
1416
1417 // We will stream the list with a TBufferFile. When reading the streamer info records back,
1418 // the read buffer includes the key and the streamed list. Therefore, we need to start streaming
1419 // with an offset of the key length. Otherwise, the offset for referencing duplicate objects in the
1420 // buffer will point to the wrong places.
1421
1422 // Figure out key length
1423 RTFString strTList{"TList"};
1424 RTFString strStreamerInfo{"StreamerInfo"};
1425 RTFString strStreamerTitle{"Doubly linked list"};
1426 auto &fileSimple = std::get<RFileSimple>(fFile);
1427 fileSimple.fControlBlock->fHeader.SetSeekInfo(fileSimple.fKeyOffset);
1428 auto keyLen = RTFKey(fileSimple.fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, strTList, strStreamerInfo,
1430 .fKeyLen;
1431
1432 TBufferFile buffer(TBuffer::kWrite, keyLen + 1);
1433 buffer.SetBufferOffset(keyLen);
1434 streamerInfoList.Streamer(buffer);
1435 assert(buffer.Length() > keyLen);
1436 const auto bufPayload = buffer.Buffer() + keyLen;
1437 const auto lenPayload = buffer.Length() - keyLen;
1438
1441
1443 fileSimple.fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, "TList", "StreamerInfo",
1444 "Doubly linked list");
1445 fileSimple.fControlBlock->fHeader.SetNbytesInfo(fileSimple.fFilePos -
1446 fileSimple.fControlBlock->fHeader.GetSeekInfo());
1447}
1448
1450{
1451 RTFString strEmpty;
1452 RTFString strRNTupleClass{"ROOT::RNTuple"};
1453 RTFString strRNTupleName{fNTupleName};
1454 RTFString strFileName{fFileName};
1455
1456 auto &fileSimple = std::get<RFileSimple>(fFile);
1457 RTFKey keyRNTuple(fileSimple.fControlBlock->fSeekNTuple, RTFHeader::kBEGIN, strRNTupleClass, strRNTupleName,
1458 strEmpty, RTFNTuple::GetSizePlusChecksum(), anchorSize);
1459
1460 fileSimple.fControlBlock->fFileRecord.SetSeekKeys(fileSimple.fKeyOffset);
1461 RTFKeyList keyList{1};
1462 RTFKey keyKeyList(fileSimple.fControlBlock->fFileRecord.GetSeekKeys(), RTFHeader::kBEGIN, strEmpty, strFileName,
1463 strEmpty, keyList.GetSize() + keyRNTuple.fKeyLen);
1464 fileSimple.Write(&keyKeyList, keyKeyList.GetHeaderSize(), fileSimple.fControlBlock->fFileRecord.GetSeekKeys());
1465 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1466 fileSimple.Write(&strFileName, strFileName.GetSize());
1467 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1468 fileSimple.Write(&keyList, keyList.GetSize());
1469 fileSimple.Write(&keyRNTuple, keyRNTuple.GetHeaderSize());
1470 // Write class name, object name, and title for this key.
1471 fileSimple.Write(&strRNTupleClass, strRNTupleClass.GetSize());
1472 fileSimple.Write(&strRNTupleName, strRNTupleName.GetSize());
1473 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1474 fileSimple.fControlBlock->fFileRecord.fNBytesKeys =
1475 fileSimple.fFilePos - fileSimple.fControlBlock->fFileRecord.GetSeekKeys();
1476 fileSimple.fKeyOffset = fileSimple.fFilePos;
1477}
1478
1480{
1481 auto &fileSimple = std::get<RFileSimple>(fFile);
1482 fileSimple.fControlBlock->fHeader.SetSeekFree(fileSimple.fKeyOffset);
1483 RTFString strEmpty;
1484 RTFString strFileName{fFileName};
1485 RTFFreeEntry freeEntry;
1486 RTFKey keyFreeList(fileSimple.fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, strEmpty, strFileName,
1487 strEmpty, freeEntry.GetSize());
1488 std::uint64_t firstFree = fileSimple.fControlBlock->fHeader.GetSeekFree() + keyFreeList.GetSize();
1489 freeEntry.Set(firstFree, std::max(2000000000ULL, ((firstFree / 1000000000ULL) + 1) * 1000000000ULL));
1490 fileSimple.WriteKey(&freeEntry, freeEntry.GetSize(), freeEntry.GetSize(),
1491 fileSimple.fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, "", fFileName, "");
1492 fileSimple.fControlBlock->fHeader.SetNbytesFree(fileSimple.fFilePos -
1493 fileSimple.fControlBlock->fHeader.GetSeekFree());
1494 fileSimple.fControlBlock->fHeader.SetEnd(fileSimple.fFilePos);
1495}
1496
1498{
1499 RTFString strRNTupleClass{"ROOT::RNTuple"};
1500 RTFString strRNTupleName{fNTupleName};
1501 RTFString strEmpty;
1502
1503 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1504 RUInt64BE checksum{XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData())};
1505 auto &fileSimple = std::get<RFileSimple>(fFile);
1506 fileSimple.fControlBlock->fSeekNTuple = fileSimple.fKeyOffset;
1507
1508 char keyBuf[RTFNTuple::GetSizePlusChecksum()];
1509
1510 // concatenate the RNTuple anchor with its checksum
1511 memcpy(keyBuf, &ntupleOnDisk, sizeof(RTFNTuple));
1512 memcpy(keyBuf + sizeof(RTFNTuple), &checksum, sizeof(checksum));
1513
1514 const auto sizeAnchor = sizeof(RTFNTuple) + sizeof(checksum);
1515 char zipAnchor[RTFNTuple::GetSizePlusChecksum()];
1517
1518 fileSimple.WriteKey(zipAnchor, szZipAnchor, sizeof(keyBuf), fileSimple.fControlBlock->fSeekNTuple, RTFHeader::kBEGIN,
1519 "ROOT::RNTuple", fNTupleName, "");
1520 return szZipAnchor;
1521}
1522
1524{
1525 RTFString strTFile{"TFile"};
1526 RTFString strFileName{fFileName};
1527 RTFString strEmpty;
1528
1529 auto &fileSimple = std::get<RFileSimple>(fFile);
1530 fileSimple.fControlBlock->fHeader = RTFHeader(defaultCompression);
1531
1532 RTFUUID uuid;
1533
1534 // First record of the file: the TFile object at offset kBEGIN (= 100)
1535 RTFKey keyRoot(RTFHeader::kBEGIN, 0, strTFile, strFileName, strEmpty,
1536 sizeof(RTFDirectory) + strFileName.GetSize() + strEmpty.GetSize() + uuid.GetSize());
1537 std::uint32_t nbytesName = keyRoot.fKeyLen + strFileName.GetSize() + 1;
1538 fileSimple.fControlBlock->fFileRecord.fNBytesName = nbytesName;
1539 fileSimple.fControlBlock->fHeader.SetNbytesName(nbytesName);
1540
1541 fileSimple.Write(&keyRoot, keyRoot.GetHeaderSize(), RTFHeader::kBEGIN);
1542 // Write class name, object name, and title for the TFile key.
1543 fileSimple.Write(&strTFile, strTFile.GetSize());
1544 fileSimple.Write(&strFileName, strFileName.GetSize());
1545 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1546 // Write the name and title of the TNamed preceding the TFile entry.
1547 fileSimple.Write(&strFileName, strFileName.GetSize());
1548 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1549 // Will be overwritten on commit
1550 fileSimple.fControlBlock->fSeekFileRecord = fileSimple.fFilePos;
1551 fileSimple.Write(&fileSimple.fControlBlock->fFileRecord, fileSimple.fControlBlock->fFileRecord.GetSize());
1552 fileSimple.Write(&uuid, uuid.GetSize());
1553
1554 // Padding bytes to allow the TFile record to grow for a big file
1555 RUInt32BE padding{0};
1556 for (int i = 0; i < 3; ++i)
1557 fileSimple.Write(&padding, sizeof(padding));
1558 fileSimple.fKeyOffset = fileSimple.fFilePos;
1559}
#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.
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 SetParent(TObject *parent)
Set parent owning this buffer.
Definition TBuffer.cxx:270
@ kWrite
Definition TBuffer.h:73
void SetBufferOffset(Int_t offset=0)
Definition TBuffer.h:93
Int_t Length() const
Definition TBuffer.h:100
char * Buffer() const
Definition TBuffer.h:96
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:1346
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:462
TString fClassName
Object Class name.
Definition TKey.h:47
A doubly linked list.
Definition TList.h:38
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:275
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.
Namespace for new ROOT classes and functions.
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