diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index d169d2348b347..eccfdc1843cec 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -323,8 +323,13 @@ void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) && (fBufCur >= fBuffer) && static_cast(fBufCur - fBuffer) <= std::numeric_limits::max() && "Byte count position is after the end of the buffer"); - const UInt_t cnt = UInt_t(fBufCur - fBuffer) - UInt_t(cntpos) - sizeof(UInt_t); + char *buf = (char *)(fBuffer + cntpos); + if ((fBufCur - fBuffer - cntpos - sizeof(UInt_t)) >= kMaxMapCount) { + tobuf(buf, 0 | kByteCountMask); + return; + } + const UInt_t cnt = UInt_t(fBufCur - fBuffer) - UInt_t(cntpos) - sizeof(UInt_t); // if true, pack byte count in two consecutive shorts, so it can // be read by ReadVersion() diff --git a/tree/ntuple/test/CMakeLists.txt b/tree/ntuple/test/CMakeLists.txt index 1df3d41dc0ef5..1c0d2cb54cf03 100644 --- a/tree/ntuple/test/CMakeLists.txt +++ b/tree/ntuple/test/CMakeLists.txt @@ -105,6 +105,11 @@ ROOT_GENERATE_DICTIONARY(StreamerFieldDict ${CMAKE_CURRENT_SOURCE_DIR}/StreamerF MODULE rfield_streamer LINKDEF StreamerFieldLinkDef.h OPTIONS -inlineInputHeader DEPENDENCIES RIO) +ROOT_ADD_GTEST(rfield_streamer_beyond rfield_streamer_beyond.cxx StreamerBeyond.cxx LIBRARIES ROOTNTuple) +ROOT_GENERATE_DICTIONARY(StreamerBeyondDict ${CMAKE_CURRENT_SOURCE_DIR}/StreamerBeyond.hxx + MODULE rfield_streamer_beyond LINKDEF StreamerBeyondLinkDef.h OPTIONS -inlineInputHeader + DEPENDENCIES RIO) + if(MSVC) set(command ${CMAKE_COMMAND} -E env "ROOTIGNOREPREFIX=1" $) else() diff --git a/tree/ntuple/test/StreamerBeyond.cxx b/tree/ntuple/test/StreamerBeyond.cxx new file mode 100644 index 0000000000000..dbe871afe35c7 --- /dev/null +++ b/tree/ntuple/test/StreamerBeyond.cxx @@ -0,0 +1 @@ +#include "StreamerBeyond.hxx" diff --git a/tree/ntuple/test/StreamerBeyond.hxx b/tree/ntuple/test/StreamerBeyond.hxx new file mode 100644 index 0000000000000..eca5facb34bd6 --- /dev/null +++ b/tree/ntuple/test/StreamerBeyond.hxx @@ -0,0 +1,14 @@ +#ifndef ROOT_RNTuple_Test_StreamerBeyond +#define ROOT_RNTuple_Test_StreamerBeyond + +#include + +#include +#include + +struct StreamerBeyond { + std::vector fOne; + std::vector fTwo; +}; + +#endif diff --git a/tree/ntuple/test/StreamerBeyondLinkDef.h b/tree/ntuple/test/StreamerBeyondLinkDef.h new file mode 100644 index 0000000000000..605c9f1a3bc66 --- /dev/null +++ b/tree/ntuple/test/StreamerBeyondLinkDef.h @@ -0,0 +1,5 @@ +#ifdef __CLING__ + +#pragma link C++ options=rntupleStreamerMode(true) struct StreamerBeyond+; + +#endif diff --git a/tree/ntuple/test/rfield_streamer_beyond.cxx b/tree/ntuple/test/rfield_streamer_beyond.cxx new file mode 100644 index 0000000000000..a655aab12a7aa --- /dev/null +++ b/tree/ntuple/test/rfield_streamer_beyond.cxx @@ -0,0 +1,70 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "StreamerBeyond.hxx" +#include "gtest/gtest.h" + +namespace { + +class FileRaii { +private: + std::string fPath; + bool fPreserveFile = false; + +public: + explicit FileRaii(const std::string &path) : fPath(path) {} + FileRaii(FileRaii &&) = default; + FileRaii(const FileRaii &) = delete; + FileRaii &operator=(FileRaii &&) = default; + FileRaii &operator=(const FileRaii &) = delete; + ~FileRaii() + { + if (!fPreserveFile) + std::remove(fPath.c_str()); + } + std::string GetPath() const { return fPath; } + + // Useful if you want to keep a test file after the test has finished running + // for debugging purposes. Should only be used locally and never pushed. + void PreserveFile() { fPreserveFile = true; } +}; + +} // anonymous namespace + +TEST(RField, StreamerBeyond) +{ + FileRaii fileGuard("test_ntuple_rfield_streamer_beyond.root"); + + { + auto model = ROOT::RNTupleModel::Create(); + auto f = ROOT::RFieldBase::Create("f", "StreamerBeyond").Unwrap(); + EXPECT_TRUE(dynamic_cast(f.get())); + model->AddField(std::move(f)); + auto writer = ROOT::RNTupleWriter::Recreate(std::move(model), "ntpl", fileGuard.GetPath()); + + auto ptr = writer->GetModel().GetDefaultEntry().GetPtr("f"); + ptr->fOne = std::vector(100000000, -1); + ptr->fTwo = std::vector(100000000, -2); + + writer->Fill(); + } + + auto reader = ROOT::RNTupleReader::Open("ntpl", fileGuard.GetPath()); + ASSERT_EQ(1u, reader->GetNEntries()); + StreamerBeyond sb; + auto view = reader->GetView("f", &sb, "StreamerBeyond"); + + view(0); + + auto ptr = view.GetValue().GetPtr(); + EXPECT_EQ(100000000u, ptr->fOne.size()); + EXPECT_EQ(-1, ptr->fOne.at(1000)); + EXPECT_EQ(100000000u, ptr->fTwo.size()); + EXPECT_EQ(-2, ptr->fTwo.at(2000)); +}