Skip to content

Commit 379cd76

Browse files
committed
[ntuple] add support for large objects in streamer field
1 parent 2eaccac commit 379cd76

3 files changed

Lines changed: 56 additions & 7 deletions

File tree

tree/ntuple/inc/ROOT/RField.hxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ public:
287287
size_t GetValueSize() const final;
288288
size_t GetAlignment() const final;
289289
// As of field version 1, the byte stream contains the byte count stack for large objects (see binary specs)
290-
std::uint32_t GetFieldVersion() const final { return 0; }
290+
std::uint32_t GetFieldVersion() const final { return 1; }
291291
std::uint32_t GetTypeVersion() const final;
292292
std::uint32_t GetTypeChecksum() const final;
293293
TClass *GetClass() const { return fClass; }

tree/ntuple/src/RFieldMeta.cxx

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <ROOT/RFieldBase.hxx>
2020
#include <ROOT/RFieldUtils.hxx>
2121
#include <ROOT/RFieldVisitor.hxx>
22+
#include <ROOT/RNTupleSerialize.hxx>
2223
#include <ROOT/RNTupleUtils.hxx>
2324
#include <ROOT/RSpan.hxx>
2425

@@ -1316,18 +1317,29 @@ std::size_t ROOT::RStreamerField::AppendImpl(const void *from)
13161317
[this](TVirtualStreamerInfo *info) { fStreamerInfos[info->GetNumber()] = info; });
13171318
fClass->Streamer(const_cast<void *>(from), buffer);
13181319

1319-
auto nbytes = buffer.Length();
1320+
const auto nbytes = buffer.Length();
1321+
std::size_t szBufCounts = 0;
13201322
R__ASSERT(nbytes >= 0);
13211323
if (static_cast<std::size_t>(nbytes) > kMaxSmallBuffer) {
1322-
throw RException(R__FAIL("large objects (>1GiB) not supported by the version 0 streamer field"));
1324+
const std::uint64_t nCounts = buffer.GetByteCounts().size();
1325+
szBufCounts = sizeof(std::uint64_t) * (2 * nCounts + 1);
1326+
auto bufCounts = Internal::MakeUninitArray<unsigned char>(szBufCounts);
1327+
std::size_t pos = Internal::RNTupleSerializer::SerializeUInt64(nCounts, bufCounts.get());
1328+
for (const auto &[bcountLoc, bcountVal] : buffer.GetByteCounts()) {
1329+
pos += Internal::RNTupleSerializer::SerializeUInt64(bcountLoc, bufCounts.get() + pos);
1330+
pos += Internal::RNTupleSerializer::SerializeUInt64(bcountVal, bufCounts.get() + pos);
1331+
}
1332+
assert(pos == szBufCounts);
1333+
fAuxiliaryColumn->AppendV(bufCounts.get(), szBufCounts);
1334+
fIndex += szBufCounts;
13231335
} else {
13241336
assert(buffer.GetByteCounts().empty());
13251337
}
13261338

13271339
fAuxiliaryColumn->AppendV(buffer.Buffer(), buffer.Length());
13281340
fIndex += nbytes;
13291341
fPrincipalColumn->Append(&fIndex);
1330-
return nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
1342+
return szBufCounts + nbytes + fPrincipalColumn->GetElement()->GetPackedSize();
13311343
}
13321344

13331345
void ROOT::RStreamerField::ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *to)
@@ -1336,10 +1348,47 @@ void ROOT::RStreamerField::ReadGlobalImpl(ROOT::NTupleSize_t globalIndex, void *
13361348
ROOT::NTupleSize_t nbytes;
13371349
fPrincipalColumn->GetCollectionInfo(globalIndex, &collectionStart, &nbytes);
13381350

1339-
if (nbytes > kMaxSmallBuffer)
1340-
throw RException(R__FAIL("large objects (>1GiB) not supported by the version 0 streamer field"));
1351+
TBufferFile::ByteCountFinder_t byteCounts;
1352+
if (nbytes > kMaxSmallBuffer) {
1353+
unsigned char bufNCounts[sizeof(std::uint64_t)];
1354+
fAuxiliaryColumn->ReadV(collectionStart, sizeof(std::uint64_t), bufNCounts);
1355+
nbytes -= sizeof(std::uint64_t);
1356+
collectionStart = collectionStart + sizeof(std::uint64_t);
1357+
1358+
std::uint64_t nCounts;
1359+
Internal::RNTupleSerializer::DeserializeUInt64(bufNCounts, nCounts);
1360+
if (nCounts > (std::numeric_limits<std::size_t>::max() / sizeof(std::uint64_t)) / 2 - 1)
1361+
throw RException(R__FAIL("invalid byte count size in streamer field: " + std::to_string(nCounts)));
1362+
const std::size_t szBufCounts = sizeof(std::uint64_t) * (2 * nCounts);
1363+
if (szBufCounts > nbytes) {
1364+
throw RException(R__FAIL("invalid byte count size in streamer field: nCounts=" + std::to_string(nCounts) +
1365+
" szBufCounts=" + std::to_string(szBufCounts) + " nbytes=" + std::to_string(nbytes)));
1366+
}
1367+
1368+
auto bufCounts = Internal::MakeUninitArray<unsigned char>(szBufCounts);
1369+
fAuxiliaryColumn->ReadV(collectionStart, szBufCounts, bufCounts.get());
1370+
nbytes -= szBufCounts;
1371+
collectionStart = collectionStart + szBufCounts;
1372+
1373+
byteCounts.reserve(nCounts);
1374+
std::size_t pos = 0;
1375+
for (std::uint64_t i = 0; i < nCounts; ++i) {
1376+
std::uint64_t bcountLoc, bcountVal;
1377+
pos += Internal::RNTupleSerializer::DeserializeUInt64(bufCounts.get() + pos, bcountLoc);
1378+
pos += Internal::RNTupleSerializer::DeserializeUInt64(bufCounts.get() + pos, bcountVal);
1379+
if ((bcountLoc > nbytes) || (bcountVal > nbytes) || (nbytes - bcountVal < bcountLoc)) {
1380+
throw RException(
1381+
R__FAIL("invalid byte count record: " + std::to_string(bcountLoc) + ", " + std::to_string(bcountVal)));
1382+
}
1383+
byteCounts.emplace(bcountLoc, bcountVal);
1384+
}
1385+
assert(pos == szBufCounts);
1386+
if (byteCounts.size() != nCounts)
1387+
throw RException(R__FAIL("duplicate byte counts"));
1388+
}
13411389

13421390
TBufferFile buffer(TBuffer::kRead, nbytes);
1391+
buffer.SetByteCounts(std::move(byteCounts));
13431392
fAuxiliaryColumn->ReadV(collectionStart, nbytes, buffer.Buffer());
13441393
fClass->Streamer(to, buffer);
13451394
}

tree/ntuple/test/rfield_streamer.cxx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -462,7 +462,7 @@ TEST(RField, StreamerFieldVersion)
462462
if (version < 2) {
463463
const auto &f = reader->GetModel().GetConstField("f");
464464
EXPECT_TRUE(dynamic_cast<const ROOT::RStreamerField *>(&f));
465-
EXPECT_EQ(0u, f.GetFieldVersion());
465+
EXPECT_EQ(1u, f.GetFieldVersion());
466466
EXPECT_EQ(version, f.GetOnDiskFieldVersion());
467467
EXPECT_EQ(2u, f.GetTypeVersion());
468468
EXPECT_EQ(137u, f.GetOnDiskTypeVersion());

0 commit comments

Comments
 (0)