Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
TBranch.cxx
Go to the documentation of this file.
1// @(#)root/tree:$Id$
2// Author: Rene Brun 12/01/96
3
4/*************************************************************************
5 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12#include "TBranchCacheInfo.h"
13
14#include "TBranch.h"
15
16#include "Bytes.h"
17#include "Compression.h"
18#include "TBasket.h"
19#include "TBranchBrowsable.h"
20#include "TBrowser.h"
21#include "TBuffer.h"
22#include "TClass.h"
23#include "TBufferFile.h"
24#include "TClonesArray.h"
25#include "TFile.h"
26#include "TLeaf.h"
27#include "TLeafB.h"
28#include "TLeafC.h"
29#include "TLeafD.h"
30#include "TLeafD32.h"
31#include "TLeafF.h"
32#include "TLeafF16.h"
33#include "TLeafI.h"
34#include "TLeafL.h"
35#include "TLeafG.h"
36#include "TLeafO.h"
37#include "TLeafObject.h"
38#include "TLeafS.h"
39#include "TMessage.h"
40#include "TROOT.h"
41#include "TSystem.h"
42#include "TMath.h"
43#include "TTree.h"
44#include "TTreeCache.h"
45#include "TTreeCacheUnzip.h"
46#include "TVirtualMutex.h"
47#include "TVirtualPad.h"
48#include "TVirtualPerfStats.h"
49#include "strlcpy.h"
50#include "snprintf.h"
51
52#include "TBranchIMTHelper.h"
53
54#include "ROOT/TIOFeatures.hxx"
55
56#include <atomic>
57#include <cstddef>
58#include <cstring>
59#include <cstdio>
60
61
63
64/** \class TBranch
65\ingroup tree
66
67A TTree is a list of TBranches
68
69A TBranch supports:
70 - The list of TLeaf describing this branch.
71 - The list of TBasket (branch buffers).
72
73See TBranch structure in TTree.
74
75See also specialized branches:
76 - TBranchObject in case the branch is one object
77 - TBranchClones in case the branch is an array of clone objects
78*/
79
80
81
82
83////////////////////////////////////////////////////////////////////////////////
84/// Default constructor. Used for I/O by default.
85
87: TNamed()
88, TAttFill(0, 1001)
89, fCompress(0)
90, fBasketSize(32000)
91, fEntryOffsetLen(1000)
92, fWriteBasket(0)
93, fEntryNumber(0)
94, fExtraBasket(nullptr)
95, fOffset(0)
96, fMaxBaskets(10)
97, fNBaskets(0)
98, fSplitLevel(0)
99, fNleaves(0)
100, fReadBasket(0)
101, fReadEntry(-1)
102, fFirstBasketEntry(-1)
103, fNextBasketEntry(-1)
104, fCurrentBasket(nullptr)
105, fEntries(0)
106, fFirstEntry(0)
107, fTotBytes(0)
108, fZipBytes(0)
109, fBranches()
110, fLeaves()
111, fBaskets(fMaxBaskets)
112, fBasketBytes(nullptr)
113, fBasketEntry(nullptr)
114, fBasketSeek(nullptr)
115, fTree(nullptr)
116, fMother(nullptr)
117, fParent(nullptr)
118, fAddress(nullptr)
119, fDirectory(nullptr)
120, fFileName("")
121, fEntryBuffer(nullptr)
122, fTransientBuffer(nullptr)
123, fBrowsables(nullptr)
124, fBulk(*this)
125, fSkipZip(false)
126, fReadLeaves(&TBranch::ReadLeavesImpl)
127, fFillLeaves(&TBranch::FillLeavesImpl)
128{
130}
131
132////////////////////////////////////////////////////////////////////////////////
133/// Create a Branch as a child of a Tree
134///
135/// * address is the address of the first item of a structure
136/// or the address of a pointer to an object (see example in TTree.cxx).
137/// * leaflist is the concatenation of all the variable names and types
138/// separated by a colon character :
139/// The variable name and the variable type are separated by a
140/// slash (/). The variable type must be 1 character. (Characters
141/// after the first are legal and will be appended to the visible
142/// name of the leaf, but have no effect.) If no type is given, the
143/// type of the variable is assumed to be the same as the previous
144/// variable. If the first variable does not have a type, it is
145/// assumed of type F by default. The list of currently supported
146/// types is given below:
147/// - `C` : a character string terminated by the 0 character
148/// - `B` : an 8 bit signed integer (`Char_t`); Treated as a character when in an array.
149/// - `b` : an 8 bit unsigned integer (`UChar_t`)
150/// - `S` : a 16 bit signed integer (`Short_t`)
151/// - `s` : a 16 bit unsigned integer (`UShort_t`)
152/// - `I` : a 32 bit signed integer (`Int_t`)
153/// - `i` : a 32 bit unsigned integer (`UInt_t`)
154/// - `F` : a 32 bit floating point (`Float_t`)
155/// - `f` : a 24 bit floating point with truncated mantissa (`Float16_t`)
156/// - `D` : a 64 bit floating point (`Double_t`)
157/// - `d` : a 24 bit truncated floating point (`Double32_t`)
158/// - `L` : a 64 bit signed integer (`Long64_t`)
159/// - `l` : a 64 bit unsigned integer (`ULong64_t`)
160/// - `G` : a long signed integer (`Long_t`, which `sizeof` is platform dependent), stored as a 64 bit integer but usually held in memory as a 64 bit integer on 64 bit machines and 32 bit on 32 bit machines. Due to this difference, this data type is **not cross-platform**.
161/// - `g` : a long unsigned integer (`ULong_t`, which `sizeof` is platform dependent), stored as a 64 bit unsigned integer but held in memory usually as a 64 bit integer on 64 bit machines and 32 bit on 32 bit machines. Due to this difference, this data type is **not cross-platform**.
162/// - `O` : [the letter `o`, not a zero] a boolean (`bool`)
163///
164/// Arrays of values are supported with the following syntax:
165/// - If leaf name has the form var[nelem], where nelem is alphanumeric, then
166/// if nelem is a leaf name, it is used as the variable size of the array,
167/// otherwise return 0.
168/// The leaf referred to by nelem **MUST** be an int (/I),
169/// - If leaf name has the form var[nelem], where nelem is a non-negative integers, then
170/// it is used as the fixed size of the array.
171/// - If leaf name has the form of a multi dimension array (e.g. var[nelem][nelem2])
172/// where nelem and nelem2 are non-negative integers) then
173/// it is used as a 2 dimensional array of fixed size.
174/// - In case of the truncated floating point types (Float16_t and Double32_t) you can
175/// furthermore specify the range in the style [xmin,xmax] or [xmin,xmax,nbits] after
176/// the type character. See `TStreamerElement::GetRange()` for further information.
177/// - Any of other form is not supported.
178///
179/// Note that the TTree will assume that all the item are contiguous in memory.
180/// On some platform, this is not always true of the member of a struct or a class,
181/// due to padding and alignment. Sorting your data member in order of decreasing
182/// sizeof usually leads to their being contiguous in memory.
183///
184/// * bufsize is the buffer size in bytes for this branch
185/// The default value is 32000 bytes and should be ok for most cases.
186/// You can specify a larger value (e.g. 256000) if your Tree is not split
187/// and each entry is large (Megabytes)
188/// A small value for bufsize is optimum if you intend to access
189/// the entries in the Tree randomly and your Tree is in split mode.
190///
191/// See an example of a Branch definition in the TTree constructor.
192///
193/// Note that in case the data type is an object, this branch can contain
194/// only this object.
195///
196/// Note that this function is invoked by TTree::Branch
197
198TBranch::TBranch(TTree *tree, const char *name, void *address, const char *leaflist, Int_t basketsize, Int_t compress)
200, TAttFill(0, 1001)
201, fCompress(compress)
202, fBasketSize((basketsize < 100) ? 100 : basketsize)
203, fEntryOffsetLen(0)
204, fWriteBasket(0)
205, fEntryNumber(0)
206, fExtraBasket(nullptr)
207, fIOFeatures(tree ? tree->GetIOFeatures().GetFeatures() : 0)
208, fOffset(0)
209, fMaxBaskets(10)
210, fNBaskets(0)
211, fSplitLevel(0)
212, fNleaves(0)
213, fReadBasket(0)
214, fReadEntry(-1)
215, fFirstBasketEntry(-1)
216, fNextBasketEntry(-1)
217, fCurrentBasket(nullptr)
218, fEntries(0)
219, fFirstEntry(0)
220, fTotBytes(0)
221, fZipBytes(0)
222, fBranches()
223, fLeaves()
224, fBaskets(fMaxBaskets)
225, fBasketBytes(nullptr)
226, fBasketEntry(nullptr)
227, fBasketSeek(nullptr)
228, fTree(tree)
229, fMother(nullptr)
230, fParent(nullptr)
231, fAddress((char *)address)
232, fDirectory(fTree->GetDirectory())
233, fFileName("")
234, fEntryBuffer(nullptr)
235, fTransientBuffer(nullptr)
236, fBrowsables(nullptr)
237, fBulk(*this)
238, fSkipZip(false)
239, fReadLeaves(&TBranch::ReadLeavesImpl)
240, fFillLeaves(&TBranch::FillLeavesImpl)
241{
243}
244
245////////////////////////////////////////////////////////////////////////////////
246/// Create a Branch as a child of another Branch
247///
248/// See documentation for
249/// TBranch::TBranch(TTree *, const char *, void *, const char *, Int_t, Int_t)
250
251TBranch::TBranch(TBranch *parent, const char *name, void *address, const char *leaflist, Int_t basketsize,
254, TAttFill(0, 1001)
255, fCompress(compress)
256, fBasketSize((basketsize < 100) ? 100 : basketsize)
257, fEntryOffsetLen(0)
258, fWriteBasket(0)
259, fEntryNumber(0)
260, fExtraBasket(nullptr)
261, fIOFeatures(parent->fIOFeatures)
262, fOffset(0)
263, fMaxBaskets(10)
264, fNBaskets(0)
265, fSplitLevel(0)
266, fNleaves(0)
267, fReadBasket(0)
268, fReadEntry(-1)
269, fFirstBasketEntry(-1)
270, fNextBasketEntry(-1)
271, fCurrentBasket(nullptr)
272, fEntries(0)
273, fFirstEntry(0)
274, fTotBytes(0)
275, fZipBytes(0)
276, fBranches()
277, fLeaves()
278, fBaskets(fMaxBaskets)
279, fBasketBytes(nullptr)
280, fBasketEntry(nullptr)
281, fBasketSeek(nullptr)
282, fTree(parent ? parent->GetTree() : nullptr)
283, fMother(parent ? parent->GetMother() : nullptr)
284, fParent(parent)
285, fAddress((char *)address)
286, fDirectory(fTree ? fTree->GetDirectory() : nullptr)
287, fFileName("")
288, fEntryBuffer(nullptr)
289, fTransientBuffer(nullptr)
290, fBrowsables(nullptr)
291, fBulk(*this)
292, fSkipZip(false)
293, fReadLeaves(&TBranch::ReadLeavesImpl)
294, fFillLeaves(&TBranch::FillLeavesImpl)
295{
297}
298
299void TBranch::Init(const char* name, const char* leaflist, Int_t compress)
300{
301 // Initialization routine called from the constructor. This should NOT be made virtual.
302
304 if ((compress == -1) && fTree->GetDirectory()) {
306 if (bfile) {
307 fCompress = bfile->GetCompressionSettings();
308 }
309 }
310
314
315 for (Int_t i = 0; i < fMaxBaskets; ++i) {
316 fBasketBytes[i] = 0;
317 fBasketEntry[i] = 0;
318 fBasketSeek[i] = 0;
319 }
320
321 //
322 // Decode the leaflist (search for : as separator).
323 //
324
325 char* nameBegin = const_cast<char*>(leaflist);
326 Int_t offset = 0;
327 auto len = strlen(leaflist);
328 // FIXME: Make these string streams instead.
329 char* leafname = new char[len + 1];
330 char* leaftype = new char[320];
331 // Note: The default leaf type is a float.
332 strlcpy(leaftype, "F",320);
333 char* pos = const_cast<char*>(leaflist);
334 const char* leaflistEnd = leaflist + len;
335 for (; pos <= leaflistEnd; ++pos) {
336 // -- Scan leaf specification and create leaves.
337 if ((*pos == ':') || (*pos == 0)) {
338 // -- Reached end of a leaf spec, create a leaf.
339 Int_t lenName = pos - nameBegin;
340 char* ctype = nullptr;
341 if (lenName) {
343 leafname[lenName] = 0;
344 ctype = strstr(leafname, "/");
345 if (ctype) {
346 *ctype = 0;
347 strlcpy(leaftype, ctype + 1,320);
348 }
349 }
350 if (lenName == 0 || ctype == leafname) {
351 Warning("TBranch","No name was given to the leaf number '%d' in the leaflist of the branch '%s'.",fNleaves,name);
352 snprintf(leafname, len + 1, "__noname%d", fNleaves);
353 }
354 TLeaf* leaf = nullptr;
355 if (leaftype[1] == '[' && !strchr(leaftype, ',')) {
356 Warning("TBranch", "Array size for branch '%s' must be specified after leaf name, not after the type name!", name);
357 // and continue for backward compatibility?
358 } else if (leaftype[1] && !strchr(leaftype, ',')) {
359 Warning("TBranch", "Extra characters after type tag '%s' for branch '%s'; must be one character.", leaftype, name);
360 // and continue for backward compatibility?
361 }
362 if (*leaftype == 'C') {
363 leaf = new TLeafC(this, leafname, leaftype);
364 } else if (*leaftype == 'O') {
365 leaf = new TLeafO(this, leafname, leaftype);
366 } else if (*leaftype == 'B') {
367 leaf = new TLeafB(this, leafname, leaftype);
368 } else if (*leaftype == 'b') {
369 leaf = new TLeafB(this, leafname, leaftype);
370 leaf->SetUnsigned();
371 } else if (*leaftype == 'S') {
372 leaf = new TLeafS(this, leafname, leaftype);
373 } else if (*leaftype == 's') {
374 leaf = new TLeafS(this, leafname, leaftype);
375 leaf->SetUnsigned();
376 } else if (*leaftype == 'I') {
377 leaf = new TLeafI(this, leafname, leaftype);
378 } else if (*leaftype == 'i') {
379 leaf = new TLeafI(this, leafname, leaftype);
380 leaf->SetUnsigned();
381 } else if (*leaftype == 'F') {
382 leaf = new TLeafF(this, leafname, leaftype);
383 } else if (*leaftype == 'f') {
384 leaf = new TLeafF16(this, leafname, leaftype);
385 } else if (*leaftype == 'L') {
386 leaf = new TLeafL(this, leafname, leaftype);
387 } else if (*leaftype == 'l') {
388 leaf = new TLeafL(this, leafname, leaftype);
389 leaf->SetUnsigned();
390 } else if (*leaftype == 'D') {
391 leaf = new TLeafD(this, leafname, leaftype);
392 } else if (*leaftype == 'd') {
393 leaf = new TLeafD32(this, leafname, leaftype);
394 } else if (*leaftype == 'G') {
395 leaf = new TLeafG(this, leafname, leaftype);
396 } else if (*leaftype == 'g') {
397 leaf = new TLeafG(this, leafname, leaftype);
398 leaf->SetUnsigned();
399 }
400 if (!leaf) {
401 Error("TLeaf", "Illegal data type for %s/%s", name, leaflist);
402 delete[] leaftype;
403 delete [] leafname;
404 MakeZombie();
405 return;
406 }
407 if (leaf->IsZombie()) {
408 delete leaf;
409 leaf = nullptr;
410 auto msg = "Illegal leaf: %s/%s. If this is a variable size C array it's possible that the branch holding the size is not available.";
411 Error("TBranch", msg, name, leaflist);
412 delete [] leafname;
413 delete[] leaftype;
414 MakeZombie();
415 return;
416 }
417 leaf->SetBranch(this);
418 leaf->SetAddress((char*) (fAddress + offset));
419 leaf->SetOffset(offset);
420 if (leaf->GetLeafCount()) {
421 // -- Leaf is a varying length array, we need an offset array.
422 fEntryOffsetLen = 1000;
423 }
424 if (leaf->InheritsFrom(TLeafC::Class())) {
425 // -- Leaf is a character string, we need an offset array.
426 fEntryOffsetLen = 1000;
427 }
428 ++fNleaves;
430 fTree->GetListOfLeaves()->Add(leaf);
431 if (*pos == 0) {
432 // -- We reached the end of the leaf specification.
433 break;
434 }
435 nameBegin = pos + 1;
436 offset += leaf->GetLenType() * leaf->GetLen();
437 }
438 }
439 delete[] leafname;
440 leafname = nullptr;
441 delete[] leaftype;
442 leaftype = nullptr;
443
444}
445
446////////////////////////////////////////////////////////////////////////////////
447/// Destructor.
448
450{
451 delete fBrowsables;
452 fBrowsables = nullptr;
453
454 // Note: We do *not* have ownership of the buffer.
455 fEntryBuffer = nullptr;
456
457 delete [] fBasketSeek;
458 fBasketSeek = nullptr;
459
460 delete [] fBasketEntry;
461 fBasketEntry = nullptr;
462
463 delete [] fBasketBytes;
464 fBasketBytes = nullptr;
465
467 delete fExtraBasket;
469 fNBaskets = 0;
470 fCurrentBasket = nullptr;
472 fNextBasketEntry = -1;
473
474 // Remove our leaves from our tree's list of leaves.
475 if (fTree) {
477 if (lst && lst->GetLast()!=-1) {
478 lst->RemoveAll(&fLeaves);
479 }
480 }
481 // And delete our leaves.
482 fLeaves.Delete();
483
485
486 // If we are in a directory and that directory is not the same
487 // directory that our tree is in, then try to find an open file
488 // with the name fFileName. If we find one, delete that file.
489 // We are attempting to close any alternate file which we have
490 // been directed to write our baskets to.
491 // FIXME: We make no attempt to check if someone else might be
492 // using this file. This is very user hostile. A violation
493 // of the principle of least surprises.
494 //
495 // Warning. Must use FindObject by name instead of fDirectory->GetFile()
496 // because two branches may point to the same file and the file
497 // may have already been deleted in the previous branch.
498 if (fDirectory && (!fTree || fDirectory != fTree->GetDirectory())) {
500
502 TFile* file = (TFile*)gROOT->GetListOfFiles()->FindObject(bFileName);
503 if (file){
504 file->Close();
505 delete file;
506 file = nullptr;
507 }
508 }
509
510 fTree = nullptr;
511 fDirectory = nullptr;
512
513 if (fTransientBuffer) {
514 delete fTransientBuffer;
515 fTransientBuffer = nullptr;
516 }
517}
518
519////////////////////////////////////////////////////////////////////////////////
520/// Returns the transient buffer currently used by this TBranch for reading/writing baskets.
521
533
534////////////////////////////////////////////////////////////////////////////////
535/// Add the basket to this branch.
536///
537/// Warning: if the basket are not 'flushed/copied' in the same
538/// order as they were created, this will induce a slow down in
539/// the insert (since we'll need to move all the record that are
540/// entere 'too early').
541/// Warning we also assume that the __current__ write basket is
542/// not present (aka has been removed) or is empty (no entries).
543
545{
546 TBasket *basket = &b;
547
548 basket->SetBranch(this);
549
550 if (fWriteBasket >= fMaxBaskets) {
552 }
554
555 if (where && startEntry < fBasketEntry[where-1]) {
556 // Need to find the right location and move the possible baskets
557
558 if (!ondisk) {
559 Warning("AddBasket","The assumption that out-of-order basket only comes from disk based ntuple is false.");
560 }
561
562 if (startEntry < fBasketEntry[0]) {
563 where = 0;
564 } else {
565 for(Int_t i=fWriteBasket-1; i>=0; --i) {
566 if (fBasketEntry[i] < startEntry) {
567 where = i+1;
568 break;
569 } else if (fBasketEntry[i] == startEntry) {
570 Error("AddBasket","An out-of-order basket matches the entry number of an existing basket.");
571 }
572 }
573 }
574
575 if (where < fWriteBasket) {
576 // We shall move the content of the array
577 for (Int_t j=fWriteBasket; j > where; --j) {
581 }
582 }
583 }
585
587 if (existing && existing->GetNevBuf()) {
588 Fatal("AddBasket", "Dropping non-empty 'write' basket in %s %s",
589 GetTree()->GetName(), GetName());
590 }
591 delete existing;
592 if (ondisk) {
593 fBasketBytes[where] = basket->GetNbytes(); // not for in mem
594 fBasketSeek[where] = basket->GetSeekKey(); // not for in mem
596 ++fWriteBasket;
597 } else {
598 ++fNBaskets;
599 // The basket we are adding becomes the new 'write' basket.
601 fTree->IncrementTotalBuffers(basket->GetBufferSize());
602 }
603
604 fEntries += basket->GetNevBuf();
605 fEntryNumber += basket->GetNevBuf();
606 if (ondisk) {
607 fTotBytes += basket->GetObjlen() + basket->GetKeylen() ;
608 fZipBytes += basket->GetNbytes();
609 fTree->AddTotBytes(basket->GetObjlen() + basket->GetKeylen());
610 fTree->AddZipBytes(basket->GetNbytes());
611 }
612}
613
614////////////////////////////////////////////////////////////////////////////////
615/// Add the start entry of the write basket (not yet created)
616
618{
619 if (fWriteBasket >= fMaxBaskets) {
621 }
623
624 if (where && startEntry < fBasketEntry[where-1]) {
625 // Need to find the right location and move the possible baskets
626
627 Fatal("AddBasket","The last basket must have the highest entry number (%s/%lld/%d).",GetName(),startEntry,fWriteBasket);
628
629 }
630 // The first basket (should) always start at zero. If we are asked to update
631 // it, this likely to be from merging 'empty' branches (base class node and the likes)
632 if (where) {
635 }
636}
637
638////////////////////////////////////////////////////////////////////////////////
639/// Loop on all leaves of this branch to back fill Basket buffer.
640///
641/// Use this routine instead of TBranch::Fill when filling a branch individually
642/// to catch up with the number of entries already in the TTree.
643///
644/// First it calls TBranch::Fill and then if the number of entries of the branch
645/// reach one of TTree cluster's boundary, the basket is flushed.
646///
647/// The function returns the number of bytes committed to the memory basket.
648/// If a write error occurs, the number of bytes returned is -1.
649/// If no data are written, because e.g. the branch is disabled,
650/// the number of bytes returned is 0.
651///
652/// To insure that the baskets of each cluster are located close by in the
653/// file, when back-filling multiple branches make sure to call BackFill
654/// for the same entry for all the branches consecutively
655/// ~~~ {.cpp}
656/// for( auto e = 0; e < tree->GetEntries(); ++e ) { // loop over entries.
657/// for( auto branch : branchCollection) {
658/// ... Make change to the data associated with the branch ...
659/// branch->BackFill();
660/// }
661/// }
662/// // Since we loop over all the branches for each new entry
663/// // all the baskets for a cluster are consecutive in the file.
664/// ~~~
665/// rather than doing all the entries of one branch at a time.
666/// ~~~ {.cpp}
667/// // Do NOT do things in the following order, it will lead to
668/// // poorly clustered files.
669/// for(auto branch : branchCollection) {
670/// for( auto e = 0; e < tree->GetEntries(); ++e ) { // loop over entries.
671/// ... Make change to the data associated with the branch ...
672/// branch->BackFill();
673/// }
674/// }
675/// // Since we loop over all the entries for one branch
676/// // all the baskets for that branch are consecutive.
677/// ~~~
678
680
681 // Get the end of the next cluster.
683 cluster.Next();
684 auto endCluster = cluster.GetNextEntry();
685
686 auto result = FillImpl(nullptr);
687
688 if ( result && GetEntries() >= endCluster ) {
689 FlushBaskets();
690 }
691
692 return result;
693}
694
695////////////////////////////////////////////////////////////////////////////////
696/// Browser interface.
697
699{
700 if (fNleaves > 1) {
702 } else {
703 // Get the name and strip any extra brackets
704 // in order to get the full arrays.
705 TString name = GetName();
706 Int_t pos = name.First('[');
707 if (pos!=kNPOS) name.Remove(pos);
708
709 GetTree()->Draw(name, "", b ? b->GetDrawOption() : "");
710 if (gPad) gPad->Update();
711 }
712}
713
714 ///////////////////////////////////////////////////////////////////////////////
715 /// Loop on all branch baskets. If the file where branch buffers reside is
716 /// writable, free the disk space associated to the baskets of the branch,
717 /// then call Reset(). If the option contains "all", delete also the baskets
718 /// for the subbranches.
719 /// The branch is reset.
720 ///
721 /// NOTE that this function must be used with extreme care. Deleting branch baskets
722 /// fragments the file and may introduce inefficiencies when adding new entries
723 /// in the Tree or later on when reading the Tree.
724
726{
727 TString opt = option;
728 opt.ToLower();
729 TFile *file = GetFile(0);
730
732 for(Int_t i=0; i<fWriteBasket; i++) {
733 if (fBasketSeek[i]) file->MakeFree(fBasketSeek[i],fBasketSeek[i]+fBasketBytes[i]-1);