Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TTreeCloner.cxx
Go to the documentation of this file.
1 // @(#)root/tree:$Id$
2 // Author: Philippe Canal 07/11/2005
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 /** \class TTreeCloner
13 \ingroup tree
14 
15 Class implementing or helping the various TTree cloning method
16 */
17 
18 #include "TBasket.h"
19 #include "TBranch.h"
20 #include "TBranchClones.h"
21 #include "TBranchElement.h"
22 #include "TStreamerInfo.h"
23 #include "TBranchRef.h"
24 #include "TError.h"
25 #include "TProcessID.h"
26 #include "TMath.h"
27 #include "TTree.h"
28 #include "TTreeCloner.h"
29 #include "TFile.h"
30 #include "TLeafB.h"
31 #include "TLeafI.h"
32 #include "TLeafL.h"
33 #include "TLeafS.h"
34 #include "TLeafO.h"
35 #include "TLeafC.h"
36 #include "TFileCacheRead.h"
37 #include "TTreeCache.h"
38 
39 #include <algorithm>
40 
41 ////////////////////////////////////////////////////////////////////////////////
42 
43 Bool_t TTreeCloner::CompareSeek::operator()(UInt_t i1, UInt_t i2)
44 {
45  if (fObject->fBasketSeek[i1] == fObject->fBasketSeek[i2]) {
46  if (fObject->fBasketEntry[i1] == fObject->fBasketEntry[i2]) {
47  return i1 < i2;
48  }
49  return fObject->fBasketEntry[i1] < fObject->fBasketEntry[i2];
50  }
51  return fObject->fBasketSeek[i1] < fObject->fBasketSeek[i2];
52 }
53 
54 ////////////////////////////////////////////////////////////////////////////////
55 
56 Bool_t TTreeCloner::CompareEntry::operator()(UInt_t i1, UInt_t i2)
57 {
58  if (fObject->fBasketEntry[i1] == fObject->fBasketEntry[i2]) {
59  return i1 < i2;
60  }
61  return fObject->fBasketEntry[i1] < fObject->fBasketEntry[i2];
62 }
63 
64 ////////////////////////////////////////////////////////////////////////////////
65 /// Constructor. This object would transfer the data from
66 /// 'from' to 'to' using the method indicated in method.
67 ///
68 /// The value of the parameter 'method' determines in which
69 /// order the branches' baskets are written to the output file.
70 ///
71 /// When a TTree is filled the data is stored in the individual
72 /// branches' basket. Each basket is written individually to
73 /// the disk as soon as it is full. In consequence the baskets
74 /// of branches that contain 'large' data chunk are written to
75 /// the disk more often.
76 ///
77 /// There is currently 3 supported sorting order:
78 ///
79 /// SortBasketsByOffset (the default)
80 /// SortBasketsByBranch
81 /// SortBasketsByEntry
82 ///
83 /// When using SortBasketsByOffset the baskets are written in
84 /// the output file in the same order as in the original file
85 /// (i.e. the basket are sorted on their offset in the original
86 /// file; Usually this also means that the baskets are sorted
87 /// on the index/number of the _last_ entry they contain)
88 ///
89 /// When using SortBasketsByBranch all the baskets of each
90 /// individual branches are stored contiguously. This tends to
91 /// optimize reading speed when reading a small number (1->5) of
92 /// branches, since all their baskets will be clustered together
93 /// instead of being spread across the file. However it might
94 /// decrease the performance when reading more branches (or the full
95 /// entry).
96 ///
97 /// When using SortBasketsByEntry the baskets with the lowest
98 /// starting entry are written first. (i.e. the baskets are
99 /// sorted on the index/number of the first entry they contain).
100 /// This means that on the file the baskets will be in the order
101 /// in which they will be needed when reading the whole tree
102 /// sequentially.
103 
104 TTreeCloner::TTreeCloner(TTree *from, TTree *to, Option_t *method, UInt_t options) :
105  fWarningMsg(),
106  fIsValid(kTRUE),
107  fNeedConversion(kFALSE),
108  fOptions(options),
109  fFromTree(from),
110  fToTree(to),
111  fMethod(method),
112  fFromBranches( from ? from->GetListOfLeaves()->GetEntries()+1 : 0),
113  fToBranches( to ? to->GetListOfLeaves()->GetEntries()+1 : 0),
114  fMaxBaskets(CollectBranches()),
115  fBasketBranchNum(new UInt_t[fMaxBaskets]),
116  fBasketNum(new UInt_t[fMaxBaskets]),
117  fBasketSeek(new Long64_t[fMaxBaskets]),
118  fBasketEntry(new Long64_t[fMaxBaskets]),
119  fBasketIndex(new UInt_t[fMaxBaskets]),
120  fPidOffset(0),
121  fCloneMethod(TTreeCloner::kDefault),
122  fToStartEntries(0),
123  fCacheSize(0LL),
124  fFileCache(nullptr),
125  fPrevCache(nullptr)
126 {
127  TString opt(method);
128  opt.ToLower();
129  if (opt.Contains("sortbasketsbybranch")) {
130  //::Info("TTreeCloner::TTreeCloner","use: kSortBasketsByBranch");
131  fCloneMethod = TTreeCloner::kSortBasketsByBranch;
132  } else if (opt.Contains("sortbasketsbyentry")) {
133  //::Info("TTreeCloner::TTreeCloner","use: kSortBasketsByEntry");
134  fCloneMethod = TTreeCloner::kSortBasketsByEntry;
135  } else {
136  //::Info("TTreeCloner::TTreeCloner","use: kSortBasketsByOffset");
137  fCloneMethod = TTreeCloner::kSortBasketsByOffset;
138  }
139  if (fToTree) fToStartEntries = fToTree->GetEntries();
140 
141  if (fFromTree == nullptr) {
142  if (to)
143  fWarningMsg.Form("An input TTree is required (cloning to %s).",
144  to->GetName());
145  else
146  fWarningMsg.Form("An input and output TTree are required.");
147  if (!(fOptions & kNoWarnings)) {
148  Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
149  }
150  fIsValid = kFALSE;
151  }
152  if (fToTree == nullptr) {
153  fWarningMsg.Form("An output TTree is required (cloning %s).",
154  from ? from->GetName() : "no tree");
155  if (!(fOptions & kNoWarnings)) {
156  Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
157  }
158  fIsValid = kFALSE;
159  } else if (fToTree->GetDirectory() == nullptr) {
160  fWarningMsg.Form("The output TTree (%s) must be associated with a directory.",
161  fToTree->GetName());
162  if (!(fOptions & kNoWarnings)) {
163  Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
164  }
165  fIsValid = kFALSE;
166  } else if (fToTree->GetCurrentFile() == nullptr) {
167  fWarningMsg.Form("The output TTree (%s) must be associated with a directory (%s) that is in a file.",
168  fToTree->GetName(),fToTree->GetDirectory()->GetName());
169  if (!(fOptions & kNoWarnings)) {
170  Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
171  }
172  fIsValid = kFALSE;
173  } else if (! fToTree->GetDirectory()->IsWritable()) {
174  if (fToTree->GetDirectory()==fToTree->GetCurrentFile()) {
175  fWarningMsg.Form("The output TTree (%s) must be associated with a writable file (%s).",
176  fToTree->GetName(),fToTree->GetCurrentFile()->GetName());
177  } else {
178  fWarningMsg.Form("The output TTree (%s) must be associated with a writable directory (%s in %s).",
179  fToTree->GetName(),fToTree->GetDirectory()->GetName(),fToTree->GetCurrentFile()->GetName());
180  }
181  if (!(fOptions & kNoWarnings)) {
182  Warning("TTreeCloner::TTreeCloner", "%s", fWarningMsg.Data());
183  }
184  fIsValid = kFALSE;
185  }
186 
187  if (fIsValid && (!(fOptions & kNoFileCache))) {
188  fCacheSize = fFromTree->GetCacheAutoSize();
189  }
190 }
191 
192 ////////////////////////////////////////////////////////////////////////////////
193 /// Execute the cloning.
194 
195 Bool_t TTreeCloner::Exec()
196 {
197  if (!IsValid()) {
198  return kFALSE;
199  }
200  CreateCache();
201  ImportClusterRanges();
202  CopyStreamerInfos();
203  CopyProcessIds();
204  CloseOutWriteBaskets();
205  CollectBaskets();
206  SortBaskets();
207  WriteBaskets();
208  CopyMemoryBaskets();
209  RestoreCache();
210 
211  return kTRUE;
212 }
213 
214 ////////////////////////////////////////////////////////////////////////////////
215 /// TTreeCloner destructor
216 
217 TTreeCloner::~TTreeCloner()
218 {
219  // The file cache was restored to its previous value at the end of Exec,
220  // we can safely delete our cache.
221  delete fFileCache;
222  delete [] fBasketBranchNum;
223  delete [] fBasketNum;
224  delete [] fBasketSeek;
225  delete [] fBasketEntry;
226  delete [] fBasketIndex;
227 }
228 
229 ////////////////////////////////////////////////////////////////////////////////
230 /// Before we can start adding new basket, we need to flush to
231 /// disk the partially filled baskets (the WriteBasket)
232 
233 void TTreeCloner::CloseOutWriteBaskets()
234 {
235  for(Int_t i=0; i<fToBranches.GetEntries(); ++i) {
236  TBranch *to = (TBranch*)fToBranches.UncheckedAt(i);
237  to->FlushOneBasket(to->GetWriteBasket());
238  }
239 }
240 
241 ////////////////////////////////////////////////////////////////////////////////
242 /// Fill the array of branches, adding the branch 'from' and 'to',
243 /// and matching the sub-branches of the 'from' and 'to' branches.
244 /// Returns the total number of baskets in all the from branch and
245 /// it sub-branches.
246 
247 UInt_t TTreeCloner::CollectBranches(TBranch *from, TBranch *to) {
248  // Since this is called from the constructor, this can not be a virtual function
249 
250  UInt_t numBaskets = 0;
251  if (from->InheritsFrom(TBranchClones::Class())) {
252  TBranchClones *fromclones = (TBranchClones*) from;
253  TBranchClones *toclones = (TBranchClones*) to;
254  numBaskets += CollectBranches(fromclones->fBranchCount, toclones->fBranchCount);
255 
256  } else if (from->InheritsFrom(TBranchElement::Class())) {
257  Int_t nb = from->GetListOfLeaves()->GetEntries();
258  Int_t fnb = to->GetListOfLeaves()->GetEntries();
259  if (nb != fnb && (nb == 0 || fnb == 0)) {
260  // We might be in the case where one branch is split
261  // while the other is not split. We must reject this match.
262  fWarningMsg.Form("The export branch and the import branch do not have the same split level. (The branch name is %s.)",
263  from->GetName());
264  if (!(fOptions & kNoWarnings)) {
265  Warning("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
266  }
267  fNeedConversion = kTRUE;
268  fIsValid = kFALSE;
269  return 0;
270  }
271  if (((TBranchElement*) from)->GetStreamerType() != ((TBranchElement*) to)->GetStreamerType()) {
272  fWarningMsg.Form("The export branch and the import branch do not have the same streamer type. (The branch name is %s.)",
273  from->GetName());
274  if (!(fOptions & kNoWarnings)) {
275  Warning("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
276  }
277  fIsValid = kFALSE;
278  return 0;
279  }
280  TBranchElement *fromelem = (TBranchElement*) from;
281  TBranchElement *toelem = (TBranchElement*) to;
282  if (fromelem->fMaximum > toelem->fMaximum) toelem->fMaximum = fromelem->fMaximum;
283  } else {
284 
285  Int_t nb = from->GetListOfLeaves()->GetEntries();
286  Int_t fnb = to->GetListOfLeaves()->GetEntries();
287  if (nb != fnb) {
288  fWarningMsg.Form("The export branch and the import branch (%s) do not have the same number of leaves (%d vs %d)",
289  from->GetName(), fnb, nb);
290  if (!(fOptions & kNoWarnings)) {
291  Error("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
292  }
293  fIsValid = kFALSE;
294  return 0;
295  }
296  for (Int_t i=0;i<nb;i++) {
297 
298  TLeaf *fromleaf = (TLeaf*)from->GetListOfLeaves()->At(i);
299  TLeaf *toleaf = (TLeaf*)to->GetListOfLeaves()->At(i);
300  if (toleaf->IsA() != fromleaf->IsA() ) {
301  // The data type do not match, we can not do a fast merge.
302  fWarningMsg.Form("The export leaf and the import leaf (%s.%s) do not have the data type (%s vs %s)",
303  from->GetName(),fromleaf->GetName(),fromleaf->GetTypeName(),toleaf->GetTypeName());
304  if (! (fOptions & kNoWarnings) ) {
305  Warning("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
306  }
307  fIsValid = kFALSE;
308  fNeedConversion = kTRUE;
309  return 0;
310  }
311  toleaf->IncludeRange( fromleaf );
312  }
313 
314  }
315 
316  fFromBranches.AddLast(from);
317  if (!from->TestBit(TBranch::kDoNotUseBufferMap)) {
318  // Make sure that we reset the Buffer's map if needed.
319  to->ResetBit(TBranch::kDoNotUseBufferMap);
320  }
321  fToBranches.AddLast(to);
322 
323  numBaskets += from->GetWriteBasket();
324  numBaskets += CollectBranches(from->GetListOfBranches(),to->GetListOfBranches());
325 
326  return numBaskets;
327 }
328 
329 ////////////////////////////////////////////////////////////////////////////////
330 /// Fill the array of branches, matching the branches of the 'from' and 'to' arrays.
331 /// Returns the total number of baskets in all the branches.
332 
333 UInt_t TTreeCloner::CollectBranches(TObjArray *from, TObjArray *to)
334 {
335  // Since this is called from the constructor, this can not be a virtual function
336 
337  Int_t fnb = from->GetEntries();
338  Int_t tnb = to->GetEntries();
339  if (!fnb || !tnb) {
340  return 0;
341  }
342 
343  UInt_t numBasket = 0;
344  Int_t fi = 0;
345  Int_t ti = 0;
346  while (ti < tnb) {
347  TBranch* fb = (TBranch*) from->UncheckedAt(fi);
348  TBranch* tb = (TBranch*) to->UncheckedAt(ti);
349  Int_t firstfi = fi;
350  while (strcmp(fb->GetName(), tb->GetName())) {
351  ++fi;
352  if (fi >= fnb) {
353  // continue at the beginning
354  fi = 0;
355  }
356  if (fi==firstfi) {
357  // We tried all the branches and there is not match.
358  fb = 0;
359  break;
360  }
361  fb = (TBranch*) from->UncheckedAt(fi);
362  }
363  if (fb) {
364  numBasket += CollectBranches(fb, tb);
365  ++fi;
366  if (fi >= fnb) {
367  fi = 0;
368  }
369  } else {
370  if (tb->GetMother()==tb) {
371  // Top level branch.
372  if (!(fOptions & kIgnoreMissingTopLevel)) {
373  fWarningMsg.Form("One of the export top level branches (%s) is not present in the import TTree.",
374  tb->GetName());
375  if (!(fOptions & kNoWarnings)) {
376  Error("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
377  }
378  fIsValid = kFALSE;
379  }
380  } else {
381  fWarningMsg.Form("One of the export sub-branches (%s) is not present in the import TTree.",
382  tb->GetName());
383  if (!(fOptions & kNoWarnings)) {
384  Error("TTreeCloner::CollectBranches", "%s", fWarningMsg.Data());
385  }
386  fIsValid = kFALSE;
387  }
388  }
389  ++ti;
390  }
391  return numBasket;
392 }
393 
394 ////////////////////////////////////////////////////////////////////////////////
395 /// Fill the array of branches, matching the branches of the 'from' and 'to' TTrees
396 /// Returns the total number of baskets in all the branches.
397 
398 UInt_t TTreeCloner::CollectBranches()
399 {
400  // Since this is called from the constructor, this can not be a virtual function
401 
402  if (!fFromTree || !fToTree) {
403  return 0;
404  }
405  UInt_t numBasket = CollectBranches(fFromTree->GetListOfBranches(),
406  fToTree->GetListOfBranches());
407 
408  if (fFromTree->GetBranchRef()) {
409  fToTree->BranchRef();
410  numBasket += CollectBranches(fFromTree->GetBranchRef(),fToTree->GetBranchRef());
411  }
412  return numBasket;
413 }
414 
415 ////////////////////////////////////////////////////////////////////////////////
416 /// Collect the information about the on-file basket that need
417 /// to be copied.
418 
419 void TTreeCloner::CollectBaskets()
420 {
421  UInt_t len = fFromBranches.GetEntries();
422 
423  for(UInt_t i=0,bi=0; i<len; ++i) {
424  TBranch *from = (TBranch*)fFromBranches.UncheckedAt(i);
425  for(Int_t b=0; b<from->GetWriteBasket(); ++b,++bi) {
426  fBasketBranchNum[bi] = i;
427  fBasketNum[bi] = b;
428  fBasketSeek[bi] = from->GetBasketSeek(b);
429  //fprintf(stderr,"For %s %d %lld\n",from->GetName(),bi,fBasketSeek[bi]);
430  fBasketEntry[bi] = from->GetBasketEntry()[b];
431  fBasketIndex[bi] = bi;
432  }
433  }
434 }
435 
436 ////////////////////////////////////////////////////////////////////////////////
437 /// Make sure that all the needed TStreamerInfo are
438 /// present in the output file
439 
440 void TTreeCloner::CopyStreamerInfos()
441 {
442  TFile *fromFile = fFromTree->GetDirectory()->GetFile();
443  TFile *toFile = fToTree->GetDirectory()->GetFile();
444  TList *l = fromFile->GetStreamerInfoList();
445  TIter next(l);
446  TStreamerInfo *oldInfo;
447  while ( (oldInfo = (TStreamerInfo*)next()) ) {
448  if (oldInfo->IsA() != TStreamerInfo::Class()) {
449  continue;
450  }
451  TStreamerInfo *curInfo = 0;
452  TClass *cl = TClass::GetClass(oldInfo->GetName());
453 
454  if (!cl->IsLoaded() || cl->GetNew()) {
455  // Insure that the TStreamerInfo is loaded
456  curInfo = (TStreamerInfo*)cl->GetStreamerInfo(oldInfo->GetClassVersion());
457  if (oldInfo->GetClassVersion()==1) {
458  // We may have a Foreign class let's look using the
459  // checksum:
460  TStreamerInfo *matchInfo = (TStreamerInfo*)cl->FindStreamerInfo(oldInfo->GetCheckSum());
461  if (matchInfo) {
462  curInfo = matchInfo;
463  }
464  }
465  curInfo->ForceWriteInfo(toFile);
466  } else {
467  // If there is no default constructor the GetStreamerInfo
468  // will not work. It also means (hopefully) that an
469  // inheriting class has a streamerInfo in the list (which
470  // will induces the setting of this streamerInfo)
471 
472  oldInfo->ForceWriteInfo(toFile);
473  }
474  }
475  delete l;
476 }
477 
478 ////////////////////////////////////////////////////////////////////////////////
479 /// Transfer the basket from the input file to the output file
480 
481 void TTreeCloner::CopyMemoryBaskets()
482 {
483  TBasket *basket = 0;
484  for(Int_t i=0; i<fToBranches.GetEntries(); ++i) {
485  TBranch *from = (TBranch*)fFromBranches.UncheckedAt( i );
486  TBranch *to = (TBranch*)fToBranches.UncheckedAt( i );
487 
488  basket = from->GetListOfBaskets()->GetEntries() ? from->GetBasket(from->GetWriteBasket()) : 0;
489  if (basket) {
490  basket = (TBasket*)basket->Clone();
491  basket->SetBranch(to);
492  to->AddBasket(*basket, kFALSE, fToStartEntries+from->GetBasketEntry()[from->GetWriteBasket()]);
493  } else {
494  to->AddLastBasket( fToStartEntries+from->GetBasketEntry()[from->GetWriteBasket()] );
495  }
496  // In older files, if the branch is a TBranchElement non-terminal 'object' branch, it's basket will contain 0
497  // events, in newer file in the same case, the write basket will be missing.
498  if (from->GetEntries()!=0 && from->GetWriteBasket()==0 && (basket==0 || basket->GetNevBuf()==0)) {
499  to->SetEntries(to->GetEntries()+from->GetEntries());
500  }
501  }
502 }
503 
504 ////////////////////////////////////////////////////////////////////////////////
505 /// Make sure that all the needed TStreamerInfo are
506 /// present in the output file
507 
508 void TTreeCloner::CopyProcessIds()
509 {
510  // NOTE: We actually need to merge the ProcessId somehow :(
511 
512  TFile *fromfile = fFromTree->GetDirectory()->GetFile();
513  TFile *tofile = fToTree->GetDirectory()->GetFile();
514 
515  fPidOffset = tofile->GetNProcessIDs();
516 
517  TIter next(fromfile->GetListOfKeys());
518  TKey *key;
519  TDirectory::TContext cur(fromfile);
520  while ((key = (TKey*)next())) {
521  if (!strcmp(key->GetClassName(),"TProcessID")) {
522  TProcessID *pid = (TProcessID*)key->ReadObjectAny(0);
523  if (!pid) continue;
524 
525  //UShort_t out = TProcessID::WriteProcessID(id,tofile);
526  UShort_t out = 0;
527  TObjArray *pids = tofile->GetListOfProcessIDs();
528  Int_t npids = tofile->GetNProcessIDs();
529  Bool_t wasIn = kFALSE;
530  for (Int_t i=0;i<npids;i++) {
531  if (pids->At(i) == pid) {out = (UShort_t)i; wasIn = kTRUE; break;}
532  }
533 
534  if (!wasIn) {
535  TDirectory *dirsav = gDirectory;
536  tofile->cd();
537  tofile->SetBit(TFile::kHasReferences);
538  pids->AddAtAndExpand(pid,npids);
539  pid->IncrementCount();
540  char name[32];
541  snprintf(name,32,"ProcessID%d",npids);
542  pid->Write(name);
543  tofile->IncrementProcessIDs();
544  if (gDebug > 0) {
545  Info("WriteProcessID", "name=%s, file=%s", name, tofile->GetName());
546  }
547  if (dirsav) dirsav->cd();
548  out = (UShort_t)npids;
549  }
550  if (out<fPidOffset) {
551  Error("CopyProcessIDs","Copied %s from %s might already exist!\n",
552  pid->GetName(),fromfile->GetName());
553  }
554  }
555  }
556 }
557 
558 ////////////////////////////////////////////////////////////////////////////////
559 /// Create a TFileCacheRead if it was requested.
560 
561 void TTreeCloner::CreateCache()
562 {
563  if (fCacheSize && fFromTree->GetCurrentFile()) {
564  TFile *f = fFromTree->GetCurrentFile();
565  auto prev = fFromTree->GetReadCache(f);
566  if (fFileCache && prev == fFileCache) {
567  return;
568  }
569  fPrevCache = prev;
570  // Remove the previous cache if any.
571  if (prev) f->SetCacheRead(nullptr, fFromTree);
572  // The constructor attach the new cache.
573  fFileCache = new TFileCacheRead(f, fCacheSize, fFromTree);
574  }
575 }
576 
577 ////////////////////////////////////////////////////////////////////////////////
578 /// Restore the TFileCacheRead to its previous value.
579 
580 void TTreeCloner::RestoreCache() {
581  if (IsValid() && fFileCache && fFromTree->GetCurrentFile()) {
582  TFile *f = fFromTree->GetCurrentFile();
583  f->SetCacheRead(nullptr,fFromTree); // Remove our file cache.
584  f->SetCacheRead(fPrevCache, fFromTree);
585  }
586 }
587 
588 ////////////////////////////////////////////////////////////////////////////////
589 /// Set the entries and import the cluster range of the
590 
591 void TTreeCloner::ImportClusterRanges()
592 {
593  // First undo, the external call to SetEntries
594  // We could improve the interface to optional tell the TTreeCloner that the
595  // SetEntries was not done.
596  fToTree->SetEntries(fToTree->GetEntries() - fFromTree->GetTree()->GetEntries());
597 
598  fToTree->ImportClusterRanges( fFromTree->GetTree() );
599 
600  // This is only updated by TTree::Fill upon seeing a Flush event in TTree::Fill
601  // So we need to propagate (this has also the advantage of turning on the
602  // history recording feature of SetAutoFlush for the next iteration)
603  fToTree->fFlushedBytes += fFromTree->fFlushedBytes;
604 
605  fToTree->SetEntries(fToTree->GetEntries() + fFromTree->GetTree()->GetEntries());
606 }
607 
608 ////////////////////////////////////////////////////////////////////////////////
609 /// Set the TFile cache size to be used.
610 /// Note that the default is to use the same size as the default TTreeCache for
611 /// the input tree.
612 /// \param size Size of the cache. Zero disable the use of the cache.
613 
614 void TTreeCloner::SetCacheSize(Int_t size)
615 {
616  fCacheSize = size;
617  if (IsValid() && fFileCache) {
618  if (fCacheSize == 0 || fCacheSize != fFileCache->GetBufferSize()) {
619  TFile *f = fFromTree->GetCurrentFile();
620  f->SetCacheRead(nullptr,fFromTree);
621  delete fFileCache;
622  fFileCache = nullptr;
623  }
624  }
625  // Note if the TFile cache is needed, it will be created at the
626  // beginning of Exec.
627 }
628 
629 ////////////////////////////////////////////////////////////////////////////////
630 /// Sort the basket according to the user request.
631 
632 void TTreeCloner::SortBaskets()
633 {
634  // Currently this sort __has to__ preserve the order
635  // of basket for each individual branch.
636 
637  switch (fCloneMethod) {
638  case kSortBasketsByBranch:
639  // nothing to do, it is already sorted.
640  break;
641  case kSortBasketsByEntry: {
642  for(UInt_t i = 0; i < fMaxBaskets; ++i) { fBasketIndex[i] = i; }
643  std::sort(fBasketIndex, fBasketIndex+fMaxBaskets, CompareEntry( this) );
644  break;
645  }
646  case kSortBasketsByOffset:
647  default: {
648  for(UInt_t i = 0; i < fMaxBaskets; ++i) { fBasketIndex[i] = i; }
649  std::sort(fBasketIndex, fBasketIndex+fMaxBaskets, CompareSeek( this) );
650  break;
651  }
652  }
653 }
654 
655 ////////////////////////////////////////////////////////////////////////////////
656 /// Fill the file cache with the next set of basket.
657 ///
658 /// \param from index of the first lement of fFromBranches to start caching
659 /// \return The index of first element of fFromBranches that is not in the cache
660 UInt_t TTreeCloner::FillCache(UInt_t from)
661 {
662  if (!fFileCache) return 0;
663  // Reset the cache
664  fFileCache->Prefetch(0, 0);
665  Long64_t size = 0;
666  for (UInt_t j = from; j < fMaxBaskets; ++j) {
667  TBranch *frombr = (TBranch *) fFromBranches.UncheckedAt(fBasketBranchNum[fBasketIndex[j]]);
668 
669 
670  Int_t index = fBasketNum[ fBasketIndex[j] ];
671  Long64_t pos = frombr->GetBasketSeek(index);
672  Int_t len = frombr->GetBasketBytes()[index];
673  if (pos && len) {
674  size += len;
675  if (size > fFileCache->GetBufferSize()) {
676  return j;
677  }
678  fFileCache->Prefetch(pos,len);
679  }
680  }
681  return fMaxBaskets;
682 }
683 
684 ////////////////////////////////////////////////////////////////////////////////
685 /// Transfer the basket from the input file to the output file
686 
687 void TTreeCloner::WriteBaskets()
688 {
689  TBasket *basket = new TBasket();
690  for(UInt_t j = 0, notCached = 0; j<fMaxBaskets; ++j) {
691  TBranch *from = (TBranch*)fFromBranches.UncheckedAt( fBasketBranchNum[ fBasketIndex[j] ] );
692  TBranch *to = (TBranch*)fToBranches.UncheckedAt( fBasketBranchNum[ fBasketIndex[j] ] );
693 
694  TFile *tofile = to->GetFile(0);
695  TFile *fromfile = from->GetFile(0);
696 
697  Int_t index = fBasketNum[ fBasketIndex[j] ];
698 
699  Long64_t pos = from->GetBasketSeek(index);
700  if (pos!=0) {
701  if (fFileCache && j >= notCached) {
702  notCached = FillCache(notCached);
703  }
704  if (from->GetBasketBytes()[index] == 0) {
705  from->GetBasketBytes()[index] = basket->ReadBasketBytes(pos, fromfile);
706  }
707  Int_t len = from->GetBasketBytes()[index];
708 
709  basket->LoadBasketBuffers(pos,len,fromfile,fFromTree);
710  basket->IncrementPidOffset(fPidOffset);
711  basket->CopyTo(tofile);
712  to->AddBasket(*basket,kTRUE,fToStartEntries + from->GetBasketEntry()[index]);
713  } else {
714  TBasket *frombasket = from->GetBasket( index );
715  if (frombasket && frombasket->GetNevBuf()>0) {
716  TBasket *tobasket = (TBasket*)frombasket->Clone();
717  tobasket->SetBranch(to);
718  to->AddBasket(*tobasket, kFALSE, fToStartEntries+from->GetBasketEntry()[index]);
719  to->FlushOneBasket(to->GetWriteBasket());
720  }
721  }
722  }
723  delete basket;
724 }