From 98eecbcd90d386e4f0a90b861c98b7c62ef54a17 Mon Sep 17 00:00:00 2001 From: ferdymercury Date: Mon, 11 Aug 2025 14:41:45 +0200 Subject: [PATCH 01/36] [io] Properly abort when buffer size overflows max integer Fixes https://github.com/root-project/root/issues/14770 [io] add more checks in TBuffer functions as suggested by jblomer --- core/base/inc/TBuffer.h | 38 ++++++------ core/base/inc/TDirectory.h | 18 +++--- core/base/inc/TObject.h | 4 +- core/base/src/TBuffer.cxx | 30 +++++----- core/base/src/TDirectory.cxx | 9 +-- core/base/src/TObject.cxx | 4 +- core/cont/inc/TCollection.h | 4 +- core/cont/inc/TMap.h | 4 +- core/cont/src/TCollection.cxx | 4 +- core/cont/src/TMap.cxx | 4 +- hist/hbook/inc/THbookBranch.h | 4 +- hist/hbook/src/THbookBranch.cxx | 4 +- hist/hbook/src/THbookFile.cxx | 2 +- hist/hist/inc/TH1.h | 4 +- hist/hist/inc/TProfile.h | 2 +- hist/hist/inc/TProfile2D.h | 2 +- hist/hist/inc/TProfile3D.h | 2 +- hist/hist/src/TH1.cxx | 7 ++- hist/hist/src/TProfile.cxx | 4 +- hist/hist/src/TProfile2D.cxx | 4 +- hist/hist/src/TProfile3D.cxx | 4 +- hist/hist/src/TScatter.cxx | 3 + hist/histpainter/src/TPainter3dAlgorithms.cxx | 4 +- io/io/inc/ROOT/TBufferMerger.hxx | 2 +- io/io/inc/TBufferFile.h | 22 +++---- io/io/inc/TBufferIO.h | 16 ++--- io/io/inc/TBufferText.h | 12 ++-- io/io/inc/TDirectoryFile.h | 12 ++-- io/io/inc/TFile.h | 18 +++--- io/io/inc/TFileCacheRead.h | 4 +- io/io/inc/TFileCacheWrite.h | 2 +- io/io/inc/TKey.h | 12 ++-- io/io/inc/TMapFile.h | 2 +- io/io/src/TBufferFile.cxx | 40 ++++++------- io/io/src/TBufferIO.cxx | 16 +++-- io/io/src/TBufferMergerFile.cxx | 2 +- io/io/src/TDirectoryFile.cxx | 27 ++++++--- io/io/src/TFile.cxx | 52 ++++++++++++----- io/io/src/TFileCacheRead.cxx | 7 ++- io/io/src/TFileCacheWrite.cxx | 9 ++- io/io/src/TKey.cxx | 33 ++++++++--- io/io/src/TMapFile.cxx | 2 +- io/sql/inc/TSQLFile.h | 8 +-- io/sql/src/TSQLFile.cxx | 4 +- io/xml/inc/TXMLFile.h | 8 +-- io/xml/src/TXMLFile.cxx | 4 +- main/src/h2root.cxx | 4 +- net/net/inc/TMessage.h | 4 +- net/net/inc/TParallelMergingFile.h | 4 +- net/net/src/TMessage.cxx | 4 +- net/net/src/TParallelMergingFile.cxx | 4 +- roottest/root/meta/MemberComments.ref | 4 +- roottest/root/meta/MemberComments_win32.ref | 4 +- roottest/root/meta/MemberComments_win64.ref | 4 +- tree/dataframe/src/RDFSnapshotHelpers.cxx | 2 +- tree/ntuple/src/RFieldMeta.cxx | 2 +- tree/tree/inc/TBranch.h | 6 +- tree/tree/inc/TBranchClones.h | 8 +-- tree/tree/inc/TBranchElement.h | 24 ++++---- tree/tree/inc/TBranchObject.h | 8 +-- tree/tree/inc/TBranchSTL.h | 4 +- tree/tree/inc/TBufferSQL.h | 4 +- tree/tree/inc/TChain.h | 2 +- tree/tree/inc/TNtuple.h | 2 +- tree/tree/inc/TNtupleD.h | 2 +- tree/tree/inc/TTree.h | 58 +++++++++---------- tree/tree/inc/TTreeCache.h | 4 +- tree/tree/inc/TTreeCacheUnzip.h | 2 +- tree/tree/inc/TTreeSQL.h | 18 +++--- tree/tree/src/TBranch.cxx | 12 +++- tree/tree/src/TBranchClones.cxx | 10 ++-- tree/tree/src/TBranchElement.cxx | 30 +++++----- tree/tree/src/TBranchObject.cxx | 10 ++-- tree/tree/src/TBranchSTL.cxx | 8 ++- tree/tree/src/TBufferSQL.cxx | 4 +- tree/tree/src/TChain.cxx | 2 +- tree/tree/src/TNtuple.cxx | 2 +- tree/tree/src/TNtupleD.cxx | 2 +- tree/tree/src/TTree.cxx | 36 ++++++------ tree/tree/src/TTreeCache.cxx | 8 +-- tree/tree/src/TTreeCacheUnzip.cxx | 6 +- tree/tree/src/TTreeSQL.cxx | 18 +++--- 82 files changed, 452 insertions(+), 353 deletions(-) diff --git a/core/base/inc/TBuffer.h b/core/base/inc/TBuffer.h index 7e90a50178ef9..d6cae935560ed 100644 --- a/core/base/inc/TBuffer.h +++ b/core/base/inc/TBuffer.h @@ -64,9 +64,9 @@ class TBuffer : public TObject { void operator=(const TBuffer &) = delete; Int_t Read(const char *name) override { return TObject::Read(name); } - Int_t Write(const char *name, Int_t opt, Int_t bufsize) override + Int_t Write(const char *name, Int_t opt, Long64_t bufsize) override { return TObject::Write(name, opt, bufsize); } - Int_t Write(const char *name, Int_t opt, Int_t bufsize) const override + Int_t Write(const char *name, Int_t opt, Long64_t bufsize) const override { return TObject::Write(name, opt, bufsize); } public: @@ -78,8 +78,8 @@ class TBuffer : public TObject { enum { kInitialSize = 1024, kMinimalSize = 128 }; TBuffer(EMode mode); - TBuffer(EMode mode, Int_t bufsize); - TBuffer(EMode mode, Int_t bufsize, void *buf, Bool_t adopt = kTRUE, ReAllocCharFun_t reallocfunc = nullptr); + TBuffer(EMode mode, Long64_t bufsize); + TBuffer(EMode mode, Long64_t bufsize, void *buf, Bool_t adopt = kTRUE, ReAllocCharFun_t reallocfunc = nullptr); virtual ~TBuffer(); Int_t GetBufferVersion() const { return fVersion; } @@ -87,10 +87,10 @@ class TBuffer : public TObject { Bool_t IsWriting() const { return (fMode & kWrite) != 0; } void SetReadMode(); void SetWriteMode(); - void SetBuffer(void *buf, UInt_t bufsize = 0, Bool_t adopt = kTRUE, ReAllocCharFun_t reallocfunc = nullptr); + void SetBuffer(void *buf, Long64_t bufsize = 0, Bool_t adopt = kTRUE, ReAllocCharFun_t reallocfunc = nullptr); ReAllocCharFun_t GetReAllocFunc() const; void SetReAllocFunc(ReAllocCharFun_t reallocfunc = nullptr); - void SetBufferOffset(Int_t offset = 0) { fBufCur = fBuffer+offset; } + void SetBufferOffset(Long64_t offset = 0) { fBufCur = fBuffer+offset; } void SetParent(TObject *parent); TObject *GetParent() const; char *Buffer() const { return fBuffer; } @@ -98,33 +98,33 @@ class TBuffer : public TObject { Int_t BufferSize() const { return fBufSize; } void DetachBuffer() { fBuffer = nullptr; } Int_t Length() const { return (Int_t)(fBufCur - fBuffer); } - void Expand(Int_t newsize, Bool_t copy = kTRUE); // expand buffer to newsize - void AutoExpand(Int_t size_needed); // expand buffer to newsize + void Expand(Long64_t newsize, Bool_t copy = kTRUE); // expand buffer to newsize + void AutoExpand(Long64_t size_needed); // expand buffer to newsize Bool_t ByteSwapBuffer(Long64_t n, EDataType type); // Byte-swap N primitive-elements in the buffer virtual Bool_t CheckObject(const TObject *obj) = 0; virtual Bool_t CheckObject(const void *obj, const TClass *ptrClass) = 0; - virtual Int_t ReadBuf(void *buf, Int_t max) = 0; - virtual void WriteBuf(const void *buf, Int_t max) = 0; + virtual Long64_t ReadBuf(void *buf, Long64_t max) = 0; + virtual void WriteBuf(const void *buf, Long64_t max) = 0; - virtual char *ReadString(char *s, Int_t max) = 0; + virtual char *ReadString(char *s, Long64_t max) = 0; virtual void WriteString(const char *s) = 0; virtual Int_t GetVersionOwner() const = 0; virtual Int_t GetMapCount() const = 0; virtual void GetMappedObject(UInt_t tag, void* &ptr, TClass* &ClassPtr) const = 0; - virtual void MapObject(const TObject *obj, UInt_t offset = 1) = 0; - virtual void MapObject(const void *obj, const TClass *cl, UInt_t offset = 1) = 0; + virtual void MapObject(const TObject *obj, ULong64_t offset = 1) = 0; + virtual void MapObject(const void *obj, const TClass *cl, ULong64_t offset = 1) = 0; virtual void Reset() = 0; virtual void InitMap() = 0; virtual void ResetMap() = 0; virtual void SetReadParam(Int_t mapsize) = 0; virtual void SetWriteParam(Int_t mapsize) = 0; - virtual Int_t CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *clss) = 0; - virtual Int_t CheckByteCount(UInt_t startpos, UInt_t bcnt, const char *classname) = 0; - virtual void SetByteCount(UInt_t cntpos, Bool_t packInVersion = kFALSE)= 0; + virtual Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss) = 0; + virtual Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const char *classname) = 0; + virtual void SetByteCount(ULong64_t cntpos, Bool_t packInVersion = kFALSE)= 0; virtual void SkipVersion(const TClass *cl = nullptr) = 0; virtual Version_t ReadVersion(UInt_t *start = nullptr, UInt_t *bcnt = nullptr, const TClass *cl = nullptr) = 0; @@ -164,7 +164,7 @@ class TBuffer : public TObject { virtual void SetPidOffset(UShort_t offset) = 0; virtual Int_t GetBufferDisplacement() const = 0; virtual void SetBufferDisplacement() = 0; - virtual void SetBufferDisplacement(Int_t skipped) = 0; + virtual void SetBufferDisplacement(Long64_t skipped) = 0; // basic types and arrays of basic types virtual void ReadFloat16 (Float_t *f, TStreamerElement *ele = nullptr) = 0; @@ -320,8 +320,8 @@ class TBuffer : public TObject { // Utilities for TStreamerInfo virtual void ForceWriteInfo(TVirtualStreamerInfo *info, Bool_t force) = 0; virtual void ForceWriteInfoClones(TClonesArray *a) = 0; - virtual Int_t ReadClones (TClonesArray *a, Int_t nobjects, Version_t objvers) = 0; - virtual Int_t WriteClones(TClonesArray *a, Int_t nobjects) = 0; + virtual Int_t ReadClones (TClonesArray *a, Long64_t nobjects, Version_t objvers) = 0; + virtual Int_t WriteClones(TClonesArray *a, Long64_t nobjects) = 0; // Utilities for TClass virtual Int_t ReadClassEmulated(const TClass *cl, void *object, const TClass *onfile_class = nullptr) = 0; diff --git a/core/base/inc/TDirectory.h b/core/base/inc/TDirectory.h index a95e91d236111..167ecc86ccdcf 100644 --- a/core/base/inc/TDirectory.h +++ b/core/base/inc/TDirectory.h @@ -254,7 +254,7 @@ can be replaced with the simpler and exception safe: virtual void Save() {} virtual Int_t SaveObjectAs(const TObject * /*obj*/, const char * /*filename*/="", Option_t * /*option*/="") const; virtual void SaveSelf(Bool_t /*force*/ = kFALSE) {} - virtual void SetBufferSize(Int_t /* bufsize */) {} + virtual void SetBufferSize(Long64_t /* bufsize */) {} virtual void SetModified() {} virtual void SetMother(TObject *mother) {fMother = (TObject*)mother;} void SetName(const char* newname) override; @@ -262,12 +262,12 @@ can be replaced with the simpler and exception safe: virtual void SetSeekDir(Long64_t) {} virtual void SetWritable(Bool_t) {} Int_t Sizeof() const override {return 0;} - virtual Int_t Write(const char * /*name*/=nullptr, Int_t /*opt*/=0, Int_t /*bufsize*/=0) override {return 0;} - virtual Int_t Write(const char * /*name*/=nullptr, Int_t /*opt*/=0, Int_t /*bufsize*/=0) const override {return 0;} - virtual Int_t WriteTObject(const TObject *obj, const char *name =nullptr, Option_t * /*option*/="", Int_t /*bufsize*/ =0); + virtual Int_t Write(const char * /*name*/=nullptr, Int_t /*opt*/=0, Long64_t /*bufsize*/=0) override {return 0;} + virtual Int_t Write(const char * /*name*/=nullptr, Int_t /*opt*/=0, Long64_t /*bufsize*/=0) const override {return 0;} + virtual Int_t WriteTObject(const TObject *obj, const char *name =nullptr, Option_t * /*option*/="", Long64_t /*bufsize*/ =0); private: /// \cond HIDDEN_SYMBOLS - Int_t WriteObject(void *obj, const char* name, Option_t *option="", Int_t bufsize=0); // Intentionally not implemented. + Int_t WriteObject(void *obj, const char* name, Option_t *option="", Long64_t bufsize=0); // Intentionally not implemented. /// \endcond public: /// \brief Write an object with proper type checking. @@ -280,7 +280,7 @@ can be replaced with the simpler and exception safe: /// from TObject. The method redirects to TDirectory::WriteObjectAny. template inline std::enable_if_t::value, Int_t> - WriteObject(const T *obj, const char *name, Option_t *option = "", Int_t bufsize = 0) + WriteObject(const T *obj, const char *name, Option_t *option = "", Long64_t bufsize = 0) { return WriteObjectAny(obj, TClass::GetClass(), name, option, bufsize); } @@ -294,12 +294,12 @@ can be replaced with the simpler and exception safe: /// TObject. The method redirects to TDirectory::WriteTObject. template inline std::enable_if_t::value, Int_t> - WriteObject(const T *obj, const char *name, Option_t *option = "", Int_t bufsize = 0) + WriteObject(const T *obj, const char *name, Option_t *option = "", Long64_t bufsize = 0) { return WriteTObject(obj, name, option, bufsize); } - virtual Int_t WriteObjectAny(const void *, const char * /*classname*/, const char * /*name*/, Option_t * /*option*/="", Int_t /*bufsize*/ =0) {return 0;} - virtual Int_t WriteObjectAny(const void *, const TClass * /*cl*/, const char * /*name*/, Option_t * /*option*/="", Int_t /*bufsize*/ =0) {return 0;} + virtual Int_t WriteObjectAny(const void *, const char * /*classname*/, const char * /*name*/, Option_t * /*option*/="", Long64_t /*bufsize*/ =0) {return 0;} + virtual Int_t WriteObjectAny(const void *, const TClass * /*cl*/, const char * /*name*/, Option_t * /*option*/="", Long64_t /*bufsize*/ =0) {return 0;} virtual void WriteDirHeader() {} virtual void WriteKeys() {} diff --git a/core/base/inc/TObject.h b/core/base/inc/TObject.h index 456c21ad10fc3..d3529f18c6919 100644 --- a/core/base/inc/TObject.h +++ b/core/base/inc/TObject.h @@ -172,8 +172,8 @@ class TObject { virtual void SetDrawOption(Option_t *option=""); // *MENU* virtual void SetUniqueID(UInt_t uid); virtual void UseCurrentStyle(); - virtual Int_t Write(const char *name = nullptr, Int_t option = 0, Int_t bufsize = 0); - virtual Int_t Write(const char *name = nullptr, Int_t option = 0, Int_t bufsize = 0) const; + virtual Int_t Write(const char *name = nullptr, Int_t option = 0, Long64_t bufsize = 0); + virtual Int_t Write(const char *name = nullptr, Int_t option = 0, Long64_t bufsize = 0) const; /// IsDestructed /// diff --git a/core/base/src/TBuffer.cxx b/core/base/src/TBuffer.cxx index 8a0c2b78e99e6..0a27db1c284c4 100644 --- a/core/base/src/TBuffer.cxx +++ b/core/base/src/TBuffer.cxx @@ -69,10 +69,10 @@ TBuffer::TBuffer(EMode mode) /// Create an I/O buffer object. Mode should be either TBuffer::kRead or /// TBuffer::kWrite. -TBuffer::TBuffer(EMode mode, Int_t bufsize) +TBuffer::TBuffer(EMode mode, Long64_t bufsize) { - if (bufsize < 0) - Fatal("TBuffer","Request to create a buffer with a negative size, likely due to an integer overflow: 0x%x for a max of 0x%x.", bufsize, kMaxBufferSize); + if (bufsize > kMaxBufferSize) + Fatal("TBuffer","Request to create a too large buffer: 0x%llx for a max of 0x%x.", bufsize, kMaxBufferSize); if (bufsize < kMinimalSize) bufsize = kMinimalSize; fBufSize = bufsize; fMode = mode; @@ -100,10 +100,10 @@ TBuffer::TBuffer(EMode mode, Int_t bufsize) /// is provided, a Fatal error will be issued if the Buffer attempts to /// expand. -TBuffer::TBuffer(EMode mode, Int_t bufsize, void *buf, Bool_t adopt, ReAllocCharFun_t reallocfunc) +TBuffer::TBuffer(EMode mode, Long64_t bufsize, void *buf, Bool_t adopt, ReAllocCharFun_t reallocfunc) { - if (bufsize < 0) - Fatal("TBuffer","Request to create a buffer with a negative size, likely due to an integer overflow: 0x%x for a max of 0x%x.", bufsize, kMaxBufferSize); + if (bufsize > kMaxBufferSize) + Fatal("TBuffer","Request to create a too large buffer: 0x%llx for a max of 0x%x.", bufsize, kMaxBufferSize); fBufSize = bufsize; fMode = mode; fVersion = 0; @@ -154,10 +154,10 @@ TBuffer::~TBuffer() /// If the size_needed is larger than the current size, the policy /// is to expand to double the current size or the size_needed which ever is largest. -void TBuffer::AutoExpand(Int_t size_needed) +void TBuffer::AutoExpand(Long64_t size_needed) { - if (size_needed < 0) { - Fatal("AutoExpand","Request to expand to a negative size, likely due to an integer overflow: 0x%x for a max of 0x%x.", size_needed, kMaxBufferSize); + if (size_needed > kMaxBufferSize) { + Fatal("AutoExpand","Request to expand a too large buffer: 0x%llx for a max of 0x%x.", size_needed, kMaxBufferSize); } if (size_needed > fBufSize) { Long64_t doubling = 2LLU * fBufSize; @@ -183,8 +183,10 @@ void TBuffer::AutoExpand(Int_t size_needed) /// is provided, a Fatal error will be issued if the Buffer attempts to /// expand. -void TBuffer::SetBuffer(void *buf, UInt_t newsiz, Bool_t adopt, ReAllocCharFun_t reallocfunc) +void TBuffer::SetBuffer(void *buf, Long64_t newsiz, Bool_t adopt, ReAllocCharFun_t reallocfunc) { + if (newsiz > kMaxBufferSize) + Fatal("SetBuffer","Request to create a too large buffer: 0x%llx for a max of 0x%x.", newsiz, kMaxBufferSize); if (fBuffer && TestBit(kIsOwner)) delete [] fBuffer; @@ -219,19 +221,19 @@ void TBuffer::SetBuffer(void *buf, UInt_t newsiz, Bool_t adopt, ReAllocCharFun_t /// In order to avoid losing data, if the current length is greater than /// the requested size, we only shrink down to the current length. -void TBuffer::Expand(Int_t newsize, Bool_t copy) +void TBuffer::Expand(Long64_t newsize, Bool_t copy) { Int_t l = Length(); - if ( (l > newsize) && copy ) { + if ( (Long64_t(l) > newsize) && copy ) { newsize = l; } const Int_t extraspace = (fMode&kWrite)!=0 ? kExtraSpace : 0; - if ( ((Long64_t)newsize+extraspace) > kMaxBufferSize) { + if ( newsize > kMaxBufferSize - kExtraSpace) { if (l < kMaxBufferSize) { newsize = kMaxBufferSize - extraspace; } else { - Fatal("Expand","Requested size (%d) is too large (max is %d).", newsize, kMaxBufferSize); + Fatal("Expand","Requested size (%lld) is too large (max is %d).", newsize, kMaxBufferSize); } } if ( (fMode&kWrite)!=0 ) { diff --git a/core/base/src/TDirectory.cxx b/core/base/src/TDirectory.cxx index a8bcee69e8db1..9e002bf406b88 100644 --- a/core/base/src/TDirectory.cxx +++ b/core/base/src/TDirectory.cxx @@ -344,11 +344,12 @@ static TBuffer* R__CreateBuffer() if (!creator) { R__LOCKGUARD(gROOTMutex); TClass *c = TClass::GetClass("TBufferFile"); - TMethod *m = c->GetMethodWithPrototype("TBufferFile","TBuffer::EMode,Int_t",kFALSE,ROOT::kExactMatch); + TMethod *m = c->GetMethodWithPrototype("TBufferFile","TBuffer::EMode,Long64_t",kFALSE,ROOT::kExactMatch); + assert(m != nullptr); creator = (tcling_callfunc_Wrapper_t)( m->InterfaceMethod() ); } TBuffer::EMode mode = TBuffer::kWrite; - Int_t size = 10000; + Long64_t size = 10000; void *args[] = { &mode, &size }; TBuffer *result; creator(nullptr,2,args,&result); @@ -1425,9 +1426,9 @@ void TDirectory::RegisterGDirectory(TDirectory::SharedGDirectory_t &gdirectory_p } //////////////////////////////////////////////////////////////////////////////// -/// \copydoc TDirectoryFile::WriteObject(const T*,const char*,Option_t*,Int_t). +/// \copydoc TDirectoryFile::WriteObject(const T*,const char*,Option_t*,Long64_t). -Int_t TDirectory::WriteTObject(const TObject *obj, const char *name, Option_t * /*option*/, Int_t /*bufsize*/) +Int_t TDirectory::WriteTObject(const TObject *obj, const char *name, Option_t * /*option*/, Long64_t /*bufsize*/) { const char *objname = "no name specified"; if (name) objname = name; diff --git a/core/base/src/TObject.cxx b/core/base/src/TObject.cxx index baac44c4fe48f..5b4a6da90c270 100644 --- a/core/base/src/TObject.cxx +++ b/core/base/src/TObject.cxx @@ -957,7 +957,7 @@ void TObject::UseCurrentStyle() /// The function returns the total number of bytes written to the file. /// It returns 0 if the object cannot be written. -Int_t TObject::Write(const char *name, Int_t option, Int_t bufsize) const +Int_t TObject::Write(const char *name, Int_t option, Long64_t bufsize) const { if (R__unlikely(option & kOnlyPrepStep)) return 0; @@ -979,7 +979,7 @@ Int_t TObject::Write(const char *name, Int_t option, Int_t bufsize) const /// Write this object to the current directory. For more see the /// const version of this method. -Int_t TObject::Write(const char *name, Int_t option, Int_t bufsize) +Int_t TObject::Write(const char *name, Int_t option, Long64_t bufsize) { return ((const TObject*)this)->Write(name, option, bufsize); } diff --git a/core/cont/inc/TCollection.h b/core/cont/inc/TCollection.h index 359154c52c08e..79a531211f2cd 100644 --- a/core/cont/inc/TCollection.h +++ b/core/cont/inc/TCollection.h @@ -208,8 +208,8 @@ class TCollection : public TObject { void SetName(const char *name) { fName = name; } virtual void SetOwner(Bool_t enable = kTRUE); virtual bool UseRWLock(Bool_t enable = true); - Int_t Write(const char *name = nullptr, Int_t option = 0, Int_t bufsize = 0) override; - Int_t Write(const char *name = nullptr, Int_t option = 0, Int_t bufsize = 0) const override; + Int_t Write(const char *name = nullptr, Int_t option = 0, Long64_t bufsize = 0) override; + Int_t Write(const char *name = nullptr, Int_t option = 0, Long64_t bufsize = 0) const override; R__ALWAYS_INLINE Bool_t IsUsingRWLock() const { return TestBit(TCollection::kUseRWLock); } diff --git a/core/cont/inc/TMap.h b/core/cont/inc/TMap.h index 6243060846119..d40de95a68564 100644 --- a/core/cont/inc/TMap.h +++ b/core/cont/inc/TMap.h @@ -85,8 +85,8 @@ friend class TMapIter; TPair *RemoveEntry(TObject *key); virtual void SetOwnerValue(Bool_t enable = kTRUE); virtual void SetOwnerKeyValue(Bool_t ownkeys = kTRUE, Bool_t ownvals = kTRUE); - Int_t Write(const char *name=nullptr, Int_t option=0, Int_t bufsize=0) override; - Int_t Write(const char *name=nullptr, Int_t option=0, Int_t bufsize=0) const override; + Int_t Write(const char *name=nullptr, Int_t option=0, Long64_t bufsize=0) override; + Int_t Write(const char *name=nullptr, Int_t option=0, Long64_t bufsize=0) const override; ClassDefOverride(TMap,3) //A (key,value) map }; diff --git a/core/cont/src/TCollection.cxx b/core/cont/src/TCollection.cxx index 81fe664eea6fb..33378c4d7774c 100644 --- a/core/cont/src/TCollection.cxx +++ b/core/cont/src/TCollection.cxx @@ -676,7 +676,7 @@ void TCollection::Streamer(TBuffer &b) /// objects using a single key specify a name and set option to /// TObject::kSingleKey (i.e. 1). -Int_t TCollection::Write(const char *name, Int_t option, Int_t bufsize) const +Int_t TCollection::Write(const char *name, Int_t option, Long64_t bufsize) const { if ((option & kSingleKey)) { return TObject::Write(name, option, bufsize); @@ -700,7 +700,7 @@ Int_t TCollection::Write(const char *name, Int_t option, Int_t bufsize) const /// objects using a single key specify a name and set option to /// TObject::kSingleKey (i.e. 1). -Int_t TCollection::Write(const char *name, Int_t option, Int_t bufsize) +Int_t TCollection::Write(const char *name, Int_t option, Long64_t bufsize) { return ((const TCollection*)this)->Write(name,option,bufsize); } diff --git a/core/cont/src/TMap.cxx b/core/cont/src/TMap.cxx index a1af3bac25490..338e636a7b1a0 100644 --- a/core/cont/src/TMap.cxx +++ b/core/cont/src/TMap.cxx @@ -401,7 +401,7 @@ void TMap::Streamer(TBuffer &b) /// objects using a single key specify a name and set option to /// TObject::kSingleKey (i.e. 1). -Int_t TMap::Write(const char *name, Int_t option, Int_t bufsize) const +Int_t TMap::Write(const char *name, Int_t option, Long64_t bufsize) const { if ((option & kSingleKey)) { return TObject::Write(name, option, bufsize); @@ -428,7 +428,7 @@ Int_t TMap::Write(const char *name, Int_t option, Int_t bufsize) const /// objects using a single key specify a name and set option to /// TObject::kSingleKey (i.e. 1). -Int_t TMap::Write(const char *name, Int_t option, Int_t bufsize) +Int_t TMap::Write(const char *name, Int_t option, Long64_t bufsize) { return ((const TMap*)this)->Write(name,option,bufsize); } diff --git a/hist/hbook/inc/THbookBranch.h b/hist/hbook/inc/THbookBranch.h index ef5881462aae4..70e4cbbbb8958 100644 --- a/hist/hbook/inc/THbookBranch.h +++ b/hist/hbook/inc/THbookBranch.h @@ -30,8 +30,8 @@ class THbookBranch : public TBranch { public: THbookBranch() {} - THbookBranch(TTree *tree, const char *name, void *address, const char *leaflist, Int_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); - THbookBranch(TBranch *branch, const char *name, void *address, const char *leaflist, Int_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + THbookBranch(TTree *tree, const char *name, void *address, const char *leaflist, Long64_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + THbookBranch(TBranch *branch, const char *name, void *address, const char *leaflist, Long64_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); ~THbookBranch() override; void Browse(TBrowser *b) override; Int_t GetEntry(Long64_t entry=0, Int_t getall=0) override; diff --git a/hist/hbook/src/THbookBranch.cxx b/hist/hbook/src/THbookBranch.cxx index a8e8f26140ff9..97d67e1759c6d 100644 --- a/hist/hbook/src/THbookBranch.cxx +++ b/hist/hbook/src/THbookBranch.cxx @@ -21,14 +21,14 @@ //////////////////////////////////////////////////////////////////////////////// -THbookBranch::THbookBranch(TTree *tree, const char *name, void *address, const char *leaflist, Int_t basketsize, Int_t compress) +THbookBranch::THbookBranch(TTree *tree, const char *name, void *address, const char *leaflist, Long64_t basketsize, Int_t compress) :TBranch(tree, name,address,leaflist,basketsize,compress) { } //////////////////////////////////////////////////////////////////////////////// -THbookBranch::THbookBranch(TBranch *branch, const char *name, void *address, const char *leaflist, Int_t basketsize, Int_t compress) +THbookBranch::THbookBranch(TBranch *branch, const char *name, void *address, const char *leaflist, Long64_t basketsize, Int_t compress) :TBranch(branch,name,address,leaflist,basketsize,compress) { } diff --git a/hist/hbook/src/THbookFile.cxx b/hist/hbook/src/THbookFile.cxx index 91212285f389b..6ff894672c83e 100644 --- a/hist/hbook/src/THbookFile.cxx +++ b/hist/hbook/src/THbookFile.cxx @@ -765,7 +765,7 @@ TObject *THbookFile::ConvertCWN(Int_t id) } - Int_t bufsize = 8000; + Long64_t bufsize = 8000; THbookBranch *branch = new THbookBranch(tree,name,(void*)&bigbuf[bufpos],fullname,bufsize); tree->GetListOfBranches()->Add(branch); branch->SetBlockName(block); diff --git a/hist/hist/inc/TH1.h b/hist/hist/inc/TH1.h index cbd931b53fc3b..73674be501916 100644 --- a/hist/hist/inc/TH1.h +++ b/hist/hist/inc/TH1.h @@ -627,13 +627,13 @@ class TH1 : public TNamed, public TAttLine, public TAttFill, public TAttMarker { const Double_t *zBins); virtual void SetBinsLength(Int_t = -1) { } //redefined in derived classes virtual void SetBinErrorOption(EBinErrorOpt type) { fBinStatErrOpt = type; } - virtual void SetBuffer(Int_t bufsize, Option_t *option=""); + virtual void SetBuffer(Long64_t bufsize, Option_t *option=""); virtual UInt_t SetCanExtend(UInt_t extendBitMask); virtual void SetContent(const Double_t *content); virtual void SetContour(Int_t nlevels, const Double_t *levels = nullptr); virtual void SetContourLevel(Int_t level, Double_t value); virtual void SetColors(Color_t linecolor = -1, Color_t markercolor = -1, Color_t fillcolor = -1); - static void SetDefaultBufferSize(Int_t bufsize=1000); + static void SetDefaultBufferSize(Long64_t bufsize=1000); static void SetDefaultSumw2(Bool_t sumw2=kTRUE); virtual void SetDirectory(TDirectory *dir); virtual void SetEntries(Double_t n) { fEntries = n; } diff --git a/hist/hist/inc/TProfile.h b/hist/hist/inc/TProfile.h index e1b00a80bd4b2..316f61f3d2eb3 100644 --- a/hist/hist/inc/TProfile.h +++ b/hist/hist/inc/TProfile.h @@ -132,7 +132,7 @@ class TProfile : public TH1D { void SetBins(Int_t nbins, Double_t xmin, Double_t xmax) override; void SetBins(Int_t nx, const Double_t *xbins) override; void SetBinsLength(Int_t n=-1) override; - void SetBuffer(Int_t bufsize, Option_t *option="") override; + void SetBuffer(Long64_t bufsize, Option_t *option="") override; virtual void SetErrorOption(Option_t *option=""); // *MENU* void Sumw2(Bool_t flag = kTRUE) override; diff --git a/hist/hist/inc/TProfile2D.h b/hist/hist/inc/TProfile2D.h index 564141207c4f3..5215608e2dd30 100644 --- a/hist/hist/inc/TProfile2D.h +++ b/hist/hist/inc/TProfile2D.h @@ -146,7 +146,7 @@ class TProfile2D : public TH2D { void SetBins(Int_t nbinsx, Double_t xmin, Double_t xmax, Int_t nbinsy, Double_t ymin, Double_t ymax) override; void SetBins(Int_t nx, const Double_t *xBins, Int_t ny, const Double_t *yBins) override; void SetBinsLength(Int_t n=-1) override; - void SetBuffer(Int_t bufsize, Option_t *option="") override; + void SetBuffer(Long64_t bufsize, Option_t *option="") override; virtual void SetErrorOption(Option_t *option=""); // *MENU* void Sumw2(Bool_t flag = kTRUE) override; Double_t GetNumberOfBins() { return fBinEntries.GetSize(); } diff --git a/hist/hist/inc/TProfile3D.h b/hist/hist/inc/TProfile3D.h index 1b82c6c5d2d89..6779bf69e1c64 100644 --- a/hist/hist/inc/TProfile3D.h +++ b/hist/hist/inc/TProfile3D.h @@ -144,7 +144,7 @@ class TProfile3D : public TH3D { void SetBins(Int_t nx, const Double_t *xBins, Int_t ny, const Double_t * yBins, Int_t nz, const Double_t *zBins) override; void SetBinsLength(Int_t n=-1) override; - void SetBuffer(Int_t bufsize, Option_t *opt="") override; + void SetBuffer(Long64_t bufsize, Option_t *opt="") override; virtual void SetErrorOption(Option_t *option=""); // *MENU* void Sumw2(Bool_t flag = kTRUE) override; diff --git a/hist/hist/src/TH1.cxx b/hist/hist/src/TH1.cxx index 7c388455af492..24635a651ebed 100644 --- a/hist/hist/src/TH1.cxx +++ b/hist/hist/src/TH1.cxx @@ -6787,8 +6787,9 @@ UInt_t TH1::GetAxisLabelStatus() const /// or equal to its upper limit, the function SetBuffer is automatically /// called with the default buffer size. -void TH1::SetDefaultBufferSize(Int_t bufsize) +void TH1::SetDefaultBufferSize(Long64_t bufsize) { + assert(bufsize <= kMaxInt); fgBufferSize = bufsize > 0 ? bufsize : 0; } @@ -8570,7 +8571,7 @@ Double_t TH1::GetContourLevelPad(Int_t level) const //////////////////////////////////////////////////////////////////////////////// /// Set the maximum number of entries to be kept in the buffer. -void TH1::SetBuffer(Int_t bufsize, Option_t * /*option*/) +void TH1::SetBuffer(Long64_t bufsize, Option_t * /*option*/) { if (fBuffer) { BufferEmpty(); @@ -8582,6 +8583,8 @@ void TH1::SetBuffer(Int_t bufsize, Option_t * /*option*/) return; } if (bufsize < 100) bufsize = 100; + if (1 + bufsize*(fDimension+1)> kMaxInt) + Fatal("SetBufferSize", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", 1 + bufsize*(fDimension+1), kMaxInt); fBufferSize = 1 + bufsize*(fDimension+1); fBuffer = new Double_t[fBufferSize]; memset(fBuffer, 0, sizeof(Double_t)*fBufferSize); diff --git a/hist/hist/src/TProfile.cxx b/hist/hist/src/TProfile.cxx index 4885ed630f111..4583f1c6362c4 100644 --- a/hist/hist/src/TProfile.cxx +++ b/hist/hist/src/TProfile.cxx @@ -1770,7 +1770,7 @@ void TProfile::SetBinsLength(Int_t n) //////////////////////////////////////////////////////////////////////////////// /// Set the buffer size in units of 8 bytes (double). -void TProfile::SetBuffer(Int_t bufsize, Option_t *) +void TProfile::SetBuffer(Long64_t bufsize, Option_t *) { if (fBuffer) { BufferEmpty(); @@ -1782,6 +1782,8 @@ void TProfile::SetBuffer(Int_t bufsize, Option_t *) return; } if (bufsize < 100) bufsize = 100; + if (1 + 3*bufsize > kMaxInt) + Fatal("SetBufferSize", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", 1 + 3*bufsize, kMaxInt); fBufferSize = 1 + 3*bufsize; fBuffer = new Double_t[fBufferSize]; memset(fBuffer,0,sizeof(Double_t)*fBufferSize); diff --git a/hist/hist/src/TProfile2D.cxx b/hist/hist/src/TProfile2D.cxx index 09e6700e8989e..cfa6c8c6d23ef 100644 --- a/hist/hist/src/TProfile2D.cxx +++ b/hist/hist/src/TProfile2D.cxx @@ -2022,7 +2022,7 @@ void TProfile2D::SetBinsLength(Int_t n) //////////////////////////////////////////////////////////////////////////////// /// Set the buffer size in units of 8 bytes (double). -void TProfile2D::SetBuffer(Int_t bufsize, Option_t *) +void TProfile2D::SetBuffer(Long64_t bufsize, Option_t *) { if (fBuffer) { BufferEmpty(); @@ -2034,6 +2034,8 @@ void TProfile2D::SetBuffer(Int_t bufsize, Option_t *) return; } if (bufsize < 100) bufsize = 100; + if (1 + 4*bufsize > kMaxInt) + Fatal("SetBufferSize", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", 1 + 4*bufsize, kMaxInt); fBufferSize = 1 + 4*bufsize; fBuffer = new Double_t[fBufferSize]; memset(fBuffer,0,sizeof(Double_t)*fBufferSize); diff --git a/hist/hist/src/TProfile3D.cxx b/hist/hist/src/TProfile3D.cxx index 4756da07a4778..32b9e6b6847e0 100644 --- a/hist/hist/src/TProfile3D.cxx +++ b/hist/hist/src/TProfile3D.cxx @@ -1391,7 +1391,7 @@ void TProfile3D::SetBinsLength(Int_t n) //////////////////////////////////////////////////////////////////////////////// /// Set the buffer size in units of 8 bytes (double). -void TProfile3D::SetBuffer(Int_t bufsize, Option_t *) +void TProfile3D::SetBuffer(Long64_t bufsize, Option_t *) { if (fBuffer) { BufferEmpty(); @@ -1403,6 +1403,8 @@ void TProfile3D::SetBuffer(Int_t bufsize, Option_t *) return; } if (bufsize < 100) bufsize = 100; + if (1 + 5*bufsize > kMaxInt) + Fatal("SetBufferSize", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", 1 + 5*bufsize, kMaxInt); fBufferSize = 1 + 5*bufsize; fBuffer = new Double_t[fBufferSize]; memset(fBuffer,0,sizeof(Double_t)*fBufferSize); diff --git a/hist/hist/src/TScatter.cxx b/hist/hist/src/TScatter.cxx index dfec152176360..5c2b6a573135b 100644 --- a/hist/hist/src/TScatter.cxx +++ b/hist/hist/src/TScatter.cxx @@ -83,6 +83,9 @@ TScatter::TScatter(Int_t n, const Double_t *x, const Double_t *y, const Double_t fMaxSize = fGraph->GetMaxSize(); Int_t bufsize = sizeof(Double_t) * fNpoints; + if (sizeof(Double_t) * fNpoints > kMaxInt || bufsize < 0) { + Fatal("TScatter", "Negative buffer size likely due to an integer overflow: 0x%x.", bufsize); + } if (col) { fColor = new Double_t[fMaxSize]; memcpy(fColor, col, bufsize); diff --git a/hist/histpainter/src/TPainter3dAlgorithms.cxx b/hist/histpainter/src/TPainter3dAlgorithms.cxx index f0884c9aa3656..7a4f7f8fb4595 100644 --- a/hist/histpainter/src/TPainter3dAlgorithms.cxx +++ b/hist/histpainter/src/TPainter3dAlgorithms.cxx @@ -2009,7 +2009,9 @@ void TPainter3dAlgorithms::InitRaster(Double_t xmin, Double_t ymin, Double_t xma fDYrast = ymax - ymin; // Create buffer for raster - Int_t bufsize = nx*ny/30 + 1; + if (Long64_t(nx)*ny / 30 + 1 > kMaxInt) + Fatal("InitRaster", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", Long64_t(nx)*ny / 30 + 1, kMaxInt); + Int_t bufsize = Long64_t(nx)*ny/30 + 1; fRaster.resize(bufsize); // S E T M A S K S diff --git a/io/io/inc/ROOT/TBufferMerger.hxx b/io/io/inc/ROOT/TBufferMerger.hxx index d3c8ac86849a2..09fd41d58e802 100644 --- a/io/io/inc/ROOT/TBufferMerger.hxx +++ b/io/io/inc/ROOT/TBufferMerger.hxx @@ -172,7 +172,7 @@ public: * This function must be called before the TBufferMergerFile gets destroyed, * or no data is appended to the TBufferMerger. */ - Int_t Write(const char *name = nullptr, Int_t opt = 0, Int_t bufsize = 0) override; + Int_t Write(const char *name = nullptr, Int_t opt = 0, Long64_t bufsize = 0) override; ClassDefOverride(TBufferMergerFile, 0); }; diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index 5aaf5f1916f83..3d2e37141fcc4 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -59,9 +59,9 @@ class TBufferFile : public TBufferIO { TBufferFile(const TBufferFile &) = delete; ///< not implemented void operator=(const TBufferFile &) = delete; ///< not implemented - Int_t CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *clss, const char* classname); - void CheckCount(UInt_t offset) override; - UInt_t CheckObject(UInt_t offset, const TClass *cl, Bool_t readClass = kFALSE); + Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss, const char* classname); + void CheckCount(UInt_t offset) override; + UInt_t CheckObject(UInt_t offset, const TClass *cl, Bool_t readClass = kFALSE); void WriteObjectClass(const void *actualObjStart, const TClass *actualClass, Bool_t cacheReuse) override; @@ -69,13 +69,13 @@ class TBufferFile : public TBufferIO { enum { kStreamedMemberWise = BIT(14) }; //added to version number to know if a collection has been stored member-wise TBufferFile(TBuffer::EMode mode); - TBufferFile(TBuffer::EMode mode, Int_t bufsize); - TBufferFile(TBuffer::EMode mode, Int_t bufsize, void *buf, Bool_t adopt = kTRUE, ReAllocCharFun_t reallocfunc = nullptr); + TBufferFile(TBuffer::EMode mode, Long64_t bufsize); + TBufferFile(TBuffer::EMode mode, Long64_t bufsize, void *buf, Bool_t adopt = kTRUE, ReAllocCharFun_t reallocfunc = nullptr); ~TBufferFile() override; - Int_t CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *clss) override; - Int_t CheckByteCount(UInt_t startpos, UInt_t bcnt, const char *classname) override; - void SetByteCount(UInt_t cntpos, Bool_t packInVersion = kFALSE) override; + Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss) override; + Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const char *classname) override; + void SetByteCount(ULong64_t cntpos, Bool_t packInVersion = kFALSE) override; void SkipVersion(const TClass *cl = nullptr) override; Version_t ReadVersion(UInt_t *start = nullptr, UInt_t *bcnt = nullptr, const TClass *cl = nullptr) override; @@ -95,10 +95,10 @@ class TBufferFile : public TBufferIO { void ClassEnd(const TClass*) override {} void ClassMember(const char*, const char * = nullptr, Int_t = -1, Int_t = -1) override {} - Int_t ReadBuf(void *buf, Int_t max) override; - void WriteBuf(const void *buf, Int_t max) override; + Long64_t ReadBuf(void *buf, Long64_t max) override; + void WriteBuf(const void *buf, Long64_t max) override; - char *ReadString(char *s, Int_t max) override; + char *ReadString(char *s, Long64_t max) override; void WriteString(const char *s) override; TClass *ReadClass(const TClass *cl = nullptr, UInt_t *objTag = nullptr) override; diff --git a/io/io/inc/TBufferIO.h b/io/io/inc/TBufferIO.h index 1500beed2616e..eeec87582d007 100644 --- a/io/io/inc/TBufferIO.h +++ b/io/io/inc/TBufferIO.h @@ -25,6 +25,8 @@ #include "TString.h" +#include + class TExMap; class TBufferIO : public TBuffer { @@ -44,8 +46,8 @@ class TBufferIO : public TBuffer { TBufferIO() {} // NOLINT: not allowed to use = default because of TObject::kIsOnHeap detection, see ROOT-10300 TBufferIO(TBuffer::EMode mode); - TBufferIO(TBuffer::EMode mode, Int_t bufsize); - TBufferIO(TBuffer::EMode mode, Int_t bufsize, void *buf, Bool_t adopt = kTRUE, + TBufferIO(TBuffer::EMode mode, Long64_t bufsize); + TBufferIO(TBuffer::EMode mode, Long64_t bufsize, void *buf, Bool_t adopt = kTRUE, ReAllocCharFun_t reallocfunc = nullptr); //////////////////////////////////////////////////////////////////////////////// @@ -80,7 +82,7 @@ class TBufferIO : public TBuffer { void SetPidOffset(UShort_t offset) override; Int_t GetBufferDisplacement() const override { return fDisplacement; } void SetBufferDisplacement() override { fDisplacement = 0; } - void SetBufferDisplacement(Int_t skipped) override { fDisplacement = (Int_t)(Length() - skipped); } + void SetBufferDisplacement(Long64_t skipped) override { assert(skipped <= kMaxInt); fDisplacement = (Int_t)(Length() - skipped); } // Utilities for objects map void SetReadParam(Int_t mapsize) override; @@ -89,8 +91,8 @@ class TBufferIO : public TBuffer { void ResetMap() override; void Reset() override; Int_t GetMapCount() const override { return fMapCount; } - void MapObject(const TObject *obj, UInt_t offset = 1) override; - void MapObject(const void *obj, const TClass *cl, UInt_t offset = 1) override; + void MapObject(const TObject *obj, ULong64_t offset = 1) override; + void MapObject(const void *obj, const TClass *cl, ULong64_t offset = 1) override; Bool_t CheckObject(const TObject *obj) override; Bool_t CheckObject(const void *obj, const TClass *ptrClass) override; void GetMappedObject(UInt_t tag, void *&ptr, TClass *&ClassPtr) const override; @@ -98,8 +100,8 @@ class TBufferIO : public TBuffer { // Utilities for TStreamerInfo void ForceWriteInfo(TVirtualStreamerInfo *info, Bool_t force) override; void ForceWriteInfoClones(TClonesArray *a) override; - Int_t ReadClones(TClonesArray *a, Int_t nobjects, Version_t objvers) override; - Int_t WriteClones(TClonesArray *a, Int_t nobjects) override; + Int_t ReadClones(TClonesArray *a, Long64_t nobjects, Version_t objvers) override; + Int_t WriteClones(TClonesArray *a, Long64_t nobjects) override; void TagStreamerInfo(TVirtualStreamerInfo *info) override; // Special basic ROOT objects and collections diff --git a/io/io/inc/TBufferText.h b/io/io/inc/TBufferText.h index e19cd7ca8d0e6..a5cbffc99deb0 100644 --- a/io/io/inc/TBufferText.h +++ b/io/io/inc/TBufferText.h @@ -75,20 +75,20 @@ class TBufferText : public TBufferIO { // virtual abstract TBuffer methods, which are not used in text streaming - Int_t CheckByteCount(UInt_t /* startpos */, UInt_t /* bcnt */, const TClass * /* clss */) final { return 0; } - Int_t CheckByteCount(UInt_t /* startpos */, UInt_t /* bcnt */, const char * /* classname */) final { return 0; } - void SetByteCount(UInt_t /* cntpos */, Bool_t /* packInVersion */ = kFALSE) final {} + Long64_t CheckByteCount(ULong64_t /* startpos */, ULong64_t /* bcnt */, const TClass * /* clss */) final { return 0; } + Long64_t CheckByteCount(ULong64_t /* startpos */, ULong64_t /* bcnt */, const char * /* classname */) final { return 0; } + void SetByteCount(ULong64_t /* cntpos */, Bool_t /* packInVersion */ = kFALSE) final {} void SkipVersion(const TClass *cl = nullptr) final; Version_t ReadVersionNoCheckSum(UInt_t *, UInt_t *) final { return 0; } - Int_t ReadBuf(void * /*buf*/, Int_t /*max*/) final + Long64_t ReadBuf(void * /*buf*/, Long64_t /*max*/) final { Error("ReadBuf", "useless in text streamers"); return 0; } - void WriteBuf(const void * /*buf*/, Int_t /*max*/) final { Error("WriteBuf", "useless in text streamers"); } + void WriteBuf(const void * /*buf*/, Long64_t /*max*/) final { Error("WriteBuf", "useless in text streamers"); } - char *ReadString(char * /*s*/, Int_t /*max*/) final + char *ReadString(char * /*s*/, Long64_t /*max*/) final { Error("ReadString", "useless"); return nullptr; diff --git a/io/io/inc/TDirectoryFile.h b/io/io/inc/TDirectoryFile.h index 16f2d2edaa4e4..d09720d4b7662 100644 --- a/io/io/inc/TDirectoryFile.h +++ b/io/io/inc/TDirectoryFile.h @@ -114,17 +114,17 @@ class TDirectoryFile : public TDirectory { void Save() override; void SaveSelf(Bool_t force = kFALSE) override; Int_t SaveObjectAs(const TObject *obj, const char *filename="", Option_t *option="") const override; - void SetBufferSize(Int_t bufsize) override; + void SetBufferSize(Long64_t bufsize) override; void SetModified() override {fModified = kTRUE;} void SetSeekDir(Long64_t v) override { fSeekDir = v; } void SetTRefAction(TObject *ref, TObject *parent) override; void SetWritable(Bool_t writable=kTRUE) override; Int_t Sizeof() const override; - Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsize=0) override; - Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsize=0) const override; - Int_t WriteTObject(const TObject *obj, const char *name=nullptr, Option_t *option="", Int_t bufsize=0) override; - Int_t WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option="", Int_t bufsize=0) override; - Int_t WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option="", Int_t bufsize=0) override; + Int_t Write(const char *name=nullptr, Int_t opt=0, Long64_t bufsize=0) override; + Int_t Write(const char *name=nullptr, Int_t opt=0, Long64_t bufsize=0) const override; + Int_t WriteTObject(const TObject *obj, const char *name=nullptr, Option_t *option="", Long64_t bufsize=0) override; + Int_t WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option="", Long64_t bufsize=0) override; + Int_t WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option="", Long64_t bufsize=0) override; void WriteDirHeader() override; void WriteKeys() override; diff --git a/io/io/inc/TFile.h b/io/io/inc/TFile.h index 4986d068468c1..360f4bd740063 100644 --- a/io/io/inc/TFile.h +++ b/io/io/inc/TFile.h @@ -295,10 +295,10 @@ class TFile : public TDirectoryFile { void Close(Option_t *option="") override; // *MENU* void Copy(TObject &) const override { MayNotUse("Copy(TObject &)"); } - virtual Bool_t Cp(const char *dst, Bool_t progressbar = kTRUE,UInt_t bufsize = 1000000); - virtual TKey* CreateKey(TDirectory* mother, const TObject* obj, const char* name, Int_t bufsize); + virtual Bool_t Cp(const char *dst, Bool_t progressbar = kTRUE, Long64_t bufsize = 1000000); + virtual TKey* CreateKey(TDirectory* mother, const TObject* obj, const char* name, Long64_t bufsize); virtual TKey* CreateKey(TDirectory* mother, const void* obj, const TClass* cl, - const char* name, Int_t bufsize); + const char* name, Long64_t bufsize); static TFile *&CurrentFile(); // Return the current file for this thread. void Delete(const char *namecycle="") override; void Draw(Option_t *option="") override; @@ -382,10 +382,10 @@ class TFile : public TDirectoryFile { virtual void SetReadCalls(Int_t readcalls = 0) { fReadCalls = readcalls; } virtual void ShowStreamerInfo(); Int_t Sizeof() const override; - void SumBuffer(Int_t bufsize); + void SumBuffer(Long64_t bufsize); virtual Bool_t WriteBuffer(const char *buf, Int_t len); - Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsize=0) override; - Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsize=0) const override; + Int_t Write(const char *name=nullptr, Int_t opt=0, Long64_t bufsize=0) override; + Int_t Write(const char *name=nullptr, Int_t opt=0, Long64_t bufsize=0) const override; virtual void WriteFree(); virtual void WriteHeader(); virtual UShort_t WriteProcessID(TProcessID *pid); @@ -413,8 +413,8 @@ class TFile : public TDirectoryFile { static void SetFileBytesRead(Long64_t bytes = 0); static void SetFileBytesWritten(Long64_t bytes = 0); - static void SetFileReadCalls(Int_t readcalls = 0); - static void SetReadaheadSize(Int_t bufsize = 256000); + static void SetFileReadCalls(Long64_t readcalls = 0); + static void SetReadaheadSize(Long64_t bytes = 256000); static void SetReadStreamerInfo(Bool_t readinfo=kTRUE); static Bool_t GetReadStreamerInfo(); @@ -426,7 +426,7 @@ class TFile : public TDirectoryFile { static const char *GetCacheFileDir(); static Bool_t ShrinkCacheFileDir(Long64_t shrinkSize, Long_t cleanupInteval = 0); static Bool_t Cp(const char *src, const char *dst, Bool_t progressbar = kTRUE, - UInt_t buffersize = 1000000); + Long64_t bufsize = 1000000); static UInt_t SetOpenTimeout(UInt_t timeout); // in ms static UInt_t GetOpenTimeout(); // in ms diff --git a/io/io/inc/TFileCacheRead.h b/io/io/inc/TFileCacheRead.h index 0f05c7e8bb199..429bb865dd8ae 100644 --- a/io/io/inc/TFileCacheRead.h +++ b/io/io/inc/TFileCacheRead.h @@ -78,7 +78,7 @@ class TFileCacheRead : public TObject { public: TFileCacheRead(); - TFileCacheRead(TFile *file, Int_t bufsize, TObject *tree = nullptr); + TFileCacheRead(TFile *file, Long64_t bufsize, TObject *tree = nullptr); ~TFileCacheRead() override; virtual Int_t AddBranch(TBranch * /*b*/, Bool_t /*subbranches*/ = kFALSE) { return 0; } virtual Int_t AddBranch(const char * /*branch*/, Bool_t /*subbranches*/ = kFALSE) { return 0; } @@ -107,7 +107,7 @@ class TFileCacheRead : public TObject { virtual Int_t ReadBufferExtNormal(char *buf, Long64_t pos, Int_t len, Int_t &loc); virtual Int_t ReadBufferExtPrefetch(char *buf, Long64_t pos, Int_t len, Int_t &loc); virtual Int_t ReadBuffer(char *buf, Long64_t pos, Int_t len); - virtual Int_t SetBufferSize(Long64_t buffersize); + virtual Int_t SetBufferSize(Long64_t bufsize); virtual void SetFile(TFile *file, TFile::ECacheAction action = TFile::kDisconnect); virtual void SetSkipZip(Bool_t /*skip*/ = kTRUE) {} // This function is only used by TTreeCacheUnzip (ignore it) virtual void Sort(); diff --git a/io/io/inc/TFileCacheWrite.h b/io/io/inc/TFileCacheWrite.h index 83366eabcfc68..e3cce1ab15e1d 100644 --- a/io/io/inc/TFileCacheWrite.h +++ b/io/io/inc/TFileCacheWrite.h @@ -32,7 +32,7 @@ class TFileCacheWrite : public TObject { public: TFileCacheWrite(); - TFileCacheWrite(TFile *file, Int_t bufsize); + TFileCacheWrite(TFile *file, Long64_t bufsize); ~TFileCacheWrite() override; virtual Bool_t Flush(); virtual Int_t GetBytesInCache() const { return fNtot; } diff --git a/io/io/inc/TKey.h b/io/io/inc/TKey.h index a94081326ada1..77635ca9b9242 100644 --- a/io/io/inc/TKey.h +++ b/io/io/inc/TKey.h @@ -52,7 +52,7 @@ class TKey : public TNamed { TDirectory *fMotherDir; ///(fBufCur - fBuffer) + assert( cntpos <= kMaxUInt && (sizeof(UInt_t) + cntpos) < static_cast(fBufCur - fBuffer) && (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) - cntpos - sizeof(UInt_t); + const UInt_t cnt = UInt_t(fBufCur - fBuffer) - UInt_t(cntpos) - sizeof(UInt_t); char *buf = (char *)(fBuffer + cntpos); // if true, pack byte count in two consecutive shorts, so it can @@ -358,11 +358,11 @@ void TBufferFile::SetByteCount(UInt_t cntpos, Bool_t packInVersion) /// Returns 0 if everything is ok, otherwise the bytecount offset /// (< 0 when read too little, >0 when read too much). -Int_t TBufferFile::CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *clss, const char *classname) +Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss, const char *classname) { if (!bcnt) return 0; - - Int_t offset = 0; + R__ASSERT(startpos <= kMaxUInt && bcnt <= kMaxUInt); + Long64_t offset = 0; Longptr_t endpos = Longptr_t(fBuffer) + startpos + bcnt + sizeof(UInt_t); @@ -373,11 +373,11 @@ Int_t TBufferFile::CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *cl if (name) { if (offset < 0) { - Error("CheckByteCount", "object of class %s read too few bytes: %d instead of %d", + Error("CheckByteCount", "object of class %s read too few bytes: %lld instead of %llu", name,bcnt+offset,bcnt); } if (offset > 0) { - Error("CheckByteCount", "object of class %s read too many bytes: %d instead of %d", + Error("CheckByteCount", "object of class %s read too many bytes: %lld instead of %llu", name,bcnt+offset,bcnt); if (fParent) Warning("CheckByteCount","%s::Streamer() not in sync with data on file %s, fix Streamer()", @@ -390,7 +390,7 @@ Int_t TBufferFile::CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *cl if ( ((char *)endpos) > fBufMax ) { offset = fBufMax-fBufCur; Error("CheckByteCount", - "Byte count probably corrupted around buffer position %d:\n\t%d for a possible maximum of %d", + "Byte count probably corrupted around buffer position %llu:\n\t%llu for a possible maximum of %lld", startpos, bcnt, offset); fBufCur = fBufMax; @@ -411,7 +411,7 @@ Int_t TBufferFile::CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *cl /// Returns 0 if everything is ok, otherwise the bytecount offset /// (< 0 when read too little, >0 when read too much). -Int_t TBufferFile::CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *clss) +Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss) { if (!bcnt) return 0; return CheckByteCount( startpos, bcnt, clss, nullptr); @@ -425,7 +425,7 @@ Int_t TBufferFile::CheckByteCount(UInt_t startpos, UInt_t bcnt, const TClass *cl /// Returns 0 if everything is ok, otherwise the bytecount offset /// (< 0 when read too little, >0 when read too much). -Int_t TBufferFile::CheckByteCount(UInt_t startpos, UInt_t bcnt, const char *classname) +Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const char *classname) { if (!bcnt) return 0; return CheckByteCount( startpos, bcnt, nullptr, classname); @@ -3332,13 +3332,13 @@ UInt_t TBufferFile::CheckObject(UInt_t offset, const TClass *cl, Bool_t readClas /// Read max bytes from the I/O buffer into buf. The function returns /// the actual number of bytes read. -Int_t TBufferFile::ReadBuf(void *buf, Int_t max) +Long64_t TBufferFile::ReadBuf(void *buf, Long64_t max) { R__ASSERT(IsReading()); if (max == 0) return 0; - Int_t n = std::min(max, (Int_t)(fBufMax - fBufCur)); + Long64_t n = std::min(max, (Long64_t)(fBufMax - fBufCur)); memcpy(buf, fBufCur, n); fBufCur += n; @@ -3349,7 +3349,7 @@ Int_t TBufferFile::ReadBuf(void *buf, Int_t max) //////////////////////////////////////////////////////////////////////////////// /// Write max bytes from buf into the I/O buffer. -void TBufferFile::WriteBuf(const void *buf, Int_t max) +void TBufferFile::WriteBuf(const void *buf, Long64_t max) { R__ASSERT(IsWriting()); @@ -3365,14 +3365,14 @@ void TBufferFile::WriteBuf(const void *buf, Int_t max) /// Read string from I/O buffer. String is read till 0 character is /// found or till max-1 characters are read (i.e. string s has max /// bytes allocated). If max = -1 no check on number of character is -/// made, reading continues till 0 character is found. +/// made, reading continues till 0 character is found or MaxInt-1 chars are read. -char *TBufferFile::ReadString(char *s, Int_t max) +char *TBufferFile::ReadString(char *s, Long64_t max) { R__ASSERT(IsReading()); - + R__ASSERT(max <= kMaxInt); char ch; - Int_t nr = 0; + Long64_t nr = 0; if (max == -1) max = kMaxInt; diff --git a/io/io/src/TBufferIO.cxx b/io/io/src/TBufferIO.cxx index 6e84623c9a56a..30df90184fee7 100644 --- a/io/io/src/TBufferIO.cxx +++ b/io/io/src/TBufferIO.cxx @@ -47,7 +47,7 @@ TBufferIO::TBufferIO(TBuffer::EMode mode) : TBuffer(mode) //////////////////////////////////////////////////////////////////////////////// /// constructor -TBufferIO::TBufferIO(TBuffer::EMode mode, Int_t bufsize) : TBuffer(mode, bufsize) +TBufferIO::TBufferIO(TBuffer::EMode mode, Long64_t bufsize) : TBuffer(mode, bufsize) { fMapSize = fgMapSize; } @@ -55,7 +55,7 @@ TBufferIO::TBufferIO(TBuffer::EMode mode, Int_t bufsize) : TBuffer(mode, bufsize //////////////////////////////////////////////////////////////////////////////// /// constructor -TBufferIO::TBufferIO(TBuffer::EMode mode, Int_t bufsize, void *buf, Bool_t adopt, ReAllocCharFun_t reallocfunc) +TBufferIO::TBufferIO(TBuffer::EMode mode, Long64_t bufsize, void *buf, Bool_t adopt, ReAllocCharFun_t reallocfunc) : TBuffer(mode, bufsize, buf, adopt, reallocfunc) { fMapSize = fgMapSize; @@ -159,8 +159,9 @@ void TBufferIO::InitMap() /// contains (via via) a pointer to itself. In that case offset must be 1 /// (default value for offset). -void TBufferIO::MapObject(const TObject *obj, UInt_t offset) +void TBufferIO::MapObject(const TObject *obj, ULong64_t offset) { + R__ASSERT(offset <= kMaxUInt); if (IsWriting()) { if (!fMap) InitMap(); @@ -192,8 +193,9 @@ void TBufferIO::MapObject(const TObject *obj, UInt_t offset) /// contains (via via) a pointer to itself. In that case offset must be 1 /// (default value for offset). -void TBufferIO::MapObject(const void *obj, const TClass *cl, UInt_t offset) +void TBufferIO::MapObject(const void *obj, const TClass *cl, ULong64_t offset) { + R__ASSERT(offset <= kMaxUInt); if (IsWriting()) { if (!fMap) InitMap(); @@ -368,8 +370,9 @@ void TBufferIO::TagStreamerInfo(TVirtualStreamerInfo *info) //////////////////////////////////////////////////////////////////////////////// /// Interface to TStreamerInfo::ReadBufferClones. -Int_t TBufferIO::ReadClones(TClonesArray *a, Int_t nobjects, Version_t objvers) +Int_t TBufferIO::ReadClones(TClonesArray *a, Long64_t nobjects, Version_t objvers) { + assert(nobjects <= kMaxInt); char **arr = (char **)a->GetObjectRef(0); char **end = arr + nobjects; // a->GetClass()->GetStreamerInfo()->ReadBufferClones(*this,a,nobjects,-1,0); @@ -381,8 +384,9 @@ Int_t TBufferIO::ReadClones(TClonesArray *a, Int_t nobjects, Version_t objvers) //////////////////////////////////////////////////////////////////////////////// /// Interface to TStreamerInfo::WriteBufferClones. -Int_t TBufferIO::WriteClones(TClonesArray *a, Int_t nobjects) +Int_t TBufferIO::WriteClones(TClonesArray *a, Long64_t nobjects) { + assert(nobjects <= kMaxInt); char **arr = reinterpret_cast(a->GetObjectRef(0)); // a->GetClass()->GetStreamerInfo()->WriteBufferClones(*this,(TClonesArray*)a,nobjects,-1,0); TStreamerInfo *info = (TStreamerInfo *)a->GetClass()->GetStreamerInfo(); diff --git a/io/io/src/TBufferMergerFile.cxx b/io/io/src/TBufferMergerFile.cxx index 951891e2a46ec..63b0b66d379e9 100644 --- a/io/io/src/TBufferMergerFile.cxx +++ b/io/io/src/TBufferMergerFile.cxx @@ -26,7 +26,7 @@ TBufferMergerFile::~TBufferMergerFile() { } -Int_t TBufferMergerFile::Write(const char *name, Int_t opt, Int_t bufsize) +Int_t TBufferMergerFile::Write(const char *name, Int_t opt, Long64_t bufsize) { // Make sure the compression of the basket is done in the unlocked thread and // not in the locked section. diff --git a/io/io/src/TDirectoryFile.cxx b/io/io/src/TDirectoryFile.cxx index 3e9c01eaf9218..17c4982cd606a 100644 --- a/io/io/src/TDirectoryFile.cxx +++ b/io/io/src/TDirectoryFile.cxx @@ -406,7 +406,7 @@ TObject *TDirectoryFile::CloneObject(const TObject *obj, Bool_t autoadd /* = kTR // during the streaming .... TFile *filsav = gFile; gFile = nullptr; - const Int_t bufsize = 10000; + const Long64_t bufsize = 10000; TBufferFile buffer(TBuffer::kWrite,bufsize); buffer.MapObject(obj); //register obj in map to handle self reference { @@ -1665,8 +1665,10 @@ void TDirectoryFile::SaveSelf(Bool_t force) /// /// See also TDirectoryFile::GetBufferSize -void TDirectoryFile::SetBufferSize(Int_t bufsize) +void TDirectoryFile::SetBufferSize(Long64_t bufsize) { + if (bufsize > kMaxInt) + Fatal("SetBufferSize", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); fBufferSize = bufsize; } @@ -1853,7 +1855,7 @@ void TDirectoryFile::Streamer(TBuffer &b) /// For allowed options see TObject::Write(). /// The directory header info is rewritten on the directory header record. -Int_t TDirectoryFile::Write(const char *, Int_t opt, Int_t bufsize) +Int_t TDirectoryFile::Write(const char *, Int_t opt, Long64_t bufsize) { if (!IsWritable()) return 0; TDirectory::TContext ctxt(this); @@ -1874,7 +1876,7 @@ Int_t TDirectoryFile::Write(const char *, Int_t opt, Int_t bufsize) //////////////////////////////////////////////////////////////////////////////// /// One can not save a const TDirectory object. -Int_t TDirectoryFile::Write(const char *n, Int_t opt, Int_t bufsize) const +Int_t TDirectoryFile::Write(const char *n, Int_t opt, Long64_t bufsize) const { Error("Write const","A const TDirectory object should not be saved. We try to proceed anyway."); return const_cast(this)->Write(n, opt, bufsize); @@ -1924,7 +1926,7 @@ Int_t TDirectoryFile::Write(const char *n, Int_t opt, Int_t bufsize) const /// WARNING: avoid special characters like '^','$','.' in the name as they /// are used by the regular expression parser (see TRegexp). -Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_t *option, Int_t bufsize) +Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_t *option, Long64_t bufsize) { TDirectory::TContext ctxt(this); @@ -1951,7 +1953,11 @@ Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_ TKey *key=0, *oldkey=0; Int_t bsize = GetBufferSize(); - if (bufsize > 0) bsize = bufsize; + if (bufsize > 0) { + if (bufsize > kMaxInt) + Fatal("WriteTObject", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); + bsize = bufsize; + } const char *oname; if (name && *name) @@ -2039,7 +2045,7 @@ Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_ /// ~~~ /// See also remarks in TDirectoryFile::WriteTObject -Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option, Int_t bufsize) +Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option, Long64_t bufsize) { TClass *cl = TClass::GetClass(classname); if (!cl) { @@ -2066,7 +2072,7 @@ Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, con /// An alternative is to call the function WriteObjectAny above. /// see TDirectoryFile::WriteTObject for comments -Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option, Int_t bufsize) +Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option, Long64_t bufsize) { TDirectory::TContext ctxt(this); @@ -2105,7 +2111,10 @@ Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const ch TKey *key, *oldkey = nullptr; Int_t bsize = GetBufferSize(); - if (bufsize > 0) bsize = bufsize; + if (bufsize > 0) { + Fatal("WriteObjectAny", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); + bsize = bufsize; + } TString opt = option; opt.ToLower(); diff --git a/io/io/src/TFile.cxx b/io/io/src/TFile.cxx index 44f2c8a912bdb..17d495bd6ac6b 100644 --- a/io/io/src/TFile.cxx +++ b/io/io/src/TFile.cxx @@ -1053,7 +1053,7 @@ void TFile::Close(Option_t *option) //////////////////////////////////////////////////////////////////////////////// /// Creates key for object and converts data to buffer. -TKey* TFile::CreateKey(TDirectory* mother, const TObject* obj, const char* name, Int_t bufsize) +TKey* TFile::CreateKey(TDirectory* mother, const TObject* obj, const char* name, Long64_t bufsize) { return new TKey(obj, name, bufsize, mother); } @@ -1061,7 +1061,7 @@ TKey* TFile::CreateKey(TDirectory* mother, const TObject* obj, const char* name, //////////////////////////////////////////////////////////////////////////////// /// Creates key for object and converts data to buffer. -TKey* TFile::CreateKey(TDirectory* mother, const void* obj, const TClass* cl, const char* name, Int_t bufsize) +TKey* TFile::CreateKey(TDirectory* mother, const void* obj, const TClass* cl, const char* name, Long64_t bufsize) { return new TKey(obj, cl, name, bufsize, mother); } @@ -2445,11 +2445,16 @@ void TFile::Streamer(TBuffer &b) //////////////////////////////////////////////////////////////////////////////// /// Increment statistics for buffer sizes of objects in this file. -void TFile::SumBuffer(Int_t bufsize) +void TFile::SumBuffer(Long64_t bufsize) { + if (bufsize > kMaxInt) + Fatal("SumBuffer", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); + else if (bufsize < 0) + Fatal("SumBuffer", "negative buffer size: 0x%llx.", bufsize); + fWritten++; - fSumBuffer += double(bufsize); - fSum2Buffer += double(bufsize) * double(bufsize); // avoid reaching MAXINT for temporary + fSumBuffer += bufsize; + fSum2Buffer += bufsize * bufsize; } //////////////////////////////////////////////////////////////////////////////// @@ -2464,7 +2469,7 @@ void TFile::SumBuffer(Int_t bufsize) /// The linked list of FREE segments is written. /// The file header is written (bytes 1->fBEGIN). -Int_t TFile::Write(const char *, Int_t opt, Int_t bufsize) +Int_t TFile::Write(const char *, Int_t opt, Long64_t bufsize) { if (!IsWritable()) { if (!TestBit(kWriteError)) { @@ -2494,7 +2499,7 @@ Int_t TFile::Write(const char *, Int_t opt, Int_t bufsize) //////////////////////////////////////////////////////////////////////////////// /// One can not save a const TDirectory object. -Int_t TFile::Write(const char *n, Int_t opt, Int_t bufsize) const +Int_t TFile::Write(const char *n, Int_t opt, Long64_t bufsize) const { Error("Write const","A const TFile object should not be saved. We try to proceed anyway."); return const_cast(this)->Write(n, opt, bufsize); @@ -4287,16 +4292,28 @@ Int_t TFile::GetReadaheadSize() } //______________________________________________________________________________ -void TFile::SetReadaheadSize(Int_t bytes) { fgReadaheadSize = bytes; } +void TFile::SetReadaheadSize(Long64_t bytes) { + assert (bytes <= kMaxInt); + fgReadaheadSize = bytes; +} //______________________________________________________________________________ -void TFile::SetFileBytesRead(Long64_t bytes) { fgBytesRead = bytes; } +void TFile::SetFileBytesRead(Long64_t bytes) { + assert (bytes <= kMaxInt); + fgBytesRead = bytes; +} //______________________________________________________________________________ -void TFile::SetFileBytesWritten(Long64_t bytes) { fgBytesWrite = bytes; } +void TFile::SetFileBytesWritten(Long64_t bytes) { + assert (bytes <= kMaxInt); + fgBytesWrite = bytes; +} //______________________________________________________________________________ -void TFile::SetFileReadCalls(Int_t readcalls) { fgReadCalls = readcalls; } +void TFile::SetFileReadCalls(Long64_t readcalls) { + assert (readcalls <= kMaxInt); + fgReadCalls = readcalls; +} //______________________________________________________________________________ Long64_t TFile::GetFileCounter() { return fgFileCounter; } @@ -4686,7 +4703,7 @@ void TFile::CpProgress(Long64_t bytesread, Long64_t size, TStopwatch &watch) /// Allows to copy this file to the dst URL. Returns kTRUE in case of success, /// kFALSE otherwise. -Bool_t TFile::Cp(const char *dst, Bool_t progressbar, UInt_t bufsize) +Bool_t TFile::Cp(const char *dst, Bool_t progressbar, Long64_t bufsize) { Bool_t rmdestiferror = kFALSE; TStopwatch watch; @@ -4741,6 +4758,11 @@ Bool_t TFile::Cp(const char *dst, Bool_t progressbar, UInt_t bufsize) sfile->Seek(0); dfile->Seek(0); + if (bufsize < 0) + Fatal("TFile::Cp", "Negative buffer size: 0x%llx.", bufsize); + else if (bufsize > kMaxUInt) { + Fatal("TFile::Cp", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxUInt); + } copybuffer = new char[bufsize]; if (!copybuffer) { ::Error("TFile::Cp", "cannot allocate the copy buffer"); @@ -4763,7 +4785,7 @@ Bool_t TFile::Cp(const char *dst, Bool_t progressbar, UInt_t bufsize) Long64_t b1 = sfile->GetBytesRead() - b00; Long64_t readsize; - if (filesize - b1 > (Long64_t)bufsize) { + if (filesize - b1 > bufsize) { readsize = bufsize; } else { readsize = filesize - b1; @@ -4789,7 +4811,7 @@ Bool_t TFile::Cp(const char *dst, Bool_t progressbar, UInt_t bufsize) goto copyout; } totalread += read; - } while (read == (Long64_t)bufsize); + } while (read == bufsize); if (progressbar) { CpProgress(totalread, filesize,watch); @@ -4818,7 +4840,7 @@ Bool_t TFile::Cp(const char *dst, Bool_t progressbar, UInt_t bufsize) /// kFALSE otherwise. Bool_t TFile::Cp(const char *src, const char *dst, Bool_t progressbar, - UInt_t bufsize) + Long64_t bufsize) { TUrl sURL(src, kTRUE); diff --git a/io/io/src/TFileCacheRead.cxx b/io/io/src/TFileCacheRead.cxx index 351adbf2b7958..8b50683fc5a4a 100644 --- a/io/io/src/TFileCacheRead.cxx +++ b/io/io/src/TFileCacheRead.cxx @@ -89,9 +89,14 @@ TFileCacheRead::TFileCacheRead() : TObject() //////////////////////////////////////////////////////////////////////////////// /// Creates a TFileCacheRead data structure. -TFileCacheRead::TFileCacheRead(TFile *file, Int_t bufsize, TObject *tree) +TFileCacheRead::TFileCacheRead(TFile *file, Long64_t bufsize, TObject *tree) : TObject() { + if (bufsize < 0) + Fatal("TFileCacheRead", "Negative buffer size: 0x%llx.", bufsize); + else if (bufsize > kMaxInt) { + Fatal("TFileCacheRead", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); + } if (bufsize <=10000) fBufferSize = 100000; else fBufferSize = bufsize; diff --git a/io/io/src/TFileCacheWrite.cxx b/io/io/src/TFileCacheWrite.cxx index 5aadb50ce8791..89c197ac0b1e8 100644 --- a/io/io/src/TFileCacheWrite.cxx +++ b/io/io/src/TFileCacheWrite.cxx @@ -49,9 +49,14 @@ TFileCacheWrite::TFileCacheWrite() : TObject() /// The size of the cache will be bufsize, /// if bufsize < 10000 a default size of 512 Kbytes is used -TFileCacheWrite::TFileCacheWrite(TFile *file, Int_t bufsize) +TFileCacheWrite::TFileCacheWrite(TFile *file, Long64_t bufsize) : TObject() { + if (bufsize < 0) + Fatal("TFileCacheWrite", "Negative buffer size: 0x%llx.", bufsize); + else if (bufsize > kMaxInt) { + Fatal("TFileCacheWrite", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); + } if (bufsize < 10000) bufsize = 512000; fBufferSize = bufsize; fSeekStart = 0; @@ -60,7 +65,7 @@ TFileCacheWrite::TFileCacheWrite(TFile *file, Int_t bufsize) fRecursive = kFALSE; fBuffer = new char[fBufferSize]; if (file) file->SetCacheWrite(this); - if (gDebug > 0) Info("TFileCacheWrite","Creating a write cache with buffersize=%d bytes",bufsize); + if (gDebug > 0) Info("TFileCacheWrite","Creating a write cache with buffersize=%lld bytes",bufsize); } //////////////////////////////////////////////////////////////////////////////// diff --git a/io/io/src/TKey.cxx b/io/io/src/TKey.cxx index 2567681d839bc..add90be50ce92 100644 --- a/io/io/src/TKey.cxx +++ b/io/io/src/TKey.cxx @@ -171,11 +171,16 @@ TKey::TKey(TDirectory* motherDir, const TKey &orig, UShort_t pidOffset) : TNamed /// Constructor called by TDirectoryFile::ReadKeys and by TFile::TFile. /// A TKey object is created to read the keys structure itself. -TKey::TKey(Long64_t pointer, Int_t nbytes, TDirectory* motherDir) : TNamed() +TKey::TKey(Long64_t pointer, Long64_t nbytes, TDirectory* motherDir) : TNamed() { Build(motherDir, "", pointer); fSeekKey = pointer; + if (nbytes > kMaxInt) { + Fatal("TKey", "Integer overflow in byte size: 0x%llx for a max of 0x%x.", nbytes, kMaxInt); + } else if (nbytes < 0) { + Fatal("TKey", "Negative byte size: 0x%llx.", nbytes); + } fNbytes = nbytes; fBuffer = new char[nbytes]; keyAbsNumber++; SetUniqueID(keyAbsNumber); @@ -187,12 +192,17 @@ TKey::TKey(Long64_t pointer, Int_t nbytes, TDirectory* motherDir) : TNamed() /// WARNING: in name avoid special characters like '^','$','.' that are used /// by the regular expression parser (see TRegexp). -TKey::TKey(const char *name, const char *title, const TClass *cl, Int_t nbytes, TDirectory* motherDir) +TKey::TKey(const char *name, const char *title, const TClass *cl, Long64_t nbytes, TDirectory* motherDir) : TNamed(name,title) { Build(motherDir, cl->GetName(), -1); fKeylen = Sizeof(); + if (nbytes > kMaxInt) { + Fatal("TKey", "Integer overflow in byte size: 0x%llx for a max of 0x%x.", nbytes, kMaxInt); + } else if (nbytes < 0) { + Fatal("TKey", "Negative byte size: 0x%llx.", nbytes); + } fObjlen = nbytes; Create(nbytes); } @@ -203,12 +213,17 @@ TKey::TKey(const char *name, const char *title, const TClass *cl, Int_t nbytes, /// WARNING: in name avoid special characters like '^','$','.' that are used /// by the regular expression parser (see TRegexp). -TKey::TKey(const TString &name, const TString &title, const TClass *cl, Int_t nbytes, TDirectory* motherDir) +TKey::TKey(const TString &name, const TString &title, const TClass *cl, Long64_t nbytes, TDirectory* motherDir) : TNamed(name,title) { Build(motherDir, cl->GetName(), -1); fKeylen = Sizeof(); + if (nbytes > kMaxInt) { + Fatal("TKey", "Integer overflow in byte size: 0x%llx for a max of 0x%x.", nbytes, kMaxInt); + } else if (nbytes < 0) { + Fatal("TKey", "Negative byte size: 0x%llx.", nbytes); + } fObjlen = nbytes; Create(nbytes); } @@ -219,7 +234,7 @@ TKey::TKey(const TString &name, const TString &title, const TClass *cl, Int_t nb /// WARNING: in name avoid special characters like '^','$','.' that are used /// by the regular expression parser (see TRegexp). -TKey::TKey(const TObject *obj, const char *name, Int_t bufsize, TDirectory* motherDir) +TKey::TKey(const TObject *obj, const char *name, Long64_t bufsize, TDirectory* motherDir) : TNamed(name, obj->GetTitle()) { R__ASSERT(obj); @@ -294,7 +309,7 @@ TKey::TKey(const TObject *obj, const char *name, Int_t bufsize, TDirectory* moth /// WARNING: in name avoid special characters like '^','$','.' that are used /// by the regular expression parser (see TRegexp). -TKey::TKey(const void *obj, const TClass *cl, const char *name, Int_t bufsize, TDirectory *motherDir) : TNamed(name, "") +TKey::TKey(const void *obj, const TClass *cl, const char *name, Long64_t bufsize, TDirectory *motherDir) : TNamed(name, "") { R__ASSERT(obj && cl); @@ -458,7 +473,7 @@ void TKey::Browse(TBrowser *b) /// If externFile!=0, key will be allocated in specified file, otherwise file /// of mother directory will be used. -void TKey::Create(Int_t nbytes, TFile* externFile) +void TKey::Create(Long64_t nbytes, TFile* externFile) { keyAbsNumber++; SetUniqueID(keyAbsNumber); @@ -468,7 +483,11 @@ void TKey::Create(Int_t nbytes, TFile* externFile) Error("Create","Cannot create key without file"); return; } - + if (nbytes > kMaxInt - fKeylen) { + Fatal("Create", "Integer overflow in byte size: 0x%llx for a max of 0x%x.", nbytes, kMaxInt); + } else if (nbytes < 0) { + Fatal("Create", "Negative byte size: 0x%llx.", nbytes); + } Int_t nsize = nbytes + fKeylen; TList *lfree = f->GetListOfFree(); TFree *f1 = (TFree*)lfree->First(); diff --git a/io/io/src/TMapFile.cxx b/io/io/src/TMapFile.cxx index 7692d5886a223..b0e610554df71 100644 --- a/io/io/src/TMapFile.cxx +++ b/io/io/src/TMapFile.cxx @@ -1124,7 +1124,7 @@ void TMapFile::ls(Option_t *) const //////////////////////////////////////////////////////////////////////////////// /// Increment statistics for buffer sizes of objects in this file. -void TMapFile::SumBuffer(Int_t bufsize) +void TMapFile::SumBuffer(Long64_t bufsize) { fWritten++; fSumBuffer += bufsize; diff --git a/io/sql/inc/TSQLFile.h b/io/sql/inc/TSQLFile.h index 6212462db8621..6c43069189b1d 100644 --- a/io/sql/inc/TSQLFile.h +++ b/io/sql/inc/TSQLFile.h @@ -205,8 +205,8 @@ class TSQLFile final : public TFile { void StopLogFile(); // *MENU* void Close(Option_t *option = "") final; // *MENU* - TKey *CreateKey(TDirectory *mother, const TObject *obj, const char *name, Int_t bufsize) final; - TKey *CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Int_t bufsize) final; + TKey *CreateKey(TDirectory *mother, const TObject *obj, const char *name, Long64_t bufsize) final; + TKey *CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Long64_t bufsize) final; void DrawMap(const char * = "*", Option_t * = "") final {} void FillBuffer(char *&) final {} void Flush() final {} @@ -245,8 +245,8 @@ class TSQLFile final : public TFile { Int_t Sizeof() const final { return 0; } Bool_t WriteBuffer(const char *, Int_t) final { return kFALSE; } - Int_t Write(const char * = nullptr, Int_t = 0, Int_t = 0) final { return 0; } - Int_t Write(const char * = nullptr, Int_t = 0, Int_t = 0) const final { return 0; } + Int_t Write(const char * = nullptr, Int_t = 0, Long64_t = 0) final { return 0; } + Int_t Write(const char * = nullptr, Int_t = 0, Long64_t = 0) const final { return 0; } void WriteFree() final {} void WriteHeader() final; void WriteStreamerInfo() final; diff --git a/io/sql/src/TSQLFile.cxx b/io/sql/src/TSQLFile.cxx index 5d4db4cfd34d9..52b5ea857c281 100644 --- a/io/sql/src/TSQLFile.cxx +++ b/io/sql/src/TSQLFile.cxx @@ -762,7 +762,7 @@ Int_t TSQLFile::ReOpen(Option_t *mode) //////////////////////////////////////////////////////////////////////////////// /// create SQL key, which will store object in data base -TKey *TSQLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *name, Int_t) +TKey *TSQLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *name, Long64_t) { return new TKeySQL(mother, obj, name); } @@ -770,7 +770,7 @@ TKey *TSQLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *na //////////////////////////////////////////////////////////////////////////////// /// create SQL key, which will store object in data base -TKey *TSQLFile::CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Int_t) +TKey *TSQLFile::CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Long64_t) { return new TKeySQL(mother, obj, cl, name); } diff --git a/io/xml/inc/TXMLFile.h b/io/xml/inc/TXMLFile.h index 44ee805a68f4e..d84afb85a11a6 100644 --- a/io/xml/inc/TXMLFile.h +++ b/io/xml/inc/TXMLFile.h @@ -53,8 +53,8 @@ class TXMLFile final : public TFile, public TXMLSetup { ~TXMLFile() override; void Close(Option_t *option = "") final; // *MENU* - TKey *CreateKey(TDirectory *mother, const TObject *obj, const char *name, Int_t bufsize) final; - TKey *CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Int_t bufsize) final; + TKey *CreateKey(TDirectory *mother, const TObject *obj, const char *name, Long64_t bufsize) final; + TKey *CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Long64_t bufsize) final; void DrawMap(const char * = "*", Option_t * = "") final {} void FillBuffer(char *&) final {} void Flush() final {} @@ -91,8 +91,8 @@ class TXMLFile final : public TFile, public TXMLSetup { Int_t Sizeof() const final { return 0; } Bool_t WriteBuffer(const char *, Int_t) final { return kFALSE; } - Int_t Write(const char * = nullptr, Int_t = 0, Int_t = 0) final { return 0; } - Int_t Write(const char * = nullptr, Int_t = 0, Int_t = 0) const final { return 0; } + Int_t Write(const char * = nullptr, Int_t = 0, Long64_t = 0) final { return 0; } + Int_t Write(const char * = nullptr, Int_t = 0, Long64_t = 0) const final { return 0; } void WriteFree() final {} void WriteHeader() final {} void WriteStreamerInfo() final; diff --git a/io/xml/src/TXMLFile.cxx b/io/xml/src/TXMLFile.cxx index 6c022942fe08b..6be31bd47f13b 100644 --- a/io/xml/src/TXMLFile.cxx +++ b/io/xml/src/TXMLFile.cxx @@ -404,7 +404,7 @@ Int_t TXMLFile::ReOpen(Option_t *mode) //////////////////////////////////////////////////////////////////////////////// /// create XML key, which will store object in xml structures -TKey *TXMLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *name, Int_t) +TKey *TXMLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *name, Long64_t) { return new TKeyXML(mother, ++fKeyCounter, obj, name); } @@ -412,7 +412,7 @@ TKey *TXMLFile::CreateKey(TDirectory *mother, const TObject *obj, const char *na //////////////////////////////////////////////////////////////////////////////// /// create XML key, which will store object in xml structures -TKey *TXMLFile::CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Int_t) +TKey *TXMLFile::CreateKey(TDirectory *mother, const void *obj, const TClass *cl, const char *name, Long64_t) { return new TKeyXML(mother, ++fKeyCounter, obj, cl, name); } diff --git a/main/src/h2root.cxx b/main/src/h2root.cxx index bd1f67996852a..8995f8ef9968e 100644 --- a/main/src/h2root.cxx +++ b/main/src/h2root.cxx @@ -265,7 +265,7 @@ extern void convert_cwn(Int_t id); extern void convert_rwn(Int_t id); Int_t golower = 1; -Int_t bufsize = 64000; +Long64_t bufsize = 64000; Int_t optcwn = 1; int main(int argc, char **argv) { @@ -293,7 +293,7 @@ int main(int argc, char **argv) optcwn = atoi(argv[7]); } if (argc > 6) { - bufsize = atoi(argv[6]); + bufsize = atol(argv[6]); } if (argc > 5) { record_size = atoi(argv[5]); diff --git a/net/net/inc/TMessage.h b/net/net/inc/TMessage.h index 90f4f377d1577..f8b964d925df3 100644 --- a/net/net/inc/TMessage.h +++ b/net/net/inc/TMessage.h @@ -62,11 +62,11 @@ friend class TXSocket; enum EStatusBits { kIsOwnerComp = BIT(19) // if TMessage owns fBufComp }; - TMessage(void *buf, Int_t bufsize, Bool_t adopt = kTRUE); // only called by T(P)Socket::Recv() + TMessage(void *buf, Long64_t bufsize, Bool_t adopt = kTRUE); // only called by T(P)Socket::Recv() void SetLength() const; // only called by T(P)Socket::Send() public: - TMessage(UInt_t what = kMESS_ANY, Int_t bufsize = TBuffer::kInitialSize); + TMessage(UInt_t what = kMESS_ANY, Long64_t bufsize = TBuffer::kInitialSize); virtual ~TMessage(); void ForceWriteInfo(TVirtualStreamerInfo *info, Bool_t force) override; diff --git a/net/net/inc/TParallelMergingFile.h b/net/net/inc/TParallelMergingFile.h index d956d1e871473..d1e34b9d0e465 100644 --- a/net/net/inc/TParallelMergingFile.h +++ b/net/net/inc/TParallelMergingFile.h @@ -53,8 +53,8 @@ class TParallelMergingFile : public TMemFile void Close(Option_t *option="") override; Bool_t OpenConnection(); Bool_t UploadAndReset(); - Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsize=0) override; - Int_t Write(const char *name=nullptr, Int_t opt=0, Int_t bufsize=0) const override; + Int_t Write(const char *name=nullptr, Int_t opt=0, Long64_t bufsize=0) override; + Int_t Write(const char *name=nullptr, Int_t opt=0, Long64_t bufsize=0) const override; void WriteStreamerInfo() override; Int_t GetServerIdx() const { return fServerIdx; } diff --git a/net/net/src/TMessage.cxx b/net/net/src/TMessage.cxx index de34e427a5a3d..d2a2a0c6c3833 100644 --- a/net/net/src/TMessage.cxx +++ b/net/net/src/TMessage.cxx @@ -42,7 +42,7 @@ Bool_t TMessage::fgEvolution = kFALSE; /// the message will be compressed in TSocket using the zip algorithm /// (only if message is > 256 bytes). -TMessage::TMessage(UInt_t what, Int_t bufsize) : +TMessage::TMessage(UInt_t what, Long64_t bufsize) : TBufferFile(TBuffer::kWrite, bufsize + 2*sizeof(UInt_t)), fCompress(ROOT::RCompressionSetting::EAlgorithm::kUseGlobal) { @@ -67,7 +67,7 @@ TMessage::TMessage(UInt_t what, Int_t bufsize) : /// Create a TMessage object for reading objects. The objects will be /// read from buf. Use the What() method to get the message type. -TMessage::TMessage(void *buf, Int_t bufsize, Bool_t adopt) : TBufferFile(TBuffer::kRead, bufsize, buf, adopt), +TMessage::TMessage(void *buf, Long64_t bufsize, Bool_t adopt) : TBufferFile(TBuffer::kRead, bufsize, buf, adopt), fCompress(ROOT::RCompressionSetting::EAlgorithm::kUseGlobal) { // skip space at the beginning of the message reserved for the message length diff --git a/net/net/src/TParallelMergingFile.cxx b/net/net/src/TParallelMergingFile.cxx index b33baadd9310a..25084e988e39f 100644 --- a/net/net/src/TParallelMergingFile.cxx +++ b/net/net/src/TParallelMergingFile.cxx @@ -177,7 +177,7 @@ Bool_t TParallelMergingFile::UploadAndReset() /// The linked list of FREE segments is written. /// The file header is written (bytes 1->fBEGIN). -Int_t TParallelMergingFile::Write(const char *, Int_t opt, Int_t bufsize) +Int_t TParallelMergingFile::Write(const char *, Int_t opt, Long64_t bufsize) { std::size_t prevSize = GetBytesWritten(); auto nbytes = TMemFile::Write(0,opt,bufsize); @@ -193,7 +193,7 @@ Int_t TParallelMergingFile::Write(const char *, Int_t opt, Int_t bufsize) //////////////////////////////////////////////////////////////////////////////// /// One can not save a const TDirectory object. -Int_t TParallelMergingFile::Write(const char *n, Int_t opt, Int_t bufsize) const +Int_t TParallelMergingFile::Write(const char *n, Int_t opt, Long64_t bufsize) const { Error("Write const","A const TFile object should not be saved. We try to proceed anyway."); return const_cast(this)->Write(n, opt, bufsize); diff --git a/roottest/root/meta/MemberComments.ref b/roottest/root/meta/MemberComments.ref index ac6b3bc4e4872..f4461b1e69630 100644 --- a/roottest/root/meta/MemberComments.ref +++ b/roottest/root/meta/MemberComments.ref @@ -537,9 +537,9 @@ OBJ: TList TList Doubly linked list : 0 OBJ: TMethod Warning : 0 void TObject::Warning(const char* method, const char* msgfmt,...) const OBJ: TMethod Write : 0 - Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Int_t bufsize = 0) + Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Long64_t bufsize = 0) OBJ: TMethod Write : 0 - Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Int_t bufsize = 0) const + Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Long64_t bufsize = 0) const OBJ: TMethod ls : 0 void TLine::ls(Option_t* option = "") const OBJ: TMethod ls : 0 diff --git a/roottest/root/meta/MemberComments_win32.ref b/roottest/root/meta/MemberComments_win32.ref index 67d1da4cedd81..ee99d1f414715 100644 --- a/roottest/root/meta/MemberComments_win32.ref +++ b/roottest/root/meta/MemberComments_win32.ref @@ -537,9 +537,9 @@ OBJ: TList TList Doubly linked list : 0 OBJ: TMethod Warning : 0 void TObject::Warning(const char* method, const char* msgfmt,...) const OBJ: TMethod Write : 0 - Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Int_t bufsize = 0) + Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Long64_t bufsize = 0) OBJ: TMethod Write : 0 - Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Int_t bufsize = 0) const + Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Long64_t bufsize = 0) const OBJ: TMethod ls : 0 void TLine::ls(Option_t* option = "") const OBJ: TMethod ls : 0 diff --git a/roottest/root/meta/MemberComments_win64.ref b/roottest/root/meta/MemberComments_win64.ref index 67d1da4cedd81..ee99d1f414715 100644 --- a/roottest/root/meta/MemberComments_win64.ref +++ b/roottest/root/meta/MemberComments_win64.ref @@ -537,9 +537,9 @@ OBJ: TList TList Doubly linked list : 0 OBJ: TMethod Warning : 0 void TObject::Warning(const char* method, const char* msgfmt,...) const OBJ: TMethod Write : 0 - Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Int_t bufsize = 0) + Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Long64_t bufsize = 0) OBJ: TMethod Write : 0 - Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Int_t bufsize = 0) const + Int_t TObject::Write(const char* name = nullptr, Int_t option = 0, Long64_t bufsize = 0) const OBJ: TMethod ls : 0 void TLine::ls(Option_t* option = "") const OBJ: TMethod ls : 0 diff --git a/tree/dataframe/src/RDFSnapshotHelpers.cxx b/tree/dataframe/src/RDFSnapshotHelpers.cxx index ce5c339a0511e..1b258383f41c1 100644 --- a/tree/dataframe/src/RDFSnapshotHelpers.cxx +++ b/tree/dataframe/src/RDFSnapshotHelpers.cxx @@ -196,7 +196,7 @@ void CreateFundamentalTypeBranch(TTree &outputTree, RBranchData &bd, void *value { // Logic taken from // TTree::BranchImpRef( - // const char* branchname, TClass* ptrClass, EDataType datatype, void* addobj, Int_t bufsize, Int_t splitlevel) + // const char *branchname, TClass *ptrClass, EDataType datatype, void *addobj, Long64_t bufsize, Int_t splitlevel) auto rootTypeChar = ROOT::Internal::RDF::TypeID2ROOTTypeName(*bd.fInputTypeID); if (rootTypeChar == ' ') { Warning("Snapshot", diff --git a/tree/ntuple/src/RFieldMeta.cxx b/tree/ntuple/src/RFieldMeta.cxx index 7d543175743ea..167a48d91cfab 100644 --- a/tree/ntuple/src/RFieldMeta.cxx +++ b/tree/ntuple/src/RFieldMeta.cxx @@ -1021,7 +1021,7 @@ class TBufferRecStreamer : public TBufferFile { RCallbackStreamerInfo fCallbackStreamerInfo; public: - TBufferRecStreamer(TBuffer::EMode mode, Int_t bufsize, RCallbackStreamerInfo callbackStreamerInfo) + TBufferRecStreamer(TBuffer::EMode mode, Long64_t bufsize, RCallbackStreamerInfo callbackStreamerInfo) : TBufferFile(mode, bufsize), fCallbackStreamerInfo(callbackStreamerInfo) { } diff --git a/tree/tree/inc/TBranch.h b/tree/tree/inc/TBranch.h index bf940f0e7ebf4..f91d1e8380495 100644 --- a/tree/tree/inc/TBranch.h +++ b/tree/tree/inc/TBranch.h @@ -200,8 +200,8 @@ class TBranch : public TNamed, public TAttFill { public: TBranch(); - TBranch(TTree *tree, const char *name, void *address, const char *leaflist, Int_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); - TBranch(TBranch *parent, const char *name, void *address, const char *leaflist, Int_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + TBranch(TTree *tree, const char *name, void *address, const char *leaflist, Long64_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + TBranch(TBranch *parent, const char *name, void *address, const char *leaflist, Long64_t basketsize=32000, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); ~TBranch() override; virtual void AddBasket(TBasket &b, bool ondisk, Long64_t startEntry); @@ -279,7 +279,7 @@ class TBranch : public TNamed, public TAttFill { virtual void SetAddress(void *add); virtual void SetObject(void *objadd); virtual void SetAutoDelete(bool autodel=true); - virtual void SetBasketSize(Int_t bufsize); + virtual void SetBasketSize(Long64_t bufsize); virtual void SetBufferAddress(TBuffer *entryBuffer); void SetCompressionAlgorithm(Int_t algorithm = ROOT::RCompressionSetting::EAlgorithm::kUseGlobal); void SetCompressionLevel(Int_t level = ROOT::RCompressionSetting::ELevel::kUseMin); diff --git a/tree/tree/inc/TBranchClones.h b/tree/tree/inc/TBranchClones.h index 6154609a91906..4f57f1587aa27 100644 --- a/tree/tree/inc/TBranchClones.h +++ b/tree/tree/inc/TBranchClones.h @@ -38,13 +38,13 @@ class TBranchClones : public TBranch { friend class TTreeCloner; - void Init(TTree *tree, TBranch *parent, const char *name, void *clonesaddress, Int_t basketsize=32000,Int_t compress=-1, Int_t splitlevel=1); + void Init(TTree *tree, TBranch *parent, const char *name, void *clonesaddress, Long64_t basketsize=32000,Int_t compress=-1, Int_t splitlevel=1); Int_t FillImpl(ROOT::Internal::TBranchIMTHelper *) override; public: TBranchClones(); - TBranchClones(TTree *tree, const char *name, void *clonesaddress, Int_t basketsize=32000,Int_t compress=-1, Int_t splitlevel=1); - TBranchClones(TBranch *parent, const char *name, void *clonesaddress, Int_t basketsize=32000,Int_t compress=-1, Int_t splitlevel=1); + TBranchClones(TTree *tree, const char *name, void *clonesaddress, Long64_t basketsize=32000,Int_t compress=-1, Int_t splitlevel=1); + TBranchClones(TBranch *parent, const char *name, void *clonesaddress, Long64_t basketsize=32000,Int_t compress=-1, Int_t splitlevel=1); ~TBranchClones() override; void Browse(TBrowser *b) override; @@ -57,7 +57,7 @@ class TBranchClones : public TBranch { void Reset(Option_t *option="") override; void ResetAfterMerge(TFileMergeInfo *) override; void SetAddress(void *add) override; - void SetBasketSize(Int_t bufsize) override; + void SetBasketSize(Long64_t bufsize) override; void SetTree(TTree *tree) override { fTree = tree; fBranchCount->SetTree(tree); } void UpdateFile() override; diff --git a/tree/tree/inc/TBranchElement.h b/tree/tree/inc/TBranchElement.h index 39c762041c414..4a451cc32bf53 100644 --- a/tree/tree/inc/TBranchElement.h +++ b/tree/tree/inc/TBranchElement.h @@ -122,12 +122,12 @@ class TBranchElement : public TBranch { void SetupInfo(); void SetBranchCount(TBranchElement* bre); void SetBranchCount2(TBranchElement* bre) { fBranchCount2 = bre; } - Int_t Unroll(const char* name, TClass* cltop, TClass* cl, char* ptr, Int_t basketsize, Int_t splitlevel, Int_t btype); + Int_t Unroll(const char* name, TClass* cltop, TClass* cl, char* ptr, Long64_t basketsize, Int_t splitlevel, Int_t btype); inline void ValidateAddress() const; - void Init(TTree *tree, TBranch *parent, const char* name, TStreamerInfo* sinfo, Int_t id, char* pointer, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t btype = 0); - void Init(TTree *tree, TBranch *parent, const char* name, TClonesArray* clones, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); - void Init(TTree *tree, TBranch *parent, const char* name, TVirtualCollectionProxy* cont, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + void Init(TTree *tree, TBranch *parent, const char* name, TStreamerInfo* sinfo, Int_t id, char* pointer, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t btype = 0); + void Init(TTree *tree, TBranch *parent, const char* name, TClonesArray* clones, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + void Init(TTree *tree, TBranch *parent, const char* name, TVirtualCollectionProxy* cont, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); void SetActionSequence(TClass *originalClass, TStreamerInfo *localInfo, TStreamerInfoActions::TActionSequence::SequenceGetter_t create, TStreamerInfoActions::TActionSequence *&actionSequence); void ReadLeavesImpl(TBuffer& b); @@ -166,12 +166,12 @@ class TBranchElement : public TBranch { // Public Interface. public: TBranchElement(); - TBranchElement(TTree *tree, const char* name, TStreamerInfo* sinfo, Int_t id, char* pointer, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t btype = 0); - TBranchElement(TTree *tree, const char* name, TClonesArray* clones, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); - TBranchElement(TTree *tree, const char* name, TVirtualCollectionProxy* cont, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); - TBranchElement(TBranch *parent, const char* name, TStreamerInfo* sinfo, Int_t id, char* pointer, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t btype = 0); - TBranchElement(TBranch *parent, const char* name, TClonesArray* clones, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); - TBranchElement(TBranch *parent, const char* name, TVirtualCollectionProxy* cont, Int_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + TBranchElement(TTree *tree, const char* name, TStreamerInfo* sinfo, Int_t id, char* pointer, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t btype = 0); + TBranchElement(TTree *tree, const char* name, TClonesArray* clones, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + TBranchElement(TTree *tree, const char* name, TVirtualCollectionProxy* cont, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + TBranchElement(TBranch *parent, const char* name, TStreamerInfo* sinfo, Int_t id, char* pointer, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t btype = 0); + TBranchElement(TBranch *parent, const char* name, TClonesArray* clones, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); + TBranchElement(TBranch *parent, const char* name, TVirtualCollectionProxy* cont, Long64_t basketsize = 32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit); ~TBranchElement() override; @@ -222,7 +222,7 @@ class TBranchElement : public TBranch { void SetAddress(void* addobj) override; bool SetMakeClass(bool decomposeObj = true) override; void SetObject(void *objadd) override; - void SetBasketSize(Int_t bufsize) override; + void SetBasketSize(Long64_t bufsize) override; virtual void SetBranchFolder() { SetBit(kBranchFolder); } virtual void SetClassName(const char* name) { fClassName = name; } void SetOffset(Int_t offset) override; @@ -233,7 +233,7 @@ class TBranchElement : public TBranch { void SetupAddresses() override; virtual void SetType(Int_t btype) { fType = btype; } void UpdateFile() override; - void Unroll(const char *name, TClass *cl, TStreamerInfo *sinfo, char* objptr, Int_t bufsize, Int_t splitlevel); + void Unroll(const char *name, TClass *cl, TStreamerInfo *sinfo, char* objptr, Long64_t bufsize, Int_t splitlevel); enum EBranchElementType { kLeafNode = 0, diff --git a/tree/tree/inc/TBranchObject.h b/tree/tree/inc/TBranchObject.h index 75dbbf96e5cf2..586b085490b23 100644 --- a/tree/tree/inc/TBranchObject.h +++ b/tree/tree/inc/TBranchObject.h @@ -42,12 +42,12 @@ class TBranchObject : public TBranch { TString fClassName; ///< Class name of referenced object TObject *fOldObject; ///< !Pointer to old object - void Init(TTree *tree, TBranch *parent, const char *name, const char *classname, void *addobj, Int_t basketsize, Int_t splitlevel, Int_t compress, bool isptrptr); + void Init(TTree *tree, TBranch *parent, const char *name, const char *classname, void *addobj, Long64_t basketsize, Int_t splitlevel, Int_t compress, bool isptrptr); public: TBranchObject(); - TBranchObject(TBranch *parent, const char *name, const char *classname, void *addobj, Int_t basketsize=32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit, bool isptrptr = true); - TBranchObject(TTree *tree, const char *name, const char *classname, void *addobj, Int_t basketsize=32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit, bool isptrptr = true); + TBranchObject(TBranch *parent, const char *name, const char *classname, void *addobj, Long64_t basketsize=32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit, bool isptrptr = true); + TBranchObject(TTree *tree, const char *name, const char *classname, void *addobj, Long64_t basketsize=32000, Int_t splitlevel = 0, Int_t compress = ROOT::RCompressionSetting::EAlgorithm::kInherit, bool isptrptr = true); ~TBranchObject() override; void Browse(TBrowser *b) override; @@ -61,7 +61,7 @@ class TBranchObject : public TBranch { void ResetAfterMerge(TFileMergeInfo *) override; void SetAddress(void *addobj) override; void SetAutoDelete(bool autodel=true) override; - void SetBasketSize(Int_t bufsize) override; + void SetBasketSize(Long64_t bufsize) override; void SetupAddresses() override; void UpdateAddress() override; diff --git a/tree/tree/inc/TBranchSTL.h b/tree/tree/inc/TBranchSTL.h index 9afff67341619..0ec1442edd6bc 100644 --- a/tree/tree/inc/TBranchSTL.h +++ b/tree/tree/inc/TBranchSTL.h @@ -24,10 +24,10 @@ class TBranchSTL: public TBranch { TBranchSTL(); TBranchSTL( TTree* tree, const char* name, TVirtualCollectionProxy* collProxy, - Int_t bufsize, Int_t splitlevel ); + Long64_t bufsize, Int_t splitlevel ); TBranchSTL( TBranch* parent, const char* name, TVirtualCollectionProxy* collProxy, - Int_t bufsize, Int_t splitlevel, + Long64_t bufsize, Int_t splitlevel, TStreamerInfo* info, Int_t id ); ~TBranchSTL() override; void Browse( TBrowser *b ) override; diff --git a/tree/tree/inc/TBufferSQL.h b/tree/tree/inc/TBufferSQL.h index 56d5e75208f2e..72908333e689c 100644 --- a/tree/tree/inc/TBufferSQL.h +++ b/tree/tree/inc/TBufferSQL.h @@ -43,8 +43,8 @@ class TBufferSQL final : public TBufferFile { public: TBufferSQL(); TBufferSQL(TBuffer::EMode mode, std::vector *vc, TString *insert_query, TSQLRow **rowPtr); - TBufferSQL(TBuffer::EMode mode, Int_t bufsiz, std::vector *vc, TString *insert_query, TSQLRow **rowPtr); - TBufferSQL(TBuffer::EMode mode, Int_t bufsiz, std::vector *vc, TString *insert_query, TSQLRow **rowPtr,void *buf, bool adopt = true); + TBufferSQL(TBuffer::EMode mode, Long64_t bufsiz, std::vector *vc, TString *insert_query, TSQLRow **rowPtr); + TBufferSQL(TBuffer::EMode mode, Long64_t bufsiz, std::vector *vc, TString *insert_query, TSQLRow **rowPtr,void *buf, bool adopt = true); ~TBufferSQL() override; void ResetOffset(); diff --git a/tree/tree/inc/TChain.h b/tree/tree/inc/TChain.h index d90971763f836..68a780e1afa95 100644 --- a/tree/tree/inc/TChain.h +++ b/tree/tree/inc/TChain.h @@ -133,7 +133,7 @@ class TChain : public TTree { virtual Long64_t Merge(const char *name, Option_t *option = ""); Long64_t Merge(TCollection *list, Option_t *option = "") override; Long64_t Merge(TCollection *list, TFileMergeInfo *info) override; - virtual Long64_t Merge(TFile *file, Int_t basketsize, Option_t *option=""); + virtual Long64_t Merge(TFile *file, Long64_t basketsize, Option_t *option=""); void Print(Option_t *option="") const override; Long64_t Process(const char *filename, Option_t *option="", Long64_t nentries=kMaxEntries, Long64_t firstentry=0) override; // *MENU* Long64_t Process(TSelector* selector, Option_t* option = "", Long64_t nentries = kMaxEntries, Long64_t firstentry = 0) override; diff --git a/tree/tree/inc/TNtuple.h b/tree/tree/inc/TNtuple.h index 3794097b6bb04..527eb4af63ffb 100644 --- a/tree/tree/inc/TNtuple.h +++ b/tree/tree/inc/TNtuple.h @@ -39,7 +39,7 @@ class TNtuple : public TTree { public: TNtuple(); - TNtuple(const char *name,const char *title, const char *varlist, Int_t bufsize=32000); + TNtuple(const char *name,const char *title, const char *varlist, Long64_t bufsize=32000); ~TNtuple() override; void Browse(TBrowser *b) override; diff --git a/tree/tree/inc/TNtupleD.h b/tree/tree/inc/TNtupleD.h index 63aded6eae49e..cac5381597e05 100644 --- a/tree/tree/inc/TNtupleD.h +++ b/tree/tree/inc/TNtupleD.h @@ -39,7 +39,7 @@ class TNtupleD : public TTree { public: TNtupleD(); - TNtupleD(const char *name,const char *title, const char *varlist, Int_t bufsize=32000); + TNtupleD(const char *name,const char *title, const char *varlist, Long64_t bufsize=32000); ~TNtupleD() override; void Browse(TBrowser *b) override; diff --git a/tree/tree/inc/TTree.h b/tree/tree/inc/TTree.h index 006b7b9fc7f3b..4250c4789da0b 100644 --- a/tree/tree/inc/TTree.h +++ b/tree/tree/inc/TTree.h @@ -81,8 +81,8 @@ namespace ROOT::Internal::TreeUtils { void TBranch__SetTree(TTree *tree, TObjArray &branches); TBranch *CallBranchImpRef(TTree &tree, const char *branchname, TClass *ptrClass, EDataType datatype, void *addobj, - Int_t bufsize = 32000, Int_t splitlevel = 99); -TBranch *CallBranchImp(TTree &tree, const char *branchname, TClass *ptrClass, void *addobj, Int_t bufsize = 32000, + Long64_t bufsize = 32000, Int_t splitlevel = 99); +TBranch *CallBranchImp(TTree &tree, const char *branchname, TClass *ptrClass, void *addobj, Long64_t bufsize = 32000, Int_t splitlevel = 99); } @@ -187,19 +187,19 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker protected: friend TBranch *ROOT::Internal::TreeUtils::CallBranchImpRef(TTree &tree, const char *branchname, TClass *ptrClass, - EDataType datatype, void *addobj, Int_t bufsize, + EDataType datatype, void *addobj, Long64_t bufsize, Int_t splitlevel); friend TBranch *ROOT::Internal::TreeUtils::CallBranchImp(TTree &tree, const char *branchname, TClass *ptrClass, - void *addobj, Int_t bufsize, Int_t splitlevel); + void *addobj, Long64_t bufsize, Int_t splitlevel); virtual void KeepCircular(); - virtual TBranch *BranchImp(const char* branchname, const char* classname, TClass* ptrClass, void* addobj, Int_t bufsize, Int_t splitlevel); - virtual TBranch *BranchImp(const char* branchname, TClass* ptrClass, void* addobj, Int_t bufsize, Int_t splitlevel); - virtual TBranch *BranchImpRef(const char* branchname, const char* classname, TClass* ptrClass, void* addobj, Int_t bufsize, Int_t splitlevel); - virtual TBranch *BranchImpRef(const char* branchname, TClass* ptrClass, EDataType datatype, void* addobj, Int_t bufsize, Int_t splitlevel); - virtual TBranch *BranchImpArr(const char* branchname, EDataType datatype, std::size_t N, void* addobj, Int_t bufsize, Int_t splitlevel); + virtual TBranch *BranchImp(const char* branchname, const char* classname, TClass* ptrClass, void* addobj, Long64_t bufsize, Int_t splitlevel); + virtual TBranch *BranchImp(const char* branchname, TClass* ptrClass, void* addobj, Long64_t bufsize, Int_t splitlevel); + virtual TBranch *BranchImpRef(const char* branchname, const char* classname, TClass* ptrClass, void* addobj, Long64_t bufsize, Int_t splitlevel); + virtual TBranch *BranchImpRef(const char* branchname, TClass* ptrClass, EDataType datatype, void* addobj, Long64_t bufsize, Int_t splitlevel); + virtual TBranch *BranchImpArr(const char* branchname, EDataType datatype, std::size_t N, void* addobj, Long64_t bufsize, Int_t splitlevel); virtual Int_t CheckBranchAddressType(TBranch* branch, TClass* ptrClass, EDataType datatype, bool ptr); - virtual TBranch *BronchExec(const char* name, const char* classname, void* addobj, bool isptrptr, Int_t bufsize, Int_t splitlevel); - friend TBranch *TTreeBranchImpRef(TTree *tree, const char* branchname, TClass* ptrClass, EDataType datatype, void* addobj, Int_t bufsize, Int_t splitlevel); + virtual TBranch *BronchExec(const char* name, const char* classname, void* addobj, bool isptrptr, Long64_t bufsize, Int_t splitlevel); + friend TBranch *TTreeBranchImpRef(TTree *tree, const char* branchname, TClass* ptrClass, EDataType datatype, void* addobj, Long64_t bufsize, Int_t splitlevel); Int_t SetBranchAddressImp(TBranch *branch, void* addr, TBranch** ptr); virtual TLeaf *GetLeafImpl(const char* branchname, const char* leafname); @@ -394,7 +394,7 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker /// A small value for bufsize is beneficial if entries in the Tree are accessed randomly and the Tree is in split mode. /// \param[in] splitlevel If T is a class or struct and splitlevel > 0, the members of the object are serialised as separate branches. /// \return Pointer to the TBranch that was created. The branch is owned by the tree. - template TBranch *Branch(const char* name, T* obj, Int_t bufsize = 32000, Int_t splitlevel = 99) + template TBranch *Branch(const char* name, T* obj, Long64_t bufsize = 32000, Int_t splitlevel = 99) { return BranchImpRef(name, TClass::GetClass(), TDataType::GetType(typeid(T)), obj, bufsize, splitlevel); } @@ -411,42 +411,42 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker /// A small value for bufsize is beneficial if entries in the Tree are accessed randomly and the Tree is in split mode. /// \param[in] splitlevel If T is a class or struct and splitlevel > 0, the members of the object are serialised as separate branches. /// \return Pointer to the TBranch that was created. The branch is owned by the tree. - template TBranch *Branch(const char* name, T** addobj, Int_t bufsize = 32000, Int_t splitlevel = 99) + template TBranch *Branch(const char* name, T** addobj, Long64_t bufsize = 32000, Int_t splitlevel = 99) { return BranchImp(name, TClass::GetClass(), addobj, bufsize, splitlevel); } - virtual Int_t Branch(TCollection* list, Int_t bufsize = 32000, Int_t splitlevel = 99, const char* name = ""); - virtual Int_t Branch(TList* list, Int_t bufsize = 32000, Int_t splitlevel = 99); - virtual Int_t Branch(const char* folder, Int_t bufsize = 32000, Int_t splitlevel = 99); - virtual TBranch *Branch(const char* name, void* address, const char* leaflist, Int_t bufsize = 32000); - TBranch *Branch(const char* name, char* address, const char* leaflist, Int_t bufsize = 32000) + virtual Int_t Branch(TCollection* list, Long64_t bufsize = 32000, Int_t splitlevel = 99, const char* name = ""); + virtual Int_t Branch(TList* list, Long64_t bufsize = 32000, Int_t splitlevel = 99); + virtual Int_t Branch(const char* folder, Long64_t bufsize = 32000, Int_t splitlevel = 99); + virtual TBranch *Branch(const char* name, void* address, const char* leaflist, Long64_t bufsize = 32000); + TBranch *Branch(const char* name, char* address, const char* leaflist, Long64_t bufsize = 32000) { // Overload to avoid confusion between this signature and the template instance. return Branch(name,(void*)address,leaflist,bufsize); } - TBranch *Branch(const char* name, Longptr_t address, const char* leaflist, Int_t bufsize = 32000) + TBranch *Branch(const char* name, Longptr_t address, const char* leaflist, Long64_t bufsize = 32000) { // Overload to avoid confusion between this signature and the template instance. return Branch(name,(void*)address,leaflist,bufsize); } - TBranch *Branch(const char* name, int address, const char* leaflist, Int_t bufsize = 32000) + TBranch *Branch(const char* name, int address, const char* leaflist, Long64_t bufsize = 32000) { // Overload to avoid confusion between this signature and the template instance. return Branch(name,(void*)(Longptr_t)address,leaflist,bufsize); } - virtual TBranch *Branch(const char* name, const char* classname, void* addobj, Int_t bufsize = 32000, Int_t splitlevel = 99); - template TBranch *Branch(const char* name, const char* classname, T* obj, Int_t bufsize = 32000, Int_t splitlevel = 99) + virtual TBranch *Branch(const char* name, const char* classname, void* addobj, Long64_t bufsize = 32000, Int_t splitlevel = 99); + template TBranch *Branch(const char* name, const char* classname, T* obj, Long64_t bufsize = 32000, Int_t splitlevel = 99) { // See BranchImpRed for details. Here we __ignore return BranchImpRef(name, classname, TClass::GetClass(), obj, bufsize, splitlevel); } - template TBranch *Branch(const char* name, const char* classname, T** addobj, Int_t bufsize = 32000, Int_t splitlevel = 99) + template TBranch *Branch(const char* name, const char* classname, T** addobj, Long64_t bufsize = 32000, Int_t splitlevel = 99) { // See BranchImp for details return BranchImp(name, classname, TClass::GetClass(), addobj, bufsize, splitlevel); } - template TBranch *Branch(const char* name, std::array *obj, Int_t bufsize = 32000, Int_t splitlevel = 99) + template TBranch *Branch(const char* name, std::array *obj, Long64_t bufsize = 32000, Int_t splitlevel = 99) { TClass *cl = TClass::GetClass(); if (cl) { @@ -457,8 +457,8 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker } return BranchImpArr(name, TDataType::GetType(typeid(T)), N, obj, bufsize, splitlevel); } - virtual TBranch *Bronch(const char* name, const char* classname, void* addobj, Int_t bufsize = 32000, Int_t splitlevel = 99); - virtual TBranch *BranchOld(const char* name, const char* classname, void* addobj, Int_t bufsize = 32000, Int_t splitlevel = 1); + virtual TBranch *Bronch(const char* name, const char* classname, void* addobj, Long64_t bufsize = 32000, Int_t splitlevel = 99); + virtual TBranch *BranchOld(const char* name, const char* classname, void* addobj, Long64_t bufsize = 32000, Int_t splitlevel = 1); virtual TBranch *BranchRef(); void Browse(TBrowser*) override; virtual Int_t BuildIndex(const char *majorname, const char *minorname = "0", bool long64major = false, bool long64minor = false); @@ -668,7 +668,7 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker virtual bool SetAlias(const char* aliasName, const char* aliasFormula); virtual void SetAutoSave(Long64_t autos = -300000000); virtual void SetAutoFlush(Long64_t autof = -30000000); - virtual void SetBasketSize(const char* bname, Int_t buffsize = 16000); + virtual void SetBasketSize(const char* bname, Long64_t bufsize = 16000); virtual Int_t SetBranchAddress(const char *bname,void *add, TBranch **ptr = nullptr); virtual Int_t SetBranchAddress(const char *bname,void *add, TClass *realClass, EDataType datatype, bool isptr); virtual Int_t SetBranchAddress(const char *bname,void *add, TBranch **ptr, TClass *realClass, EDataType datatype, bool isptr); @@ -751,8 +751,8 @@ class TTree : public TNamed, public TAttLine, public TAttFill, public TAttMarker virtual Int_t StopCacheLearningPhase(); virtual Int_t UnbinnedFit(const char* funcname, const char* varexp, const char* selection = "", Option_t* option = "", Long64_t nentries = kMaxEntries, Long64_t firstentry = 0); void UseCurrentStyle() override; - Int_t Write(const char *name=nullptr, Int_t option=0, Int_t bufsize=0) override; - Int_t Write(const char *name=nullptr, Int_t option=0, Int_t bufsize=0) const override; + Int_t Write(const char *name=nullptr, Int_t option=0, Long64_t bufsize=0) override; + Int_t Write(const char *name=nullptr, Int_t option=0, Long64_t bufsize=0) const override; ClassDefOverride(TTree, 20) // Tree descriptor (the main ROOT I/O class) }; diff --git a/tree/tree/inc/TTreeCache.h b/tree/tree/inc/TTreeCache.h index 14a30d52cf459..adf2fbf98e222 100644 --- a/tree/tree/inc/TTreeCache.h +++ b/tree/tree/inc/TTreeCache.h @@ -127,7 +127,7 @@ class TTreeCache : public TFileCacheRead { public: TTreeCache(); - TTreeCache(TTree *tree, Int_t bufsize=0); + TTreeCache(TTree *tree, Long64_t bufsize=0); ~TTreeCache() override; Int_t AddBranch(TBranch *b, bool subgbranches = false) override; Int_t AddBranch(const char *branch, bool subbranches = false) override; @@ -162,7 +162,7 @@ class TTreeCache : public TFileCacheRead { virtual void ResetCache(); void ResetMissCache(); // Reset the miss cache. void SetAutoCreated(bool val) {fAutoCreated = val;} - Int_t SetBufferSize(Long64_t buffersize) override; + Int_t SetBufferSize(Long64_t bufsize) override; virtual void SetEntryRange(Long64_t emin, Long64_t emax); void SetFile(TFile *file, TFile::ECacheAction action=TFile::kDisconnect) override; virtual void SetLearnPrefill(EPrefillType type = kNoPrefill); diff --git a/tree/tree/inc/TTreeCacheUnzip.h b/tree/tree/inc/TTreeCacheUnzip.h index 28177e2ca61fb..71553d86be09d 100644 --- a/tree/tree/inc/TTreeCacheUnzip.h +++ b/tree/tree/inc/TTreeCacheUnzip.h @@ -117,7 +117,7 @@ class TTreeCacheUnzip : public TTreeCache { public: TTreeCacheUnzip(); - TTreeCacheUnzip(TTree *tree, Int_t buffersize=0); + TTreeCacheUnzip(TTree *tree, Long64_t buffersize=0); ~TTreeCacheUnzip() override; Int_t AddBranch(TBranch *b, bool subbranches = false) override; diff --git a/tree/tree/inc/TTreeSQL.h b/tree/tree/inc/TTreeSQL.h index 5fedf6d5d64bb..b51051b1bf63b 100644 --- a/tree/tree/inc/TTreeSQL.h +++ b/tree/tree/inc/TTreeSQL.h @@ -65,21 +65,21 @@ class TTreeSQL : public TTree { bool CreateTable(const TString& table); TBasket *CreateBasket(TBranch * br) override; - TBranch *BranchImp(const char *branchname, const char *classname, TClass *ptrClass, void *addobj, Int_t bufsize, Int_t splitlevel) override; - TBranch *BranchImp(const char *branchname, TClass *ptrClass, void *addobj, Int_t bufsize, Int_t splitlevel) override; + TBranch *BranchImp(const char *branchname, const char *classname, TClass *ptrClass, void *addobj, Long64_t bufsize, Int_t splitlevel) override; + TBranch *BranchImp(const char *branchname, TClass *ptrClass, void *addobj, Long64_t bufsize, Int_t splitlevel) override; public: TTreeSQL(TSQLServer * server, TString DB, const TString& table); ~TTreeSQL() override; - Int_t Branch(TCollection *list, Int_t bufsize=32000, Int_t splitlevel=99, const char *name="") override; - Int_t Branch(TList *list, Int_t bufsize=32000, Int_t splitlevel=99) override; - Int_t Branch(const char *folder, Int_t bufsize=32000, Int_t splitlevel=99) override; - TBranch *Bronch(const char *name, const char *classname, void *addobj, Int_t bufsize=32000, Int_t splitlevel=99) override; - TBranch *BranchOld(const char *name, const char *classname, void *addobj, Int_t bufsize=32000, Int_t splitlevel=1) override; - TBranch *Branch(const char *name, const char *classname, void *addobj, Int_t bufsize=32000, Int_t splitlevel=99) override; + Int_t Branch(TCollection *list, Long64_t bufsize=32000, Int_t splitlevel=99, const char *name="") override; + Int_t Branch(TList *list, Long64_t bufsize=32000, Int_t splitlevel=99) override; + Int_t Branch(const char *folder, Long64_t bufsize=32000, Int_t splitlevel=99) override; + TBranch *Bronch(const char *name, const char *classname, void *addobj, Long64_t bufsize=32000, Int_t splitlevel=99) override; + TBranch *BranchOld(const char *name, const char *classname, void *addobj, Long64_t bufsize=32000, Int_t splitlevel=1) override; + TBranch *Branch(const char *name, const char *classname, void *addobj, Long64_t bufsize=32000, Int_t splitlevel=99) override; - TBranch *Branch(const char *name, void *address, const char *leaflist, Int_t bufsize) override; + TBranch *Branch(const char *name, void *address, const char *leaflist, Long64_t bufsize) override; Int_t Fill() override; Int_t GetEntry(Long64_t entry=0, Int_t getall=0) override; diff --git a/tree/tree/src/TBranch.cxx b/tree/tree/src/TBranch.cxx index a44cf713d1916..6b9d1102625a9 100644 --- a/tree/tree/src/TBranch.cxx +++ b/tree/tree/src/TBranch.cxx @@ -195,7 +195,7 @@ TBranch::TBranch() /// /// Note that this function is invoked by TTree::Branch -TBranch::TBranch(TTree *tree, const char *name, void *address, const char *leaflist, Int_t basketsize, Int_t compress) +TBranch::TBranch(TTree *tree, const char *name, void *address, const char *leaflist, Long64_t basketsize, Int_t compress) : TNamed(name, leaflist) , TAttFill(0, 1001) , fCompress(compress) @@ -239,6 +239,8 @@ TBranch::TBranch(TTree *tree, const char *name, void *address, const char *leafl , fReadLeaves(&TBranch::ReadLeavesImpl) , fFillLeaves(&TBranch::FillLeavesImpl) { + if (basketsize > kMaxInt) + Fatal("TBranch", "Integer overflow in basket size: 0x%llx for a max of 0x%x.", basketsize, kMaxInt); Init(name,leaflist,compress); } @@ -248,7 +250,7 @@ TBranch::TBranch(TTree *tree, const char *name, void *address, const char *leafl /// See documentation for /// TBranch::TBranch(TTree *, const char *, void *, const char *, Int_t, Int_t) -TBranch::TBranch(TBranch *parent, const char *name, void *address, const char *leaflist, Int_t basketsize, +TBranch::TBranch(TBranch *parent, const char *name, void *address, const char *leaflist, Long64_t basketsize, Int_t compress) : TNamed(name, leaflist) , TAttFill(0, 1001) @@ -293,6 +295,8 @@ TBranch::TBranch(TBranch *parent, const char *name, void *address, const char *l , fReadLeaves(&TBranch::ReadLeavesImpl) , fFillLeaves(&TBranch::FillLeavesImpl) { + if (basketsize > kMaxInt) + Fatal("TBranch", "Integer overflow in basket size: 0x%llx for a max of 0x%x.", basketsize, kMaxInt); Init(name,leaflist,compress); } @@ -2738,8 +2742,10 @@ void TBranch::SetAutoDelete(bool autodel) /// Set the basket size /// The function makes sure that the basket size is greater than fEntryOffsetlen -void TBranch::SetBasketSize(Int_t bufsize) +void TBranch::SetBasketSize(Long64_t bufsize) { + if (bufsize > kMaxInt) + Fatal("SetBasketSize", "Integer overflow in basket size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); Int_t minsize = 100 + fName.Length(); if (bufsize < minsize+fEntryOffsetLen) bufsize = minsize+fEntryOffsetLen; fBasketSize = bufsize; diff --git a/tree/tree/src/TBranchClones.cxx b/tree/tree/src/TBranchClones.cxx index 13c51dbc03278..7909c8cfd75ce 100644 --- a/tree/tree/src/TBranchClones.cxx +++ b/tree/tree/src/TBranchClones.cxx @@ -49,7 +49,7 @@ TBranchClones::TBranchClones() //////////////////////////////////////////////////////////////////////////////// /// Constructor. -TBranchClones::TBranchClones(TTree *tree, const char* name, void* pointer, Int_t basketsize, Int_t compress, Int_t splitlevel) +TBranchClones::TBranchClones(TTree *tree, const char* name, void* pointer, Long64_t basketsize, Int_t compress, Int_t splitlevel) : TBranch() , fList(nullptr) , fRead(0) @@ -63,7 +63,7 @@ TBranchClones::TBranchClones(TTree *tree, const char* name, void* pointer, Int_t //////////////////////////////////////////////////////////////////////////////// /// Constructor. -TBranchClones::TBranchClones(TBranch *parent, const char* name, void* pointer, Int_t basketsize, Int_t compress, Int_t splitlevel) +TBranchClones::TBranchClones(TBranch *parent, const char* name, void* pointer, Long64_t basketsize, Int_t compress, Int_t splitlevel) : TBranch() , fList(nullptr) , fRead(0) @@ -77,7 +77,7 @@ TBranchClones::TBranchClones(TBranch *parent, const char* name, void* pointer, I //////////////////////////////////////////////////////////////////////////////// /// Initialization (non-virtual, to be called from constructor). -void TBranchClones::Init(TTree *tree, TBranch *parent, const char* name, void* pointer, Int_t basketsize, Int_t compress, Int_t splitlevel) +void TBranchClones::Init(TTree *tree, TBranch *parent, const char* name, void* pointer, Long64_t basketsize, Int_t compress, Int_t splitlevel) { if (tree==nullptr && parent!=nullptr) tree = parent->GetTree(); fTree = tree; @@ -110,6 +110,8 @@ void TBranchClones::Init(TTree *tree, TBranch *parent, const char* name, void* p fSplitLevel = splitlevel; // Create a branch to store the array count. + if (basketsize > kMaxInt) + Fatal("Init", "Integer overflow in basket size: 0x%llx for a max of 0x%x.", basketsize, kMaxInt); if (basketsize < 100) { basketsize = 100; } @@ -369,7 +371,7 @@ void TBranchClones::SetAddress(void* addr) //////////////////////////////////////////////////////////////////////////////// /// Reset basket size for all sub-branches. -void TBranchClones::SetBasketSize(Int_t bufsize) +void TBranchClones::SetBasketSize(Long64_t bufsize) { TBranch::SetBasketSize(bufsize); diff --git a/tree/tree/src/TBranchElement.cxx b/tree/tree/src/TBranchElement.cxx index a8227be2e1bac..175035d6ef8e9 100644 --- a/tree/tree/src/TBranchElement.cxx +++ b/tree/tree/src/TBranchElement.cxx @@ -209,7 +209,7 @@ TBranchElement::TBranchElement() /// /// If splitlevel > 0 this branch in turn is split into sub-branches. -TBranchElement::TBranchElement(TTree *tree, const char* bname, TStreamerInfo* sinfo, Int_t id, char* pointer, Int_t basketsize, Int_t splitlevel, Int_t btype) +TBranchElement::TBranchElement(TTree *tree, const char* bname, TStreamerInfo* sinfo, Int_t id, char* pointer, Long64_t basketsize, Int_t splitlevel, Int_t btype) : TBranch() , fClassName(sinfo->GetName()) , fParentName() @@ -256,7 +256,7 @@ TBranchElement::TBranchElement(TTree *tree, const char* bname, TStreamerInfo* si /// /// If splitlevel > 0 this branch in turn is split into sub-branches. -TBranchElement::TBranchElement(TBranch *parent, const char* bname, TStreamerInfo* sinfo, Int_t id, char* pointer, Int_t basketsize, Int_t splitlevel, Int_t btype) +TBranchElement::TBranchElement(TBranch *parent, const char* bname, TStreamerInfo* sinfo, Int_t id, char* pointer, Long64_t basketsize, Int_t splitlevel, Int_t btype) : TBranch() , fClassName(sinfo->GetName()) , fParentName() @@ -301,7 +301,7 @@ TBranchElement::TBranchElement(TBranch *parent, const char* bname, TStreamerInfo /// /// If splitlevel > 0 this branch in turn is split into sub-branches. -void TBranchElement::Init(TTree *tree, TBranch *parent,const char* bname, TStreamerInfo* sinfo, Int_t id, char* pointer, Int_t basketsize, Int_t splitlevel, Int_t btype) +void TBranchElement::Init(TTree *tree, TBranch *parent,const char* bname, TStreamerInfo* sinfo, Int_t id, char* pointer, Long64_t basketsize, Int_t splitlevel, Int_t btype) { TString name(bname); @@ -367,7 +367,8 @@ void TBranchElement::Init(TTree *tree, TBranch *parent,const char* bname, TStrea // Make sure the basket is big enough to contain the // entry offset array plus 100 bytes of data. // - + if (basketsize > kMaxInt) + Fatal("Init", "Integer overflow in basket size: 0x%llx for a max of 0x%x.", basketsize, kMaxInt); if (basketsize < (100 + fEntryOffsetLen)) { basketsize = 100 + fEntryOffsetLen; } @@ -693,7 +694,7 @@ void TBranchElement::Init(TTree *tree, TBranch *parent,const char* bname, TStrea /// /// If splitlevel > 0 this branch in turn is split into sub branches. -TBranchElement::TBranchElement(TTree *tree, const char* bname, TClonesArray* clones, Int_t basketsize, Int_t splitlevel, Int_t compress) +TBranchElement::TBranchElement(TTree *tree, const char* bname, TClonesArray* clones, Long64_t basketsize, Int_t splitlevel, Int_t compress) : TBranch() , fClassName("TClonesArray") , fParentName() @@ -720,7 +721,7 @@ TBranchElement::TBranchElement(TTree *tree, const char* bname, TClonesArray* clo /// /// If splitlevel > 0 this branch in turn is split into sub branches. -TBranchElement::TBranchElement(TBranch *parent, const char* bname, TClonesArray* clones, Int_t basketsize, Int_t splitlevel, Int_t compress) +TBranchElement::TBranchElement(TBranch *parent, const char* bname, TClonesArray* clones, Long64_t basketsize, Int_t splitlevel, Int_t compress) : TBranch() , fClassName("TClonesArray") , fParentName() @@ -747,7 +748,7 @@ TBranchElement::TBranchElement(TBranch *parent, const char* bname, TClonesArray* /// /// If splitlevel > 0 this branch in turn is split into sub branches. -void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TClonesArray* clones, Int_t basketsize, Int_t splitlevel, Int_t compress) +void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TClonesArray* clones, Long64_t basketsize, Int_t splitlevel, Int_t compress) { fCollProxy = nullptr; fSplitLevel = splitlevel; @@ -848,7 +849,7 @@ void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TClon /// /// If splitlevel > 0 this branch in turn is split into sub branches. -TBranchElement::TBranchElement(TTree *tree, const char* bname, TVirtualCollectionProxy* cont, Int_t basketsize, Int_t splitlevel, Int_t compress) +TBranchElement::TBranchElement(TTree *tree, const char* bname, TVirtualCollectionProxy* cont, Long64_t basketsize, Int_t splitlevel, Int_t compress) : TBranch() , fClassName(cont->GetCollectionClass()->GetName()) , fParentName() @@ -874,7 +875,7 @@ TBranchElement::TBranchElement(TTree *tree, const char* bname, TVirtualCollectio /// /// If splitlevel > 0 this branch in turn is split into sub branches. -TBranchElement::TBranchElement(TBranch *parent, const char* bname, TVirtualCollectionProxy* cont, Int_t basketsize, Int_t splitlevel, Int_t compress) +TBranchElement::TBranchElement(TBranch *parent, const char* bname, TVirtualCollectionProxy* cont, Long64_t basketsize, Int_t splitlevel, Int_t compress) : TBranch() , fClassName(cont->GetCollectionClass()->GetName()) , fParentName() @@ -900,7 +901,7 @@ TBranchElement::TBranchElement(TBranch *parent, const char* bname, TVirtualColle /// /// If splitlevel > 0 this branch in turn is split into sub branches. -void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TVirtualCollectionProxy* cont, Int_t basketsize, Int_t splitlevel, Int_t compress) +void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TVirtualCollectionProxy* cont, Long64_t basketsize, Int_t splitlevel, Int_t compress) { fCollProxy = cont->Generate(); TString name( bname ); @@ -945,7 +946,8 @@ void TBranchElement::Init(TTree *tree, TBranch *parent, const char* bname, TVirt fCompress = bfile->GetCompressionSettings(); } } - + if (basketsize > kMaxInt) + Fatal("Init", "Integer overflow in basket size: 0x%llx for a max of 0x%x.", basketsize, kMaxInt); if (basketsize < 100) { basketsize = 100; } @@ -5574,7 +5576,7 @@ void TBranchElement::SetAddressImpl(void* addr, bool implied, Int_t offset) //////////////////////////////////////////////////////////////////////////////// /// Reset the basket size for all sub-branches of this branch element. -void TBranchElement::SetBasketSize(Int_t bufsize) +void TBranchElement::SetBasketSize(Long64_t bufsize) { TBranch::SetBasketSize(bufsize); Int_t nbranches = fBranches.GetEntriesFast(); @@ -6136,7 +6138,7 @@ void TBranchElement::Streamer(TBuffer& R__b) /// This version of Unroll was formerly embedded in TTree::BronchExec /// It is moved here so we can make sure to call SetReadActionSequence. -void TBranchElement::Unroll(const char *name, TClass *cl, TStreamerInfo *sinfo, char* objptr, Int_t bufsize, Int_t splitlevel) +void TBranchElement::Unroll(const char *name, TClass *cl, TStreamerInfo *sinfo, char* objptr, Long64_t bufsize, Int_t splitlevel) { // // Do we have a final dot in our name? @@ -6270,7 +6272,7 @@ void TBranchElement::Unroll(const char *name, TClass *cl, TStreamerInfo *sinfo, /// except for a TObject base class of a class which has the /// can ignore tobject streamer flag set. -Int_t TBranchElement::Unroll(const char* name, TClass* clParent, TClass* cl, char* ptr, Int_t basketsize, Int_t splitlevel, Int_t btype) +Int_t TBranchElement::Unroll(const char* name, TClass* clParent, TClass* cl, char* ptr, Long64_t basketsize, Int_t splitlevel, Int_t btype) { //---------------------------------------------------------------------------- // Handling the case of STL collections of pointers diff --git a/tree/tree/src/TBranchObject.cxx b/tree/tree/src/TBranchObject.cxx index 7f77edee333ee..5f955502ed22c 100644 --- a/tree/tree/src/TBranchObject.cxx +++ b/tree/tree/src/TBranchObject.cxx @@ -46,7 +46,7 @@ TBranchObject::TBranchObject() //////////////////////////////////////////////////////////////////////////////// /// Create a BranchObject. -TBranchObject::TBranchObject(TTree *tree, const char* name, const char* classname, void* addobj, Int_t basketsize, Int_t splitlevel, Int_t compress, bool isptrptr /* = true */) +TBranchObject::TBranchObject(TTree *tree, const char* name, const char* classname, void* addobj, Long64_t basketsize, Int_t splitlevel, Int_t compress, bool isptrptr /* = true */) : TBranch() { Init(tree,nullptr,name,classname,addobj,basketsize,splitlevel,compress,isptrptr); @@ -55,7 +55,7 @@ TBranchObject::TBranchObject(TTree *tree, const char* name, const char* classnam //////////////////////////////////////////////////////////////////////////////// /// Create a BranchObject. -TBranchObject::TBranchObject(TBranch *parent, const char* name, const char* classname, void* addobj, Int_t basketsize, Int_t splitlevel, Int_t compress, bool isptrptr /* = true */) +TBranchObject::TBranchObject(TBranch *parent, const char* name, const char* classname, void* addobj, Long64_t basketsize, Int_t splitlevel, Int_t compress, bool isptrptr /* = true */) : TBranch() { Init(nullptr,parent,name,classname,addobj,basketsize,splitlevel,compress,isptrptr); @@ -64,7 +64,7 @@ TBranchObject::TBranchObject(TBranch *parent, const char* name, const char* clas //////////////////////////////////////////////////////////////////////////////// /// Initialization routine (run from the constructor so do not make this function virtual) -void TBranchObject::Init(TTree *tree, TBranch *parent, const char* name, const char* classname, void* addobj, Int_t basketsize, Int_t /*splitlevel*/, Int_t compress, bool isptrptr) +void TBranchObject::Init(TTree *tree, TBranch *parent, const char* name, const char* classname, void* addobj, Long64_t basketsize, Int_t /*splitlevel*/, Int_t compress, bool isptrptr) { if (tree==nullptr && parent!=nullptr) tree = parent->GetTree(); fTree = tree; @@ -113,6 +113,8 @@ void TBranchObject::Init(TTree *tree, TBranch *parent, const char* name, const c if (basketsize < 100) { basketsize = 100; } + if (basketsize > kMaxInt) + Fatal("Init", "Integer overflow in basket size: 0x%llx for a max of 0x%x.", basketsize, kMaxInt); fBasketSize = basketsize; fAddress = (char*) addobj; fClassName = classname; @@ -527,7 +529,7 @@ void TBranchObject::SetAutoDelete(bool autodel) //////////////////////////////////////////////////////////////////////////////// /// Reset basket size for all subbranches of this branch. -void TBranchObject::SetBasketSize(Int_t bufsize) +void TBranchObject::SetBasketSize(Long64_t bufsize) { TBranch::SetBasketSize(bufsize); diff --git a/tree/tree/src/TBranchSTL.cxx b/tree/tree/src/TBranchSTL.cxx index 7e69b3ba0422b..3703d5d9df421 100644 --- a/tree/tree/src/TBranchSTL.cxx +++ b/tree/tree/src/TBranchSTL.cxx @@ -46,10 +46,12 @@ TBranchSTL::TBranchSTL(): TBranchSTL::TBranchSTL(TTree *tree, const char *name, TVirtualCollectionProxy *collProxy, - Int_t bufsize, Int_t splitlevel ) + Long64_t bufsize, Int_t splitlevel ) { fTree = tree; fCollProxy = collProxy; + if (bufsize > kMaxInt) + Fatal("TBranchSTL", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); fBasketSize = bufsize; fSplitLevel = splitlevel; fContName = collProxy->GetCollectionClass()->GetName(); @@ -88,11 +90,13 @@ TBranchSTL::TBranchSTL(TTree *tree, const char *name, TBranchSTL::TBranchSTL(TBranch* parent, const char* name, TVirtualCollectionProxy* collProxy, - Int_t bufsize, Int_t splitlevel, + Long64_t bufsize, Int_t splitlevel, TStreamerInfo* info, Int_t id ) { fTree = parent->GetTree(); fCollProxy = collProxy; + if (bufsize > kMaxInt) + Fatal("TBranchSTL", "Integer overflow in buffer size: 0x%llx for a max of 0x%x.", bufsize, kMaxInt); fBasketSize = bufsize; fSplitLevel = splitlevel; fContName = collProxy->GetCollectionClass()->GetName(); diff --git a/tree/tree/src/TBufferSQL.cxx b/tree/tree/src/TBufferSQL.cxx index 2e93a05253040..ba4d09306ef5c 100644 --- a/tree/tree/src/TBufferSQL.cxx +++ b/tree/tree/src/TBufferSQL.cxx @@ -41,7 +41,7 @@ TBufferSQL::TBufferSQL(TBuffer::EMode mode, std::vector *vc, //////////////////////////////////////////////////////////////////////////////// /// Constructor. -TBufferSQL::TBufferSQL(TBuffer::EMode mode, Int_t bufsize, std::vector *vc, +TBufferSQL::TBufferSQL(TBuffer::EMode mode, Long64_t bufsize, std::vector *vc, TString *insert_query, TSQLRow ** r) : TBufferFile(mode,bufsize), fColumnVec(vc), fInsertQuery(insert_query), fRowPtr(r) @@ -52,7 +52,7 @@ TBufferSQL::TBufferSQL(TBuffer::EMode mode, Int_t bufsize, std::vector *v //////////////////////////////////////////////////////////////////////////////// /// Constructor. -TBufferSQL::TBufferSQL(TBuffer::EMode mode, Int_t bufsize, std::vector *vc, +TBufferSQL::TBufferSQL(TBuffer::EMode mode, Long64_t bufsize, std::vector *vc, TString *insert_query, TSQLRow ** r, void *buf, bool adopt) : TBufferFile(mode,bufsize,buf,adopt), diff --git a/tree/tree/src/TChain.cxx b/tree/tree/src/TChain.cxx index 4d04087428db4..a0a40ea75d0b4 100644 --- a/tree/tree/src/TChain.cxx +++ b/tree/tree/src/TChain.cxx @@ -2034,7 +2034,7 @@ Long64_t TChain::Merge(TCollection* /* list */, TFileMergeInfo *) /// } /// ~~~ -Long64_t TChain::Merge(TFile* file, Int_t basketsize, Option_t* option) +Long64_t TChain::Merge(TFile* file, Long64_t basketsize, Option_t* option) { // We must have been passed a file, we will use it // later to reset the compression level of the branches. diff --git a/tree/tree/src/TNtuple.cxx b/tree/tree/src/TNtuple.cxx index 69dbc12efd682..2ff552bbcd8ba 100644 --- a/tree/tree/src/TNtuple.cxx +++ b/tree/tree/src/TNtuple.cxx @@ -62,7 +62,7 @@ TNtuple::TNtuple(): TTree() /// - Use TTree to create branches with variables of different data types. /// - Use TTree when the number of branches is large (> 100). -TNtuple::TNtuple(const char *name, const char *title, const char *varlist, Int_t bufsize) +TNtuple::TNtuple(const char *name, const char *title, const char *varlist, Long64_t bufsize) :TTree(name,title) { Int_t i; diff --git a/tree/tree/src/TNtupleD.cxx b/tree/tree/src/TNtupleD.cxx index 1aa334611895c..b4ec305581499 100644 --- a/tree/tree/src/TNtupleD.cxx +++ b/tree/tree/src/TNtupleD.cxx @@ -61,7 +61,7 @@ TNtupleD::TNtupleD(): TTree() /// - Use TTree to create branches with variables of different data types. /// - Use TTree when the number of branches is large (> 100). -TNtupleD::TNtupleD(const char *name, const char *title, const char *varlist, Int_t bufsize) +TNtupleD::TNtupleD(const char *name, const char *title, const char *varlist, Long64_t bufsize) :TTree(name,title) { Int_t i; diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx index 26ab8bdb4f33d..0893cc95f2080 100644 --- a/tree/tree/src/TTree.cxx +++ b/tree/tree/src/TTree.cxx @@ -1579,7 +1579,7 @@ namespace { /// \see TTree::Branch() /// -TBranch* TTree::BranchImp(const char* branchname, const char* classname, TClass* ptrClass, void* addobj, Int_t bufsize, Int_t splitlevel) +TBranch* TTree::BranchImp(const char* branchname, const char* classname, TClass* ptrClass, void* addobj, Long64_t bufsize, Int_t splitlevel) { TClass* claim = TClass::GetClass(classname); if (!ptrClass) { @@ -1627,7 +1627,7 @@ TBranch* TTree::BranchImp(const char* branchname, const char* classname, TClass* /// Same as TTree::Branch but automatic detection of the class name. /// \see TTree::Branch -TBranch* TTree::BranchImp(const char* branchname, TClass* ptrClass, void* addobj, Int_t bufsize, Int_t splitlevel) +TBranch* TTree::BranchImp(const char* branchname, TClass* ptrClass, void* addobj, Long64_t bufsize, Int_t splitlevel) { if (!ptrClass) { Error("Branch", "The pointer specified for %s is not of a class known to ROOT", branchname); @@ -1660,7 +1660,7 @@ TBranch* TTree::BranchImp(const char* branchname, TClass* ptrClass, void* addobj /// Same as TTree::Branch but automatic detection of the class name. /// \see TTree::Branch -TBranch* TTree::BranchImpRef(const char* branchname, const char *classname, TClass* ptrClass, void *addobj, Int_t bufsize, Int_t splitlevel) +TBranch* TTree::BranchImpRef(const char* branchname, const char *classname, TClass* ptrClass, void *addobj, Long64_t bufsize, Int_t splitlevel) { TClass* claim = TClass::GetClass(classname); if (!ptrClass) { @@ -1720,7 +1720,7 @@ TBranch* TTree::BranchImpRef(const char* branchname, const char *classname, TCla /// Same as TTree::Branch but automatic detection of the class name. /// \see TTree::Branch -TBranch* TTree::BranchImpRef(const char* branchname, TClass* ptrClass, EDataType datatype, void* addobj, Int_t bufsize, Int_t splitlevel) +TBranch* TTree::BranchImpRef(const char* branchname, TClass* ptrClass, EDataType datatype, void* addobj, Long64_t bufsize, Int_t splitlevel) { if (!ptrClass) { if (datatype == kOther_t || datatype == kNoType_t) { @@ -1756,7 +1756,7 @@ TBranch* TTree::BranchImpRef(const char* branchname, TClass* ptrClass, EDataType //////////////////////////////////////////////////////////////////////////////// // Wrapper to turn Branch call with an std::array into the relevant leaf list // call -TBranch *TTree::BranchImpArr(const char *branchname, EDataType datatype, std::size_t N, void *addobj, Int_t bufsize, +TBranch *TTree::BranchImpArr(const char *branchname, EDataType datatype, std::size_t N, void *addobj, Long64_t bufsize, Int_t /* splitlevel */) { if (datatype == kOther_t || datatype == kNoType_t) { @@ -1774,7 +1774,7 @@ TBranch *TTree::BranchImpArr(const char *branchname, EDataType datatype, std::si //////////////////////////////////////////////////////////////////////////////// /// Deprecated function. Use next function instead. -Int_t TTree::Branch(TList* li, Int_t bufsize /* = 32000 */ , Int_t splitlevel /* = 99 */) +Int_t TTree::Branch(TList* li, Long64_t bufsize /* = 32000 */ , Int_t splitlevel /* = 99 */) { return Branch((TCollection*) li, bufsize, splitlevel); } @@ -1861,7 +1861,7 @@ Int_t TTree::Branch(TList* li, Int_t bufsize /* = 32000 */ , Int_t splitlevel /* /// } /// ~~~ -Int_t TTree::Branch(TCollection* li, Int_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */, const char* name /* = "" */) +Int_t TTree::Branch(TCollection* li, Long64_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */, const char* name /* = "" */) { if (!li) { @@ -1908,7 +1908,7 @@ Int_t TTree::Branch(TCollection* li, Int_t bufsize /* = 32000 */, Int_t splitlev /// Create one branch for each element in the folder. /// Returns the total number of branches created. -Int_t TTree::Branch(const char* foldername, Int_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) +Int_t TTree::Branch(const char* foldername, Long64_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) { TObject* ob = gROOT->FindObjectAny(foldername); if (!ob) { @@ -2014,7 +2014,7 @@ Int_t TTree::Branch(const char* foldername, Int_t bufsize /* = 32000 */, Int_t s /// A small value for bufsize is optimum if you intend to access /// the entries in the Tree randomly and your Tree is in split mode. -TBranch* TTree::Branch(const char* name, void* address, const char* leaflist, Int_t bufsize /* = 32000 */) +TBranch* TTree::Branch(const char* name, void* address, const char* leaflist, Long64_t bufsize /* = 32000 */) { TBranch* branch = new TBranch(this, name, address, leaflist, bufsize); if (branch->IsZombie()) { @@ -2049,7 +2049,7 @@ TBranch* TTree::Branch(const char* name, void* address, const char* leaflist, In /// Note: if the split level is set to the default (99), TTree::Branch will /// not issue a warning if the class can not be split. -TBranch* TTree::Branch(const char* name, const char* classname, void* addobj, Int_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) +TBranch* TTree::Branch(const char* name, const char* classname, void* addobj, Long64_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) { if (fgBranchStyle == 1) { return Bronch(name, classname, addobj, bufsize, splitlevel); @@ -2102,7 +2102,7 @@ TBranch* TTree::Branch(const char* name, const char* classname, void* addobj, In /// A small value for bufsize is optimum if you intend to access /// the entries in the Tree randomly and your Tree is in split mode. -TBranch* TTree::BranchOld(const char* name, const char* classname, void* addobj, Int_t bufsize /* = 32000 */, Int_t splitlevel /* = 1 */) +TBranch* TTree::BranchOld(const char* name, const char* classname, void* addobj, Long64_t bufsize /* = 32000 */, Int_t splitlevel /* = 1 */) { TClass* cl = TClass::GetClass(classname); if (!cl) { @@ -2432,7 +2432,7 @@ TBranch* TTree::BranchRef() /// Note: if the split level is set to the default (99), TTree::Branch will /// not issue a warning if the class can not be split. -TBranch* TTree::Bronch(const char* name, const char* classname, void* addr, Int_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) +TBranch* TTree::Bronch(const char* name, const char* classname, void* addr, Long64_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) { return BronchExec(name, classname, addr, true, bufsize, splitlevel); } @@ -2440,7 +2440,7 @@ TBranch* TTree::Bronch(const char* name, const char* classname, void* addr, Int_ //////////////////////////////////////////////////////////////////////////////// /// Helper function implementing TTree::Bronch and TTree::Branch(const char *name, T &obj); -TBranch* TTree::BronchExec(const char* name, const char* classname, void* addr, bool isptrptr, Int_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) +TBranch* TTree::BronchExec(const char* name, const char* classname, void* addr, bool isptrptr, Long64_t bufsize /* = 32000 */, Int_t splitlevel /* = 99 */) { TClass* cl = TClass::GetClass(classname); if (!cl) { @@ -8563,7 +8563,7 @@ void TTree::SetAutoSave(Long64_t autos) /// see TRegexp for wildcarding options /// bufsize = branch basket size -void TTree::SetBasketSize(const char* bname, Int_t bufsize) +void TTree::SetBasketSize(const char* bname, Long64_t bufsize) { Int_t nleaves = fLeaves.GetEntriesFast(); TRegexp re(bname, true); @@ -10007,7 +10007,7 @@ void TTree::UseCurrentStyle() /// Write this object to the current directory. For more see TObject::Write /// If option & kFlushBasket, call FlushBasket before writing the tree. -Int_t TTree::Write(const char *name, Int_t option, Int_t bufsize) const +Int_t TTree::Write(const char *name, Int_t option, Long64_t bufsize) const { FlushBasketsImpl(); if (R__unlikely(option & kOnlyPrepStep)) @@ -10019,7 +10019,7 @@ Int_t TTree::Write(const char *name, Int_t option, Int_t bufsize) const /// Write this object to the current directory. For more see TObject::Write /// If option & kFlushBasket, call FlushBasket before writing the tree. -Int_t TTree::Write(const char *name, Int_t option, Int_t bufsize) +Int_t TTree::Write(const char *name, Int_t option, Long64_t bufsize) { return ((const TTree*)this)->Write(name, option, bufsize); } @@ -10126,13 +10126,13 @@ Option_t* TTreeFriendLeafIter::GetOption() const } TBranch *ROOT::Internal::TreeUtils::CallBranchImpRef(TTree &tree, const char *branchname, TClass *ptrClass, - EDataType datatype, void *addobj, Int_t bufsize, Int_t splitlevel) + EDataType datatype, void *addobj, Long64_t bufsize, Int_t splitlevel) { return tree.BranchImpRef(branchname, ptrClass, datatype, addobj, bufsize, splitlevel); } TBranch *ROOT::Internal::TreeUtils::CallBranchImp(TTree &tree, const char *branchname, TClass *ptrClass, void *addobj, - Int_t bufsize, Int_t splitlevel) + Long64_t bufsize, Int_t splitlevel) { return tree.BranchImp(branchname, ptrClass, addobj, bufsize, splitlevel); } diff --git a/tree/tree/src/TTreeCache.cxx b/tree/tree/src/TTreeCache.cxx index 285cbc6b47c3d..45720e634b115 100644 --- a/tree/tree/src/TTreeCache.cxx +++ b/tree/tree/src/TTreeCache.cxx @@ -316,7 +316,7 @@ TTreeCache::TTreeCache() : TFileCacheRead(), fPrefillType(GetConfiguredPrefillTy //////////////////////////////////////////////////////////////////////////////// /// Constructor. -TTreeCache::TTreeCache(TTree *tree, Int_t bufsize) +TTreeCache::TTreeCache(TTree *tree, Long64_t bufsize) : TFileCacheRead(tree->GetCurrentFile(), bufsize, tree), fEntryMax(tree->GetEntriesFast()), fEntryNext(0), fBrNames(new TList), fTree(tree), fPrefillType(GetConfiguredPrefillType()) { @@ -2069,15 +2069,15 @@ void TTreeCache::ResetCache() /// - 1 if some or all of the buffer content has been made unavailable /// - -1 on error -Int_t TTreeCache::SetBufferSize(Long64_t buffersize) +Int_t TTreeCache::SetBufferSize(Long64_t bufsize) { Int_t prevsize = GetBufferSize(); - Int_t res = TFileCacheRead::SetBufferSize(buffersize); + Int_t res = TFileCacheRead::SetBufferSize(bufsize); if (res < 0) { return res; } - if (res == 0 && buffersize <= prevsize) { + if (res == 0 && bufsize <= prevsize) { return res; } diff --git a/tree/tree/src/TTreeCacheUnzip.cxx b/tree/tree/src/TTreeCacheUnzip.cxx index 507a23d0e218c..ced580eabd241 100644 --- a/tree/tree/src/TTreeCacheUnzip.cxx +++ b/tree/tree/src/TTreeCacheUnzip.cxx @@ -174,7 +174,7 @@ TTreeCacheUnzip::TTreeCacheUnzip() : TTreeCache(), //////////////////////////////////////////////////////////////////////////////// /// Constructor. -TTreeCacheUnzip::TTreeCacheUnzip(TTree *tree, Int_t bufsize) : TTreeCache(tree,bufsize), +TTreeCacheUnzip::TTreeCacheUnzip(TTree *tree, Long64_t bufsize) : TTreeCache(tree,bufsize), fAsyncReading(false), fEmpty(true), fCycle(0), @@ -356,9 +356,9 @@ bool TTreeCacheUnzip::FillBuffer() /// - 1 if some or all of the buffer content has been made unavailable /// - -1 on error -Int_t TTreeCacheUnzip::SetBufferSize(Long64_t buffersize) +Int_t TTreeCacheUnzip::SetBufferSize(Long64_t bufsize) { - Int_t res = TTreeCache::SetBufferSize(buffersize); + Int_t res = TTreeCache::SetBufferSize(bufsize); if (res < 0) { return res; } diff --git a/tree/tree/src/TTreeSQL.cxx b/tree/tree/src/TTreeSQL.cxx index 6e7106d35f96c..d47b36fb0bd5a 100644 --- a/tree/tree/src/TTreeSQL.cxx +++ b/tree/tree/src/TTreeSQL.cxx @@ -73,7 +73,7 @@ TTreeSQL::TTreeSQL(TSQLServer *server, TString DB, const TString& table) : /// Not implemented yet TBranch* TTreeSQL::BranchImp(const char *, const char *, - TClass *, void *, Int_t , + TClass *, void *, Long64_t , Int_t ) { Fatal("BranchImp","Not implemented yet"); @@ -84,7 +84,7 @@ TBranch* TTreeSQL::BranchImp(const char *, const char *, /// Not implemented yet TBranch* TTreeSQL::BranchImp(const char *, TClass *, - void *, Int_t , Int_t ) + void *, Long64_t , Int_t ) { Fatal("BranchImp","Not implemented yet"); return nullptr; @@ -92,7 +92,7 @@ TBranch* TTreeSQL::BranchImp(const char *, TClass *, //////////////////////////////////////////////////////////////////////////////// /// Not implemented yet -Int_t TTreeSQL::Branch(TCollection *, Int_t, +Int_t TTreeSQL::Branch(TCollection *, Long64_t, Int_t, const char *) { Fatal("Branch","Not implemented yet"); @@ -102,7 +102,7 @@ Int_t TTreeSQL::Branch(TCollection *, Int_t, //////////////////////////////////////////////////////////////////////////////// /// Not implemented yet -Int_t TTreeSQL::Branch(TList *, Int_t, Int_t) +Int_t TTreeSQL::Branch(TList *, Long64_t, Int_t) { Fatal("Branch","Not implemented yet"); return 0; @@ -111,7 +111,7 @@ Int_t TTreeSQL::Branch(TList *, Int_t, Int_t) //////////////////////////////////////////////////////////////////////////////// /// Not implemented yet -Int_t TTreeSQL::Branch(const char *, Int_t , +Int_t TTreeSQL::Branch(const char *, Long64_t , Int_t) { Fatal("Branch","Not implemented yet"); @@ -122,7 +122,7 @@ Int_t TTreeSQL::Branch(const char *, Int_t , /// Not implemented yet TBranch* TTreeSQL::Bronch(const char *, const char *, void *, - Int_t, Int_t) + Long64_t, Int_t) { Fatal("Bronch","Not implemented yet"); return nullptr; @@ -132,7 +132,7 @@ TBranch* TTreeSQL::Bronch(const char *, const char *, void *, /// Not implemented yet TBranch* TTreeSQL::BranchOld(const char *, const char *, - void *, Int_t, Int_t) + void *, Long64_t, Int_t) { Fatal("BranchOld","Not implemented yet"); return nullptr; @@ -142,7 +142,7 @@ TBranch* TTreeSQL::BranchOld(const char *, const char *, /// Not implemented yet TBranch *TTreeSQL::Branch(const char *, const char *, void *, - Int_t, Int_t) + Long64_t, Int_t) { Fatal("Branch","Not implemented yet"); return nullptr; @@ -152,7 +152,7 @@ TBranch *TTreeSQL::Branch(const char *, const char *, void *, /// Create a branch TBranch * TTreeSQL::Branch(const char *name, void *address, - const char *leaflist, Int_t bufsize) + const char *leaflist, Long64_t bufsize) { Int_t nb = fBranches.GetEntriesFast(); TBranch *branch; From e0ac6299b6b93b32dd0b09d8a84b500bcb9c608d Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sat, 29 Nov 2025 18:36:40 +0100 Subject: [PATCH 02/36] io: Add data structure for 64 bit byte count. In order to support 64 bits byte counts, which do not fit in the space reserved for them in the stream (4 bytes minus 2 control bits) and to work around the fact that the variables that holds the position and the byte count information in Streamer functions are only 32 bits (see arguments to TBufferFile::WriteVersion and TBufferFile::ReadVersion), we need to pass them indirectly when they needs 64 bits Since the streaming is inherently serial, we can leverage the sequence of calls and cache the 64 bits values in a queue. The bytecount that can not be stored in place inside the io stream will be held in a collection (fByteCounts) that need to be stored externally to the buffer. --- io/io/inc/TBufferFile.h | 13 +++++++++++++ io/io/src/TBufferFile.cxx | 16 ++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index 3d2e37141fcc4..209fb617348d2 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -52,6 +52,16 @@ class TBufferFile : public TBufferIO { TStreamerInfo *fInfo{nullptr}; ///< Pointer to TStreamerInfo object writing/reading the buffer InfoList_t fInfoStack; ///< Stack of pointers to the TStreamerInfos + using ByteCountLocator_t = std::size_t; // This might become a pair if we implement chunked keys + using ByteCountStack_t = std::vector; + ByteCountStack_t fByteCountStack; ///; + // fByteCounts will be stored either in the header/summary tkey or at the end + // of the last segment/chunk for a large TKey. + ByteCountFinder_t fByteCounts; ///< Map to find the byte count value for a given position + // Default ctor TBufferFile() {} // NOLINT: not allowed to use = default because of TObject::kIsOnHeap detection, see ROOT-10300 @@ -62,6 +72,7 @@ class TBufferFile : public TBufferIO { Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss, const char* classname); void CheckCount(UInt_t offset) override; UInt_t CheckObject(UInt_t offset, const TClass *cl, Bool_t readClass = kFALSE); + UInt_t ReserveByteCount(); void WriteObjectClass(const void *actualObjStart, const TClass *actualClass, Bool_t cacheReuse) override; @@ -106,6 +117,8 @@ class TBufferFile : public TBufferIO { TObject *ReadObject(const TClass *cl) override; + ByteCountFinder_t GetByteCounts() const { return fByteCounts; } + using TBufferIO::CheckObject; // basic types and arrays of basic types diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index dd533c88a20ce..1a8948faff1fe 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -3327,6 +3327,22 @@ UInt_t TBufferFile::CheckObject(UInt_t offset, const TClass *cl, Bool_t readClas return offset; } +//////////////////////////////////////////////////////////////////////////////// +/// Reserve space for a leading byte count and return the position where to +/// store the byte count value. +/// +/// \param[in] mask The mask to apply to the placeholder value (default kByteCountMask) +/// \return The position (cntpos) where the byte count should be stored later, +/// or 0 if the position exceeds kMaxInt + +UInt_t TBufferFile::ReserveByteCount() +{ + // reserve space for leading byte count + auto full_cntpos = fBufCur - fBuffer; + fByteCountStack.push_back(full_cntpos); + *this << (UInt_t)kByteCountMask; // placeholder for byte count + return full_cntpos < kMaxInt ? full_cntpos : kMaxInt; +} //////////////////////////////////////////////////////////////////////////////// /// Read max bytes from the I/O buffer into buf. The function returns From fc757e5fe60f9210b8dcc2461d25fbce0fc0d708 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sat, 29 Nov 2025 18:55:15 +0100 Subject: [PATCH 03/36] io: Support 64 bits position/byte-count in reading --- io/io/src/TBufferFile.cxx | 82 +++++++++++++++++++++++++++++++++++---- 1 file changed, 74 insertions(+), 8 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 1a8948faff1fe..dc3c596ebc148 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -360,10 +360,30 @@ void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss, const char *classname) { - if (!bcnt) return 0; - R__ASSERT(startpos <= kMaxUInt && bcnt <= kMaxUInt); - Long64_t offset = 0; + R__ASSERT(!fByteCountStack.empty()); + if (startpos == kMaxInt) { + // The position is above 1GB and was cached using a 32 bit variable. + startpos = fByteCountStack.back(); + } + if (bcnt == kMaxInt) { + // in case we are checking a byte count for which we postponed + // the writing because it was too large, we retrieve it now + auto it = fByteCounts.find(startpos); + if (it != fByteCounts.end()) { + bcnt = it->second; + } else { + bcnt = 0; + Error("CheckByteCount", + "Could not find byte count for position %llu in the byte count map (size=%zu)", + startpos, fByteCounts.size()); + } + } + fByteCountStack.pop_back(); + + if (!bcnt) + return 0; + Long64_t offset = 0; Longptr_t endpos = Longptr_t(fBuffer) + startpos + bcnt + sizeof(UInt_t); if (Longptr_t(fBufCur) != endpos) { @@ -390,7 +410,7 @@ Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const T if ( ((char *)endpos) > fBufMax ) { offset = fBufMax-fBufCur; Error("CheckByteCount", - "Byte count probably corrupted around buffer position %llu:\n\t%llu for a possible maximum of %lld", + "Byte count probably corrupted around buffer position %llu:\n\tByte count is %llu while the buffer size is %lld", startpos, bcnt, offset); fBufCur = fBufMax; @@ -2749,6 +2769,8 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) bcnt = 0; } else { fVersion = 1; + if (objTag) + fByteCountStack.push_back(fBufCur - fBuffer); startpos = UInt_t(fBufCur-fBuffer); *this >> tag; } @@ -2932,7 +2954,10 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass if (startpos) { // before reading object save start position - *startpos = UInt_t(fBufCur-fBuffer); + auto full_startpos = fBufCur - fBuffer; + *startpos = full_startpos < kMaxInt ? UInt_t(full_startpos) : kMaxInt; + if (bcnt) + fByteCountStack.push_back(full_startpos); } // read byte count (older files don't have byte count) @@ -2956,9 +2981,30 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass fBufCur -= sizeof(UInt_t); v.cnt = 0; } - if (bcnt) *bcnt = (v.cnt & ~kByteCountMask); + if (bcnt) { + if (!v.cnt) { + // no byte count stored + *bcnt = 0; + if (startpos) // Undo the push_back only if it happened. + fByteCountStack.pop_back(); + } else { + *bcnt = (v.cnt & ~kByteCountMask); + if (*bcnt == 0) { + // The byte count was stored but is zero, this means the data + // did not fit and thus we stored it in 'fByteCounts' instead. + // Mark this case by setting startpos to kMaxInt. + *bcnt = kMaxInt; + if (startpos) + *startpos = kMaxInt; + } + } + } frombuf(this->fBufCur,&version); + // NOTE: The code above is not straightforward to refactor by a call + // to ReadVersionNoCheckSum because of the following code need the + // 'raw' value in `v`. + if (version<=1) { if (version <= 0) { if (cl) { @@ -3038,7 +3084,10 @@ Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) if (startpos) { // before reading object save start position - *startpos = UInt_t(fBufCur-fBuffer); + auto full_startpos = fBufCur - fBuffer; + *startpos = full_startpos < kMaxInt ? UInt_t(full_startpos) : kMaxInt; + if (bcnt) + fByteCountStack.push_back(full_startpos); } // read byte count (older files don't have byte count) @@ -3062,7 +3111,24 @@ Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) fBufCur -= sizeof(UInt_t); v.cnt = 0; } - if (bcnt) *bcnt = (v.cnt & ~kByteCountMask); + if (bcnt) { + if (!v.cnt) { + // no byte count stored + *bcnt = 0; + if (startpos) // Undo the push_back only if it happened. + fByteCountStack.pop_back(); + } else { + *bcnt = (v.cnt & ~kByteCountMask); + if (*bcnt == 0) { + // The byte count was stored but is zero, this means the data + // did not fit and thus we stored it in 'fByteCounts' instead. + // Mark this case by setting startpos to kMaxInt. + *bcnt = kMaxInt; + if (startpos) + *startpos = kMaxInt; + } + } + } frombuf(this->fBufCur,&version); return version; From 571aebe5f840b60eb322d9151e2dfa12707c20a2 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sat, 29 Nov 2025 18:54:43 +0100 Subject: [PATCH 04/36] io: Support 64 bits position/byte-count in writing --- io/io/src/TBufferFile.cxx | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index dc3c596ebc148..fb33ab45c9a9a 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -323,7 +323,26 @@ 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); + + // We can either make this unconditional or we could split the routine + // in two, one with a new signature and guarantee to get the 64bit position + // (which may be chunk number + local offset) and one with the old signature + // which uses the stack to get the position and call the new one. + // This (of course) also requires that we do the 'same' to the WriteVersion + // routines. + R__ASSERT( !fByteCountStack.empty() ); + if (cntpos == kMaxInt) { + cntpos = fByteCountStack.back(); + } + fByteCountStack.pop_back(); + // if we are not in the same TKey chunk or if the cntpos is too large to fit in UInt_t + // let's postpone the writing of the byte count + const ULong64_t full_cnt = ULong64_t(fBufCur - fBuffer) - cntpos - sizeof(UInt_t); + if (full_cnt >= kMaxMapCount) { + fByteCounts[cntpos] = full_cnt; + return; + } + UInt_t cnt = static_cast(full_cnt); char *buf = (char *)(fBuffer + cntpos); // if true, pack byte count in two consecutive shorts, so it can @@ -2714,8 +2733,7 @@ void TBufferFile::WriteObjectClass(const void *actualObjectStart, const TClass * } // reserve space for leading byte count - UInt_t cntpos = UInt_t(fBufCur-fBuffer); - fBufCur += sizeof(UInt_t); + UInt_t cntpos = ReserveByteCount(); // write class of object first Int_t mapsize = fMap->Capacity(); // The slot depends on the capacity and WriteClass might induce an increase. @@ -3214,8 +3232,7 @@ UInt_t TBufferFile::WriteVersion(const TClass *cl, Bool_t useBcnt) UInt_t cntpos = 0; if (useBcnt) { // reserve space for leading byte count - cntpos = UInt_t(fBufCur-fBuffer); - fBufCur += sizeof(UInt_t); + cntpos = ReserveByteCount(); } Version_t version = cl->GetClassVersion(); @@ -3244,8 +3261,7 @@ UInt_t TBufferFile::WriteVersionMemberWise(const TClass *cl, Bool_t useBcnt) UInt_t cntpos = 0; if (useBcnt) { // reserve space for leading byte count - cntpos = UInt_t(fBufCur-fBuffer); - fBufCur += sizeof(UInt_t); + cntpos = ReserveByteCount(); } Version_t version = cl->GetClassVersion(); From a58fc4b4caffb55605815baa3cc6ec54703023d2 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sat, 29 Nov 2025 18:58:00 +0100 Subject: [PATCH 05/36] io/nfc: add comment --- tree/tree/src/TBranch.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tree/tree/src/TBranch.cxx b/tree/tree/src/TBranch.cxx index 6b9d1102625a9..768434017ea86 100644 --- a/tree/tree/src/TBranch.cxx +++ b/tree/tree/src/TBranch.cxx @@ -979,6 +979,7 @@ Int_t TBranch::FillEntryBuffer(TBasket* basket, TBuffer* buf, Int_t& lnew) fEntryBuffer->SetBufferOffset(objectStart); *fEntryBuffer >> tag; if (tag & kByteCountMask) { + // Ignore byte count. *fEntryBuffer >> tag; } if (tag == kNewClassTag) { @@ -1457,7 +1458,7 @@ bool TBranch::SupportsBulkRead() const { /// the number of elements corresponding to each entries. /// /// For each entry the number of elements is the multiplication of -/// +/// /// ~~~{.cpp} /// TLeaf *leaf = static_cast(branch->GetListOfLeaves()->At(0)); /// auto len = leaf->GetLen(); From 952f6abe355f654817f4b1940fbca4c017576b36 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 2 Dec 2025 11:09:44 -0600 Subject: [PATCH 06/36] io: Add Set/GetByteCounts to TBufferFile Note to store and restore the larger than 1GB byte count use: // Copy the content of the const reference. auto bytecounts{b.GetByteCounts()}; ... b.SetByteCounts(std::move(bytecounts)); --- io/io/inc/TBufferFile.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index 209fb617348d2..19bd6808dc3a7 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -117,7 +117,8 @@ class TBufferFile : public TBufferIO { TObject *ReadObject(const TClass *cl) override; - ByteCountFinder_t GetByteCounts() const { return fByteCounts; } + const ByteCountFinder_t& GetByteCounts() const { return fByteCounts; } + void SetByteCounts(ByteCountFinder_t &&byteCounts) { fByteCounts = std::move(byteCounts); } using TBufferIO::CheckObject; From 20df43f75746f6bb023c7f6ad6663295c03641b2 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 8 Dec 2025 14:24:29 -0600 Subject: [PATCH 07/36] io: Add reset of new bytecount infrastructure --- io/io/inc/TBufferFile.h | 2 ++ io/io/src/TBufferFile.cxx | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index 19bd6808dc3a7..ba6d0be4d881d 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -120,6 +120,8 @@ class TBufferFile : public TBufferIO { const ByteCountFinder_t& GetByteCounts() const { return fByteCounts; } void SetByteCounts(ByteCountFinder_t &&byteCounts) { fByteCounts = std::move(byteCounts); } + void ResetMap() override; + using TBufferIO::CheckObject; // basic types and arrays of basic types diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index fb33ab45c9a9a..fc28af2c82be7 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -114,6 +114,16 @@ TBufferFile::~TBufferFile() { } +//////////////////////////////////////////////////////////////////////////////// +/// Reset buffer maps and clear byte-count tracking structures. + +void TBufferFile::ResetMap() +{ + TBufferIO::ResetMap(); + fByteCountStack.clear(); + fByteCounts.clear(); +} + //////////////////////////////////////////////////////////////////////////////// /// Increment level. From db4b8f508ee02ad1f463186a03e37e1e6ffad436 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 8 Dec 2025 14:25:17 -0600 Subject: [PATCH 08/36] io: Add new bytecount test --- io/io/test/CMakeLists.txt | 4 ++ io/io/test/testByteCount.C | 119 +++++++++++++++++++++++++++++++++++ io/io/test/testByteCount.ref | 2 + 3 files changed, 125 insertions(+) create mode 100644 io/io/test/testByteCount.C create mode 100644 io/io/test/testByteCount.ref diff --git a/io/io/test/CMakeLists.txt b/io/io/test/CMakeLists.txt index 2e9efbd370683..b65a1886a6d17 100644 --- a/io/io/test/CMakeLists.txt +++ b/io/io/test/CMakeLists.txt @@ -40,3 +40,7 @@ endif() # ROOT_EXECUTABLE(TMapFileTest TMapFileTest.cxx LIBRARIES RIO Hist New) # ROOT_ADD_TEST(io-io-test-TMapFileTest COMMAND TMapFileTest complete) #endif() + +ROOTTEST_ADD_TEST(testLargeByteCounts + MACRO testByteCount.C + OUTREF testByteCount.ref) diff --git a/io/io/test/testByteCount.C b/io/io/test/testByteCount.C new file mode 100644 index 0000000000000..4c9af3f9707df --- /dev/null +++ b/io/io/test/testByteCount.C @@ -0,0 +1,119 @@ +{ + int errors = 0; + int expectedByteCounts = 1; + + // TBufferFile currently reject size larger than 2GB. + // SetBufferOffset does not check against the size, + // so we can provide and use a larger buffer. + std::vector databuffer{}; + databuffer.reserve( 4*1024*1024*1024ll ); + TBufferFile b(TBuffer::kWrite, 2*1024*1024*1024ll-100, databuffer.data(), false /* don't adopt */); + { + // Regular object at offset 0 + UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); + b.SetBufferOffset(1000); + b.SetByteCount(R__c, kTRUE); + } + { + // Regular object + UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); + b.SetBufferOffset(2000); + b.SetByteCount(R__c, kTRUE); + } + { + // Object larger than 1GB + UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); + b.SetBufferOffset(4000+1*1024*1024*1024ll); + b.SetByteCount(R__c, kTRUE); + } + { + // Regular object located past 1GB + UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); + b.SetBufferOffset(8000+1*1024*1024*1024ll); + b.SetByteCount(R__c, kTRUE); + } + { + ++expectedByteCounts; + // Object larger than 1GB start after 1GB + // NOTE: this does not yet fit, we are writing past the end. + // Need to lift the 2GB limit for TBuffer first. + // However the lifting might be temporary, so this might need to be + // moved to a test that stored objects in a TFile. + UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); + b.SetBufferOffset(12000+2*1024*1024*1024ll); + b.SetByteCount(R__c, kTRUE); + } + + + // To make a copy instead of using the const references: + auto bytecounts{ b.GetByteCounts() }; + if (bytecounts.size() != expectedByteCounts) { + ++errors; + std::cerr << "The number of bytecount is not as expected (1), it is " << bytecounts.size() << '\n'; + std::cerr << "The full list is:\n"; + for(auto bc : bytecounts) + std::cerr << "values: " << bc.first << " , " << bc.second << '\n'; + } + + // Rewind. Other code use Reset instead of SetBufferOffset + b.SetReadMode(); + b.Reset(); + b.SetByteCounts(std::move(bytecounts)); + + UInt_t R__s = 0; + UInt_t R__c = 0; + { + // Regular object at offset 0 + auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); + b.SetBufferOffset(1000); + auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); + if (res != 0) { + ++errors; + // We can assume there as already an error message in CheckByCount + } + } + { + // Regular object + auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); + b.SetBufferOffset(2000); + auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); + if (res != 0) { + ++errors; + // We can assume there as already an error message in CheckByCount + } + } + { + // Object larger than 1GB + auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); + b.SetBufferOffset(4000+1*1024*1024*1024ll); + auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); + if (res != 0) { + ++errors; + // We can assume there as already an error message in CheckByCount + } + } + { + // Regular object located past 1GB + auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); + b.SetBufferOffset(8000+1*1024*1024*1024ll); + auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); + if (res != 0) { + ++errors; + // We can assume there as already an error message in CheckByCount + } + } + { + // Object larger than 1GB start after 1GB + // NOTE: this does not yet fit. + auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); + b.SetBufferOffset(12000+2*1024*1024*1024ll); + auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); + if (res != 0) { + ++errors; + // We can assume there as already an error message in CheckByCount + } + } + + std::cerr << "The end.\n"; + return errors; +} diff --git a/io/io/test/testByteCount.ref b/io/io/test/testByteCount.ref new file mode 100644 index 0000000000000..a5a2b78b46929 --- /dev/null +++ b/io/io/test/testByteCount.ref @@ -0,0 +1,2 @@ +Processing io/io/test/testByteCount.C... +The end. From 7104209978af1f16b7a449049d0a70c009d863b0 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 11 Dec 2025 12:49:44 -0600 Subject: [PATCH 09/36] io: Suppress error message about large byte count --- io/io/src/TBufferFile.cxx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index fc28af2c82be7..f0d0a06064dc6 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -374,8 +374,10 @@ void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) tobuf(buf, cnt | kByteCountMask); if (cnt >= kMaxMapCount) { - Error("WriteByteCount", "bytecount too large (more than %d)", kMaxMapCount); - // exception + // NOTE: This should now be unreachable. + Fatal("WriteByteCount", + "Control flow error, code should be unreachable and wrote a bytecount that is too large (more than %d)", + kMaxMapCount); } } @@ -3334,7 +3336,11 @@ void TBufferFile::StreamObject(TObject *obj) void TBufferFile::CheckCount(UInt_t offset) { - if (IsWriting()) { + // FIXME: + // We could continue to issue the error for cases where we don't want to + // support storing more than 1GB in a single TBufferFile. + const bool bufferLimitedToMaxMapCount = false; + if (bufferLimitedToMaxMapCount && IsWriting()) { if (offset >= kMaxMapCount) { Error("CheckCount", "buffer offset too large (larger than %d)", kMaxMapCount); // exception From 2ebb74d8a39d08e8a1e8f271f262b11ec43776f3 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 11 Dec 2025 13:02:11 -0600 Subject: [PATCH 10/36] [NFC] io white space / byte count related --- io/io/inc/TBufferFile.h | 2 +- io/io/src/TBufferFile.cxx | 5 +++-- io/io/test/testByteCount.C | 21 ++++++++++----------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index ba6d0be4d881d..143f8ac374a69 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -117,7 +117,7 @@ class TBufferFile : public TBufferIO { TObject *ReadObject(const TClass *cl) override; - const ByteCountFinder_t& GetByteCounts() const { return fByteCounts; } + const ByteCountFinder_t &GetByteCounts() const { return fByteCounts; } void SetByteCounts(ByteCountFinder_t &&byteCounts) { fByteCounts = std::move(byteCounts); } void ResetMap() override; diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index f0d0a06064dc6..0be3f74ac63fa 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -340,7 +340,7 @@ void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) // which uses the stack to get the position and call the new one. // This (of course) also requires that we do the 'same' to the WriteVersion // routines. - R__ASSERT( !fByteCountStack.empty() ); + R__ASSERT(!fByteCountStack.empty()); if (cntpos == kMaxInt) { cntpos = fByteCountStack.back(); } @@ -441,7 +441,8 @@ Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const T if ( ((char *)endpos) > fBufMax ) { offset = fBufMax-fBufCur; Error("CheckByteCount", - "Byte count probably corrupted around buffer position %llu:\n\tByte count is %llu while the buffer size is %lld", + "Byte count probably corrupted around buffer position %llu:\n\tByte count is %llu while the buffer size " + "is %lld", startpos, bcnt, offset); fBufCur = fBufMax; diff --git a/io/io/test/testByteCount.C b/io/io/test/testByteCount.C index 4c9af3f9707df..b152e3700aa75 100644 --- a/io/io/test/testByteCount.C +++ b/io/io/test/testByteCount.C @@ -6,8 +6,8 @@ // SetBufferOffset does not check against the size, // so we can provide and use a larger buffer. std::vector databuffer{}; - databuffer.reserve( 4*1024*1024*1024ll ); - TBufferFile b(TBuffer::kWrite, 2*1024*1024*1024ll-100, databuffer.data(), false /* don't adopt */); + databuffer.reserve(4 * 1024 * 1024 * 1024ll); + TBufferFile b(TBuffer::kWrite, 2 * 1024 * 1024 * 1024ll - 100, databuffer.data(), false /* don't adopt */); { // Regular object at offset 0 UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); @@ -23,13 +23,13 @@ { // Object larger than 1GB UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); - b.SetBufferOffset(4000+1*1024*1024*1024ll); + b.SetBufferOffset(4000 + 1 * 1024 * 1024 * 1024ll); b.SetByteCount(R__c, kTRUE); } { // Regular object located past 1GB UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); - b.SetBufferOffset(8000+1*1024*1024*1024ll); + b.SetBufferOffset(8000 + 1 * 1024 * 1024 * 1024ll); b.SetByteCount(R__c, kTRUE); } { @@ -40,18 +40,17 @@ // However the lifting might be temporary, so this might need to be // moved to a test that stored objects in a TFile. UInt_t R__c = b.WriteVersion(TExMap::Class(), kTRUE); - b.SetBufferOffset(12000+2*1024*1024*1024ll); + b.SetBufferOffset(12000 + 2 * 1024 * 1024 * 1024ll); b.SetByteCount(R__c, kTRUE); } - // To make a copy instead of using the const references: - auto bytecounts{ b.GetByteCounts() }; + auto bytecounts{b.GetByteCounts()}; if (bytecounts.size() != expectedByteCounts) { ++errors; std::cerr << "The number of bytecount is not as expected (1), it is " << bytecounts.size() << '\n'; std::cerr << "The full list is:\n"; - for(auto bc : bytecounts) + for (auto bc : bytecounts) std::cerr << "values: " << bc.first << " , " << bc.second << '\n'; } @@ -85,7 +84,7 @@ { // Object larger than 1GB auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); - b.SetBufferOffset(4000+1*1024*1024*1024ll); + b.SetBufferOffset(4000 + 1 * 1024 * 1024 * 1024ll); auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); if (res != 0) { ++errors; @@ -95,7 +94,7 @@ { // Regular object located past 1GB auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); - b.SetBufferOffset(8000+1*1024*1024*1024ll); + b.SetBufferOffset(8000 + 1 * 1024 * 1024 * 1024ll); auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); if (res != 0) { ++errors; @@ -106,7 +105,7 @@ // Object larger than 1GB start after 1GB // NOTE: this does not yet fit. auto version = b.ReadVersion(&R__s, &R__c, TExMap::Class()); - b.SetBufferOffset(12000+2*1024*1024*1024ll); + b.SetBufferOffset(12000 + 2 * 1024 * 1024 * 1024ll); auto res = b.CheckByteCount(R__s, R__c, TExMap::Class()); if (res != 0) { ++errors; From db272d209d4747664b9fe8b343274f3a26f5004c Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 5 Feb 2026 19:12:43 +0100 Subject: [PATCH 11/36] NFC io: fix doc typo --- io/io/src/TBufferFile.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 0be3f74ac63fa..c9f982a872616 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -324,7 +324,7 @@ void TBufferFile::WriteCharStar(char *s) //////////////////////////////////////////////////////////////////////////////// /// Set byte count at position cntpos in the buffer. Generate warning if -/// count larger than kMaxMapCount. The count is excluded its own size. +/// count larger than kMaxMapCount. The count is excluding its own size. /// \note If underflow or overflow, an Error ir raised (stricter checks in Debug mode) void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) From 60d81bbd4f31adc2eea5e8c3753f40e70c0c9c7c Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 5 Feb 2026 19:15:54 +0100 Subject: [PATCH 12/36] io: doc clarifications - bytecount related --- io/io/src/TBufferFile.cxx | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index c9f982a872616..3d32ed85ad109 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -415,6 +415,7 @@ Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const T return 0; Long64_t offset = 0; + // End position is buffer location + start position + byte count + size of byte count field Longptr_t endpos = Longptr_t(fBuffer) + startpos + bcnt + sizeof(UInt_t); if (Longptr_t(fBufCur) != endpos) { @@ -2800,6 +2801,8 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) bcnt = 0; } else { fVersion = 1; + // When objTag is not used, the caller is not interested in the byte + // count and will not (can not) call CheckByteCount. if (objTag) fByteCountStack.push_back(fBufCur - fBuffer); startpos = UInt_t(fBufCur-fBuffer); @@ -2978,6 +2981,9 @@ void TBufferFile::SkipVersion(const TClass *cl) //////////////////////////////////////////////////////////////////////////////// /// Read class version from I/O buffer. +/// If passing startpos and bcnt, CheckByteCount must be called later with +/// the same startpos and bcnt (this is needed to properly handle the byte +/// count stack used to support very large buffers) Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass *cl) { @@ -3108,6 +3114,15 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass //////////////////////////////////////////////////////////////////////////////// /// Read class version from I/O buffer, when the caller knows for sure that /// there is no checksum written/involved. +/// +/// This routine can be used from custom streamers when it is known that the +/// class was always versioned (and thus never stored a checksum within the buffer). +/// This allows to disambiguate the case where the class used to have a version +/// number equal to zero and did not save a byte count and is now versioned. +/// +/// When reading the version number being zero, without the byte count we have +/// no way to guess whether the class was version un-versioned or had previously +/// a version number equal to zero. Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) { @@ -3430,9 +3445,8 @@ UInt_t TBufferFile::CheckObject(UInt_t offset, const TClass *cl, Bool_t readClas /// Reserve space for a leading byte count and return the position where to /// store the byte count value. /// -/// \param[in] mask The mask to apply to the placeholder value (default kByteCountMask) /// \return The position (cntpos) where the byte count should be stored later, -/// or 0 if the position exceeds kMaxInt +/// or kOverflowPosition if the position exceeds kMaxCountPosition UInt_t TBufferFile::ReserveByteCount() { From 3ce353dc9d0283e5a0a13227ffdcf31747481058 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 5 Feb 2026 19:28:27 +0100 Subject: [PATCH 13/36] io: Clarify byte count related limits --- io/io/src/TBufferFile.cxx | 78 ++++++++++++++++++++++++--------------- 1 file changed, 49 insertions(+), 29 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 3d32ed85ad109..a304fb0093196 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -15,6 +15,21 @@ \ingroup IO The concrete implementation of TBuffer for writing/reading to/from a ROOT file or socket. + +Limits: +Byte Count positions (refered to as `cntpos` or `startpos` in the code) + - Internally they are passed as a ULong64_t + - In legacy user code they are held in UInt_t variables (See type used in + TBufferFile::WriteVersion and TBufferFile::ReadVersion) + - The positions passed by value are limited to 4GB (kOverflowPosition) + and the positions are held in fByteCountStack but retrieved only when + higher than 4GB (kOverflowCount). +Byte Count values: + - Internally they are passed as a ULong64_t + - In legacy user code they are held in UInt_t variables (See type used in + TBufferFile::WriteVersion and TBufferFile::ReadVersion) + - The values passed by value are limited to 1GB (kMaxMapCount) + - The values larger than kMaxMapCount are held in fByteCounts. */ #include @@ -46,15 +61,18 @@ The concrete implementation of TBuffer for writing/reading to/from a ROOT file o #include "Bswapcpy.h" #endif +constexpr UInt_t kOverflowPosition = 0xFFFFFFFF; +constexpr UInt_t kOverflowCount = 0xFFFFFFFF; +constexpr UInt_t kMaxCountPosition = 0xFFFFFFFE; // We reserve the highest value to mark overflow positions -const UInt_t kNewClassTag = 0xFFFFFFFF; -const UInt_t kClassMask = 0x80000000; // OR the class index with this -const UInt_t kByteCountMask = 0x40000000; // OR the byte count with this -const UInt_t kMaxMapCount = 0x3FFFFFFE; // last valid fMapCount and byte count -const Version_t kByteCountVMask = 0x4000; // OR the version byte count with this -const Version_t kMaxVersion = 0x3FFF; // highest possible version number -const Int_t kMapOffset = 2; // first 2 map entries are taken by null obj and self obj - +constexpr UInt_t kNewClassTag = 0xFFFFFFFF; +constexpr UInt_t kClassMask = 0x80000000; // OR the class index with this +constexpr UInt_t kByteCountMask = 0x40000000; // OR the byte count with this +constexpr UInt_t kMaxMapCount = 0x3FFFFFFE; // last valid fMapCount and byte count +constexpr UInt_t kMaxRecordByteCount = kMaxMapCount; // last valid byte count +constexpr Version_t kByteCountVMask = 0x4000; // OR the version byte count with this +constexpr Version_t kMaxVersion = 0x3FFF; // highest possible version number +constexpr Int_t kMapOffset = 2; // first 2 map entries are taken by null obj and self obj //////////////////////////////////////////////////////////////////////////////// @@ -323,13 +341,13 @@ void TBufferFile::WriteCharStar(char *s) } //////////////////////////////////////////////////////////////////////////////// -/// Set byte count at position cntpos in the buffer. Generate warning if -/// count larger than kMaxMapCount. The count is excluding its own size. +/// Set byte count at position cntpos in the buffer. +/// The count is excluding its own size. /// \note If underflow or overflow, an Error ir raised (stricter checks in Debug mode) void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) { - assert( cntpos <= kMaxUInt && (sizeof(UInt_t) + cntpos) < static_cast(fBufCur - fBuffer) + assert( cntpos <= kOverflowPosition && (sizeof(UInt_t) + cntpos) < static_cast(fBufCur - fBuffer) && (fBufCur >= fBuffer) && static_cast(fBufCur - fBuffer) <= std::numeric_limits::max() && "Byte count position is after the end of the buffer"); @@ -341,14 +359,15 @@ void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) // This (of course) also requires that we do the 'same' to the WriteVersion // routines. R__ASSERT(!fByteCountStack.empty()); - if (cntpos == kMaxInt) { + if (cntpos == kOverflowPosition) { + // The position is above 4GB but was cached using a 32 bit variable. cntpos = fByteCountStack.back(); } fByteCountStack.pop_back(); // if we are not in the same TKey chunk or if the cntpos is too large to fit in UInt_t // let's postpone the writing of the byte count const ULong64_t full_cnt = ULong64_t(fBufCur - fBuffer) - cntpos - sizeof(UInt_t); - if (full_cnt >= kMaxMapCount) { + if (full_cnt >= kMaxRecordByteCount) { fByteCounts[cntpos] = full_cnt; return; } @@ -373,11 +392,11 @@ void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) } else tobuf(buf, cnt | kByteCountMask); - if (cnt >= kMaxMapCount) { + if (cnt >= kMaxRecordByteCount) { // NOTE: This should now be unreachable. Fatal("WriteByteCount", "Control flow error, code should be unreachable and wrote a bytecount that is too large (more than %d)", - kMaxMapCount); + kMaxRecordByteCount); } } @@ -391,12 +410,13 @@ void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) Long64_t TBufferFile::CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss, const char *classname) { - R__ASSERT(!fByteCountStack.empty()); - if (startpos == kMaxInt) { - // The position is above 1GB and was cached using a 32 bit variable. + R__ASSERT(!fByteCountStack.empty() && startpos <= kOverflowPosition && bcnt <= kOverflowCount + && "Byte count stack is empty or invalid startpos/bcnt"); + if (startpos == kOverflowPosition) { + // The position is above 4GB but was cached using a 32 bit variable. startpos = fByteCountStack.back(); } - if (bcnt == kMaxInt) { + if (bcnt == kOverflowCount) { // in case we are checking a byte count for which we postponed // the writing because it was too large, we retrieve it now auto it = fByteCounts.find(startpos); @@ -2992,7 +3012,7 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass if (startpos) { // before reading object save start position auto full_startpos = fBufCur - fBuffer; - *startpos = full_startpos < kMaxInt ? UInt_t(full_startpos) : kMaxInt; + *startpos = full_startpos <= kMaxCountPosition ? UInt_t(full_startpos) : kOverflowPosition; if (bcnt) fByteCountStack.push_back(full_startpos); } @@ -3029,10 +3049,10 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass if (*bcnt == 0) { // The byte count was stored but is zero, this means the data // did not fit and thus we stored it in 'fByteCounts' instead. - // Mark this case by setting startpos to kMaxInt. - *bcnt = kMaxInt; + // Mark this case by setting startpos to kOverflowCount. + *bcnt = kOverflowCount; if (startpos) - *startpos = kMaxInt; + *startpos = kOverflowPosition; } } } @@ -3131,7 +3151,7 @@ Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) if (startpos) { // before reading object save start position auto full_startpos = fBufCur - fBuffer; - *startpos = full_startpos < kMaxInt ? UInt_t(full_startpos) : kMaxInt; + *startpos = full_startpos < kMaxCountPosition ? UInt_t(full_startpos) : kOverflowPosition; if (bcnt) fByteCountStack.push_back(full_startpos); } @@ -3168,10 +3188,10 @@ Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) if (*bcnt == 0) { // The byte count was stored but is zero, this means the data // did not fit and thus we stored it in 'fByteCounts' instead. - // Mark this case by setting startpos to kMaxInt. - *bcnt = kMaxInt; + // Mark this case by setting startpos to kOverflowCount. + *bcnt = kOverflowCount; if (startpos) - *startpos = kMaxInt; + *startpos = kOverflowPosition; } } } @@ -3357,7 +3377,7 @@ void TBufferFile::CheckCount(UInt_t offset) // support storing more than 1GB in a single TBufferFile. const bool bufferLimitedToMaxMapCount = false; if (bufferLimitedToMaxMapCount && IsWriting()) { - if (offset >= kMaxMapCount) { + if (offset > kMaxRecordByteCount) { // We have kMaxMapCount == kMaxRecordByteCount Error("CheckCount", "buffer offset too large (larger than %d)", kMaxMapCount); // exception } @@ -3454,7 +3474,7 @@ UInt_t TBufferFile::ReserveByteCount() auto full_cntpos = fBufCur - fBuffer; fByteCountStack.push_back(full_cntpos); *this << (UInt_t)kByteCountMask; // placeholder for byte count - return full_cntpos < kMaxInt ? full_cntpos : kMaxInt; + return full_cntpos <= kMaxCountPosition ? full_cntpos : kOverflowPosition; } //////////////////////////////////////////////////////////////////////////////// From 9c5d132c4e3074635e17c28986affc0576f74809 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sun, 8 Feb 2026 15:00:57 +0100 Subject: [PATCH 14/36] io: ReadVersion should always take both position and byte count --- io/io/src/TBufferFile.cxx | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index a304fb0093196..92bacafdcd231 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -3007,14 +3007,15 @@ void TBufferFile::SkipVersion(const TClass *cl) Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass *cl) { + assert((!startpos && !bcnt) || (startpos && bcnt)); // both or none should be set + Version_t version; if (startpos) { // before reading object save start position auto full_startpos = fBufCur - fBuffer; *startpos = full_startpos <= kMaxCountPosition ? UInt_t(full_startpos) : kOverflowPosition; - if (bcnt) - fByteCountStack.push_back(full_startpos); + fByteCountStack.push_back(full_startpos); } // read byte count (older files don't have byte count) @@ -3039,11 +3040,11 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass v.cnt = 0; } if (bcnt) { + // We also have (asserted) that (startpos != nullptr) if (!v.cnt) { // no byte count stored *bcnt = 0; - if (startpos) // Undo the push_back only if it happened. - fByteCountStack.pop_back(); + fByteCountStack.pop_back(); } else { *bcnt = (v.cnt & ~kByteCountMask); if (*bcnt == 0) { @@ -3051,8 +3052,7 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass // did not fit and thus we stored it in 'fByteCounts' instead. // Mark this case by setting startpos to kOverflowCount. *bcnt = kOverflowCount; - if (startpos) - *startpos = kOverflowPosition; + *startpos = kOverflowPosition; } } } @@ -3146,14 +3146,15 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) { + assert((!startpos && !bcnt) || (startpos && bcnt)); // both or none should be set + Version_t version; if (startpos) { // before reading object save start position auto full_startpos = fBufCur - fBuffer; *startpos = full_startpos < kMaxCountPosition ? UInt_t(full_startpos) : kOverflowPosition; - if (bcnt) - fByteCountStack.push_back(full_startpos); + fByteCountStack.push_back(full_startpos); } // read byte count (older files don't have byte count) @@ -3181,8 +3182,7 @@ Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) if (!v.cnt) { // no byte count stored *bcnt = 0; - if (startpos) // Undo the push_back only if it happened. - fByteCountStack.pop_back(); + fByteCountStack.pop_back(); } else { *bcnt = (v.cnt & ~kByteCountMask); if (*bcnt == 0) { @@ -3190,8 +3190,7 @@ Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) // did not fit and thus we stored it in 'fByteCounts' instead. // Mark this case by setting startpos to kOverflowCount. *bcnt = kOverflowCount; - if (startpos) - *startpos = kOverflowPosition; + *startpos = kOverflowPosition; } } } From 81a9f5ef6acc1eb969f54c1abfed2834d41b4846 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Sun, 8 Feb 2026 17:34:45 +0100 Subject: [PATCH 15/36] io: Correct handling of byte count pos in TBufferFile::ReadObjectAny --- io/io/src/TBufferFile.cxx | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 92bacafdcd231..fbe038bff88e5 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -2593,7 +2593,8 @@ void *TBufferFile::ReadObjectAny(const TClass *clCast) InitMap(); // before reading object save start position - UInt_t startpos = UInt_t(fBufCur-fBuffer); + ULong64_t startpos = static_cast(fBufCur-fBuffer); + ULong64_t cntpos = startpos <= kMaxCountPosition ? startpos : kOverflowPosition; // attempt to load next object as TClass clCast UInt_t tag; // either tag or byte count @@ -2613,7 +2614,7 @@ void *TBufferFile::ReadObjectAny(const TClass *clCast) Error("ReadObject", "got object of wrong class! requested %s but got %s", clCast->GetName(), clRef->GetName()); - CheckByteCount(startpos, tag, (TClass *)nullptr); // avoid mis-leading byte count error message + CheckByteCount(cntpos, tag, (TClass *)nullptr); // avoid mis-leading byte count error message return 0; // We better return at this point } baseOffset = 0; // For now we do not support requesting from a class that is the base of one of the class for which there is transformation to .... @@ -2628,7 +2629,7 @@ void *TBufferFile::ReadObjectAny(const TClass *clCast) //we cannot mix a compiled class with an emulated class in the inheritance Error("ReadObject", "trying to read an emulated class (%s) to store in a compiled pointer (%s)", clRef->GetName(),clCast->GetName()); - CheckByteCount(startpos, tag, (TClass *)nullptr); // avoid mis-leading byte count error message + CheckByteCount(cntpos, tag, (TClass *)nullptr); // avoid mis-leading byte count error message return 0; } } @@ -2640,7 +2641,7 @@ void *TBufferFile::ReadObjectAny(const TClass *clCast) obj = (char *) (Longptr_t)fMap->GetValue(startpos+kMapOffset); if (obj == (void*) -1) obj = nullptr; if (obj) { - CheckByteCount(startpos, tag, (TClass *)nullptr); + CheckByteCount(cntpos, tag, (TClass *)nullptr); return (obj + baseOffset); } } @@ -2652,7 +2653,7 @@ void *TBufferFile::ReadObjectAny(const TClass *clCast) MapObject((TObject*) -1, startpos+kMapOffset); else MapObject((void*)nullptr, nullptr, fMapCount); - CheckByteCount(startpos, tag, (TClass *)nullptr); + CheckByteCount(cntpos, tag, (TClass *)nullptr); return 0; } @@ -2711,7 +2712,7 @@ void *TBufferFile::ReadObjectAny(const TClass *clCast) // let the object read itself clRef->Streamer( obj, *this, clOnfile ); - CheckByteCount(startpos, tag, clRef); + CheckByteCount(cntpos, tag, clRef); } return obj+baseOffset; From 673f57b127ccfed62e6e9863f29f04bd5b2b941f Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 11 Feb 2026 15:22:34 +0100 Subject: [PATCH 16/36] io: Fix up clarify byte count related limits --- io/io/src/TBufferFile.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index fbe038bff88e5..69d35e703ad9f 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -347,7 +347,8 @@ void TBufferFile::WriteCharStar(char *s) void TBufferFile::SetByteCount(ULong64_t cntpos, Bool_t packInVersion) { - assert( cntpos <= kOverflowPosition && (sizeof(UInt_t) + cntpos) < static_cast(fBufCur - fBuffer) + assert( (cntpos == kOverflowPosition || + (cntpos < kOverflowPosition && (sizeof(UInt_t) + cntpos) < static_cast(fBufCur - fBuffer))) && (fBufCur >= fBuffer) && static_cast(fBufCur - fBuffer) <= std::numeric_limits::max() && "Byte count position is after the end of the buffer"); From 5a6fc36d605db9319a2f281be754d3fa66f1d063 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Thu, 12 Feb 2026 23:29:16 +0100 Subject: [PATCH 17/36] io: In TBufferFile::ReadClass fix recording of byte count pos. This fixes TBufferFile::ReadObjectAny handling of long range byte counts --- io/io/src/TBufferFile.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 69d35e703ad9f..ff72db6ba2dc8 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -2599,6 +2599,7 @@ void *TBufferFile::ReadObjectAny(const TClass *clCast) // attempt to load next object as TClass clCast UInt_t tag; // either tag or byte count + // ReadClass will push on fByteCountStack if needed. TClass *clRef = ReadClass(clCast, &tag); TClass *clOnfile = nullptr; Int_t baseOffset = 0; @@ -2826,7 +2827,7 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) // When objTag is not used, the caller is not interested in the byte // count and will not (can not) call CheckByteCount. if (objTag) - fByteCountStack.push_back(fBufCur - fBuffer); + fByteCountStack.push_back(fBufCur - fBuffer - sizeof(UInt_t)); startpos = UInt_t(fBufCur-fBuffer); *this >> tag; } From 087665f3efd6b7aa13faee624b3258922182acc1 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 11 Feb 2026 13:19:29 +0100 Subject: [PATCH 18/36] io: compile testLargeByteCounts --- io/io/test/CMakeLists.txt | 8 ++++++-- io/io/test/{testByteCount.C => testByteCount.cxx} | 10 +++++++++- io/io/test/testByteCount.ref | 1 + 3 files changed, 16 insertions(+), 3 deletions(-) rename io/io/test/{testByteCount.C => testByteCount.cxx} (96%) diff --git a/io/io/test/CMakeLists.txt b/io/io/test/CMakeLists.txt index b65a1886a6d17..61f381acf0274 100644 --- a/io/io/test/CMakeLists.txt +++ b/io/io/test/CMakeLists.txt @@ -41,6 +41,10 @@ endif() # ROOT_ADD_TEST(io-io-test-TMapFileTest COMMAND TMapFileTest complete) #endif() +ROOTTEST_COMPILE_MACRO(testByteCount.cxx + FIXTURES_SETUP io-io-tobj-fixture) + ROOTTEST_ADD_TEST(testLargeByteCounts - MACRO testByteCount.C - OUTREF testByteCount.ref) + MACRO testByteCount.cxx+ + OUTREF testByteCount.ref + FIXTURES_REQUIRED io-io-tobj-fixture) diff --git a/io/io/test/testByteCount.C b/io/io/test/testByteCount.cxx similarity index 96% rename from io/io/test/testByteCount.C rename to io/io/test/testByteCount.cxx index b152e3700aa75..07a6966786e56 100644 --- a/io/io/test/testByteCount.C +++ b/io/io/test/testByteCount.cxx @@ -1,6 +1,14 @@ +#include "TBufferFile.h" +#include "TExMap.h" + +#include +#include + + +int testByteCount() { int errors = 0; - int expectedByteCounts = 1; + unsigned int expectedByteCounts = 1; // TBufferFile currently reject size larger than 2GB. // SetBufferOffset does not check against the size, diff --git a/io/io/test/testByteCount.ref b/io/io/test/testByteCount.ref index a5a2b78b46929..2ac0fa3121d7c 100644 --- a/io/io/test/testByteCount.ref +++ b/io/io/test/testByteCount.ref @@ -1,2 +1,3 @@ Processing io/io/test/testByteCount.C... The end. +(int) 0 From 0ffcf52b4400967dbf526a2775524be89d37bf92 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 16 Feb 2026 11:26:16 -0600 Subject: [PATCH 19/36] io: Add missing CheckByteCount in TStreamerElements. Those were essentially replaced by a 'set the cursor to the end' call in commit 7d25b75b8877a03ee579e97d91ee8f719721d6d5. It is dubious whether these changes are needed or were an heurestic to ignore (temporarily !?) race conditions. --- core/meta/src/TStreamerElement.cxx | 14 +++++++++++++- io/io/src/TStreamerInfo.cxx | 4 ++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/core/meta/src/TStreamerElement.cxx b/core/meta/src/TStreamerElement.cxx index 561fca629178d..ef74518c37948 100644 --- a/core/meta/src/TStreamerElement.cxx +++ b/core/meta/src/TStreamerElement.cxx @@ -556,9 +556,12 @@ void TStreamerElement::Streamer(TBuffer &R__b) if (R__v > 3) { if (TestBit(kHasRange)) GetRange(GetTitle(),fXmin,fXmax,fFactor); } - //R__b.CheckByteCount(R__s, R__c, TStreamerElement::IsA()); R__b.ClassEnd(TStreamerElement::Class()); + // Dubious reset to the expected end it was added as part the commit + // log "Several protections added in case of multiple files being read/updated + // in parallel." R__b.SetBufferOffset(R__s+R__c+sizeof(UInt_t)); + R__b.CheckByteCount(R__s, R__c, TStreamerElement::Class()); ResetBit(TStreamerElement::kCache); ResetBit(TStreamerElement::kWrite); @@ -832,6 +835,7 @@ void TStreamerBase::Streamer(TBuffer &R__b) } R__b.ClassEnd(TStreamerBase::Class()); R__b.SetBufferOffset(R__s+R__c+sizeof(UInt_t)); + R__b.CheckByteCount(R__s, R__c, TStreamerBase::Class()); } else { R__b.WriteClassBuffer(TStreamerBase::Class(),this); } @@ -1001,7 +1005,11 @@ void TStreamerBasicPointer::Streamer(TBuffer &R__b) R__b >> fCountVersion; fCountName.Streamer(R__b); fCountClass.Streamer(R__b); + // Dubious reset to the expected end it was added as part the commit + // log "Several protections added in case of multiple files being read/updated + // in parallel." R__b.SetBufferOffset(R__s+R__c+sizeof(UInt_t)); + R__b.CheckByteCount(R__s, R__c, TStreamerBasicPointer::Class()); } else { R__b.WriteClassBuffer(TStreamerBasicPointer::Class(),this); } @@ -1105,7 +1113,11 @@ void TStreamerLoop::Streamer(TBuffer &R__b) R__b >> fCountVersion; fCountName.Streamer(R__b); fCountClass.Streamer(R__b); + // Dubious reset to the expected end it was added as part the commit + // log "Several protections added in case of multiple files being read/updated + // in parallel." R__b.SetBufferOffset(R__s+R__c+sizeof(UInt_t)); + R__b.CheckByteCount(R__s, R__c, TStreamerLoop::Class()); } else { R__b.WriteClassBuffer(TStreamerLoop::Class(),this); } diff --git a/io/io/src/TStreamerInfo.cxx b/io/io/src/TStreamerInfo.cxx index 3900b54b37cf8..4fb7814c48b8c 100644 --- a/io/io/src/TStreamerInfo.cxx +++ b/io/io/src/TStreamerInfo.cxx @@ -5503,7 +5503,11 @@ void TStreamerInfo::Streamer(TBuffer &R__b) R__b.ClassMember("fElements","TObjArray*"); R__b >> fElements; R__b.ClassEnd(TStreamerInfo::Class()); + // Dubious reset to the expected end it was added as part the commit + // log "Several protections added in case of multiple files being read/updated + // in parallel." R__b.SetBufferOffset(R__s+R__c+sizeof(UInt_t)); + R__b.CheckByteCount(R__s, R__c, TStreamerInfo::Class()); ResetBit(kIsCompiled); ResetBit(kBuildOldUsed); ResetBit(kBuildRunning); From 4eb37ea3d39863974310210f8371a9370ace89d2 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 16 Feb 2026 11:30:58 -0600 Subject: [PATCH 20/36] io/roofit: Correct CheckByteCount placement. Due to the need for using a stack of bytecount position, we can no longer support redundant calls to CheckByteCount. --- roofit/roofitcore/src/RooCategory.cxx | 4 ++-- roofit/roofitcore/src/RooDataHist.cxx | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/roofit/roofitcore/src/RooCategory.cxx b/roofit/roofitcore/src/RooCategory.cxx index 054f44d760e61..4b416087b426d 100644 --- a/roofit/roofitcore/src/RooCategory.cxx +++ b/roofit/roofitcore/src/RooCategory.cxx @@ -445,6 +445,7 @@ void RooCategory::Streamer(TBuffer &R__b) installLegacySharedProp(props); // props was allocated by I/O system, we cannot delete here in case it gets reused + R__b.CheckByteCount(R__s, R__c, RooCategory::IsA()); } else if (R__v == 2) { RooAbsCategoryLValue::Streamer(R__b); @@ -453,6 +454,7 @@ void RooCategory::Streamer(TBuffer &R__b) props->Streamer(R__b); installLegacySharedProp(props.get()); + R__b.CheckByteCount(R__s, R__c, RooCategory::IsA()); } else { // Starting at v3, ranges are shared using a shared pointer, which cannot be read by ROOT's I/O. // Instead, ranges are written as a normal pointer, and here we restore the sharing. @@ -461,8 +463,6 @@ void RooCategory::Streamer(TBuffer &R__b) _rangesPointerForIO = nullptr; } - R__b.CheckByteCount(R__s, R__c, RooCategory::IsA()); - } else { // Since we cannot write shared pointers yet, assign the shared ranges to a normal pointer, // write, and restore. diff --git a/roofit/roofitcore/src/RooDataHist.cxx b/roofit/roofitcore/src/RooDataHist.cxx index 73fd5b60e5e0b..6c0a8112c65af 100644 --- a/roofit/roofitcore/src/RooDataHist.cxx +++ b/roofit/roofitcore/src/RooDataHist.cxx @@ -2352,7 +2352,6 @@ void RooDataHist::Streamer(TBuffer &R__b) { if (R__v > 2) { R__b.ReadClassBuffer(RooDataHist::Class(),this,R__v,R__s,R__c); - R__b.CheckByteCount(R__s, R__c, RooDataHist::IsA()); initialize(nullptr, false); } else { From 0daeba0cb3b3c4f0a46f67e7970a568e7e5c1f3e Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 16 Feb 2026 12:51:43 -0600 Subject: [PATCH 21/36] NFC io: tighten local variable definition --- io/io/src/TBufferFile.cxx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index ff72db6ba2dc8..96ce2d5ce3f4c 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -2811,11 +2811,9 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) R__ASSERT(IsReading()); // read byte count and/or tag (older files don't have byte count) - TClass *cl; if (fBufCur < fBuffer || fBufCur > fBufMax) { fBufCur = fBufMax; - cl = (TClass*)-1; - return cl; + return (TClass*)-1; } UInt_t bcnt, tag, startpos = 0; *this >> bcnt; @@ -2838,6 +2836,7 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) return 0; } + TClass *cl; if (tag == kNewClassTag) { // got a new class description followed by a new object From b62f06fb02f8ce16ecabc3e5ee948a3c6a80be56 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 16 Feb 2026 12:52:37 -0600 Subject: [PATCH 22/36] io: CheckByteCount need class pointer/name as argument --- io/io/src/TStreamerInfoReadBuffer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index 8135f9416fc82..9bf5d4eaf9a1e 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -1221,7 +1221,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, } } } - b.CheckByteCount(start,count,aElement->GetFullName()); + b.CheckByteCount(start, count, aElement->GetClass()); continue; } if (pstreamer == 0) { From 96dbb09809bcc700d07ba7e630a9f0ccb7389119 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 16 Feb 2026 12:58:33 -0600 Subject: [PATCH 23/36] io: Add debugging utility for mismatch Set/CheckByteCount --- io/io/inc/TBufferFile.h | 9 +++-- io/io/src/TBufferFile.cxx | 73 ++++++++++++++++++++++++++++++++------- 2 files changed, 68 insertions(+), 14 deletions(-) diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index 143f8ac374a69..bada9600751ac 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -53,7 +53,12 @@ class TBufferFile : public TBufferIO { InfoList_t fInfoStack; ///< Stack of pointers to the TStreamerInfos using ByteCountLocator_t = std::size_t; // This might become a pair if we implement chunked keys - using ByteCountStack_t = std::vector; + struct ByteCountLocationInfo { + ByteCountLocator_t locator; ///< Position where the byte count value is stored + const TClass* cl; ///< Class for which the byte count is reserved + const TClass* alt; + }; + using ByteCountStack_t = std::vector; ByteCountStack_t fByteCountStack; ///GetName() && strcmp(passedClassName, stackClass->GetName()) == 0); + }; if (startpos == kOverflowPosition) { // The position is above 4GB but was cached using a 32 bit variable. - startpos = fByteCountStack.back(); + startpos = fByteCountStack.back().locator; + // See below + R__ASSERT((fByteCountStack.back().cl == nullptr || clss == fByteCountStack.back().cl) + && "Class on the byte count position stack does not match the passed class"); + } else { + // This assert allows to reject cases that used to be valid (missing or + // redundant call to SetByteCount) but are now an error because the + // position is passed through a stack (and redundantly via the argument + // for 'small' buffers). + const auto stackClass = fByteCountStack.back().cl; + const auto altStackClass = fByteCountStack.back().alt; + const bool classMatches = classMatcher(stackClass, clss, classname) || + classMatcher(altStackClass, clss, classname); + if (startpos != fByteCountStack.back().locator) { + const char *stackClassName = stackClass ? stackClass->GetName() : "not specified"; + const char *passedClassName = clss ? clss->GetName() : classname ? classname : "not specified"; + Fatal("CheckByteCount", + "Incorrect Streamer Function.\n\tByte count position stack mismatch: expected %zu but got %llu.\n\tClass on stack is %s, passed class is %s", + fByteCountStack.back().locator, startpos, + stackClassName ? stackClassName : "nullptr", passedClassName ? passedClassName : "nullptr"); + } else if (!classMatches) { + const char *stackClassName = stackClass ? stackClass->GetName() : nullptr; + const char *passedClassName = clss ? clss->GetName() : classname; + if (classMatcher(stackClass, clss, classname)) { + // In case the mismatch comes from the alt class. + stackClassName = altStackClass ? altStackClass->GetName() : nullptr; + } + Fatal("CheckByteCount", + "Incorrect Streamer Function.\n\tClass mismatch for byte count position %llu: expected %s but got %s", + startpos, stackClassName ? stackClassName : "nullptr", passedClassName ? passedClassName : "nullptr"); + } } if (bcnt == kOverflowCount) { // in case we are checking a byte count for which we postponed @@ -2770,7 +2812,7 @@ void TBufferFile::WriteObjectClass(const void *actualObjectStart, const TClass * } // reserve space for leading byte count - UInt_t cntpos = ReserveByteCount(); + UInt_t cntpos = ReserveByteCount(actualClass); // write class of object first Int_t mapsize = fMap->Capacity(); // The slot depends on the capacity and WriteClass might induce an increase. @@ -2825,7 +2867,8 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) // When objTag is not used, the caller is not interested in the byte // count and will not (can not) call CheckByteCount. if (objTag) - fByteCountStack.push_back(fBufCur - fBuffer - sizeof(UInt_t)); + // Note: the actual class is set later on. + fByteCountStack.push_back({fBufCur - fBuffer - sizeof(UInt_t), clReq, nullptr}); startpos = UInt_t(fBufCur-fBuffer); *this >> tag; } @@ -2884,7 +2927,11 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) } // return bytecount in objTag - if (objTag) *objTag = (bcnt & ~kByteCountMask); + if (objTag) { + *objTag = (bcnt & ~kByteCountMask); + if (cl) + fByteCountStack.back().alt = cl; + } // case of unknown class if (!cl) cl = (TClass*)-1; @@ -3017,7 +3064,7 @@ Version_t TBufferFile::ReadVersion(UInt_t *startpos, UInt_t *bcnt, const TClass // before reading object save start position auto full_startpos = fBufCur - fBuffer; *startpos = full_startpos <= kMaxCountPosition ? UInt_t(full_startpos) : kOverflowPosition; - fByteCountStack.push_back(full_startpos); + fByteCountStack.push_back({(size_t)full_startpos, cl, nullptr}); } // read byte count (older files don't have byte count) @@ -3156,7 +3203,8 @@ Version_t TBufferFile::ReadVersionNoCheckSum(UInt_t *startpos, UInt_t *bcnt) // before reading object save start position auto full_startpos = fBufCur - fBuffer; *startpos = full_startpos < kMaxCountPosition ? UInt_t(full_startpos) : kOverflowPosition; - fByteCountStack.push_back(full_startpos); + // TODO: Extend ReadVersionNoCheckSum to take the class pointer. + fByteCountStack.push_back({(size_t)full_startpos, nullptr, nullptr}); } // read byte count (older files don't have byte count) @@ -3281,7 +3329,7 @@ UInt_t TBufferFile::WriteVersion(const TClass *cl, Bool_t useBcnt) UInt_t cntpos = 0; if (useBcnt) { // reserve space for leading byte count - cntpos = ReserveByteCount(); + cntpos = ReserveByteCount(cl); } Version_t version = cl->GetClassVersion(); @@ -3310,7 +3358,7 @@ UInt_t TBufferFile::WriteVersionMemberWise(const TClass *cl, Bool_t useBcnt) UInt_t cntpos = 0; if (useBcnt) { // reserve space for leading byte count - cntpos = ReserveByteCount(); + cntpos = ReserveByteCount(cl); } Version_t version = cl->GetClassVersion(); @@ -3466,14 +3514,15 @@ UInt_t TBufferFile::CheckObject(UInt_t offset, const TClass *cl, Bool_t readClas /// Reserve space for a leading byte count and return the position where to /// store the byte count value. /// +/// \param[in] cl The class for which we are reserving the byte count, used for error reporting. /// \return The position (cntpos) where the byte count should be stored later, /// or kOverflowPosition if the position exceeds kMaxCountPosition -UInt_t TBufferFile::ReserveByteCount() +UInt_t TBufferFile::ReserveByteCount(const TClass *cl) { // reserve space for leading byte count - auto full_cntpos = fBufCur - fBuffer; - fByteCountStack.push_back(full_cntpos); + size_t full_cntpos = fBufCur - fBuffer; + fByteCountStack.push_back({full_cntpos, cl, nullptr}); *this << (UInt_t)kByteCountMask; // placeholder for byte count return full_cntpos <= kMaxCountPosition ? full_cntpos : kOverflowPosition; } From 3dcf78389dcce486e1a20f2e63e0d39654e69215 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 16 Feb 2026 20:17:54 -0600 Subject: [PATCH 24/36] io/tree: Remove redundant CheckByteCount --- tree/tree/src/TBranch.cxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/tree/tree/src/TBranch.cxx b/tree/tree/src/TBranch.cxx index 768434017ea86..e0cfeb01c36b2 100644 --- a/tree/tree/src/TBranch.cxx +++ b/tree/tree/src/TBranch.cxx @@ -3096,10 +3096,8 @@ void TBranch::Streamer(TBuffer& b) fBasketSeek [fWriteBasket] = fBasketSeek [fWriteBasket-1]; } - // Check Byte Count is not needed since it was done in ReadBuffer if (!fSplitLevel && fBranches.GetEntriesFast()) fSplitLevel = 1; gROOT->SetReadingObject(false); - b.CheckByteCount(R__s, R__c, TBranch::IsA()); if (IsA() == TBranch::Class()) { if (fNleaves == 0) { fReadLeaves = &TBranch::ReadLeaves0Impl; From 43222a8c88ec56215c001f31fc8dda511492138c Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 17 Feb 2026 12:29:46 -0600 Subject: [PATCH 25/36] io: byte count stack handling in skip object --- core/base/inc/TBuffer.h | 17 +++++++++++++++++ io/io/inc/TBufferFile.h | 1 + io/io/inc/TBufferJSON.h | 1 + io/io/src/TBufferFile.cxx | 21 ++++++++++++++++++++- io/io/src/TBufferJSON.cxx | 1 + io/io/src/TStreamerInfoReadBuffer.cxx | 2 +- io/sql/inc/TBufferSQL2.h | 1 + io/sql/src/TBufferSQL2.cxx | 8 ++++++++ io/xml/inc/TBufferXML.h | 1 + io/xml/src/TBufferXML.cxx | 9 +++++++++ 10 files changed, 60 insertions(+), 2 deletions(-) diff --git a/core/base/inc/TBuffer.h b/core/base/inc/TBuffer.h index d6cae935560ed..71c2a214dbd1e 100644 --- a/core/base/inc/TBuffer.h +++ b/core/base/inc/TBuffer.h @@ -135,6 +135,23 @@ class TBuffer : public TObject { virtual void *ReadObjectAny(const TClass* cast) = 0; virtual void SkipObjectAny() = 0; + /** \brief Skip, based on a known start position and byte count, to the end of the current object. + * + * \warning Advanced use only. + * + * This overload exists primarily for error handling within a Streamer. + * + * A typical use case is a Streamer with a flow like: + * - Read version and bytecount + * - Start reading the data + * - Detect an error (e.g. missing some information for a CollectionProxy) + * - Properly set the cursor to the end of the object to allow reading to continue. + * + * Because the actual byte count information for \e large byte counts is kept only on the + * internal byte-count stack, there are only two viable options to support this use case: + * provide this overload, or make the stack accessible (e.g. via a getter). + */ + virtual void SkipObjectAny(Long64_t start, UInt_t bytecount) = 0; virtual void TagStreamerInfo(TVirtualStreamerInfo* info) = 0; virtual void IncrementLevel(TVirtualStreamerInfo* info) = 0; diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index bada9600751ac..e6f69c726793c 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -102,6 +102,7 @@ class TBufferFile : public TBufferIO { void *ReadObjectAny(const TClass* cast) override; void SkipObjectAny() override; + void SkipObjectAny(Long64_t start, UInt_t bytecount) override; void IncrementLevel(TVirtualStreamerInfo* info) override; void SetStreamerElementNumber(TStreamerElement*,Int_t) override {} diff --git a/io/io/inc/TBufferJSON.h b/io/io/inc/TBufferJSON.h index 8ab2d521e6a1d..6f7a8ca944c84 100644 --- a/io/io/inc/TBufferJSON.h +++ b/io/io/inc/TBufferJSON.h @@ -109,6 +109,7 @@ class TBufferJSON final : public TBufferText { void *ReadObjectAny(const TClass *clCast) final; void SkipObjectAny() final; + void SkipObjectAny(Long64_t start, UInt_t bytecount) final; // these methods used in streamer info to indicate currently streamed element, void IncrementLevel(TVirtualStreamerInfo *) final; diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 231121dcdb42c..994a01c73a543 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -2612,7 +2612,26 @@ void TBufferFile::SkipObjectAny() { UInt_t start, count; ReadVersion(&start, &count); - SetBufferOffset(start+count+sizeof(UInt_t)); + if (count == kOverflowPosition) + SetBufferOffset(start+ fByteCountStack.back().locator + sizeof(UInt_t)); + else + SetBufferOffset(start+count+sizeof(UInt_t)); + // The byte count location is pushed on the stack only if there is + // actually a byte count. + if (count) + fByteCountStack.pop_back(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Skip any kind of object from buffer + +void TBufferFile::SkipObjectAny(Long64_t start, UInt_t count) +{ + if (count == kOverflowPosition) + SetBufferOffset(start + fByteCountStack.back().locator + sizeof(UInt_t)); + else + SetBufferOffset(start + count + sizeof(UInt_t)); + fByteCountStack.pop_back(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/io/io/src/TBufferJSON.cxx b/io/io/src/TBufferJSON.cxx index de880d809b583..63dc652cee39e 100644 --- a/io/io/src/TBufferJSON.cxx +++ b/io/io/src/TBufferJSON.cxx @@ -2589,6 +2589,7 @@ void *TBufferJSON::ReadObjectAny(const TClass *expectedClass) /// Skip any kind of object from buffer void TBufferJSON::SkipObjectAny() {} +void TBufferJSON::SkipObjectAny(Long64_t, UInt_t) {} //////////////////////////////////////////////////////////////////////////////// /// Write object to buffer. Only used from TBuffer diff --git a/io/io/src/TStreamerInfoReadBuffer.cxx b/io/io/src/TStreamerInfoReadBuffer.cxx index 9bf5d4eaf9a1e..611abf936bacb 100644 --- a/io/io/src/TStreamerInfoReadBuffer.cxx +++ b/io/io/src/TStreamerInfoReadBuffer.cxx @@ -1279,7 +1279,7 @@ Int_t TStreamerInfo::ReadBuffer(TBuffer &b, const T &arr, // and the collection is always empty, // So let's skip the rest (which requires the StreamerInfo of the valueClass ... which we do not have) - b.SetBufferOffset(start+count+sizeof(UInt_t)); + b.SkipObjectAny(start, count); continue; } diff --git a/io/sql/inc/TBufferSQL2.h b/io/sql/inc/TBufferSQL2.h index 1c7c7e696adae..1217c4f370c57 100644 --- a/io/sql/inc/TBufferSQL2.h +++ b/io/sql/inc/TBufferSQL2.h @@ -149,6 +149,7 @@ class TBufferSQL2 final : public TBufferText { void *ReadObjectAny(const TClass *clCast) final; void SkipObjectAny() final; + void SkipObjectAny(Long64_t start, UInt_t bytecount) final; void IncrementLevel(TVirtualStreamerInfo *) final; void SetStreamerElementNumber(TStreamerElement *elem, Int_t comp_type) final; diff --git a/io/sql/src/TBufferSQL2.cxx b/io/sql/src/TBufferSQL2.cxx index eae8309d44560..c0d5d59dea4cc 100644 --- a/io/sql/src/TBufferSQL2.cxx +++ b/io/sql/src/TBufferSQL2.cxx @@ -862,6 +862,14 @@ void TBufferSQL2::SkipObjectAny() { } +//////////////////////////////////////////////////////////////////////////////// +/// Skip any kind of object from buffer +/// Actually skip only one node on current level of xml structure + +void TBufferSQL2::SkipObjectAny(Long64_t, UInt_t) +{ +} + //////////////////////////////////////////////////////////////////////////////// /// Write object to buffer. Only used from TBuffer diff --git a/io/xml/inc/TBufferXML.h b/io/xml/inc/TBufferXML.h index 4210e1eb159dc..319b0aa2e454e 100644 --- a/io/xml/inc/TBufferXML.h +++ b/io/xml/inc/TBufferXML.h @@ -77,6 +77,7 @@ class TBufferXML final : public TBufferText, public TXMLSetup { void *ReadObjectAny(const TClass *clCast) final; void SkipObjectAny() final; + void SkipObjectAny(Long64_t start, UInt_t bytecount) final; void IncrementLevel(TVirtualStreamerInfo *) final; void SetStreamerElementNumber(TStreamerElement *elem, Int_t comp_type) final; diff --git a/io/xml/src/TBufferXML.cxx b/io/xml/src/TBufferXML.cxx index 6aafce756b643..fdfa96d3a2bcd 100644 --- a/io/xml/src/TBufferXML.cxx +++ b/io/xml/src/TBufferXML.cxx @@ -1510,6 +1510,15 @@ void TBufferXML::SkipObjectAny() ShiftStack("skipobjectany"); } +//////////////////////////////////////////////////////////////////////////////// +/// Skip any kind of object from buffer +/// Actually skip only one node on current level of xml structure + +void TBufferXML::SkipObjectAny(Long64_t, UInt_t) +{ + ShiftStack("skipobjectany"); +} + //////////////////////////////////////////////////////////////////////////////// /// Write object to buffer. Only used from TBuffer From 1814b3281f6b69ae31039d27334833f39c89b2e8 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 17 Feb 2026 17:22:48 -0600 Subject: [PATCH 26/36] io: Add TBuffer::ByteCountWriter RAII. This simplify the implementation of calling `ReserveByteCount` and then `SetByteCount` and could also simplify removing the use of the ByteCountStack in some/most cases. Note: `ReserveByteCount` is protected and the existing code that does the same semantic action was only recording the position in a 32 bits integer. That code needs to be updated by either making `ReserverByteCount` public (making removal of external use of the ByteCountStack more difficult) or by introducing a RAII that hides the size of the integer. --- core/base/inc/TBuffer.h | 24 ++++++++++++++++++++++++ io/io/inc/TBufferFile.h | 2 +- io/io/inc/TBufferText.h | 2 ++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/core/base/inc/TBuffer.h b/core/base/inc/TBuffer.h index 71c2a214dbd1e..fb0e369fca388 100644 --- a/core/base/inc/TBuffer.h +++ b/core/base/inc/TBuffer.h @@ -69,6 +69,8 @@ class TBuffer : public TObject { Int_t Write(const char *name, Int_t opt, Long64_t bufsize) const override { return TObject::Write(name, opt, bufsize); } + virtual UInt_t ReserveByteCount(const TClass *) = 0; + public: enum EMode { kRead = 0, kWrite = 1 }; enum EStatusBits { @@ -125,6 +127,28 @@ class TBuffer : public TObject { virtual Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss) = 0; virtual Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const char *classname) = 0; virtual void SetByteCount(ULong64_t cntpos, Bool_t packInVersion = kFALSE)= 0; + class ByteCountWriter { + TBuffer &fBuffer; + Bool_t fPackInVersion; + ULong64_t fCntPos; + public: + ByteCountWriter() = delete; + ByteCountWriter(const ByteCountWriter&) = delete; + ByteCountWriter& operator=(const ByteCountWriter&) = delete; + ByteCountWriter(ByteCountWriter&&) = delete; + ByteCountWriter& operator=(ByteCountWriter&&) = delete; + + ByteCountWriter(TBuffer &buf, const TClass *cl, Bool_t packInVersion = kFALSE) : fBuffer(buf), fPackInVersion(packInVersion) { + // We could split ReserveByteCount in a 32bit version that uses + // the ByteCountStack and another version that always returns the + // long range position. For now keep it 'simpler' by always using + // the stack. + fCntPos = fBuffer.ReserveByteCount(cl); + } + ~ByteCountWriter() { + fBuffer.SetByteCount(fCntPos, fPackInVersion); + } + }; virtual void SkipVersion(const TClass *cl = nullptr) = 0; virtual Version_t ReadVersion(UInt_t *start = nullptr, UInt_t *bcnt = nullptr, const TClass *cl = nullptr) = 0; diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index e6f69c726793c..2b7d221ea2a77 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -77,7 +77,7 @@ class TBufferFile : public TBufferIO { Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss, const char* classname); void CheckCount(UInt_t offset) override; UInt_t CheckObject(UInt_t offset, const TClass *cl, Bool_t readClass = kFALSE); - UInt_t ReserveByteCount(const TClass *cl); + UInt_t ReserveByteCount(const TClass *cl) override; void WriteObjectClass(const void *actualObjStart, const TClass *actualClass, Bool_t cacheReuse) override; diff --git a/io/io/inc/TBufferText.h b/io/io/inc/TBufferText.h index a5cbffc99deb0..aa177cbca4bde 100644 --- a/io/io/inc/TBufferText.h +++ b/io/io/inc/TBufferText.h @@ -23,6 +23,8 @@ class TBufferText : public TBufferIO { TBufferText(); TBufferText(TBuffer::EMode mode, TObject *parent = nullptr); + UInt_t ReserveByteCount(const TClass *) override { return 0; } + public: ~TBufferText() override; From 61e21e45a19022f3d7fc2d140639ad70d02b6d28 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 17 Feb 2026 17:26:41 -0600 Subject: [PATCH 27/36] io: Upgrade TArray::WriteArray to support long range byte count --- core/cont/src/TArray.cxx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/core/cont/src/TArray.cxx b/core/cont/src/TArray.cxx index a5283e5523d53..6589c88c510c1 100644 --- a/core/cont/src/TArray.cxx +++ b/core/cont/src/TArray.cxx @@ -90,18 +90,16 @@ void TArray::WriteArray(TBuffer &b, const TArray *a) b << (UInt_t) 0; } else { + TClass *cl = a->IsA(); // Reserve space for leading byte count - UInt_t cntpos = UInt_t(b.Length()); - b.SetBufferOffset(Int_t(cntpos+sizeof(UInt_t))); + TBuffer::ByteCountWriter bcnt(b, cl); - TClass *cl = a->IsA(); b.WriteClass(cl); - ((TArray *)a)->Streamer(b); + const_cast(a)->Streamer(b); - // Write byte count - b.SetByteCount(cntpos); + // The byte count is written automatically by the ByteCountWriter destructor } } From c13304a141a415734fbc452dd5c8445eca47a98f Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Wed, 18 Feb 2026 10:39:28 -0600 Subject: [PATCH 28/36] roottest: Fix cmake macro used outside of roottest dir. In order to allow using the roottest CMake macro outside of the roottest sub-directory, we need to make the variable used by those macros 'cache variable' so that they can use elsewhere. This is a hack and works only after the second CMake configuration/invocation. --- cmake/modules/RootMacros.cmake | 4 ++++ roottest/CMakeLists.txt | 16 ++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index f7f5183fe45c5..4ed41de4f8e44 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -45,6 +45,10 @@ else() set(ROOT_LIBRARY_PROPERTIES ${ROOT_LIBRARY_PROPERTIES} ${ROOT_LIBRARY_PROPERTIES_NO_VERSION} ) endif() +if (NOT DEFINED ROOT_root_CMD) + set(ROOT_root_CMD $) +endif() + include(CMakeParseArguments) #--------------------------------------------------------------------------------------------------- diff --git a/roottest/CMakeLists.txt b/roottest/CMakeLists.txt index 7b290c389767d..427bb0e311fc7 100644 --- a/roottest/CMakeLists.txt +++ b/roottest/CMakeLists.txt @@ -29,10 +29,10 @@ if(MSVC) execute_process(COMMAND ${ROOT_CONFIG_EXECUTABLE} "--tutdir" OUTPUT_VARIABLE ROOT_TUTORIALS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) cmake_path(CONVERT "${ROOT_TUTORIALS_DIR}" TO_CMAKE_PATH_LIST ROOT_TUTORIALS_DIR) set(ROOT_root_CMD ${ROOTSYS}/bin/root.exe) - set(ROOT_hadd_CMD ${ROOTSYS}/bin/hadd.exe) - set(ROOT_genreflex_CMD ${ROOTSYS}/bin/genreflex.exe) - set(ROOT_rootcint_CMD ${ROOTSYS}/bin/rootcint.exe) - set(ROOT_rootcling_CMD ${ROOTSYS}/bin/rootcling.exe) + set(ROOT_hadd_CMD ${ROOTSYS}/bin/hadd.exe CACHE INTERNAL "ROOTTEST variable for hadd executable") + set(ROOT_genreflex_CMD ${ROOTSYS}/bin/genreflex.exe CACHE INTERNAL "ROOTTEST variable for genreflex executable") + set(ROOT_rootcint_CMD ${ROOTSYS}/bin/rootcint.exe CACHE INTERNAL "ROOTTEST variable for rootcint executable") + set(ROOT_rootcling_CMD ${ROOTSYS}/bin/rootcling.exe CACHE INTERNAL "ROOTTEST variable for rootcling executable") if(CMAKE_GENERATOR MATCHES Ninja) set(ROOT_LIBRARIES Core RIO Net Hist Gpad Graf Tree Rint Matrix MathCore) else() @@ -59,10 +59,10 @@ else() execute_process(COMMAND ${ROOT_CONFIG_EXECUTABLE} "--tutdir" OUTPUT_VARIABLE ROOT_TUTORIALS_DIR OUTPUT_STRIP_TRAILING_WHITESPACE) set(ROOT_LIBRARIES Core RIO Net Hist Gpad Tree Rint Matrix MathCore) set(ROOT_root_CMD ${ROOTSYS}/bin/root.exe) - set(ROOT_hadd_CMD ${ROOTSYS}/bin/hadd) - set(ROOT_genreflex_CMD ${ROOTSYS}/bin/genreflex) - set(ROOT_rootcint_CMD ${ROOTSYS}/bin/rootcint) - set(ROOT_rootcling_CMD rootcling) + set(ROOT_hadd_CMD ${ROOTSYS}/bin/hadd CACHE INTERNAL "ROOTTEST variable for hadd executable") + set(ROOT_genreflex_CMD ${ROOTSYS}/bin/genreflex CACHE INTERNAL "ROOTTEST variable for genreflex executable") + set(ROOT_rootcint_CMD ${ROOTSYS}/bin/rootcint CACHE INTERNAL "ROOTTEST variable for rootcint executable") + set(ROOT_rootcling_CMD rootcling CACHE INTERNAL "ROOTTEST variable for rootcling executable") endif() get_filename_component(ROOT_LIBRARY_DIR "${ROOTSYS}/lib" ABSOLUTE) From 66a0453eb74794e3b75cd8b0923b212f0c7eebdb Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 20 Feb 2026 16:05:05 -0600 Subject: [PATCH 29/36] io-test: disable large byte count test on 32bits platforms --- io/io/test/CMakeLists.txt | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/io/io/test/CMakeLists.txt b/io/io/test/CMakeLists.txt index 61f381acf0274..cd5103b6e5fc5 100644 --- a/io/io/test/CMakeLists.txt +++ b/io/io/test/CMakeLists.txt @@ -41,10 +41,21 @@ endif() # ROOT_ADD_TEST(io-io-test-TMapFileTest COMMAND TMapFileTest complete) #endif() -ROOTTEST_COMPILE_MACRO(testByteCount.cxx - FIXTURES_SETUP io-io-tobj-fixture) +# Detect bitness. +# FIXME: This is also done in roottest/CMakeLists.txt which is included 'too late' for +# the tests here. We should unify this logic and do it only once. +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(_64BIT 1) +elseif(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(_32BIT 1) +endif() + +if(NOT _32BIT) + ROOTTEST_COMPILE_MACRO(testByteCount.cxx + FIXTURES_SETUP io-io-tobj-fixture) -ROOTTEST_ADD_TEST(testLargeByteCounts - MACRO testByteCount.cxx+ - OUTREF testByteCount.ref - FIXTURES_REQUIRED io-io-tobj-fixture) + ROOTTEST_ADD_TEST(testLargeByteCounts + MACRO testByteCount.cxx+ + OUTREF testByteCount.ref + FIXTURES_REQUIRED io-io-tobj-fixture) +endif() \ No newline at end of file From f900f0c95e18fc06f31c68aebfcc21705c036a41 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Fri, 20 Feb 2026 15:40:55 -0600 Subject: [PATCH 30/36] cmake: Include VStudio in the `Ninja` `RESOURCE_LOCK`. When using the Microsoft Visual Studio generator we have the exact same problem, that it might trigger a rebuild of ROOT so we need to apply the same solution. See 06e00a270e6e4f1157bfc344ad0dcdb6c45bb01a. PS. We might want to eventually rename the resource lock. --- cmake/modules/RootCTest.cmake | 7 ++++--- cmake/modules/RootMacros.cmake | 6 +++--- roottest/cling/dict/ROOT-8096/CMakeLists.txt | 2 +- roottest/cling/stl/dicts/CMakeLists.txt | 2 +- roottest/root/io/rootcint/sigbug/CMakeLists.txt | 2 +- roottest/root/io/tmpifile/CMakeLists.txt | 2 +- roottest/root/io/transient/base/CMakeLists.txt | 2 +- roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt | 2 +- 8 files changed, 13 insertions(+), 12 deletions(-) diff --git a/cmake/modules/RootCTest.cmake b/cmake/modules/RootCTest.cmake index e434770ce0729..aaa92587165a6 100644 --- a/cmake/modules/RootCTest.cmake +++ b/cmake/modules/RootCTest.cmake @@ -51,13 +51,14 @@ foreach(d ${test_list}) endif() endforeach() -# When ninja is in use, tests that compile an executable might try to rebuild the entire build tree. -# If multiple of these are invoked in parallel, ninja will suffer from race conditions. +# When ninja or the Microsoft generator are in use, tests that compile an executable might try +# to rebuild the entire build tree. If multiple of these are invoked in parallel, ninja will +# suffer from race conditions. # To solve this, do the following: # - Add a test that updates the build tree (equivalent to "ninja all"). This one will run in complete isolation. # - Make all tests that require a ninja build depend on the above test. # - Use a RESOURCE_LOCK on all tests that invoke ninja, so no two tests will invoke ninja in parallel -if(CMAKE_GENERATOR MATCHES Ninja) +if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") add_test(NAME ninja-build-all COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}) set_tests_properties(ninja-build-all PROPERTIES diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index 4ed41de4f8e44..47db6ce65cbd8 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -2661,7 +2661,7 @@ macro(ROOTTEST_GENERATE_DICTIONARY dictname) -- ${always-make}) set_property(TEST ${GENERATE_DICTIONARY_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) - if(CMAKE_GENERATOR MATCHES Ninja) + if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() @@ -2769,7 +2769,7 @@ macro(ROOTTEST_GENERATE_REFLEX_DICTIONARY dictionary) -- ${always-make}) set_property(TEST ${GENERATE_REFLEX_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) - if(CMAKE_GENERATOR MATCHES Ninja) + if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() @@ -2881,7 +2881,7 @@ macro(ROOTTEST_GENERATE_EXECUTABLE executable) RESOURCE_LOCK ${ARG_RESOURCE_LOCK}) endif() - if(CMAKE_GENERATOR MATCHES Ninja) + if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/cling/dict/ROOT-8096/CMakeLists.txt b/roottest/cling/dict/ROOT-8096/CMakeLists.txt index 07c916ce01b19..b88a0b9ff1a66 100644 --- a/roottest/cling/dict/ROOT-8096/CMakeLists.txt +++ b/roottest/cling/dict/ROOT-8096/CMakeLists.txt @@ -20,7 +20,7 @@ add_test(NAME roottest-cling-dict-ROOT-8096-build -- ${always-make}) # --target ${targetname_libgen}${fast}) set_property(TEST roottest-cling-dict-ROOT-8096-build PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) -if(CMAKE_GENERATOR MATCHES Ninja) +if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/cling/stl/dicts/CMakeLists.txt b/roottest/cling/stl/dicts/CMakeLists.txt index c31d0a3ee25b3..162e6111ebcbe 100644 --- a/roottest/cling/stl/dicts/CMakeLists.txt +++ b/roottest/cling/stl/dicts/CMakeLists.txt @@ -11,7 +11,7 @@ ROOTTEST_LINKER_LIBRARY(stldictTest TEST MyClass1.cpp MyClass2.cpp MyClass3.cpp # of targets. Doing so right now would build the dictionaries twice. ROOT_ADD_TEST(roottest-cling-stl-dicts-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target stldictTest${fast} -- ${always-make}) -if(CMAKE_GENERATOR MATCHES Ninja) +if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/io/rootcint/sigbug/CMakeLists.txt b/roottest/root/io/rootcint/sigbug/CMakeLists.txt index a87613d25dbe6..7277dce32c311 100644 --- a/roottest/root/io/rootcint/sigbug/CMakeLists.txt +++ b/roottest/root/io/rootcint/sigbug/CMakeLists.txt @@ -19,7 +19,7 @@ add_test(NAME ${GENERATE_DICTIONARY_TEST} ${build_config} --target ${dictname}${fast} -- ${always-make}) -if(CMAKE_GENERATOR MATCHES Ninja) +if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/io/tmpifile/CMakeLists.txt b/roottest/root/io/tmpifile/CMakeLists.txt index 840f475bcba53..fc94946fa7f64 100644 --- a/roottest/root/io/tmpifile/CMakeLists.txt +++ b/roottest/root/io/tmpifile/CMakeLists.txt @@ -13,7 +13,7 @@ ROOTTEST_ADD_TEST(split-fail ROOTTEST_ADD_TEST(libjetevent-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target JetEvent${fast} -- ${always-make}) -if(CMAKE_GENERATOR MATCHES Ninja) +if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/io/transient/base/CMakeLists.txt b/roottest/root/io/transient/base/CMakeLists.txt index c59bacde1dfa2..5d4b65b5cecea 100644 --- a/roottest/root/io/transient/base/CMakeLists.txt +++ b/roottest/root/io/transient/base/CMakeLists.txt @@ -10,7 +10,7 @@ add_test(NAME roottest-root-io-transient-base-build --target base${fast} -- ${always-make}) set_property(TEST roottest-root-io-transient-base-build PROPERTY FIXTURES_SETUP root-io-transient-base-build) -if(CMAKE_GENERATOR MATCHES Ninja) +if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt b/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt index 18ffb14556114..0bf392ff26a8d 100644 --- a/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt +++ b/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt @@ -27,7 +27,7 @@ if(NOT MSVC OR win_broken_tests) ROOTTEST_ADD_TEST(PyCoolLib-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target PyCoolLib${fast} -- ${always-make} FIXTURES_REQUIRED PyCool-reflex-dict) - if(CMAKE_GENERATOR MATCHES Ninja) + if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() From f5308ea009be9f885288891da8d4d2517d81f7c9 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 23 Feb 2026 10:53:04 -0600 Subject: [PATCH 31/36] cmake: Use single variable to select Ninja Build resource lock --- cmake/modules/RootCTest.cmake | 3 ++- cmake/modules/RootMacros.cmake | 6 +++--- roottest/cling/dict/ROOT-8096/CMakeLists.txt | 2 +- roottest/cling/stl/dicts/CMakeLists.txt | 2 +- roottest/root/io/rootcint/sigbug/CMakeLists.txt | 2 +- roottest/root/io/tmpifile/CMakeLists.txt | 2 +- roottest/root/io/transient/base/CMakeLists.txt | 2 +- roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/cmake/modules/RootCTest.cmake b/cmake/modules/RootCTest.cmake index aaa92587165a6..7904c708f5e3a 100644 --- a/cmake/modules/RootCTest.cmake +++ b/cmake/modules/RootCTest.cmake @@ -58,11 +58,12 @@ endforeach() # - Add a test that updates the build tree (equivalent to "ninja all"). This one will run in complete isolation. # - Make all tests that require a ninja build depend on the above test. # - Use a RESOURCE_LOCK on all tests that invoke ninja, so no two tests will invoke ninja in parallel -if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") +if(GeneratorNeedsBuildSerialization) add_test(NAME ninja-build-all COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}) set_tests_properties(ninja-build-all PROPERTIES RESOURCE_LOCK NINJA_BUILD FIXTURES_SETUP NINJA_BUILD_ALL RUN_SERIAL True) + set(GeneratorNeedsBuildSerialization True) endif() diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index 47db6ce65cbd8..2576752fd0e7a 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -2661,7 +2661,7 @@ macro(ROOTTEST_GENERATE_DICTIONARY dictname) -- ${always-make}) set_property(TEST ${GENERATE_DICTIONARY_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) - if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") + if(GeneratorNeedsBuildSerialization) set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() @@ -2769,7 +2769,7 @@ macro(ROOTTEST_GENERATE_REFLEX_DICTIONARY dictionary) -- ${always-make}) set_property(TEST ${GENERATE_REFLEX_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) - if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") + if(GeneratorNeedsBuildSerialization) set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() @@ -2881,7 +2881,7 @@ macro(ROOTTEST_GENERATE_EXECUTABLE executable) RESOURCE_LOCK ${ARG_RESOURCE_LOCK}) endif() - if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") + if(GeneratorNeedsBuildSerialization) set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/cling/dict/ROOT-8096/CMakeLists.txt b/roottest/cling/dict/ROOT-8096/CMakeLists.txt index b88a0b9ff1a66..4614dba98fa9b 100644 --- a/roottest/cling/dict/ROOT-8096/CMakeLists.txt +++ b/roottest/cling/dict/ROOT-8096/CMakeLists.txt @@ -20,7 +20,7 @@ add_test(NAME roottest-cling-dict-ROOT-8096-build -- ${always-make}) # --target ${targetname_libgen}${fast}) set_property(TEST roottest-cling-dict-ROOT-8096-build PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) -if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") +if(GeneratorNeedsBuildSerialization) set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/cling/stl/dicts/CMakeLists.txt b/roottest/cling/stl/dicts/CMakeLists.txt index 162e6111ebcbe..874281c54f2cb 100644 --- a/roottest/cling/stl/dicts/CMakeLists.txt +++ b/roottest/cling/stl/dicts/CMakeLists.txt @@ -11,7 +11,7 @@ ROOTTEST_LINKER_LIBRARY(stldictTest TEST MyClass1.cpp MyClass2.cpp MyClass3.cpp # of targets. Doing so right now would build the dictionaries twice. ROOT_ADD_TEST(roottest-cling-stl-dicts-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target stldictTest${fast} -- ${always-make}) -if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") +if(GeneratorNeedsBuildSerialization) set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/io/rootcint/sigbug/CMakeLists.txt b/roottest/root/io/rootcint/sigbug/CMakeLists.txt index 7277dce32c311..90bdad91bd6e6 100644 --- a/roottest/root/io/rootcint/sigbug/CMakeLists.txt +++ b/roottest/root/io/rootcint/sigbug/CMakeLists.txt @@ -19,7 +19,7 @@ add_test(NAME ${GENERATE_DICTIONARY_TEST} ${build_config} --target ${dictname}${fast} -- ${always-make}) -if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") +if(GeneratorNeedsBuildSerialization) set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/io/tmpifile/CMakeLists.txt b/roottest/root/io/tmpifile/CMakeLists.txt index fc94946fa7f64..01598393fc184 100644 --- a/roottest/root/io/tmpifile/CMakeLists.txt +++ b/roottest/root/io/tmpifile/CMakeLists.txt @@ -13,7 +13,7 @@ ROOTTEST_ADD_TEST(split-fail ROOTTEST_ADD_TEST(libjetevent-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target JetEvent${fast} -- ${always-make}) -if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") +if(GeneratorNeedsBuildSerialization) set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/io/transient/base/CMakeLists.txt b/roottest/root/io/transient/base/CMakeLists.txt index 5d4b65b5cecea..9b80c6833a991 100644 --- a/roottest/root/io/transient/base/CMakeLists.txt +++ b/roottest/root/io/transient/base/CMakeLists.txt @@ -10,7 +10,7 @@ add_test(NAME roottest-root-io-transient-base-build --target base${fast} -- ${always-make}) set_property(TEST roottest-root-io-transient-base-build PROPERTY FIXTURES_SETUP root-io-transient-base-build) -if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") +if(GeneratorNeedsBuildSerialization) set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() diff --git a/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt b/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt index 0bf392ff26a8d..d28459ed56103 100644 --- a/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt +++ b/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt @@ -27,7 +27,7 @@ if(NOT MSVC OR win_broken_tests) ROOTTEST_ADD_TEST(PyCoolLib-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target PyCoolLib${fast} -- ${always-make} FIXTURES_REQUIRED PyCool-reflex-dict) - if(CMAKE_GENERATOR MATCHES Ninja OR CMAKE_GENERATOR MATCHES "Visual Studio") + if(GeneratorNeedsBuildSerialization) set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) endif() From d4f1ba6c810a3de7d550106c4f61982de1ad5759 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 23 Feb 2026 10:56:59 -0600 Subject: [PATCH 32/36] cmake: Rename Ninja Build resource lock to CMake Build. It is need by at least Ninja and the Visual Studio generator --- cmake/modules/RootCTest.cmake | 8 ++++---- cmake/modules/RootMacros.cmake | 12 ++++++------ roottest/cling/dict/ROOT-8096/CMakeLists.txt | 4 ++-- roottest/cling/stl/dicts/CMakeLists.txt | 4 ++-- roottest/root/io/rootcint/sigbug/CMakeLists.txt | 4 ++-- roottest/root/io/tmpifile/CMakeLists.txt | 4 ++-- roottest/root/io/transient/base/CMakeLists.txt | 4 ++-- .../root/meta/genreflex/ROOT-5768/CMakeLists.txt | 4 ++-- 8 files changed, 22 insertions(+), 22 deletions(-) diff --git a/cmake/modules/RootCTest.cmake b/cmake/modules/RootCTest.cmake index 7904c708f5e3a..a46d7b6d08d9f 100644 --- a/cmake/modules/RootCTest.cmake +++ b/cmake/modules/RootCTest.cmake @@ -59,11 +59,11 @@ endforeach() # - Make all tests that require a ninja build depend on the above test. # - Use a RESOURCE_LOCK on all tests that invoke ninja, so no two tests will invoke ninja in parallel if(GeneratorNeedsBuildSerialization) - add_test(NAME ninja-build-all + add_test(NAME cmake-build-all COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}) - set_tests_properties(ninja-build-all PROPERTIES - RESOURCE_LOCK NINJA_BUILD - FIXTURES_SETUP NINJA_BUILD_ALL + set_tests_properties(cmake-build-all PROPERTIES + RESOURCE_LOCK CMAKE_BUILD + FIXTURES_SETUP CMAKE_BUILD_ALL RUN_SERIAL True) set(GeneratorNeedsBuildSerialization True) endif() diff --git a/cmake/modules/RootMacros.cmake b/cmake/modules/RootMacros.cmake index 2576752fd0e7a..f702d88413e79 100644 --- a/cmake/modules/RootMacros.cmake +++ b/cmake/modules/RootMacros.cmake @@ -2662,8 +2662,8 @@ macro(ROOTTEST_GENERATE_DICTIONARY dictname) set_property(TEST ${GENERATE_DICTIONARY_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) if(GeneratorNeedsBuildSerialization) - set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() if (ARG_FIXTURES_SETUP) @@ -2770,8 +2770,8 @@ macro(ROOTTEST_GENERATE_REFLEX_DICTIONARY dictionary) set_property(TEST ${GENERATE_REFLEX_TEST} PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) if(GeneratorNeedsBuildSerialization) - set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST ${GENERATE_REFLEX_TEST} APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() if (ARG_FIXTURES_SETUP) @@ -2882,8 +2882,8 @@ macro(ROOTTEST_GENERATE_EXECUTABLE executable) endif() if(GeneratorNeedsBuildSerialization) - set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST ${GENERATE_EXECUTABLE_TEST} APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() if(MSVC AND NOT CMAKE_GENERATOR MATCHES Ninja) diff --git a/roottest/cling/dict/ROOT-8096/CMakeLists.txt b/roottest/cling/dict/ROOT-8096/CMakeLists.txt index 4614dba98fa9b..e86dfb94915d3 100644 --- a/roottest/cling/dict/ROOT-8096/CMakeLists.txt +++ b/roottest/cling/dict/ROOT-8096/CMakeLists.txt @@ -21,8 +21,8 @@ add_test(NAME roottest-cling-dict-ROOT-8096-build # --target ${targetname_libgen}${fast}) set_property(TEST roottest-cling-dict-ROOT-8096-build PROPERTY ENVIRONMENT ${ROOTTEST_ENVIRONMENT}) if(GeneratorNeedsBuildSerialization) - set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST roottest-cling-dict-ROOT-8096-build APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() if(MSVC AND NOT CMAKE_GENERATOR MATCHES Ninja) diff --git a/roottest/cling/stl/dicts/CMakeLists.txt b/roottest/cling/stl/dicts/CMakeLists.txt index 874281c54f2cb..58eb53d5d9839 100644 --- a/roottest/cling/stl/dicts/CMakeLists.txt +++ b/roottest/cling/stl/dicts/CMakeLists.txt @@ -12,6 +12,6 @@ ROOTTEST_LINKER_LIBRARY(stldictTest TEST MyClass1.cpp MyClass2.cpp MyClass3.cpp ROOT_ADD_TEST(roottest-cling-stl-dicts-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target stldictTest${fast} -- ${always-make}) if(GeneratorNeedsBuildSerialization) - set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST roottest-cling-stl-dicts-build APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() diff --git a/roottest/root/io/rootcint/sigbug/CMakeLists.txt b/roottest/root/io/rootcint/sigbug/CMakeLists.txt index 90bdad91bd6e6..29643d57e2638 100644 --- a/roottest/root/io/rootcint/sigbug/CMakeLists.txt +++ b/roottest/root/io/rootcint/sigbug/CMakeLists.txt @@ -20,6 +20,6 @@ add_test(NAME ${GENERATE_DICTIONARY_TEST} --target ${dictname}${fast} -- ${always-make}) if(GeneratorNeedsBuildSerialization) - set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST ${GENERATE_DICTIONARY_TEST} APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() diff --git a/roottest/root/io/tmpifile/CMakeLists.txt b/roottest/root/io/tmpifile/CMakeLists.txt index 01598393fc184..d8ce592226b0a 100644 --- a/roottest/root/io/tmpifile/CMakeLists.txt +++ b/roottest/root/io/tmpifile/CMakeLists.txt @@ -14,8 +14,8 @@ ROOTTEST_ADD_TEST(split-fail ROOTTEST_ADD_TEST(libjetevent-build COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target JetEvent${fast} -- ${always-make}) if(GeneratorNeedsBuildSerialization) - set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST roottest-root-io-tmpifile-libjetevent-build APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() ROOTTEST_ADD_TEST(sync-rate diff --git a/roottest/root/io/transient/base/CMakeLists.txt b/roottest/root/io/transient/base/CMakeLists.txt index 9b80c6833a991..820907cd30af4 100644 --- a/roottest/root/io/transient/base/CMakeLists.txt +++ b/roottest/root/io/transient/base/CMakeLists.txt @@ -11,8 +11,8 @@ add_test(NAME roottest-root-io-transient-base-build -- ${always-make}) set_property(TEST roottest-root-io-transient-base-build PROPERTY FIXTURES_SETUP root-io-transient-base-build) if(GeneratorNeedsBuildSerialization) - set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST roottest-root-io-transient-base-build APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() ROOTTEST_ADD_TEST(WriteFile diff --git a/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt b/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt index d28459ed56103..26027743a4683 100644 --- a/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt +++ b/roottest/root/meta/genreflex/ROOT-5768/CMakeLists.txt @@ -28,7 +28,7 @@ if(NOT MSVC OR win_broken_tests) COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} ${build_config} --target PyCoolLib${fast} -- ${always-make} FIXTURES_REQUIRED PyCool-reflex-dict) if(GeneratorNeedsBuildSerialization) - set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY RESOURCE_LOCK NINJA_BUILD) - set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY FIXTURES_REQUIRED NINJA_BUILD_ALL) + set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY RESOURCE_LOCK CMAKE_BUILD) + set_property(TEST roottest-root-meta-genreflex-ROOT-5768-PyCoolLib-build APPEND PROPERTY FIXTURES_REQUIRED CMAKE_BUILD_ALL) endif() endif() From dabb9af110b4462d393d75ed7455c27c71471896 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Mon, 23 Feb 2026 14:29:28 -0600 Subject: [PATCH 33/36] cmake: Add missing config to roottest (MSVC) (re)build --- cmake/modules/RootCTest.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/RootCTest.cmake b/cmake/modules/RootCTest.cmake index a46d7b6d08d9f..3cbc55098c073 100644 --- a/cmake/modules/RootCTest.cmake +++ b/cmake/modules/RootCTest.cmake @@ -60,7 +60,7 @@ endforeach() # - Use a RESOURCE_LOCK on all tests that invoke ninja, so no two tests will invoke ninja in parallel if(GeneratorNeedsBuildSerialization) add_test(NAME cmake-build-all - COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR}) + COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --config ${build_configuration}) set_tests_properties(cmake-build-all PROPERTIES RESOURCE_LOCK CMAKE_BUILD FIXTURES_SETUP CMAKE_BUILD_ALL From 9e65a7a3048019c665785b36c360353d419006bb Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 24 Feb 2026 14:16:17 -0600 Subject: [PATCH 34/36] io: Extend code documentation related to ByteCount --- core/base/inc/TBuffer.h | 37 +++++++++++++++++++++++++++++++++++++ io/io/inc/TBufferFile.h | 9 +++++++-- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/core/base/inc/TBuffer.h b/core/base/inc/TBuffer.h index fb0e369fca388..af6b0bb000f8d 100644 --- a/core/base/inc/TBuffer.h +++ b/core/base/inc/TBuffer.h @@ -69,6 +69,13 @@ class TBuffer : public TObject { Int_t Write(const char *name, Int_t opt, Long64_t bufsize) const override { return TObject::Write(name, opt, bufsize); } + //////////////////////////////////////////////////////////////////////////////// + /// Reserve space for a leading byte count and return the position where to + /// store the byte count value. + /// + /// \param[in] cl The class for which we are reserving the byte count, used for error reporting. + /// \return The position (cntpos) where the byte count should be stored later, + /// or kOverflowPosition if the position exceeds kMaxCountPosition virtual UInt_t ReserveByteCount(const TClass *) = 0; public: @@ -127,6 +134,36 @@ class TBuffer : public TObject { virtual Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const TClass *clss) = 0; virtual Long64_t CheckByteCount(ULong64_t startpos, ULong64_t bcnt, const char *classname) = 0; virtual void SetByteCount(ULong64_t cntpos, Bool_t packInVersion = kFALSE)= 0; + + /// \class TBuffer::ByteCountWriter + /// \ingroup Base + /// \brief RAII helper to automatically write the byte count for an object + /// to be used in the rare case where writing the class version number + /// and the byte count are decoupled. + /// + /// `ByteCountWriter` encapsulates the pattern: + /// 1. Reserve space for a leading byte count with ReserveByteCount(). + /// 2. Stream the object content. + /// 3. Finalize the byte count with SetByteCount(). + /// + /// \note Create the instance as a local variable and keep it alive until all + /// the bytes that should be counted have been written to the buffer. + /// + /// ### Example + /// \code{.cpp} + /// void MyClass::Streamer(TBuffer &b) + /// { + /// if (b.IsWriting()) { + /// // Reserve space for the byte count and auto-finalize on scope exit. + /// TBuffer::ByteCountWriter bcnt(b, MyClass::Class()); + /// + /// b.WriteClass(MyClass::Class()); + /// // ... stream members ... + /// } else { + /// // ... read members ... + /// } + /// } + /// \endcode class ByteCountWriter { TBuffer &fBuffer; Bool_t fPackInVersion; diff --git a/io/io/inc/TBufferFile.h b/io/io/inc/TBufferFile.h index 2b7d221ea2a77..0c532786d7e51 100644 --- a/io/io/inc/TBufferFile.h +++ b/io/io/inc/TBufferFile.h @@ -54,8 +54,13 @@ class TBufferFile : public TBufferIO { using ByteCountLocator_t = std::size_t; // This might become a pair if we implement chunked keys struct ByteCountLocationInfo { - ByteCountLocator_t locator; ///< Position where the byte count value is stored - const TClass* cl; ///< Class for which the byte count is reserved + ///< Position where the byte count value is stored + ByteCountLocator_t locator; + ///< Class for which the byte count is reserved. Usually this is the + ///< the base class or type of the pointer. + const TClass* cl; + ///< Alternative class that might used. Usually this is the derived + ///< class or the actual type of the object. const TClass* alt; }; using ByteCountStack_t = std::vector; From b1c71fd48285a125293fbac483299526cec8e144 Mon Sep 17 00:00:00 2001 From: Philippe Canal Date: Tue, 24 Feb 2026 14:18:03 -0600 Subject: [PATCH 35/36] NFC: white space --- io/io/src/TBufferFile.cxx | 3 ++- io/io/test/CMakeLists.txt | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/io/io/src/TBufferFile.cxx b/io/io/src/TBufferFile.cxx index 994a01c73a543..5742ff68db112 100644 --- a/io/io/src/TBufferFile.cxx +++ b/io/io/src/TBufferFile.cxx @@ -2885,9 +2885,10 @@ TClass *TBufferFile::ReadClass(const TClass *clReq, UInt_t *objTag) fVersion = 1; // When objTag is not used, the caller is not interested in the byte // count and will not (can not) call CheckByteCount. - if (objTag) + if (objTag) { // Note: the actual class is set later on. fByteCountStack.push_back({fBufCur - fBuffer - sizeof(UInt_t), clReq, nullptr}); + } startpos = UInt_t(fBufCur-fBuffer); *this >> tag; } diff --git a/io/io/test/CMakeLists.txt b/io/io/test/CMakeLists.txt index cd5103b6e5fc5..c39c0eacf39a0 100644 --- a/io/io/test/CMakeLists.txt +++ b/io/io/test/CMakeLists.txt @@ -58,4 +58,4 @@ if(NOT _32BIT) MACRO testByteCount.cxx+ OUTREF testByteCount.ref FIXTURES_REQUIRED io-io-tobj-fixture) -endif() \ No newline at end of file +endif() From ea1e38793fc3a565f705d8ad200da69b3ffe337b Mon Sep 17 00:00:00 2001 From: Jakob Blomer Date: Tue, 2 Dec 2025 11:45:50 +0100 Subject: [PATCH 36/36] [io] add unit test infrastructure for streaming large objects Uses RStreamerField as a test bed for (de-)serializing large objects with TBufferFile. --- tree/ntuple/test/CMakeLists.txt | 5 ++ tree/ntuple/test/StreamerBeyond.cxx | 1 + tree/ntuple/test/StreamerBeyond.hxx | 14 +++++ tree/ntuple/test/StreamerBeyondLinkDef.h | 5 ++ tree/ntuple/test/rfield_streamer_beyond.cxx | 70 +++++++++++++++++++++ 5 files changed, 95 insertions(+) create mode 100644 tree/ntuple/test/StreamerBeyond.cxx create mode 100644 tree/ntuple/test/StreamerBeyond.hxx create mode 100644 tree/ntuple/test/StreamerBeyondLinkDef.h create mode 100644 tree/ntuple/test/rfield_streamer_beyond.cxx diff --git a/tree/ntuple/test/CMakeLists.txt b/tree/ntuple/test/CMakeLists.txt index 17278482d9cf2..7c991ee0119e4 100644 --- a/tree/ntuple/test/CMakeLists.txt +++ b/tree/ntuple/test/CMakeLists.txt @@ -107,6 +107,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)); +}