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 auto res = GetNTupleProperAtOffset(offset);
758 return res;
759}
760
762{
763 auto offset = keyOffset;
764 RTFKey key;
765 ReadBuffer(&key, sizeof(key), offset);
766 offset = key.GetSeekKey() + key.fKeyLen;
767
768 // size of a RTFNTuple version 2 (min supported version); future anchor versions can grow.
769 constexpr size_t kMinNTupleSize = 78;
770 static_assert(kMinNTupleSize == RTFNTuple::GetSizePlusChecksum());
771 if (key.fObjLen < kMinNTupleSize) {
772 return R__FAIL("invalid anchor size: " + std::to_string(key.fObjLen) + " < " + std::to_string(sizeof(RTFNTuple)));
773 }
774
775 // The object length can be smaller than the size of RTFNTuple if it comes from a past RNTuple class version,
776 // or larger than it if it comes from a future RNTuple class version.
777 auto bufAnchor = MakeUninitArray<unsigned char>(std::max<size_t>(key.fObjLen, sizeof(RTFNTuple)));
778 RTFNTuple *ntuple = new (bufAnchor.get()) RTFNTuple;
779
780 const auto objNbytes = key.GetSize() - key.fKeyLen;
781 if (objNbytes != key.fObjLen) {
782 // Read into a temporary buffer
783 auto unzipBuf = MakeUninitArray<unsigned char>(std::max<size_t>(key.fObjLen, sizeof(RTFNTuple)));
785 // Unzip into the final buffer
787 } else {
789 }
790
791 // We require that future class versions only append members and store the checksum in the last 8 bytes
792 // Checksum calculation: strip byte count, class version, fChecksum member
793 const auto lenCkData = key.fObjLen - ntuple->GetOffsetCkData() - sizeof(uint64_t);
794 const auto ckCalc = XXH3_64bits(ntuple->GetPtrCkData(), lenCkData);
795 uint64_t ckOnDisk;
796
797 RUInt64BE *ckOnDiskPtr = reinterpret_cast<RUInt64BE *>(bufAnchor.get() + key.fObjLen - sizeof(uint64_t));
798 ckOnDisk = static_cast<uint64_t>(*ckOnDiskPtr);
799 if (ckCalc != ckOnDisk) {
800 return R__FAIL("RNTuple anchor checksum mismatch");
801 }
802
803 return CreateAnchor(ntuple->fVersionEpoch, ntuple->fVersionMajor, ntuple->fVersionMinor, ntuple->fVersionPatch,
804 ntuple->fSeekHeader, ntuple->fNBytesHeader, ntuple->fLenHeader, ntuple->fSeekFooter,
805 ntuple->fNBytesFooter, ntuple->fLenFooter, ntuple->fMaxKeySize);
806}
807
809{
810 RBareFileHeader fileHeader;
811 ReadBuffer(&fileHeader, sizeof(fileHeader), 0);
812 RTFString name;
813 auto offset = sizeof(fileHeader);
814 ReadBuffer(&name, 1, offset);
815 ReadBuffer(&name, name.GetSize(), offset);
816 std::string_view foundName(name.fData, name.fLName);
817 if (foundName != ntupleName) {
818 return R__FAIL("expected RNTuple named '" + std::string(ntupleName) + "' but instead found '" +
819 std::string(foundName) + "' in file '" + fRawFile->GetUrl() + "'");
820 }
821 offset += name.GetSize();
822
823 RTFNTuple ntuple;
824 ReadBuffer(&ntuple, sizeof(ntuple), offset);
825 std::uint64_t onDiskChecksum;
827 auto checksum = XXH3_64bits(ntuple.GetPtrCkData(), ntuple.GetSizeCkData());
828 if (checksum != static_cast<uint64_t>(onDiskChecksum))
829 return R__FAIL("RNTuple bare file: anchor checksum mismatch");
830
831 return CreateAnchor(ntuple.fVersionEpoch, ntuple.fVersionMajor, ntuple.fVersionMinor, ntuple.fVersionPatch,
832 ntuple.fSeekHeader, ntuple.fNBytesHeader, ntuple.fLenHeader, ntuple.fSeekFooter,
833 ntuple.fNBytesFooter, ntuple.fLenFooter, ntuple.fMaxKeySize);
834}
835
836void ROOT::Internal::RMiniFileReader::ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
837{
838 size_t nread;
839 if (fMaxKeySize == 0 || nbytes <= fMaxKeySize) {
840 // Fast path: read single blob
841 nread = fRawFile->ReadAt(buffer, nbytes, offset);
842 } else {
843 // Read chunked blob. See RNTupleFileWriter::WriteBlob() for details.
844 const size_t nChunks = ComputeNumChunks(nbytes, fMaxKeySize);
845 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
846 const size_t nbytesFirstChunk = fMaxKeySize - nbytesChunkOffsets;
847 uint8_t *bufCur = reinterpret_cast<uint8_t *>(buffer);
848
849 // Read first chunk
850 nread = fRawFile->ReadAt(bufCur, fMaxKeySize, offset);
851 R__ASSERT(nread == fMaxKeySize);
852 // NOTE: we read the entire chunk in `bufCur`, but we only advance the pointer by `nbytesFirstChunk`,
853 // since the last part of `bufCur` will later be overwritten by the next chunk's payload.
854 // We do this to avoid a second ReadAt to read in the chunk offsets.
857
860
862 std::uint64_t *curChunkOffset = &chunkOffsets[0];
863
864 do {
865 std::uint64_t chunkOffset;
868
869 const size_t bytesToRead = std::min<size_t>(fMaxKeySize, remainingBytes);
870 // Ensure we don't read outside of the buffer
871 R__ASSERT(static_cast<size_t>(bufCur - reinterpret_cast<uint8_t *>(buffer)) <= nbytes - bytesToRead);
872
873 auto nbytesRead = fRawFile->ReadAt(bufCur, bytesToRead, chunkOffset);
875
879 } while (remainingBytes > 0);
880 }
882}
883
884////////////////////////////////////////////////////////////////////////////////
885
886/// Prepare a blob key in the provided buffer, which must provide space for kBlobKeyLen bytes. Note that the array type
887/// is purely documentation, the argument is actually just a pointer.
889 unsigned char buffer[kBlobKeyLen])
890{
891 RTFString strClass{kBlobClassName};
892 RTFString strObject;
893 RTFString strTitle;
894 RTFKey keyHeader(offset, RTFHeader::kBEGIN, strClass, strObject, strTitle, len, nbytes);
895 R__ASSERT(keyHeader.fKeyLen == kBlobKeyLen);
896
897 // Copy structures into the buffer.
898 unsigned char *writeBuffer = buffer;
899 memcpy(writeBuffer, &keyHeader, keyHeader.GetHeaderSize());
900 writeBuffer += keyHeader.GetHeaderSize();
901 memcpy(writeBuffer, &strClass, strClass.GetSize());
902 writeBuffer += strClass.GetSize();
904 writeBuffer += strObject.GetSize();
905 memcpy(writeBuffer, &strTitle, strTitle.GetSize());
906 writeBuffer += strTitle.GetSize();
907 R__ASSERT(writeBuffer == buffer + kBlobKeyLen);
908}
909
910////////////////////////////////////////////////////////////////////////////////
911
913
915{
916 static_assert(kHeaderBlockSize % kBlockAlign == 0, "invalid header block size");
917 if (bufferSize % kBlockAlign != 0)
918 throw RException(R__FAIL("Buffer size not a multiple of alignment: " + std::to_string(bufferSize)));
919 fBlockSize = bufferSize;
920
921 std::align_val_t blockAlign{kBlockAlign};
922 fHeaderBlock = static_cast<unsigned char *>(::operator new[](kHeaderBlockSize, blockAlign));
923 memset(fHeaderBlock, 0, kHeaderBlockSize);
924 fBlock = static_cast<unsigned char *>(::operator new[](fBlockSize, blockAlign));
925 memset(fBlock, 0, fBlockSize);
926}
927
929{
930 if (fFile)
931 fclose(fFile);
932
933 std::align_val_t blockAlign{kBlockAlign};
934 if (fHeaderBlock)
935 ::operator delete[](fHeaderBlock, blockAlign);
936 if (fBlock)
937 ::operator delete[](fBlock, blockAlign);
938}
939
940namespace {
941int FSeek64(FILE *stream, std::int64_t offset, int origin)
942{
943#ifdef R__SEEK64
944 return fseeko64(stream, offset, origin);
945#else
946 return fseek(stream, offset, origin);
947#endif
948}
949} // namespace
950
952{
953 // Write the last partially filled block, which may still need appropriate alignment for Direct I/O.
954 // If it is the first block, get the updated header block.
955 if (fBlockOffset == 0) {
956 std::size_t headerBlockSize = kHeaderBlockSize;
957 if (headerBlockSize > fFilePos) {
958 headerBlockSize = fFilePos;
959 }
960 memcpy(fBlock, fHeaderBlock, headerBlockSize);
961 }
962
963 std::size_t retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
964 if (retval)
965 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
966
967 std::size_t lastBlockSize = fFilePos - fBlockOffset;
968 R__ASSERT(lastBlockSize <= fBlockSize);
969 if (fDirectIO) {
970 // Round up to a multiple of kBlockAlign.
971 lastBlockSize += kBlockAlign - 1;
972 lastBlockSize = (lastBlockSize / kBlockAlign) * kBlockAlign;
973 R__ASSERT(lastBlockSize <= fBlockSize);
974 }
975 retval = fwrite(fBlock, 1, lastBlockSize, fFile);
976 if (retval != lastBlockSize)
977 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
978
979 // Write the (updated) header block, unless it was part of the write above.
980 if (fBlockOffset > 0) {
981 retval = FSeek64(fFile, 0, SEEK_SET);
982 if (retval)
983 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
984
985 retval = fwrite(fHeaderBlock, 1, kHeaderBlockSize, fFile);
986 if (retval != RFileSimple::kHeaderBlockSize)
987 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
988 }
989
990 retval = fflush(fFile);
991 if (retval)
992 throw RException(R__FAIL(std::string("Flush failed: ") + strerror(errno)));
993}
994
995void ROOT::Internal::RNTupleFileWriter::RFileSimple::Write(const void *buffer, size_t nbytes, std::int64_t offset)
996{
997 R__ASSERT(fFile);
998 size_t retval;
999 if ((offset >= 0) && (static_cast<std::uint64_t>(offset) != fFilePos)) {
1000 fFilePos = offset;
1001 }
1002
1003 // Keep header block to overwrite on commit.
1004 if (fFilePos < kHeaderBlockSize) {
1005 std::size_t headerBytes = nbytes;
1006 if (fFilePos + headerBytes > kHeaderBlockSize) {
1007 headerBytes = kHeaderBlockSize - fFilePos;
1008 }
1009 memcpy(fHeaderBlock + fFilePos, buffer, headerBytes);
1010 }
1011
1012 R__ASSERT(fFilePos >= fBlockOffset);
1013
1014 while (nbytes > 0) {
1015 std::uint64_t posInBlock = fFilePos % fBlockSize;
1016 std::uint64_t blockOffset = fFilePos - posInBlock;
1017 if (blockOffset != fBlockOffset) {
1018 // Write the block.
1019 retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
1020 if (retval)
1021 throw RException(R__FAIL(std::string("Seek failed: ") + strerror(errno)));
1022
1023 retval = fwrite(fBlock, 1, fBlockSize, fFile);
1024 if (retval != fBlockSize)
1025 throw RException(R__FAIL(std::string("write failed: ") + strerror(errno)));
1026
1027 // Null the buffer contents for good measure.
1028 memset(fBlock, 0, fBlockSize);
1029 }
1030
1031 fBlockOffset = blockOffset;
1032 std::size_t blockSize = nbytes;
1033 if (blockSize > fBlockSize - posInBlock) {
1034 blockSize = fBlockSize - posInBlock;
1035 }
1036 memcpy(fBlock + posInBlock, buffer, blockSize);
1037 buffer = static_cast<const unsigned char *>(buffer) + blockSize;
1038 nbytes -= blockSize;
1039 fFilePos += blockSize;
1040 }
1041}
1042
1043std::uint64_t
1044ROOT::Internal::RNTupleFileWriter::RFileSimple::WriteKey(const void *buffer, std::size_t nbytes, std::size_t len,
1045 std::int64_t offset, std::uint64_t directoryOffset,
1046 const std::string &className, const std::string &objectName,
1047 const std::string &title)
1048{
1049 if (offset > 0)
1050 fKeyOffset = offset;
1051 RTFString strClass{className};
1052 RTFString strObject{objectName};
1053 RTFString strTitle{title};
1054
1055 RTFKey key(fKeyOffset, directoryOffset, strClass, strObject, strTitle, len, nbytes);
1056 Write(&key, key.GetHeaderSize(), fKeyOffset);
1057 Write(&strClass, strClass.GetSize());
1058 Write(&strObject, strObject.GetSize());
1059 Write(&strTitle, strTitle.GetSize());
1060 auto offsetData = fFilePos;
1061 // The next key starts after the data.
1062 fKeyOffset = offsetData + nbytes;
1063 if (buffer)
1064 Write(buffer, nbytes);
1065
1066 return offsetData;
1067}
1068
1070 unsigned char keyBuffer[kBlobKeyLen])
1071{
1072 if (keyBuffer) {
1073 PrepareBlobKey(fKeyOffset, nbytes, len, keyBuffer);
1074 } else {
1075 unsigned char localKeyBuffer[kBlobKeyLen];
1076 PrepareBlobKey(fKeyOffset, nbytes, len, localKeyBuffer);
1077 Write(localKeyBuffer, kBlobKeyLen, fKeyOffset);
1078 }
1079
1080 auto offsetData = fKeyOffset + kBlobKeyLen;
1081 // The next key starts after the data.
1082 fKeyOffset = offsetData + nbytes;
1083
1084 return offsetData;
1085}
1086
1087////////////////////////////////////////////////////////////////////////////////
1088
1089void ROOT::Internal::RNTupleFileWriter::RFileProper::Write(const void *buffer, size_t nbytes, std::int64_t offset)
1090{
1091 fDirectory->GetFile()->Seek(offset);
1092 bool rv = fDirectory->GetFile()->WriteBuffer((char *)(buffer), nbytes);
1093 if (rv)
1094 throw RException(R__FAIL("WriteBuffer failed."));
1095}
1096
1098 unsigned char keyBuffer[kBlobKeyLen])
1099{
1100 std::uint64_t offsetKey;
1101 RKeyBlob keyBlob(fDirectory->GetFile());
1102 // Since it is unknown beforehand if offsetKey is beyond the 2GB limit or not,
1103 // RKeyBlob will always reserve space for a big key (version >= 1000)
1104 keyBlob.Reserve(nbytes, &offsetKey);
1105
1106 if (keyBuffer) {
1107 PrepareBlobKey(offsetKey, nbytes, len, keyBuffer);
1108 } else {
1109 unsigned char localKeyBuffer[kBlobKeyLen];
1110 PrepareBlobKey(offsetKey, nbytes, len, localKeyBuffer);
1111 Write(localKeyBuffer, kBlobKeyLen, offsetKey);
1112 }
1113
1114 if (keyBlob.WasAllocatedInAFreeSlot()) {
1115 // If the key was allocated in a free slot, the last 4 bytes of its buffer contain the new size
1116 // of the remaining free slot and we need to write it to disk before the key gets destroyed at the end of the
1117 // function.
1118 Write(keyBlob.GetBuffer() + nbytes, sizeof(Int_t), offsetKey + kBlobKeyLen + nbytes);
1119 }
1120
1121 auto offsetData = offsetKey + kBlobKeyLen;
1122
1123 return offsetData;
1124}
1125
1126////////////////////////////////////////////////////////////////////////////////
1127
1129 : fNTupleName(name)
1130{
1131 auto &fileSimple = fFile.emplace<RFileSimple>();
1132 fileSimple.fControlBlock = std::make_unique<ROOT::Internal::RTFileControlBlock>();
1134 auto infoRNTuple = RNTuple::Class()->GetStreamerInfo();
1136}
1137
1139
1140std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1141ROOT::Internal::RNTupleFileWriter::Recreate(std::string_view ntupleName, std::string_view path,
1143{
1144 std::string fileName(path);
1145 size_t idxDirSep = fileName.find_last_of("\\/");
1146 if (idxDirSep != std::string::npos) {
1147 fileName.erase(0, idxDirSep + 1);
1148 }
1149#ifdef R__LINUX
1150 int flags = O_WRONLY | O_CREAT | O_TRUNC;
1151#ifdef O_LARGEFILE
1152 // Add the equivalent flag that is passed by fopen64.
1153 flags |= O_LARGEFILE;
1154#endif
1155 if (options.GetUseDirectIO()) {
1156 flags |= O_DIRECT;
1157 }
1158 int fd = open(std::string(path).c_str(), flags, 0666);
1159 if (fd == -1) {
1160 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1161 }
1162 FILE *fileStream = fdopen(fd, "wb");
1163#else
1164#ifdef R__SEEK64
1165 FILE *fileStream = fopen64(std::string(path.data(), path.size()).c_str(), "wb");
1166#else
1167 FILE *fileStream = fopen(std::string(path.data(), path.size()).c_str(), "wb");
1168#endif
1169#endif
1170 if (!fileStream) {
1171 throw RException(R__FAIL(std::string("open failed for file \"") + std::string(path) + "\": " + strerror(errno)));
1172 }
1173 // RNTupleFileWriter::RFileSimple does its own buffering, turn off additional buffering from C stdio.
1174 std::setvbuf(fileStream, nullptr, _IONBF, 0);
1175
1176 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, options.GetMaxKeySize()));
1177 RFileSimple &fileSimple = std::get<RFileSimple>(writer->fFile);
1178 fileSimple.fFile = fileStream;
1179 fileSimple.fDirectIO = options.GetUseDirectIO();
1180 fileSimple.AllocateBuffers(options.GetWriteBufferSize());
1181 writer->fFileName = fileName;
1182
1183 int defaultCompression = options.GetCompression();
1184 switch (containerFormat) {
1185 case EContainerFormat::kTFile: writer->WriteTFileSkeleton(defaultCompression); break;
1186 case EContainerFormat::kBare:
1187 writer->fIsBare = true;
1188 writer->WriteBareFileSkeleton(defaultCompression);
1189 break;
1190 default: R__ASSERT(false && "Internal error: unhandled container format");
1191 }
1192
1193 return writer;
1194}
1195
1196std::unique_ptr<ROOT::Internal::RNTupleFileWriter>
1198 std::uint64_t maxKeySize)
1199{
1200 TFile *file = fileOrDirectory.GetFile();
1201 if (!file)
1202 throw RException(R__FAIL("invalid attempt to add an RNTuple to a directory that is not backed by a file"));
1203 assert(file->IsBinary());
1204
1205 auto writer = std::unique_ptr<RNTupleFileWriter>(new RNTupleFileWriter(ntupleName, maxKeySize));
1206 auto &fileProper = writer->fFile.emplace<RFileProper>();
1207 fileProper.fDirectory = &fileOrDirectory;
1208 return writer;
1209}
1210
1212{
1213 RFileSimple *fileSimple = std::get_if<RFileSimple>(&fFile);
1214 if (!fileSimple)
1215 throw RException(R__FAIL("invalid attempt to seek non-simple writer"));
1216
1217 fileSimple->fFilePos = offset;
1218 fileSimple->fKeyOffset = offset;
1219 // The next Write() will Flush() if necessary.
1220}
1221
1226
1228{
1229 if (auto fileProper = std::get_if<RFileProper>(&fFile)) {
1230 // Easy case, the ROOT file header and the RNTuple streaming is taken care of by TFile
1231 fileProper->fDirectory->WriteObject(&fNTupleAnchor, fNTupleName.c_str());
1232
1233 // Make sure the streamer info records used in the RNTuple are written to the file
1235 buf.SetParent(fileProper->fDirectory->GetFile());
1236 for (auto [_, info] : fStreamerInfoMap)
1237 buf.TagStreamerInfo(info);
1238
1239 fileProper->fDirectory->GetFile()->Write();
1240 return;
1241 }
1242
1243 // Writing by C file stream: prepare the container format header and stream the RNTuple anchor object
1244 auto &fileSimple = std::get<RFileSimple>(fFile);
1245
1246 if (fIsBare) {
1247 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1248 // Compute the checksum
1249 std::uint64_t checksum = XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData());
1250 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekNTuple, &ntupleOnDisk, ntupleOnDisk.GetSize());
1251 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekNTuple + ntupleOnDisk.GetSize(), &checksum,
1252 sizeof(checksum));
1253 fileSimple.Flush();
1254 return;
1255 }
1256
1257 auto anchorSize = WriteTFileNTupleKey(compression);
1258 WriteTFileKeysList(anchorSize); // NOTE: this is written uncompressed
1259 WriteTFileStreamerInfo(compression);
1260 WriteTFileFreeList(); // NOTE: this is written uncompressed
1261
1262 // Update header and TFile record
1263 memcpy(fileSimple.fHeaderBlock, &fileSimple.fControlBlock->fHeader, fileSimple.fControlBlock->fHeader.GetSize());
1264 R__ASSERT(fileSimple.fControlBlock->fSeekFileRecord + fileSimple.fControlBlock->fFileRecord.GetSize() <
1265 RFileSimple::kHeaderBlockSize);
1266 memcpy(fileSimple.fHeaderBlock + fileSimple.fControlBlock->fSeekFileRecord, &fileSimple.fControlBlock->fFileRecord,
1267 fileSimple.fControlBlock->fFileRecord.GetSize());
1268
1269 fileSimple.Flush();
1270}
1271
1272std::uint64_t ROOT::Internal::RNTupleFileWriter::WriteBlob(const void *data, size_t nbytes, size_t len)
1273{
1274 auto writeKey = [this](const void *payload, size_t nBytes, size_t length) {
1275 std::uint64_t offset = ReserveBlob(nBytes, length);
1276 WriteIntoReservedBlob(payload, nBytes, offset);
1277 return offset;
1278 };
1279
1280 const std::uint64_t maxKeySize = fNTupleAnchor.fMaxKeySize;
1281 R__ASSERT(maxKeySize > 0);
1282 // We don't need the object length except for seeing compression ratios in TFile::Map()
1283 // Make sure that the on-disk object length fits into the TKey header.
1284 if (static_cast<std::uint64_t>(len) > static_cast<std::uint64_t>(std::numeric_limits<std::uint32_t>::max()))
1285 len = nbytes;
1286
1287 if (nbytes <= maxKeySize) {
1288 // Fast path: only write 1 key.
1289 return writeKey(data, nbytes, len);
1290 }
1291
1292 /**
1293 * Writing a key bigger than the max allowed size. In this case we split the payload
1294 * into multiple keys, reserving the end of the first key payload for pointers to the
1295 * next ones. E.g. if a key needs to be split into 3 chunks, the first chunk will have
1296 * the format:
1297 * +--------------------+
1298 * | |
1299 * | Data |
1300 * |--------------------|
1301 * | pointer to chunk 2 |
1302 * | pointer to chunk 3 |
1303 * +--------------------+
1304 */
1305 const size_t nChunks = ComputeNumChunks(nbytes, maxKeySize);
1306 const size_t nbytesChunkOffsets = (nChunks - 1) * sizeof(std::uint64_t);
1308 // Skip writing the first chunk, it will be written last (in the file) below.
1309
1310 const uint8_t *chunkData = reinterpret_cast<const uint8_t *>(data) + nbytesFirstChunk;
1312
1314 std::uint64_t chunkOffsetIdx = 0;
1315
1316 do {
1317 const size_t bytesNextChunk = std::min<size_t>(remainingBytes, maxKeySize);
1318 const std::uint64_t offset = writeKey(chunkData, bytesNextChunk, bytesNextChunk);
1319
1322
1325
1326 } while (remainingBytes > 0);
1327
1328 // Write the first key, with part of the data and the pointers to (logically) following keys appended.
1329 const std::uint64_t firstOffset = ReserveBlob(maxKeySize, maxKeySize);
1330 WriteIntoReservedBlob(data, nbytesFirstChunk, firstOffset);
1331 const std::uint64_t chunkOffsetsOffset = firstOffset + nbytesFirstChunk;
1332 WriteIntoReservedBlob(chunkOffsetsToWrite.get(), nbytesChunkOffsets, chunkOffsetsOffset);
1333
1334 return firstOffset;
1335}
1336
1337std::uint64_t
1338ROOT::Internal::RNTupleFileWriter::ReserveBlob(size_t nbytes, size_t len, unsigned char keyBuffer[kBlobKeyLen])
1339{
1340 // ReserveBlob cannot be used to reserve a multi-key blob
1341 R__ASSERT(nbytes <= fNTupleAnchor.GetMaxKeySize());
1342
1343 std::uint64_t offset;
1344 if (auto *fileSimple = std::get_if<RFileSimple>(&fFile)) {
1345 if (fIsBare) {
1346 offset = fileSimple->fKeyOffset;
1347 fileSimple->fKeyOffset += nbytes;
1348 } else {
1349 offset = fileSimple->ReserveBlobKey(nbytes, len, keyBuffer);
1350 }
1351 } else {
1352 auto &fileProper = std::get<RFileProper>(fFile);
1353 offset = fileProper.ReserveBlobKey(nbytes, len, keyBuffer);
1354 }
1355 return offset;
1356}
1357
1358void ROOT::Internal::RNTupleFileWriter::WriteIntoReservedBlob(const void *buffer, size_t nbytes, std::int64_t offset)
1359{
1360 if (auto *fileSimple = std::get_if<RFileSimple>(&fFile)) {
1361 fileSimple->Write(buffer, nbytes, offset);
1362 } else {
1363 auto &fileProper = std::get<RFileProper>(fFile);
1364 fileProper.Write(buffer, nbytes, offset);
1365 }
1366}
1367
1369{
1370 auto offset = WriteBlob(data, nbytes, lenHeader);
1371 fNTupleAnchor.fLenHeader = lenHeader;
1372 fNTupleAnchor.fNBytesHeader = nbytes;
1373 fNTupleAnchor.fSeekHeader = offset;
1374 return offset;
1375}
1376
1378{
1379 auto offset = WriteBlob(data, nbytes, lenFooter);
1380 fNTupleAnchor.fLenFooter = lenFooter;
1381 fNTupleAnchor.fNBytesFooter = nbytes;
1382 fNTupleAnchor.fSeekFooter = offset;
1383 return offset;
1384}
1385
1387{
1388 RBareFileHeader bareHeader;
1389 bareHeader.fCompress = defaultCompression;
1390 auto &fileSimple = std::get<RFileSimple>(fFile);
1391 fileSimple.Write(&bareHeader, sizeof(bareHeader), 0);
1392 RTFString ntupleName{fNTupleName};
1393 fileSimple.Write(&ntupleName, ntupleName.GetSize());
1394
1395 // Write zero-initialized ntuple to reserve the space; will be overwritten on commit
1396 RTFNTuple ntupleOnDisk;
1397 fileSimple.fControlBlock->fSeekNTuple = fileSimple.fFilePos;
1398 fileSimple.Write(&ntupleOnDisk, ntupleOnDisk.GetSize());
1399 std::uint64_t checksum = 0;
1400 fileSimple.Write(&checksum, sizeof(checksum));
1401 fileSimple.fKeyOffset = fileSimple.fFilePos;
1402}
1403
1405{
1406 // The streamer info record is a TList of TStreamerInfo object. We cannot use
1407 // RNTupleSerializer::SerializeStreamerInfos because that uses TBufferIO::WriteObject.
1408 // This would prepend the streamed TList with self-decription information.
1409 // The streamer info record is just the streamed TList.
1410
1412 for (auto [_, info] : fStreamerInfoMap) {
1414 }
1415
1416 // We will stream the list with a TBufferFile. When reading the streamer info records back,
1417 // the read buffer includes the key and the streamed list. Therefore, we need to start streaming
1418 // with an offset of the key length. Otherwise, the offset for referencing duplicate objects in the
1419 // buffer will point to the wrong places.
1420
1421 // Figure out key length
1422 RTFString strTList{"TList"};
1423 RTFString strStreamerInfo{"StreamerInfo"};
1424 RTFString strStreamerTitle{"Doubly linked list"};
1425 auto &fileSimple = std::get<RFileSimple>(fFile);
1426 fileSimple.fControlBlock->fHeader.SetSeekInfo(fileSimple.fKeyOffset);
1427 auto keyLen = RTFKey(fileSimple.fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, strTList, strStreamerInfo,
1429 .fKeyLen;
1430
1431 TBufferFile buffer(TBuffer::kWrite, keyLen + 1);
1432 buffer.SetBufferOffset(keyLen);
1433 streamerInfoList.Streamer(buffer);
1434 assert(buffer.Length() > keyLen);
1435 const auto bufPayload = buffer.Buffer() + keyLen;
1436 const auto lenPayload = buffer.Length() - keyLen;
1437
1440
1442 fileSimple.fControlBlock->fHeader.GetSeekInfo(), RTFHeader::kBEGIN, "TList", "StreamerInfo",
1443 "Doubly linked list");
1444 fileSimple.fControlBlock->fHeader.SetNbytesInfo(fileSimple.fFilePos -
1445 fileSimple.fControlBlock->fHeader.GetSeekInfo());
1446}
1447
1449{
1450 RTFString strEmpty;
1451 RTFString strRNTupleClass{"ROOT::RNTuple"};
1452 RTFString strRNTupleName{fNTupleName};
1453 RTFString strFileName{fFileName};
1454
1455 auto &fileSimple = std::get<RFileSimple>(fFile);
1456 RTFKey keyRNTuple(fileSimple.fControlBlock->fSeekNTuple, RTFHeader::kBEGIN, strRNTupleClass, strRNTupleName,
1457 strEmpty, RTFNTuple::GetSizePlusChecksum(), anchorSize);
1458
1459 fileSimple.fControlBlock->fFileRecord.SetSeekKeys(fileSimple.fKeyOffset);
1460 RTFKeyList keyList{1};
1461 RTFKey keyKeyList(fileSimple.fControlBlock->fFileRecord.GetSeekKeys(), RTFHeader::kBEGIN, strEmpty, strFileName,
1462 strEmpty, keyList.GetSize() + keyRNTuple.fKeyLen);
1463 fileSimple.Write(&keyKeyList, keyKeyList.GetHeaderSize(), fileSimple.fControlBlock->fFileRecord.GetSeekKeys());
1464 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1465 fileSimple.Write(&strFileName, strFileName.GetSize());
1466 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1467 fileSimple.Write(&keyList, keyList.GetSize());
1468 fileSimple.Write(&keyRNTuple, keyRNTuple.GetHeaderSize());
1469 // Write class name, object name, and title for this key.
1470 fileSimple.Write(&strRNTupleClass, strRNTupleClass.GetSize());
1471 fileSimple.Write(&strRNTupleName, strRNTupleName.GetSize());
1472 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1473 fileSimple.fControlBlock->fFileRecord.fNBytesKeys =
1474 fileSimple.fFilePos - fileSimple.fControlBlock->fFileRecord.GetSeekKeys();
1475 fileSimple.fKeyOffset = fileSimple.fFilePos;
1476}
1477
1479{
1480 auto &fileSimple = std::get<RFileSimple>(fFile);
1481 fileSimple.fControlBlock->fHeader.SetSeekFree(fileSimple.fKeyOffset);
1482 RTFString strEmpty;
1483 RTFString strFileName{fFileName};
1484 RTFFreeEntry freeEntry;
1485 RTFKey keyFreeList(fileSimple.fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, strEmpty, strFileName,
1486 strEmpty, freeEntry.GetSize());
1487 std::uint64_t firstFree = fileSimple.fControlBlock->fHeader.GetSeekFree() + keyFreeList.GetSize();
1488 freeEntry.Set(firstFree, std::max(2000000000ULL, ((firstFree / 1000000000ULL) + 1) * 1000000000ULL));
1489 fileSimple.WriteKey(&freeEntry, freeEntry.GetSize(), freeEntry.GetSize(),
1490 fileSimple.fControlBlock->fHeader.GetSeekFree(), RTFHeader::kBEGIN, "", fFileName, "");
1491 fileSimple.fControlBlock->fHeader.SetNbytesFree(fileSimple.fFilePos -
1492 fileSimple.fControlBlock->fHeader.GetSeekFree());
1493 fileSimple.fControlBlock->fHeader.SetEnd(fileSimple.fFilePos);
1494}
1495
1497{
1498 RTFString strRNTupleClass{"ROOT::RNTuple"};
1499 RTFString strRNTupleName{fNTupleName};
1500 RTFString strEmpty;
1501
1502 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1503 RUInt64BE checksum{XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData())};
1504 auto &fileSimple = std::get<RFileSimple>(fFile);
1505 fileSimple.fControlBlock->fSeekNTuple = fileSimple.fKeyOffset;
1506
1507 char keyBuf[RTFNTuple::GetSizePlusChecksum()];
1508
1509 // concatenate the RNTuple anchor with its checksum
1510 memcpy(keyBuf, &ntupleOnDisk, sizeof(RTFNTuple));
1511 memcpy(keyBuf + sizeof(RTFNTuple), &checksum, sizeof(checksum));
1512
1513 const auto sizeAnchor = sizeof(RTFNTuple) + sizeof(checksum);
1514 char zipAnchor[RTFNTuple::GetSizePlusChecksum()];
1516
1517 fileSimple.WriteKey(zipAnchor, szZipAnchor, sizeof(keyBuf), fileSimple.fControlBlock->fSeekNTuple, RTFHeader::kBEGIN,
1518 "ROOT::RNTuple", fNTupleName, "");
1519 return szZipAnchor;
1520}
1521
1523{
1524 RTFString strTFile{"TFile"};
1525 RTFString strFileName{fFileName};
1526 RTFString strEmpty;
1527
1528 auto &fileSimple = std::get<RFileSimple>(fFile);
1529 fileSimple.fControlBlock->fHeader = RTFHeader(defaultCompression);
1530
1531 RTFUUID uuid;
1532
1533 // First record of the file: the TFile object at offset kBEGIN (= 100)
1534 RTFKey keyRoot(RTFHeader::kBEGIN, 0, strTFile, strFileName, strEmpty,
1535 sizeof(RTFDirectory) + strFileName.GetSize() + strEmpty.GetSize() + uuid.GetSize());
1536 std::uint32_t nbytesName = keyRoot.fKeyLen + strFileName.GetSize() + 1;
1537 fileSimple.fControlBlock->fFileRecord.fNBytesName = nbytesName;
1538 fileSimple.fControlBlock->fHeader.SetNbytesName(nbytesName);
1539
1540 fileSimple.Write(&keyRoot, keyRoot.GetHeaderSize(), RTFHeader::kBEGIN);
1541 // Write class name, object name, and title for the TFile key.
1542 fileSimple.Write(&strTFile, strTFile.GetSize());
1543 fileSimple.Write(&strFileName, strFileName.GetSize());
1544 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1545 // Write the name and title of the TNamed preceding the TFile entry.
1546 fileSimple.Write(&strFileName, strFileName.GetSize());
1547 fileSimple.Write(&strEmpty, strEmpty.GetSize());
1548 // Will be overwritten on commit
1549 fileSimple.fControlBlock->fSeekFileRecord = fileSimple.fFilePos;
1550 fileSimple.Write(&fileSimple.fControlBlock->fFileRecord, fileSimple.fControlBlock->fFileRecord.GetSize());
1551 fileSimple.Write(&uuid, uuid.GetSize());
1552
1553 // Padding bytes to allow the TFile record to grow for a big file
1554 RUInt32BE padding{0};
1555 for (int i = 0; i < 3; ++i)
1556 fileSimple.Write(&padding, sizeof(padding));
1557 fileSimple.fKeyOffset = fileSimple.fFilePos;
1558}
#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 > GetNTupleProperAtOffset(std::uint64_t keyOffset)
Loads an RNTuple anchor from a TFile at the given file offset (unzipping it if necessary).
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