47#ifndef R__LITTLE_ENDIAN
50#define R__LITTLE_ENDIAN 1
52#define R__LITTLE_ENDIAN 0
63 std::uint16_t fValBE = 0;
64 static std::uint16_t
Swap(std::uint16_t val)
66#if R__LITTLE_ENDIAN == 1
67 return RByteSwap<
sizeof(val)>::bswap(val);
74 RUInt16BE() =
default;
75 explicit RUInt16BE(
const std::uint16_t val) : fValBE(
Swap(val)) {}
76 operator std::uint16_t()
const {
return Swap(fValBE); }
77 RUInt16BE &
operator=(
const std::uint16_t val)
87 std::uint32_t fValBE = 0;
88 static std::uint32_t
Swap(std::uint32_t val)
90#if R__LITTLE_ENDIAN == 1
91 return RByteSwap<
sizeof(val)>::bswap(val);
98 RUInt32BE() =
default;
99 explicit RUInt32BE(
const std::uint32_t val) : fValBE(
Swap(val)) {}
100 operator std::uint32_t()
const {
return Swap(fValBE); }
101 RUInt32BE &
operator=(
const std::uint32_t val)
111 std::int32_t fValBE = 0;
112 static std::int32_t
Swap(std::int32_t val)
114#if R__LITTLE_ENDIAN == 1
115 return RByteSwap<
sizeof(val)>::bswap(val);
122 RInt32BE() =
default;
123 explicit RInt32BE(
const std::int32_t val) : fValBE(
Swap(val)) {}
124 operator std::int32_t()
const {
return Swap(fValBE); }
125 RInt32BE &
operator=(
const std::int32_t val)
135 std::uint64_t fValBE = 0;
136 static std::uint64_t
Swap(std::uint64_t val)
138#if R__LITTLE_ENDIAN == 1
139 return RByteSwap<
sizeof(val)>::bswap(val);
146 RUInt64BE() =
default;
147 explicit RUInt64BE(
const std::uint64_t val) : fValBE(
Swap(val)) {}
148 operator std::uint64_t()
const {
return Swap(fValBE); }
149 RUInt64BE &
operator=(
const std::uint64_t val)
159 unsigned char fLName{0};
161 RTFString() =
default;
162 RTFString(
const std::string &str)
167 fLName = str.length();
168 memcpy(fData, str.data(), fLName);
170 std::size_t GetSize()
const
184 auto now = std::chrono::system_clock::now();
185 auto tt = std::chrono::system_clock::to_time_t(now);
186 auto tm = *localtime(&
tt);
187 fDatetime = (tm.tm_year + 1900 - 1995) << 26 | (tm.tm_mon + 1) << 22 | tm.tm_mday << 17 | tm.tm_hour << 12 |
188 tm.tm_min << 6 | tm.tm_sec;
190 explicit RTFDatetime(RUInt32BE val) : fDatetime(val) {}
196 RUInt16BE fVersion{4};
197 RUInt32BE fObjLen{0};
198 RTFDatetime fDatetime;
199 RUInt16BE fKeyLen{0};
203 RUInt32BE fSeekKey{0};
204 RUInt32BE fSeekPdir{0};
207 RUInt64BE fSeekKey{0};
208 RUInt64BE fSeekPdir{0};
212 std::uint32_t fKeyHeaderSize{18 +
sizeof(fInfoShort)};
214 RTFKey() : fInfoShort() {}
215 RTFKey(std::uint64_t seekKey, std::uint64_t seekPdir,
const RTFString &clName,
const RTFString &objName,
216 const RTFString &titleName, std::size_t szObjInMem, std::size_t szObjOnDisk = 0)
218 R__ASSERT(szObjInMem <= std::numeric_limits<std::uint32_t>::max());
219 R__ASSERT(szObjOnDisk <= std::numeric_limits<std::uint32_t>::max());
220 fObjLen = szObjInMem;
221 if ((seekKey >
static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) ||
222 (seekPdir >
static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max()))) {
223 fKeyHeaderSize = 18 +
sizeof(fInfoLong);
224 fKeyLen = fKeyHeaderSize + clName.GetSize() + objName.GetSize() + titleName.GetSize();
225 fInfoLong.fSeekKey = seekKey;
226 fInfoLong.fSeekPdir = seekPdir;
227 fVersion = fVersion + 1000;
229 fKeyHeaderSize = 18 +
sizeof(fInfoShort);
230 fKeyLen = fKeyHeaderSize + clName.GetSize() + objName.GetSize() + titleName.GetSize();
231 fInfoShort.fSeekKey = seekKey;
232 fInfoShort.fSeekPdir = seekPdir;
234 fNbytes = fKeyLen + ((szObjOnDisk == 0) ? szObjInMem : szObjOnDisk);
239 if (fVersion >= 1000)
241 std::uint32_t seekKey = fInfoShort.fSeekKey;
242 std::uint32_t seekPdir = fInfoShort.fSeekPdir;
243 fInfoLong.fSeekKey = seekKey;
244 fInfoLong.fSeekPdir = seekPdir;
245 fKeyHeaderSize = fKeyHeaderSize +
sizeof(fInfoLong) -
sizeof(fInfoShort);
246 fKeyLen = fKeyLen +
sizeof(fInfoLong) -
sizeof(fInfoShort);
247 fNbytes = fNbytes +
sizeof(fInfoLong) -
sizeof(fInfoShort);
248 fVersion = fVersion + 1000;
251 std::uint32_t GetSize()
const
259 std::uint32_t GetHeaderSize()
const
261 if (fVersion >= 1000)
262 return 18 +
sizeof(fInfoLong);
263 return 18 +
sizeof(fInfoShort);
266 std::uint64_t GetSeekKey()
const
268 if (fVersion >= 1000)
269 return fInfoLong.fSeekKey;
270 return fInfoShort.fSeekKey;
276 char fMagic[4]{
'r',
'o',
'o',
't'};
279 RUInt32BE fBEGIN{100};
283 RUInt32BE fSeekFree{0};
284 RUInt32BE fNbytesFree{0};
286 RUInt32BE fNbytesName{0};
287 unsigned char fUnits{4};
288 RUInt32BE fCompress{0};
289 RUInt32BE fSeekInfo{0};
290 RUInt32BE fNbytesInfo{0};
294 RUInt64BE fSeekFree{0};
295 RUInt32BE fNbytesFree{0};
297 RUInt32BE fNbytesName{0};
298 unsigned char fUnits{8};
299 RUInt32BE fCompress{0};
300 RUInt64BE fSeekInfo{0};
301 RUInt32BE fNbytesInfo{0};
305 RTFHeader() : fInfoShort() {}
306 RTFHeader(
int compression) : fInfoShort() { fInfoShort.fCompress = compression; }
310 if (fVersion >= 1000000)
314 std::uint32_t end = fInfoShort.fEND;
315 std::uint32_t seekFree = fInfoShort.fSeekFree;
316 std::uint32_t nbytesFree = fInfoShort.fNbytesFree;
317 std::uint32_t nFree = fInfoShort.fNfree;
318 std::uint32_t nbytesName = fInfoShort.fNbytesName;
319 std::uint32_t compress = fInfoShort.fCompress;
320 std::uint32_t seekInfo = fInfoShort.fSeekInfo;
321 std::uint32_t nbytesInfo = fInfoShort.fNbytesInfo;
322 fInfoLong.fEND = end;
323 fInfoLong.fSeekFree = seekFree;
324 fInfoLong.fNbytesFree = nbytesFree;
325 fInfoLong.fNfree = nFree;
326 fInfoLong.fNbytesName = nbytesName;
327 fInfoLong.fUnits = 8;
328 fInfoLong.fCompress = compress;
329 fInfoLong.fSeekInfo = seekInfo;
330 fInfoLong.fNbytesInfo = nbytesInfo;
331 fVersion = fVersion + 1000000;
335 bool IsBigFile(std::uint64_t
offset = 0)
const
337 return (fVersion >= 1000000) || (
offset >
static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max()));
340 std::uint32_t GetSize()
const
342 std::uint32_t sizeHead = 4 +
sizeof(fVersion) +
sizeof(fBEGIN);
344 return sizeHead +
sizeof(fInfoLong);
345 return sizeHead +
sizeof(fInfoShort);
348 std::uint64_t GetEnd()
const
351 return fInfoLong.fEND;
352 return fInfoShort.fEND;
355 void SetEnd(std::uint64_t
value)
357 if (IsBigFile(
value)) {
359 fInfoLong.fEND =
value;
361 fInfoShort.fEND =
value;
365 std::uint64_t GetSeekFree()
const
368 return fInfoLong.fSeekFree;
369 return fInfoShort.fSeekFree;
372 void SetSeekFree(std::uint64_t
value)
374 if (IsBigFile(
value)) {
376 fInfoLong.fSeekFree =
value;
378 fInfoShort.fSeekFree =
value;
382 void SetNbytesFree(std::uint32_t
value)
385 fInfoLong.fNbytesFree =
value;
387 fInfoShort.fNbytesFree =
value;
391 void SetNbytesName(std::uint32_t
value)
394 fInfoLong.fNbytesName =
value;
396 fInfoShort.fNbytesName =
value;
400 std::uint64_t GetSeekInfo()
const
403 return fInfoLong.fSeekInfo;
404 return fInfoShort.fSeekInfo;
407 void SetSeekInfo(std::uint64_t
value)
409 if (IsBigFile(
value)) {
411 fInfoLong.fSeekInfo =
value;
413 fInfoShort.fSeekInfo =
value;
417 void SetNbytesInfo(std::uint32_t
value)
420 fInfoLong.fNbytesInfo =
value;
422 fInfoShort.fNbytesInfo =
value;
426 void SetCompression(std::uint32_t
value)
429 fInfoLong.fCompress =
value;
431 fInfoShort.fCompress =
value;
438 RUInt16BE fVersion{1};
450 RTFFreeEntry() : fInfoShort() {}
451 void Set(std::uint64_t first, std::uint64_t last)
453 if (last >
static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
454 fVersion = fVersion + 1000;
455 fInfoLong.fFirst = first;
456 fInfoLong.fLast = last;
458 fInfoShort.fFirst = first;
459 fInfoShort.fLast = last;
462 std::uint32_t GetSize() {
return (fVersion >= 1000) ? 18 : 10; }
468 std::uint32_t GetSize()
const {
return sizeof(RTFKeyList); }
469 explicit RTFKeyList(std::uint32_t nKeys) : fNKeys(nKeys) {}
474 RUInt16BE fClassVersion{5};
477 RUInt32BE fNBytesKeys{0};
478 RUInt32BE fNBytesName{0};
482 RUInt32BE fSeekDir{100};
483 RUInt32BE fSeekParent{0};
484 RUInt32BE fSeekKeys{0};
487 RUInt64BE fSeekDir{100};
488 RUInt64BE fSeekParent{0};
489 RUInt64BE fSeekKeys{0};
493 RTFFile() : fInfoShort() {}
496 std::uint32_t GetSize()
const
498 if (fClassVersion >= 1000)
499 return sizeof(RTFFile);
500 return 18 +
sizeof(fInfoShort);
503 std::uint64_t GetSeekKeys()
const
505 if (fClassVersion >= 1000)
506 return fInfoLong.fSeekKeys;
507 return fInfoShort.fSeekKeys;
510 void SetSeekKeys(std::uint64_t seekKeys)
512 if (seekKeys >
static_cast<unsigned int>(std::numeric_limits<std::int32_t>::max())) {
513 std::uint32_t seekDir = fInfoShort.fSeekDir;
514 std::uint32_t seekParent = fInfoShort.fSeekParent;
515 fInfoLong.fSeekDir = seekDir;
516 fInfoLong.fSeekParent = seekParent;
517 fInfoLong.fSeekKeys = seekKeys;
518 fClassVersion = fClassVersion + 1000;
520 fInfoShort.fSeekKeys = seekKeys;
527 RUInt16BE fVersionClass{1};
528 unsigned char fUUID[16] = {0};
531 std::uint32_t GetSize()
const {
return sizeof(RTFUUID); }
540 RUInt32BE fByteCount{0x40000000 | (
sizeof(RTFNTuple) -
sizeof(fByteCount))};
541 RUInt16BE fVersionClass{2};
542 RUInt16BE fVersionEpoch{0};
543 RUInt16BE fVersionMajor{0};
544 RUInt16BE fVersionMinor{0};
545 RUInt16BE fVersionPatch{0};
546 RUInt64BE fSeekHeader{0};
547 RUInt64BE fNBytesHeader{0};
548 RUInt64BE fLenHeader{0};
549 RUInt64BE fSeekFooter{0};
550 RUInt64BE fNBytesFooter{0};
551 RUInt64BE fLenFooter{0};
552 RUInt64BE fMaxKeySize{0};
554 static constexpr std::uint32_t GetSizePlusChecksum() {
return sizeof(RTFNTuple) +
sizeof(std::uint64_t); }
556 RTFNTuple() =
default;
571 std::uint32_t GetSize()
const {
return sizeof(RTFNTuple); }
573 std::uint32_t GetOffsetCkData() {
return sizeof(fByteCount) +
sizeof(fVersionClass); }
574 std::uint32_t GetSizeCkData() {
return GetSize() - GetOffsetCkData(); }
575 unsigned char *GetPtrCkData() {
return reinterpret_cast<unsigned char *
>(
this) + GetOffsetCkData(); }
579struct RBareFileHeader {
580 char fMagic[7]{
'r',
'n',
't',
'u',
'p',
'l',
'e'};
583 RUInt32BE fFormatVersion{1};
584 RUInt32BE fCompress{0};
591constexpr char const *kBlobClassName =
"RBlob";
593constexpr char const *kNTupleClassName =
"ROOT::RNTuple";
598namespace Experimental {
625 void Reserve(
size_t nbytes, std::uint64_t *seekKey)
643 constexpr size_t kChunkOffsetSize =
sizeof(std::uint64_t);
645 assert(nbytes > maxChunkSize);
646 size_t nChunks = (nbytes + maxChunkSize - 1) / maxChunkSize;
648 size_t nbytesTail = nbytes % maxChunkSize;
649 size_t nbytesExtra = (nbytesTail > 0) * (maxChunkSize - nbytesTail);
650 size_t nbytesChunkOffsets = (nChunks - 1) * kChunkOffsetSize;
651 if (nbytesChunkOffsets > nbytesExtra) {
653 nbytesChunkOffsets += kChunkOffsetSize;
660 R__ASSERT(nbytesChunkOffsets <= maxChunkSize);
672 if (std::string(ident, 4) ==
"root")
673 return GetNTupleProper(ntupleName);
675 return GetNTupleBare(ntupleName);
681 RTFHeader fileHeader;
682 ReadBuffer(&fileHeader,
sizeof(fileHeader), 0);
686 ReadBuffer(&key,
sizeof(key), fileHeader.fBEGIN);
688 std::uint64_t
offset = fileHeader.fBEGIN + key.fKeyLen;
698 offset = file.GetSeekKeys();
704 for (
unsigned int i = 0; i < nKeys; ++i) {
706 auto offsetNextKey =
offset + key.fKeyLen;
708 offset += key.GetHeaderSize();
711 if (std::string_view(
name.fData,
name.fLName) != kNTupleClassName) {
718 if (std::string_view(
name.fData,
name.fLName) == ntupleName) {
725 return R__FAIL(
"no RNTuple named '" + std::string(ntupleName) +
"' in file '" + fRawFile->GetUrl() +
"'");
728 offset = key.GetSeekKey() + key.fKeyLen;
731 constexpr size_t kMinNTupleSize = 78;
732 static_assert(kMinNTupleSize == RTFNTuple::GetSizePlusChecksum());
733 if (key.fObjLen < kMinNTupleSize) {
734 return R__FAIL(
"invalid anchor size: " + std::to_string(key.fObjLen) +
" < " + std::to_string(
sizeof(RTFNTuple)));
738 auto bufAnchor = std::make_unique<unsigned char[]>(std::max<size_t>(key.fObjLen,
sizeof(RTFNTuple)));
739 RTFNTuple *ntuple =
new (bufAnchor.get()) RTFNTuple;
741 auto objNbytes = key.GetSize() - key.fKeyLen;
743 if (objNbytes != key.fObjLen) {
745 decompressor.
Unzip(bufAnchor.get(), objNbytes, key.fObjLen);
750 auto lenCkData = key.fObjLen - ntuple->GetOffsetCkData() -
sizeof(uint64_t);
751 auto ckCalc = XXH3_64bits(ntuple->GetPtrCkData(), lenCkData);
754 RUInt64BE *ckOnDiskPtr =
reinterpret_cast<RUInt64BE *
>(bufAnchor.get() + key.fObjLen -
sizeof(uint64_t));
755 ckOnDisk =
static_cast<uint64_t
>(*ckOnDiskPtr);
756 if (ckCalc != ckOnDisk) {
757 return R__FAIL(
"RNTuple anchor checksum mismatch");
760 return CreateAnchor(ntuple->fVersionEpoch, ntuple->fVersionMajor, ntuple->fVersionMinor, ntuple->fVersionPatch,
761 ntuple->fSeekHeader, ntuple->fNBytesHeader, ntuple->fLenHeader, ntuple->fSeekFooter,
762 ntuple->fNBytesFooter, ntuple->fLenFooter, ntuple->fMaxKeySize);
768 RBareFileHeader fileHeader;
769 ReadBuffer(&fileHeader,
sizeof(fileHeader), 0);
771 auto offset =
sizeof(fileHeader);
774 std::string_view foundName(
name.fData,
name.fLName);
775 if (foundName != ntupleName) {
776 return R__FAIL(
"expected RNTuple named '" + std::string(ntupleName) +
"' but instead found '" +
777 std::string(foundName) +
"' in file '" + fRawFile->GetUrl() +
"'");
783 std::uint64_t onDiskChecksum;
784 ReadBuffer(&onDiskChecksum,
sizeof(onDiskChecksum),
offset +
sizeof(ntuple));
785 auto checksum = XXH3_64bits(ntuple.GetPtrCkData(), ntuple.GetSizeCkData());
786 if (checksum !=
static_cast<uint64_t
>(onDiskChecksum))
787 return R__FAIL(
"RNTuple bare file: anchor checksum mismatch");
789 return CreateAnchor(ntuple.fVersionEpoch, ntuple.fVersionMajor, ntuple.fVersionMinor, ntuple.fVersionPatch,
790 ntuple.fSeekHeader, ntuple.fNBytesHeader, ntuple.fLenHeader, ntuple.fSeekFooter,
791 ntuple.fNBytesFooter, ntuple.fLenFooter, ntuple.fMaxKeySize);
797 if (fMaxKeySize == 0 || nbytes <= fMaxKeySize) {
799 nread = fRawFile->ReadAt(buffer, nbytes,
offset);
803 const size_t nbytesChunkOffsets = (nChunks - 1) *
sizeof(std::uint64_t);
804 const size_t nbytesFirstChunk = fMaxKeySize - nbytesChunkOffsets;
805 uint8_t *bufCur =
reinterpret_cast<uint8_t *
>(buffer);
808 nread = fRawFile->ReadAt(bufCur, fMaxKeySize,
offset);
813 bufCur += nbytesFirstChunk;
814 nread -= nbytesChunkOffsets;
816 const auto chunkOffsets = std::make_unique<std::uint64_t[]>(nChunks - 1);
817 memcpy(chunkOffsets.get(), bufCur, nbytesChunkOffsets);
819 size_t remainingBytes = nbytes - nbytesFirstChunk;
820 std::uint64_t *curChunkOffset = &chunkOffsets[0];
823 std::uint64_t chunkOffset;
827 const size_t bytesToRead = std::min<size_t>(fMaxKeySize, remainingBytes);
829 R__ASSERT(
static_cast<size_t>(bufCur -
reinterpret_cast<uint8_t *
>(buffer)) <= nbytes - bytesToRead);
831 auto nbytesRead = fRawFile->ReadAt(bufCur, bytesToRead, chunkOffset);
834 nread += bytesToRead;
835 bufCur += bytesToRead;
836 remainingBytes -= bytesToRead;
837 }
while (remainingBytes > 0);
848 static_assert(kHeaderBlockSize % kBlockAlign == 0,
"invalid header block size");
849 if (bufferSize % kBlockAlign != 0)
850 throw RException(
R__FAIL(
"Buffer size not a multiple of alignment: " + std::to_string(bufferSize)));
851 fBlockSize = bufferSize;
853 std::align_val_t blockAlign{kBlockAlign};
854 fHeaderBlock =
static_cast<unsigned char *
>(::operator
new[](kHeaderBlockSize, blockAlign));
855 memset(fHeaderBlock, 0, kHeaderBlockSize);
856 fBlock =
static_cast<unsigned char *
>(::operator
new[](fBlockSize, blockAlign));
857 memset(fBlock, 0, fBlockSize);
865 std::align_val_t blockAlign{kBlockAlign};
867 ::operator
delete[](fHeaderBlock, blockAlign);
869 ::operator
delete[](fBlock, blockAlign);
873int FSeek64(FILE *stream, std::int64_t
offset,
int origin)
876 return fseeko64(stream,
offset, origin);
878 return fseek(stream,
offset, origin);
887 if (fBlockOffset == 0) {
888 std::size_t headerBlockSize = kHeaderBlockSize;
889 if (headerBlockSize > fFilePos) {
890 headerBlockSize = fFilePos;
892 memcpy(fBlock, fHeaderBlock, headerBlockSize);
895 std::size_t retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
899 std::size_t lastBlockSize = fFilePos - fBlockOffset;
903 lastBlockSize += kBlockAlign - 1;
904 lastBlockSize = (lastBlockSize / kBlockAlign) * kBlockAlign;
907 retval = fwrite(fBlock, 1, lastBlockSize, fFile);
908 if (retval != lastBlockSize)
912 if (fBlockOffset > 0) {
913 retval = FSeek64(fFile, 0, SEEK_SET);
917 retval = fwrite(fHeaderBlock, 1, kHeaderBlockSize, fFile);
918 if (retval != RFileSimple::kHeaderBlockSize)
922 retval = fflush(fFile);
932 if ((
offset >= 0) && (
static_cast<std::uint64_t
>(
offset) != fFilePos)) {
937 if (fFilePos < kHeaderBlockSize) {
938 std::size_t headerBytes = nbytes;
939 if (fFilePos + headerBytes > kHeaderBlockSize) {
940 headerBytes = kHeaderBlockSize - fFilePos;
942 memcpy(fHeaderBlock + fFilePos, buffer, headerBytes);
948 std::uint64_t posInBlock = fFilePos % fBlockSize;
949 std::uint64_t blockOffset = fFilePos - posInBlock;
950 if (blockOffset != fBlockOffset) {
952 retval = FSeek64(fFile, fBlockOffset, SEEK_SET);
956 retval = fwrite(fBlock, 1, fBlockSize, fFile);
957 if (retval != fBlockSize)
961 memset(fBlock, 0, fBlockSize);
964 fBlockOffset = blockOffset;
965 std::size_t blockSize = nbytes;
966 if (blockSize > fBlockSize - posInBlock) {
967 blockSize = fBlockSize - posInBlock;
969 memcpy(fBlock + posInBlock, buffer, blockSize);
970 buffer =
static_cast<const unsigned char *
>(buffer) + blockSize;
972 fFilePos += blockSize;
977 const void *buffer, std::size_t nbytes, std::size_t
len, std::int64_t
offset, std::uint64_t directoryOffset,
978 const std::string &className,
const std::string &objectName,
const std::string &title)
982 RTFString strClass{className};
983 RTFString strObject{objectName};
984 RTFString strTitle{title};
986 RTFKey key(fKeyOffset, directoryOffset, strClass, strObject, strTitle,
len, nbytes);
987 Write(&key, key.fKeyHeaderSize, fKeyOffset);
988 Write(&strClass, strClass.GetSize());
989 Write(&strObject, strObject.GetSize());
990 Write(&strTitle, strTitle.GetSize());
991 auto offsetData = fFilePos;
993 fKeyOffset = offsetData + nbytes;
995 Write(buffer, nbytes);
1007 bool rv = fFile->WriteBuffer((
char *)(buffer), nbytes);
1015 std::uint64_t offsetKey;
1019 keyBlob.
Reserve(nbytes, &offsetKey);
1022 RTFString strClass{kBlobClassName};
1023 RTFString strObject;
1025 RTFKey keyHeader(
offset,
offset, strClass, strObject, strTitle,
len, nbytes);
1027 keyHeader.MakeBigKey();
1029 Write(&keyHeader, keyHeader.fKeyHeaderSize,
offset);
1030 offset += keyHeader.fKeyHeaderSize;
1031 Write(&strClass, strClass.GetSize(),
offset);
1032 offset += strClass.GetSize();
1033 Write(&strObject, strObject.GetSize(),
offset);
1034 offset += strObject.GetSize();
1035 Write(&strTitle, strTitle.GetSize(),
offset);
1036 offset += strTitle.GetSize();
1037 auto offsetData =
offset;
1039 Write(buffer, nbytes,
offset);
1057std::unique_ptr<ROOT::Experimental::Internal::RNTupleFileWriter>
1062 std::string fileName(path);
1063 size_t idxDirSep = fileName.find_last_of(
"\\/");
1064 if (idxDirSep != std::string::npos) {
1065 fileName.erase(0, idxDirSep + 1);
1068 int flags = O_WRONLY | O_CREAT | O_TRUNC;
1071 flags |= O_LARGEFILE;
1076 int fd = open(std::string(path).c_str(), flags, 0666);
1077 FILE *fileStream = fdopen(fd,
"wb");
1080 FILE *fileStream = fopen64(std::string(path.data(), path.size()).c_str(),
"wb");
1082 FILE *fileStream = fopen(std::string(path.data(), path.size()).c_str(),
"wb");
1087 std::setvbuf(fileStream,
nullptr, _IONBF, 0);
1090 writer->fFileSimple.fFile = fileStream;
1093 writer->fFileName = fileName;
1096 switch (containerFormat) {
1097 case EContainerFormat::kTFile:
writer->WriteTFileSkeleton(defaultCompression);
break;
1098 case EContainerFormat::kBare:
1100 writer->WriteBareFileSkeleton(defaultCompression);
1102 default:
R__ASSERT(
false &&
"Internal error: unhandled container format");
1108std::unique_ptr<ROOT::Experimental::Internal::RNTupleFileWriter>
1110 std::uint64_t maxKeySize)
1113 writer->fFileProper.fFile = &file;
1120 fStreamerInfoMap.insert(streamerInfos.cbegin(), streamerInfos.cend());
1127 fFileProper.fFile->WriteObject(&fNTupleAnchor, fNTupleName.c_str());
1132 for (
auto [
_, info] : fStreamerInfoMap)
1135 fFileProper.fFile->Write();
1143 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1145 std::uint64_t checksum = XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData());
1146 memcpy(fFileSimple.fHeaderBlock + fFileSimple.fControlBlock->fSeekNTuple, &ntupleOnDisk, ntupleOnDisk.GetSize());
1147 memcpy(fFileSimple.fHeaderBlock + fFileSimple.fControlBlock->fSeekNTuple + ntupleOnDisk.GetSize(), &checksum,
1149 fFileSimple.Flush();
1153 WriteTFileNTupleKey();
1154 WriteTFileKeysList();
1155 WriteTFileStreamerInfo();
1156 WriteTFileFreeList();
1159 memcpy(fFileSimple.fHeaderBlock, &fFileSimple.fControlBlock->fHeader, fFileSimple.fControlBlock->fHeader.GetSize());
1160 R__ASSERT(fFileSimple.fControlBlock->fSeekFileRecord + fFileSimple.fControlBlock->fFileRecord.GetSize() <
1161 RFileSimple::kHeaderBlockSize);
1162 memcpy(fFileSimple.fHeaderBlock + fFileSimple.fControlBlock->fSeekFileRecord,
1163 &fFileSimple.fControlBlock->fFileRecord, fFileSimple.fControlBlock->fFileRecord.GetSize());
1165 fFileSimple.Flush();
1170 auto writeKey = [
this](
const void *payload,
size_t nBytes,
size_t length) {
1174 offset = fFileSimple.fKeyOffset;
1175 fFileSimple.Write(payload, nBytes);
1176 fFileSimple.fKeyOffset += nBytes;
1178 offset = fFileSimple.WriteKey(payload, nBytes,
length, -1, 100, kBlobClassName);
1181 offset = fFileProper.WriteKey(payload, nBytes,
length);
1186 const std::uint64_t maxKeySize = fNTupleAnchor.fMaxKeySize;
1190 if (
static_cast<std::uint64_t
>(
len) >
static_cast<std::uint64_t
>(std::numeric_limits<std::uint32_t>::max()))
1193 if (nbytes <= maxKeySize) {
1195 return writeKey(
data, nbytes,
len);
1212 const size_t nbytesChunkOffsets = (nChunks - 1) *
sizeof(std::uint64_t);
1213 const size_t nbytesFirstChunk = maxKeySize - nbytesChunkOffsets;
1216 const uint8_t *chunkData =
reinterpret_cast<const uint8_t *
>(
data) + nbytesFirstChunk;
1217 size_t remainingBytes = nbytes - nbytesFirstChunk;
1219 const auto chunkOffsetsToWrite = std::make_unique<std::uint64_t[]>(nChunks - 1);
1220 std::uint64_t chunkOffsetIdx = 0;
1223 const size_t bytesNextChunk = std::min<size_t>(remainingBytes, maxKeySize);
1224 const std::uint64_t
offset = writeKey(chunkData, bytesNextChunk, bytesNextChunk);
1229 remainingBytes -= bytesNextChunk;
1230 chunkData += bytesNextChunk;
1232 }
while (remainingBytes > 0);
1235 const std::uint64_t firstOffset = ReserveBlob(maxKeySize, maxKeySize);
1236 WriteIntoReservedBlob(
data, nbytesFirstChunk, firstOffset);
1237 const std::uint64_t chunkOffsetsOffset = firstOffset + nbytesFirstChunk;
1238 WriteIntoReservedBlob(chunkOffsetsToWrite.get(), nbytesChunkOffsets, chunkOffsetsOffset);
1246 R__ASSERT(nbytes <= fNTupleAnchor.GetMaxKeySize());
1251 offset = fFileSimple.fKeyOffset;
1252 fFileSimple.fKeyOffset += nbytes;
1254 offset = fFileSimple.WriteKey(
nullptr, nbytes,
len, -1, 100, kBlobClassName);
1257 offset = fFileProper.WriteKey(
nullptr, nbytes,
len);
1266 fFileSimple.Write(buffer, nbytes,
offset);
1268 fFileProper.Write(buffer, nbytes,
offset);
1275 auto offset = WriteBlob(
data, nbytes, lenHeader);
1276 fNTupleAnchor.fLenHeader = lenHeader;
1277 fNTupleAnchor.fNBytesHeader = nbytes;
1278 fNTupleAnchor.fSeekHeader =
offset;
1285 auto offset = WriteBlob(
data, nbytes, lenFooter);
1286 fNTupleAnchor.fLenFooter = lenFooter;
1287 fNTupleAnchor.fNBytesFooter = nbytes;
1288 fNTupleAnchor.fSeekFooter =
offset;
1294 RBareFileHeader bareHeader;
1295 bareHeader.fCompress = defaultCompression;
1296 fFileSimple.Write(&bareHeader,
sizeof(bareHeader), 0);
1297 RTFString ntupleName{fNTupleName};
1298 fFileSimple.Write(&ntupleName, ntupleName.GetSize());
1301 RTFNTuple ntupleOnDisk;
1302 fFileSimple.fControlBlock->fSeekNTuple = fFileSimple.fFilePos;
1303 fFileSimple.Write(&ntupleOnDisk, ntupleOnDisk.GetSize());
1304 std::uint64_t checksum = 0;
1305 fFileSimple.Write(&checksum,
sizeof(checksum));
1306 fFileSimple.fKeyOffset = fFileSimple.fFilePos;
1316 TList streamerInfoList;
1317 for (
auto [
_, info] : fStreamerInfoMap) {
1318 streamerInfoList.
Add(info);
1327 RTFString strTList{
"TList"};
1328 RTFString strStreamerInfo{
"StreamerInfo"};
1329 RTFString strStreamerTitle{
"Doubly linked list"};
1330 fFileSimple.fControlBlock->fHeader.SetSeekInfo(fFileSimple.fKeyOffset);
1332 RTFKey(fFileSimple.fControlBlock->fHeader.GetSeekInfo(), 100, strTList, strStreamerInfo, strStreamerTitle, 0)
1338 assert(buffer.
Length() > keyLen);
1339 const auto bufPayload = buffer.
Buffer() + keyLen;
1340 const auto lenPayload = buffer.
Length() - keyLen;
1343 auto zipStreamerInfos = std::make_unique<unsigned char[]>(lenPayload);
1344 auto szZipStreamerInfos = compressor.
Zip(bufPayload, lenPayload, 1, zipStreamerInfos.get());
1346 fFileSimple.WriteKey(zipStreamerInfos.get(), szZipStreamerInfos, lenPayload,
1347 fFileSimple.fControlBlock->fHeader.GetSeekInfo(), 100,
"TList",
"StreamerInfo",
1348 "Doubly linked list");
1349 fFileSimple.fControlBlock->fHeader.SetNbytesInfo(fFileSimple.fFilePos -
1350 fFileSimple.fControlBlock->fHeader.GetSeekInfo());
1356 RTFString strRNTupleClass{
"ROOT::RNTuple"};
1357 RTFString strRNTupleName{fNTupleName};
1358 RTFString strFileName{fFileName};
1360 RTFKey keyRNTuple(fFileSimple.fControlBlock->fSeekNTuple, 100, strRNTupleClass, strRNTupleName, strEmpty,
1361 RTFNTuple::GetSizePlusChecksum());
1363 fFileSimple.fControlBlock->fFileRecord.SetSeekKeys(fFileSimple.fKeyOffset);
1364 RTFKeyList keyList{1};
1365 RTFKey keyKeyList(fFileSimple.fControlBlock->fFileRecord.GetSeekKeys(), 100, strEmpty, strFileName, strEmpty,
1366 keyList.GetSize() + keyRNTuple.fKeyLen);
1367 fFileSimple.Write(&keyKeyList, keyKeyList.fKeyHeaderSize, fFileSimple.fControlBlock->fFileRecord.GetSeekKeys());
1368 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1369 fFileSimple.Write(&strFileName, strFileName.GetSize());
1370 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1371 fFileSimple.Write(&keyList, keyList.GetSize());
1372 fFileSimple.Write(&keyRNTuple, keyRNTuple.fKeyHeaderSize);
1374 fFileSimple.Write(&strRNTupleClass, strRNTupleClass.GetSize());
1375 fFileSimple.Write(&strRNTupleName, strRNTupleName.GetSize());
1376 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1377 fFileSimple.fControlBlock->fFileRecord.fNBytesKeys =
1378 fFileSimple.fFilePos - fFileSimple.fControlBlock->fFileRecord.GetSeekKeys();
1379 fFileSimple.fKeyOffset = fFileSimple.fFilePos;
1384 fFileSimple.fControlBlock->fHeader.SetSeekFree(fFileSimple.fKeyOffset);
1386 RTFString strFileName{fFileName};
1387 RTFFreeEntry freeEntry;
1388 RTFKey keyFreeList(fFileSimple.fControlBlock->fHeader.GetSeekFree(), 100, strEmpty, strFileName, strEmpty,
1389 freeEntry.GetSize());
1390 std::uint64_t firstFree = fFileSimple.fControlBlock->fHeader.GetSeekFree() + keyFreeList.GetSize();
1391 freeEntry.Set(firstFree, std::max(2000000000ULL, ((firstFree / 1000000000ULL) + 1) * 1000000000ULL));
1392 fFileSimple.WriteKey(&freeEntry, freeEntry.GetSize(), freeEntry.GetSize(),
1393 fFileSimple.fControlBlock->fHeader.GetSeekFree(), 100,
"", fFileName,
"");
1394 fFileSimple.fControlBlock->fHeader.SetNbytesFree(fFileSimple.fFilePos -
1395 fFileSimple.fControlBlock->fHeader.GetSeekFree());
1396 fFileSimple.fControlBlock->fHeader.SetEnd(fFileSimple.fFilePos);
1401 RTFString strRNTupleClass{
"ROOT::RNTuple"};
1402 RTFString strRNTupleName{fNTupleName};
1405 RTFNTuple ntupleOnDisk(fNTupleAnchor);
1406 RUInt64BE checksum{XXH3_64bits(ntupleOnDisk.GetPtrCkData(), ntupleOnDisk.GetSizeCkData())};
1407 fFileSimple.fControlBlock->fSeekNTuple = fFileSimple.fKeyOffset;
1409 char keyBuf[RTFNTuple::GetSizePlusChecksum()];
1412 memcpy(keyBuf, &ntupleOnDisk,
sizeof(RTFNTuple));
1413 memcpy(keyBuf +
sizeof(RTFNTuple), &checksum,
sizeof(checksum));
1415 fFileSimple.WriteKey(keyBuf,
sizeof(keyBuf),
sizeof(keyBuf), fFileSimple.fControlBlock->fSeekNTuple, 100,
1416 "ROOT::RNTuple", fNTupleName,
"");
1421 RTFString strTFile{
"TFile"};
1422 RTFString strFileName{fFileName};
1425 fFileSimple.fControlBlock->fHeader = RTFHeader(defaultCompression);
1430 RTFKey keyRoot(100, 0, strTFile, strFileName, strEmpty,
1431 sizeof(RTFFile) + strFileName.GetSize() + strEmpty.GetSize() + uuid.GetSize());
1432 std::uint32_t nbytesName = keyRoot.fKeyLen + strFileName.GetSize() + 1;
1433 fFileSimple.fControlBlock->fFileRecord.fNBytesName = nbytesName;
1434 fFileSimple.fControlBlock->fHeader.SetNbytesName(nbytesName);
1436 fFileSimple.Write(&keyRoot, keyRoot.fKeyHeaderSize, 100);
1438 fFileSimple.Write(&strTFile, strTFile.GetSize());
1439 fFileSimple.Write(&strFileName, strFileName.GetSize());
1440 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1442 fFileSimple.Write(&strFileName, strFileName.GetSize());
1443 fFileSimple.Write(&strEmpty, strEmpty.GetSize());
1445 fFileSimple.fControlBlock->fSeekFileRecord = fFileSimple.fFilePos;
1446 fFileSimple.Write(&fFileSimple.fControlBlock->fFileRecord, fFileSimple.fControlBlock->fFileRecord.GetSize());
1447 fFileSimple.Write(&uuid, uuid.GetSize());
1450 RUInt32BE padding{0};
1451 for (
int i = 0; i < 3; ++i)
1452 fFileSimple.Write(&padding,
sizeof(padding));
1453 fFileSimple.fKeyOffset = fFileSimple.fFilePos;
#define R__FAIL(msg)
Short-hand to return an RResult<T> in an error state; the RError is implicitly converted into RResult...
static size_t ComputeNumChunks(size_t nbytes, size_t maxChunkSize)
#define ROOT_VERSION_CODE
#define ClassDefInlineOverride(name, id)
#define R__ASSERT(e)
Checks condition e and reports a fatal error if it's false.
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
Binding & operator=(OUT(*fun)(void))
void ReadBuffer(char *&buffer) override
The RKeyBlob writes an invisible key into a TFile.
void Reserve(size_t nbytes, std::uint64_t *seekKey)
Register a new key for a data record of size nbytes.
RResult< RNTuple > GetNTupleProper(std::string_view ntupleName)
Used when the file turns out to be a TFile container.
RMiniFileReader()=default
RResult< RNTuple > GetNTupleBare(std::string_view ntupleName)
Used when the file container turns out to be a bare file.
RResult< RNTuple > GetNTuple(std::string_view ntupleName)
Extracts header and footer location for the RNTuple identified by ntupleName.
void ReadBuffer(void *buffer, size_t nbytes, std::uint64_t offset)
Reads a given byte range from the file into the provided memory buffer.
Helper class to compress data blocks in the ROOT compression frame format.
size_t Zip(const void *from, size_t nbytes, int compression, Writer_t fnWriter)
Returns the size of the compressed data.
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.
RNTupleFileWriter(std::string_view name, std::uint64_t maxKeySize)
std::uint64_t WriteBlob(const void *data, size_t nbytes, size_t len)
Writes a new record as an RBlob key into the file.
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...
static std::unique_ptr< RNTupleFileWriter > Append(std::string_view ntupleName, TFile &file, std::uint64_t maxKeySize)
Add a new RNTuple identified by ntupleName to the existing TFile.
void WriteTFileKeysList()
Write the TList with the RNTuple key.
void UpdateStreamerInfos(const RNTupleSerializer::StreamerInfoMap_t &streamerInfos)
Ensures that the streamer info records passed as argument are written to the file.
void Commit()
Writes the RNTuple key to the file so that the header and footer keys can be found.
std::uint64_t ReserveBlob(size_t nbytes, size_t len)
Reserves a new record as an RBlob key in the file.
RFileSimple fFileSimple
For simple use cases, survives without libRIO dependency.
void WriteTFileNTupleKey()
The only key that will be visible in file->ls()
void WriteTFileFreeList()
Last record in the file.
EContainerFormat
For testing purposes, RNTuple data can be written into a bare file container instead of a ROOT file.
static std::unique_ptr< RNTupleFileWriter > Recreate(std::string_view ntupleName, std::string_view path, EContainerFormat containerFormat, const RNTupleWriteOptions &options)
Create or truncate the local file given by path with the new empty RNTuple identified by ntupleName.
void WriteTFileSkeleton(int defaultCompression)
For a TFile container written by a C file stream, write the header and TFile object.
void WriteBareFileSkeleton(int defaultCompression)
For a bare file, which is necessarily written by a C file stream, write file header.
void WriteTFileStreamerInfo()
Write the compressed streamer info record with the description of the RNTuple class.
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...
RNTuple fNTupleAnchor
Header and footer location of the ntuple, written on Commit()
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...
RNTupleSerializer::StreamerInfoMap_t fStreamerInfoMap
Set of streamer info records that should be written to the file.
static std::uint32_t SerializeUInt64(std::uint64_t val, void *buffer)
std::map< Int_t, TVirtualStreamerInfo * > StreamerInfoMap_t
static std::uint32_t DeserializeUInt64(const void *buffer, std::uint64_t &val)
Base class for all ROOT issued exceptions.
Common user-tunable settings for storing ntuples.
std::size_t GetWriteBufferSize() const
int GetCompression() const
bool GetUseDirectIO() const
std::uint64_t GetMaxKeySize() const
The class is used as a return type for operations that can fail; wraps a value of type T or an RError...
The RRawFile provides read-only access to local and remote files.
Representation of an RNTuple data set in a ROOT file.
std::uint64_t GetLenFooter() const
std::uint64_t GetSeekHeader() const
std::uint16_t GetVersionMajor() const
std::uint64_t GetNBytesFooter() const
std::uint64_t GetNBytesHeader() const
std::uint64_t fMaxKeySize
The maximum size for a TKey payload. Payloads bigger than this size will be written as multiple blobs...
std::uint16_t GetVersionMinor() const
std::uint16_t GetVersionPatch() const
std::uint64_t GetMaxKeySize() const
std::uint64_t GetLenHeader() const
std::uint64_t GetSeekFooter() const
std::uint16_t GetVersionEpoch() const
The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket.
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.
void SetBufferOffset(Int_t offset=0)
TVirtualStreamerInfo * GetStreamerInfo(Int_t version=0, Bool_t isTransient=kFALSE) const
returns a pointer to the TVirtualStreamerInfo object for version If the object does not exist,...
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Int_t Sizeof() const override
Return the size in bytes of the key header structure.
Int_t fVersion
Key version identifier.
Short_t fKeylen
Number of bytes for the key itself.
Long64_t fSeekKey
Location of object on file.
virtual void Create(Int_t nbytes, TFile *f=nullptr)
Create a TKey object of specified size.
TString fClassName
Object Class name.
void Streamer(TBuffer &) override
Stream all objects in the collection to or from the I/O buffer.
void Add(TObject *obj) override
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)
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.
void Write(const void *buffer, size_t nbytes, std::int64_t offset)
Low-level writing using a TFile.
std::uint64_t WriteKey(const void *buffer, size_t nbytes, size_t len)
Writes an RBlob opaque key with the provided buffer as data record and returns the offset of the reco...
void AllocateBuffers(std::size_t bufferSize)
std::unique_ptr< ROOT::Experimental::Internal::RTFileControlBlock > fControlBlock
Keeps track of TFile control structures, which need to be updated on committing the data set.
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.
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 ...
If a TFile container is written by a C stream (simple file), on dataset commit, the file header and t...
std::uint64_t fSeekNTuple
std::uint64_t fSeekFileRecord