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