From eef558ed37c04a0f9bab8e1753442feb00a73fb4 Mon Sep 17 00:00:00 2001
From: Philippe Canal <pcanal@fnal.gov>
Date: Thu, 9 Jun 2011 17:18:42 +0000
Subject: [PATCH] From Brian and Philippe:

Reduce the memory used by a TTree in half.

Refactor the code reading and writing the TBasket data.
A single transient buffer holding the compressed data is
now managed by TTree (and could be made thread local)
rather than having one per TBranch.


git-svn-id: http://root.cern.ch/svn/root/trunk@39642 27541ba8-7e3a-0410-8455-c3a389f83636
---
 tree/tree/inc/TBasket.h   |  16 +-
 tree/tree/inc/TTree.h     |   3 +
 tree/tree/src/TBasket.cxx | 426 ++++++++++++++++++++++----------------
 tree/tree/src/TTree.cxx   |  21 ++
 4 files changed, 278 insertions(+), 188 deletions(-)

diff --git a/tree/tree/inc/TBasket.h b/tree/tree/inc/TBasket.h
index 932b5df561c..c699b1dcb51 100644
--- a/tree/tree/inc/TBasket.h
+++ b/tree/tree/inc/TBasket.h
@@ -40,7 +40,14 @@ class TBasket : public TKey {
 private:
    TBasket(const TBasket&);            // TBasket objects are not copiable.
    TBasket& operator=(const TBasket&); // TBasket objects are not copiable.
-      
+
+   // Internal corner cases for ReadBasketBuffers
+   Int_t ReadBasketBuffersUnzip(char*, Int_t, Bool_t, TFile*);
+   Int_t ReadBasketBuffersUncompressedCase();
+
+   // Helper for managing the compressed buffer.
+   void InitializeCompressedBuffer(Int_t len, TFile* file);
+ 
 protected:
    Int_t       fBufferSize;      //fBuffer length in bytes
    Int_t       fNevBufSize;      //Length in Int_t of fEntryOffset OR fixed length of each entry if fEntryOffset is null!
@@ -50,9 +57,10 @@ protected:
    Int_t      *fDisplacement;    //![fNevBuf] Displacement of entries in fBuffer(TKey)
    Int_t      *fEntryOffset;     //[fNevBuf] Offset of entries in fBuffer(TKey)
    TBranch    *fBranch;          //Pointer to the basket support branch
-   Int_t       fCompressedSize;  //!Size of the allocated memroy in fCompressedBuffer
-   char       *fCompressedBuffer;//!Temporary place holder for the compressed buffer if needed.
-   
+   TBuffer    *fCompressedBufferRef; //! Compressed buffer.
+   Bool_t      fOwnsCompressedBuffer; //! Whether or not we own the compressed buffer.
+   Int_t       fLastWriteBufferSize; //! Size of the buffer last time we wrote it to disk
+
 public:
    
    TBasket();
diff --git a/tree/tree/inc/TTree.h b/tree/tree/inc/TTree.h
index f0ec9e8da52..cb62ea98a09 100644
--- a/tree/tree/inc/TTree.h
+++ b/tree/tree/inc/TTree.h
@@ -139,6 +139,7 @@ protected:
    TList         *fClones;            //! List of cloned trees which share our addresses
    TBranchRef    *fBranchRef;         //  Branch supporting the TRefTable (if any)
    UInt_t         fFriendLockStatus;  //! Record which method is locking the friend recursion
+   TBuffer       *fTransientBuffer;   //! Pointer to the current transient buffer.
 
    static Int_t     fgBranchStyle;      //  Old/New branch style
    static Long64_t  fgMaxTreeSize;      //  Maximum size of a file containg a Tree
@@ -408,6 +409,7 @@ public:
    TTreeFormula           *GetSelect()    { return GetPlayer()->GetSelect(); }
    virtual Long64_t        GetSelectedRows() { return GetPlayer()->GetSelectedRows(); }
    virtual Int_t           GetTimerInterval() const { return fTimerInterval; }
+           TBuffer*        GetTransientBuffer(Int_t size);
    virtual Long64_t        GetTotBytes() const { return fTotBytes; }
    virtual TTree          *GetTree() const { return const_cast<TTree*>(this); }
    virtual TVirtualIndex  *GetTreeIndex() const { return fTreeIndex; }
@@ -536,6 +538,7 @@ public:
    virtual Int_t           Write(const char *name=0, Int_t option=0, Int_t bufsize=0);
    virtual Int_t           Write(const char *name=0, Int_t option=0, Int_t bufsize=0) const;
 
+
    ClassDef(TTree,19)  //Tree descriptor (the main ROOT I/O class)
 };
 
diff --git a/tree/tree/src/TBasket.cxx b/tree/tree/src/TBasket.cxx
index 94cb4f334f4..8148fb41f2b 100644
--- a/tree/tree/src/TBasket.cxx
+++ b/tree/tree/src/TBasket.cxx
@@ -20,6 +20,20 @@
 #include "TVirtualPerfStats.h"
 #include "TTimeStamp.h"
 
+// TODO: Copied from TBranch.cxx
+#if (__GNUC__ >= 3) || defined(__INTEL_COMPILER)
+#if !defined(R__unlikely)
+  #define R__unlikely(expr) __builtin_expect(!!(expr), 0)
+#endif
+#if !defined(R__likely)
+  #define R__likely(expr) __builtin_expect(!!(expr), 1)
+#endif
+#else
+  #define R__unlikely(expr) expr
+  #define R__likely(expr) expr
+#endif
+
+
 extern "C" void R__zip (Int_t cxlevel, Int_t *nin, char *bufin, Int_t *lout, char *bufout, Int_t *nout);
 extern "C" void R__unzip(Int_t *nin, UChar_t *bufin, Int_t *lout, char *bufout, Int_t *nout);
 extern "C" int R__unzip_header(Int_t *nin, UChar_t *bufin, Int_t *lout);
@@ -37,7 +51,7 @@ ClassImp(TBasket)
 //
 
 //_______________________________________________________________________
-TBasket::TBasket() : fCompressedSize(0),fCompressedBuffer(0)
+TBasket::TBasket() : fCompressedBufferRef(0), fLastWriteBufferSize(0)
 {
    // Default contructor.
 
@@ -54,10 +68,9 @@ TBasket::TBasket() : fCompressedSize(0),fCompressedBuffer(0)
 }
 
 //_______________________________________________________________________
-TBasket::TBasket(TDirectory *motherDir) : TKey(motherDir),fCompressedSize(0),fCompressedBuffer(0)
+TBasket::TBasket(TDirectory *motherDir) : TKey(motherDir),fCompressedBufferRef(0), fLastWriteBufferSize(0)
 {
    // Constructor used during reading.
-
    fDisplacement  = 0;
    fEntryOffset   = 0;
    fBufferRef     = 0;
@@ -72,7 +85,7 @@ TBasket::TBasket(TDirectory *motherDir) : TKey(motherDir),fCompressedSize(0),fCo
 
 //_______________________________________________________________________
 TBasket::TBasket(const char *name, const char *title, TBranch *branch) : 
-   TKey(branch->GetDirectory()),fCompressedSize(0),fCompressedBuffer(0)
+   TKey(branch->GetDirectory()), fLastWriteBufferSize(0)
 {
    // Basket normal constructor, used during writing.
 
@@ -93,7 +106,14 @@ TBasket::TBasket(const char *name, const char *title, TBranch *branch) :
    }
    fHeaderOnly  = kTRUE;
    fLast        = 0; // Must initialize before calling Streamer()
-   
+   if (branch && branch->GetTree()) {
+      fCompressedBufferRef = branch->GetTree()->GetTransientBuffer(fBufferSize);
+      fOwnsCompressedBuffer = kFALSE;
+      if (!fCompressedBufferRef) {
+         fCompressedBufferRef = new TBufferFile(TBuffer::kRead, fBufferSize);
+         fOwnsCompressedBuffer = kTRUE;
+      }
+   }
    Streamer(*fBufferRef);
    fKeylen      = fBufferRef->Length();
    fObjlen      = fBufferSize - fKeylen;
@@ -115,12 +135,16 @@ TBasket::~TBasket()
 
    if (fDisplacement) delete [] fDisplacement;
    if (fEntryOffset)  delete [] fEntryOffset;
-   if (fBuffer == fCompressedBuffer) fBuffer = 0;
-   if (fCompressedBuffer) delete [] fCompressedBuffer;
+   if (fBufferRef) delete fBufferRef;
+   fBufferRef = 0;
+   fBuffer = 0;
    fDisplacement= 0;
    fEntryOffset = 0;
-   fCompressedSize = 0;
-   fCompressedBuffer = 0;
+   // Note we only delete the compressed buffer if we own it
+   if (fCompressedBufferRef && fOwnsCompressedBuffer) {
+       delete fCompressedBufferRef;
+       fCompressedBufferRef = 0;
+   }
 }
 
 //_______________________________________________________________________
@@ -174,19 +198,17 @@ void TBasket::DeleteEntryOffset()
 Int_t TBasket::DropBuffers()
 {
    // Drop buffers of this basket if it is not the current basket.
-
    if (!fBuffer && !fBufferRef) return 0;
 
    if (fDisplacement) delete [] fDisplacement;
    if (fEntryOffset)  delete [] fEntryOffset;
    if (fBufferRef)    delete fBufferRef;
-   if (fCompressedBuffer) delete [] fCompressedBuffer;
+   if (fCompressedBufferRef && fOwnsCompressedBuffer) delete fCompressedBufferRef;
    fBufferRef   = 0;
+   fCompressedBufferRef = 0;
    fBuffer      = 0;
    fDisplacement= 0;
    fEntryOffset = 0;
-   fCompressedSize = 0;
-   fCompressedBuffer = 0;
    fBranch->GetTree()->IncrementTotalBuffers(-fBufferSize);
    return fBufferSize;
 }
@@ -284,6 +306,91 @@ void TBasket::MoveEntries(Int_t dentries)
    fNevBuf -= dentries;
 }
 
+#define OLD_CASE_EXPRESSION fObjlen==fNbytes-fKeylen && GetBranch()->GetCompressionLevel()!=0 && file->GetVersion()<=30401
+//_______________________________________________________________________
+Int_t TBasket::ReadBasketBuffersUncompressedCase()
+{
+   // By-passing buffer unzipping has been requested and is
+   // possible (only 1 entry in this basket). 
+   fBuffer = fBufferRef->Buffer();
+            
+   // Make sure that the buffer is set at the END of the data
+   fBufferRef->SetBufferOffset(fNbytes);
+            
+   // Indicate that this buffer is weird.
+   fBufferRef->SetBit(TBufferFile::kNotDecompressed);
+            
+   // Usage of this mode assume the existance of only ONE
+   // entry in this basket.
+   delete [] fEntryOffset; fEntryOffset = 0;
+   delete [] fDisplacement; fDisplacement = 0;
+            
+   fBranch->GetTree()->IncrementTotalBuffers(fBufferSize);
+   return 0;
+}
+
+//_______________________________________________________________________
+Int_t TBasket::ReadBasketBuffersUnzip(char* buffer, Int_t size, Bool_t mustFree, TFile* file)
+{
+   // We always create the TBuffer for the basket but it hold the buffer from the cache.
+   if (fBufferRef) {
+      fBufferRef->SetBuffer(buffer, size, mustFree);
+      fBufferRef->SetReadMode();
+      fBufferRef->Reset();
+   } else {
+      fBufferRef = new TBufferFile(TBuffer::kRead, size, buffer, mustFree);
+   }
+   fBufferRef->SetParent(file);
+
+   Streamer(*fBufferRef);
+   
+   if (IsZombie()) {
+      return -1;
+   }
+   
+   Bool_t oldCase = OLD_CASE_EXPRESSION;
+
+   if ((fObjlen > fNbytes-fKeylen || oldCase) && TestBit(TBufferFile::kNotDecompressed) && (fNevBuf==1)) {
+      return TBasket::ReadBasketBuffersUncompressedCase();
+   }
+
+   fBuffer = fBufferRef->Buffer();
+   return fObjlen+fKeylen;
+}
+
+//_______________________________________________________________________
+static inline TBuffer* R__initializeReadBasketBuffer(TBuffer* bufferRef, Int_t len, TFile* file)
+{
+   // Initialize a buffer for reading if it is not already initialized
+   
+   TBuffer* result;
+   if (R__likely(bufferRef)) {
+      bufferRef->SetReadMode();
+      Int_t curBufferSize = bufferRef->BufferSize();
+      if (curBufferSize < len) {
+         // Experience shows that giving 5% "wiggle-room" decreases churn.
+         bufferRef->Expand(Int_t(len*1.05));
+      }
+      bufferRef->Reset();
+      result = bufferRef;
+   } else {
+      result = new TBufferFile(TBuffer::kRead, len);
+   }
+   result->SetParent(file);
+   return result;
+}
+
+//_______________________________________________________________________
+void TBasket::InitializeCompressedBuffer(Int_t len, TFile* file)
+{
+    // Initialize the compressed buffer; either from the TTree or create a local one.
+    Bool_t compressedBufferExists = fCompressedBufferRef != NULL;
+    fCompressedBufferRef = R__initializeReadBasketBuffer(fCompressedBufferRef, len, file);
+    if (!compressedBufferExists) {
+        fOwnsCompressedBuffer = kTRUE;
+    }
+}
+
 //_______________________________________________________________________
 Int_t TBasket::ReadBasketBuffers(Long64_t pos, Int_t len, TFile *file)
 {
@@ -301,182 +408,119 @@ Int_t TBasket::ReadBasketBuffers(Long64_t pos, Int_t len, TFile *file)
    // There is a lot of code duplication but it was necesary to assure
    // the expected behavior when there is no cache.
 
-
    if(!fBranch->GetDirectory()) {
       return -1;
-   }
-   Int_t badread= 0;
+   }  
+   
+   Bool_t oldCase;
+   char *rawUncompressedBuffer, *rawCompressedBuffer;
+   Int_t uncompressedBufferLen;
 
+   // See if the cache has already unzipped the buffer for us.
    TFileCacheRead *pf = file->GetCacheRead();
-   char *buffer = 0;
-   Bool_t free = kTRUE; // Must we free this buffer or does it make part of the cache? 
-   Int_t res = -1;
+   if (pf) {
+      Int_t res = -1;
+      Bool_t free = kTRUE;
+      char *buffer;
+      res = pf->GetUnzipBuffer(&buffer, pos, len, &free);
+      if (R__unlikely(res >= 0)) {
+         len = ReadBasketBuffersUnzip(buffer, res, free, file);
+         // Note that in the kNotDecompressed case, the above function will return 0;
+         // In such a case, we should stop processing
+         if (len <= 0) return -len;
+         goto AfterBuffer;
+      }
+   }
 
-   if (pf) res = pf->GetUnzipBuffer(&buffer, pos, len, &free);
+   // Initialize the buffer to hold the compressed data.
+   InitializeCompressedBuffer(len, file);
+   if (!fCompressedBufferRef) {
+       Error("ReadBasketBuffers", "Unable to allocate buffer.");
+       return 1;
+   }
+   rawCompressedBuffer = fCompressedBufferRef->Buffer();
 
-   if (res >= 0) {
+   // Read from the file and unstream the header information.
+   if (file->ReadBuffer(rawCompressedBuffer,pos,len)) {
+      return 1;
+   }
+   Streamer(*fCompressedBufferRef);
+   if (IsZombie()) {
+      return 1;
+   }
 
-      // We always create the TBuffer for the basket but it will be a shell only,
-      // since we pass the pointer to the low level buffer
-      if (fBufferRef) {
-         fBufferRef->SetBuffer(buffer, res, free);
-         fBufferRef->SetReadMode();
-         fBufferRef->Reset();
-      } else {
-         fBufferRef = new TBufferFile(TBuffer::kRead, res, buffer, free);
+   // Initialize buffer to hold the uncompressed data
+   // Note that in previous versions we didn't allocate buffers until we verified
+   // the zip headers; this is no longer beforehand as the buffer lifetime is scoped
+   // to the TBranch.
+   uncompressedBufferLen = len > fObjlen+fKeylen ? len : fObjlen+fKeylen;
+   fBufferRef = R__initializeReadBasketBuffer(fBufferRef, uncompressedBufferLen, file);
+   rawUncompressedBuffer = fBufferRef->Buffer();
+   fBuffer = rawUncompressedBuffer;
+
+   oldCase = OLD_CASE_EXPRESSION;
+   // Case where ROOT thinks the buffer is compressed.  Copy over the key and uncompress the object
+   if (fObjlen > fNbytes-fKeylen || oldCase) {
+      if (R__unlikely(TestBit(TBufferFile::kNotDecompressed) && (fNevBuf==1))) {
+         return ReadBasketBuffersUncompressedCase();
       }
-      fBufferRef->SetParent(file);
 
-      Streamer(*fBufferRef);
-   
-      if (IsZombie()) {
-         badread = 1;
-         return badread;         
+      // Optional monitor for zip time profiling.
+      Double_t start;
+      if (R__unlikely(gPerfStats)) {
+         start = TTimeStamp();
       }
 
-      Bool_t oldCase = fObjlen==fNbytes-fKeylen
-         && GetBranch()->GetCompressionLevel()!=0
-         && file->GetVersion()<=30401;
-      if (fObjlen > fNbytes-fKeylen || oldCase) {
-         if (TestBit(TBufferFile::kNotDecompressed) && (fNevBuf==1)) {
-            // By-passing buffer unzipping has been requested and is
-            // possible (only 1 entry in this basket).
-            fBuffer = fBufferRef->Buffer();
-
-            // Make sure that the buffer is set at the END of the data
-            fBufferRef->SetBufferOffset(fNbytes);
-
-            // Indicate that this buffer is weird.
-            fBufferRef->SetBit(TBufferFile::kNotDecompressed);
-
-            // Usage of this mode assume the existance of only ONE
-            // entry in this basket.
-            delete [] fEntryOffset; fEntryOffset = 0;
-            delete [] fDisplacement; fDisplacement = 0;
-
-            fBranch->GetTree()->IncrementTotalBuffers(fBufferSize);
-            return badread;
+      memcpy(rawUncompressedBuffer, rawCompressedBuffer, fKeylen);
+      char *rawUncompressedObjectBuffer = rawUncompressedBuffer+fKeylen;
+      UChar_t *rawCompressedObjectBuffer = (UChar_t*)rawCompressedBuffer+fKeylen;
+      Int_t nin, nbuf;
+      Int_t nout = 0, noutot = 0, nintot = 0;
+
+      // Unzip all the compressed objects in the compressed object buffer.
+      while (1) {
+         // Check the header for errors.
+         if (R__unlikely(R__unzip_header(&nin, rawCompressedObjectBuffer, &nbuf) != 0)) {
+            Error("ReadBasketBuffers", "Inconsistency found in header (nin=%d, nbuf=%d)", nin, nbuf);
+            break;
          }
-      }
-
-      fBuffer = fBufferRef->Buffer();
-      len = fObjlen+fKeylen;
-   }
-   else{
-      if (fBufferRef) {
-         fBufferRef->SetReadMode();
-         if (fBufferRef->BufferSize() < len) {
-            fBufferRef->Expand(len);
+         if (R__unlikely(oldCase && (nin > fObjlen || nbuf > fObjlen))) {
+            //buffer was very likely not compressed in an old version
+            memcpy(rawUncompressedBuffer+fKeylen, rawCompressedObjectBuffer+fKeylen, fObjlen);
+            goto AfterBuffer;
          }
-         fBufferRef->Reset();
-      } else {
-         fBufferRef = new TBufferFile(TBuffer::kRead, len);
-      }
-      fBufferRef->SetParent(file);
 
-      buffer = fBufferRef->Buffer();
-      if (file->ReadBuffer(buffer,pos,len)) {
-         badread = 1;
-         return badread;
+         R__unzip(&nin, rawCompressedObjectBuffer, &nbuf, rawUncompressedObjectBuffer, &nout);
+         if (!nout) break;
+         noutot += nout;
+         nintot += nin;
+         if (noutot >= fObjlen) break;
+         rawCompressedObjectBuffer += nin;
+         rawUncompressedObjectBuffer += nout;
       }
 
-      Streamer(*fBufferRef);
-
-      if (IsZombie()) {
-         badread = 1;
-         return badread;         
+      // Make sure the uncompressed numbers are consistent with header.
+      if (R__unlikely(noutot != fObjlen)) {
+         Error("ReadBasketBuffers", "fNbytes = %d, fKeylen = %d, fObjlen = %d, noutot = %d, nout=%d, nin=%d, nbuf=%d", fNbytes,fKeylen,fObjlen, noutot,nout,nin,nbuf);
+         fBranch->GetTree()->IncrementTotalBuffers(fBufferSize);
+         return 1;
       }
-      
-      Bool_t oldCase = fObjlen==fNbytes-fKeylen
-         && GetBranch()->GetCompressionLevel()!=0
-         && file->GetVersion()<=30401;
-      if (fObjlen > fNbytes-fKeylen || oldCase) {
-         if (TestBit(TBufferFile::kNotDecompressed) && (fNevBuf==1)) {
-            // By-passing buffer unzipping has been requested and is
-            // possible (only 1 entry in this basket).
-            fBuffer = fBufferRef->Buffer();
-            
-            // Make sure that the buffer is set at the END of the data
-            fBufferRef->SetBufferOffset(fNbytes);
-            
-            // Indicate that this buffer is weird.
-            fBufferRef->SetBit(TBufferFile::kNotDecompressed);
-            
-            // Usage of this mode assume the existance of only ONE
-            // entry in this basket.
-            delete [] fEntryOffset; fEntryOffset = 0;
-            delete [] fDisplacement; fDisplacement = 0;
-            
-            fBranch->GetTree()->IncrementTotalBuffers(fBufferSize);
-            return badread;
-         }
-
-         // For monitoring the cost of the unzipping.
-         Double_t start = 0;
-         if (gPerfStats != 0) start = TTimeStamp();
-
-         if ((fObjlen+fKeylen) > fCompressedSize) {
-            /* early consistency check before potentially large memory is being allocated */
-            Int_t nin, nbuf;
-            UChar_t *bufcur = (UChar_t *)&buffer[fKeylen];
-            if (R__unzip_header(&nin, bufcur, &nbuf)!=0) {
-               Error("ReadBasketBuffers", "Inconsistency found in header (nin=%d, nbuf=%d)", nin, nbuf);
-               badread = 1;
-               return badread;
-            }
-            if (fCompressedSize) delete [] fCompressedBuffer;
-            fCompressedSize = fObjlen+fKeylen;
-            fCompressedBuffer = new char[fCompressedSize];
-         }
-         fBuffer = fCompressedBuffer;
-         memcpy(fBuffer,buffer,fKeylen);
-         char *objbuf = fBuffer + fKeylen;
-         UChar_t *bufcur = (UChar_t *)&buffer[fKeylen];
-         Int_t nin, nbuf;
-         Int_t nout = 0;
-         Int_t noutot = 0;
-         while (1) {
-            Int_t hc = R__unzip_header(&nin, bufcur, &nbuf);
-            if (hc!=0) break;
-            if (oldCase && (nin > fObjlen || nbuf > fObjlen)) {
-               //buffer was very likely not compressed in an old version
-               delete [] fBuffer;
-               fBuffer = fBufferRef->Buffer();
-               goto AfterBuffer;
-            }
-            R__unzip(&nin, bufcur, &nbuf, objbuf, &nout);
-            if (!nout) break;
-            noutot += nout;
-            if (noutot >= fObjlen) break;
-            bufcur += nin;
-            objbuf += nout;
-         }
-         if (noutot != fObjlen) {
-            Error("ReadBasketBuffers", "fNbytes = %d, fKeylen = %d, fObjlen = %d, noutot = %d, nout=%d, nin=%d, nbuf=%d", fNbytes,fKeylen,fObjlen, noutot,nout,nin,nbuf);
-            badread = 1;
-         }
-         // Switch the 2 buffers
-         char *temp = fBufferRef->Buffer();
-         Int_t templen = fBufferRef->BufferSize();
-         fBufferRef->ResetBit(TBuffer::kIsOwner);
-         fBufferRef->SetBuffer(fBuffer, fCompressedSize, kTRUE); // Do adopt the buffer.
-         fCompressedBuffer = temp;
-         fCompressedSize = templen;
-         len = fObjlen+fKeylen;
-         if (gPerfStats != 0) {
-            gPerfStats->FileUnzipEvent(file,pos,start,fCompressedSize,fObjlen);
-         }
-      } else {
-         fBuffer = fBufferRef->Buffer();
+      len = fObjlen+fKeylen;
+      if (R__unlikely(gPerfStats)) {
+         gPerfStats->FileUnzipEvent(file,pos,start,nintot,fObjlen);
       }
+   } else {
+      // Nothing is compressed - copy over wholesale.
+      memcpy(rawUncompressedBuffer, rawCompressedBuffer, len);
    }
- AfterBuffer:
 
-   fBranch->GetTree()->IncrementTotalBuffers(fBufferSize);
+AfterBuffer:
 
-   // read offsets table
-   if (badread || !fBranch->GetEntryOffsetLen()) {
-      return badread;
+   fBranch->GetTree()->IncrementTotalBuffers(fBufferSize);
+   
+   // Read offsets table if needed.
+   if (!fBranch->GetEntryOffsetLen()) {
+      return 0;
    }
    delete [] fEntryOffset;
    fEntryOffset = 0;
@@ -486,19 +530,20 @@ Int_t TBasket::ReadBasketBuffers(Long64_t pos, Int_t len, TFile *file)
       fEntryOffset = new Int_t[fNevBuf+1];
       fEntryOffset[0] = fKeylen;
       Warning("ReadBasketBuffers","basket:%s has fNevBuf=%d but fEntryOffset=0, pos=%lld, len=%d, fNbytes=%d, fObjlen=%d, trying to repair",GetName(),fNevBuf,pos,len,fNbytes,fObjlen);
-      return badread;
+      return 0;
    }
+   // Read the array of diplacement if any.
    delete [] fDisplacement;
-   fDisplacement = 0;
+   fDisplacement = 0; 
    if (fBufferRef->Length() != len) {
       // There is more data in the buffer!  It is the displacement
       // array.  If len is less than TBuffer::kMinimalSize the actual
       // size of the buffer is too large, so we can not use the
       // fBufferRef->BufferSize()
       fBufferRef->ReadArray(fDisplacement);
-   }
+   }        
 
-   return badread;
+   return 0;
 }
 
 //_______________________________________________________________________
@@ -531,13 +576,14 @@ void TBasket::Reset()
    Int_t curSize = fBufferRef->BufferSize();
    // fBufferLen at this point is already reset, so use indirect measurements
    Int_t curLen = (GetObjlen() + GetKeylen());
+   Long_t newSize = -1;
    if (curSize > 2*curLen)
    {
       Long_t curBsize = fBranch->GetBasketSize();      
       if (curSize > 2*curBsize ) {
          Long_t avgSize = (Long_t)(fBranch->GetTotBytes() / (1+fBranch->GetWriteBasket())); // Average number of bytes per basket so far
          if (curSize > 2*avgSize) {
-            Long_t newSize = curBsize;
+            newSize = curBsize;
             if (curLen > newSize) {
                newSize = curLen;
             }
@@ -545,10 +591,22 @@ void TBasket::Reset()
                newSize = avgSize;
             }
             newSize = newSize + 512 - newSize%512;  // Wiggle room and alignment (512 is same as in OptimizeBaskets)
-            fBufferRef->Expand(newSize,kFALSE);     // Expand without copying the existing data.
          }
       }
    }
+   /*
+      Philippe has asked us to keep this turned off until we finish memory fragmentation studies.
+   // If fBufferRef grew since we last saw it, shrink it to 105% of the occupied size
+   if (curSize > fLastWriteBufferSize) {
+      if (newSize == -1) {
+         newSize = Int_t(1.05*Float_t(fBufferRef->Length()));
+      }
+      fLastWriteBufferSize = newSize;
+   }
+   */
+   if (newSize != -1) {
+       fBufferRef->Expand(newSize,kFALSE);     // Expand without copying the existing data.
+   }
 
    TKey::Reset();
 
@@ -779,8 +837,8 @@ Int_t TBasket::WriteBuffer()
       return -1;
    }
    fMotherDir = file; // fBranch->GetDirectory();
-   
-   if (fBufferRef->TestBit(TBufferFile::kNotDecompressed)) {
+  
+   if (R__unlikely(fBufferRef->TestBit(TBufferFile::kNotDecompressed))) {
       // Read the basket information that was saved inside the buffer.
       Bool_t writing = fBufferRef->IsWriting();
       fBufferRef->SetReadMode();
@@ -830,12 +888,13 @@ Int_t TBasket::WriteBuffer()
       //if (cxlevel == 2) cxlevel--; RB: I cannot remember why we had this!
       Int_t nbuffers = fObjlen/kMAXBUF;
       Int_t buflen = fKeylen + fObjlen + 28; //add 28 bytes in case object is placed in a deleted gap
-      if (buflen > fCompressedSize) {
-         if (fCompressedSize) delete [] fCompressedBuffer;
-         fCompressedSize = buflen;
-         fCompressedBuffer = new char[fCompressedSize];
+      InitializeCompressedBuffer(buflen, file);
+      if (!fCompressedBufferRef) {
+         Warning("WriteBuffer", "Unable to allocate the compressed buffer");
+         return -1;
       }
-      fBuffer = fCompressedBuffer;
+      fCompressedBufferRef->SetWriteMode();
+      fBuffer = fCompressedBufferRef->Buffer();
       char *objbuf = fBufferRef->Buffer() + fKeylen;
       char *bufcur = &fBuffer[fKeylen];
       noutot = 0;
@@ -851,9 +910,8 @@ Int_t TBasket::WriteBuffer()
          // buffer is larger than the input. In this case, we write the original uncompressed buffer
          if (nout == 0 || nout >= fObjlen) {
             nout = fObjlen;
-            // We use do delete fBuffer here, we no longer want to since
-            // the buffer (held by fCompressedBuffer) might be re-used later.
-            // delete [] fBuffer;
+            // We used to delete fBuffer here, we no longer want to since
+            // the buffer (held by fCompressedBufferRef) might be re-used later.
             fBuffer = fBufferRef->Buffer();
             Create(fObjlen,file);
             fBufferRef->SetBufferOffset(0);
diff --git a/tree/tree/src/TTree.cxx b/tree/tree/src/TTree.cxx
index 1ca2797eb20..34e630e300f 100644
--- a/tree/tree/src/TTree.cxx
+++ b/tree/tree/src/TTree.cxx
@@ -635,6 +635,7 @@ TTree::TTree()
 , fClones(0)
 , fBranchRef(0)
 , fFriendLockStatus(0)
+, fTransientBuffer(0)
 {
    // Default constructor and I/O constructor.
    //
@@ -701,6 +702,7 @@ TTree::TTree(const char* name, const char* title, Int_t splitlevel /* = 99 */)
 , fClones(0)
 , fBranchRef(0)
 , fFriendLockStatus(0)
+, fTransientBuffer(0)
 {
    // Normal tree constructor.
    //
@@ -843,6 +845,25 @@ TTree::~TTree()
    // Must be done after the destruction of friends.
    // Note: We do *not* own our directory.
    fDirectory = 0;
+
+   if (fTransientBuffer) {
+      delete fTransientBuffer;
+      fTransientBuffer = 0;
+   }
+}
+
+//______________________________________________________________________________
+TBuffer* TTree::GetTransientBuffer(Int_t size)
+{
+    // Returns the transient buffer currently used by this TTree for reading/writing baskets
+    if (fTransientBuffer) {
+       if (fTransientBuffer->BufferSize() < size) {
+          fTransientBuffer->Expand(size);
+       }
+       return fTransientBuffer;
+    }
+    fTransientBuffer = new TBufferFile(TBuffer::kRead, size);
+    return fTransientBuffer;
 }
 
 //______________________________________________________________________________
-- 
GitLab