Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TDirectoryFile.cxx
Go to the documentation of this file.
1 // @(#)root/io:$Id$
2 // Author: Rene Brun 22/01/2007
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2007, 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 /**
13  \class TDirectoryFile
14  \ingroup IO
15 
16  A ROOT file is structured in Directories (like a file system).
17  Each Directory has a list of Keys (see TKeys) and a list of objects
18  in memory. A Key is a small object that describes the type and location
19  of a persistent object in a file. The persistent object may be a directory.
20 Begin_Macro
21 ../../../tutorials/io/fildir.C
22 End_Macro
23  The structure of a file is shown in TFile::TFile
24 */
25 
26 #include "Riostream.h"
27 #include "Strlen.h"
28 #include "TDirectoryFile.h"
29 #include "TFile.h"
30 #include "TBufferFile.h"
31 #include "TBufferJSON.h"
32 #include "TMapFile.h"
33 #include "TClassTable.h"
34 #include "TInterpreter.h"
35 #include "THashList.h"
36 #include "TBrowser.h"
37 #include "TFree.h"
38 #include "TKey.h"
39 #include "TStreamerInfo.h"
40 #include "TROOT.h"
41 #include "TError.h"
42 #include "Bytes.h"
43 #include "TClass.h"
44 #include "TRegexp.h"
45 #include "TSystem.h"
46 #include "TStreamerElement.h"
47 #include "TProcessUUID.h"
48 #include "TVirtualMutex.h"
50 
51 const UInt_t kIsBigFile = BIT(16);
52 const Int_t kMaxLen = 2048;
53 
54 ClassImp(TDirectoryFile);
55 
56 
57 ////////////////////////////////////////////////////////////////////////////////
58 /// Default TDirectoryFile constructor
59 
60 TDirectoryFile::TDirectoryFile()
61 {
62  /// Intentionally placed here
63  /// when TDirectoryFile() = default; used, mac1014/cxx17 fails on some tests
64  /// Problem with TObject::IsOnHeap() failing
65 }
66 
67 
68 ////////////////////////////////////////////////////////////////////////////////
69 /// Create a new TDirectoryFile
70 ///
71 /// A new directory with a name and a title is created in the current directory.
72 /// The directory header information is immediately saved on the file
73 /// A new key is added in the parent directory.
74 /// When this constructor is called from a class directly derived
75 /// from TDirectoryFile, the third argument, classname, MUST be specified.
76 /// In this case, classname must be the name of the derived class.
77 ///
78 /// Note that the directory name cannot contain slashes.
79 
80 TDirectoryFile::TDirectoryFile(const char *name, const char *title, Option_t *classname, TDirectory* initMotherDir)
81 {
82  // We must not publish this objects to the list of RecursiveRemove (indirectly done
83  // by 'Appending' this object to it's mother) before the object is completely
84  // initialized.
85  // However a better option would be to delay the publishing until the very end,
86  // but it is currently done in the middle of the initialization (by Build which
87  // is a public interface) ....
88  R__LOCKGUARD(gROOTMutex);
89 
90  fName = name;
91  fTitle = title;
92 
93  if (!initMotherDir) initMotherDir = gDirectory;
94 
95  if (strchr(name,'/')) {
96  ::Error("TDirectoryFile","directory name (%s) cannot contain a slash", name);
97  gDirectory = nullptr;
98  return;
99  }
100  if (strlen(GetName()) == 0) {
101  ::Error("TDirectoryFile","directory name cannot be \"\"");
102  gDirectory = nullptr;
103  return;
104  }
105 
106  BuildDirectoryFile(initMotherDir ? initMotherDir->GetFile() : nullptr, initMotherDir);
107 
108  TDirectory* motherdir = GetMotherDir();
109  TFile* f = TDirectoryFile::GetFile();
110 
111  if (!motherdir || !f) return;
112  if (!f->IsWritable()) return; //*-* in case of a directory in memory
113  if (motherdir->GetKey(name)) {
114  Error("TDirectoryFile","An object with name %s exists already", name);
115  return;
116  }
117  TClass *cl = nullptr;
118  if (classname[0]) {
119  cl = TClass::GetClass(classname);
120  if (!cl) {
121  Error("TDirectoryFile","Invalid class name: %s",classname);
122  return;
123  }
124  } else {
125  cl = TDirectoryFile::IsA();
126  }
127 
128  fBufferSize = 0;
129  fWritable = kTRUE;
130 
131  InitDirectoryFile(cl);
132 
133  fModified = kFALSE;
134 
135  // Temporarily redundant, see comment on lock early in the function.
136  // R__LOCKGUARD(gROOTMutex);
137  gROOT->GetUUIDs()->AddUUID(fUUID,this);
138  // We should really be doing this now rather than in Build, see
139  // comment at the start of the function.
140  // if (initMotherDir && strlen(GetName()) != 0) initMotherDir->Append(this);
141 }
142 
143 ////////////////////////////////////////////////////////////////////////////////
144 /// Initialize the key associated with this directory (and the related
145 /// data members.
146 
147 void TDirectoryFile::InitDirectoryFile(TClass *cl)
148 {
149  TFile* f = GetFile(); // NOLINT: silence clang-tidy warnings
150  if (f->IsBinary()) {
151  if (!cl) {
152  cl = IsA(); // NOLINT: silence clang-tidy warnings
153  }
154  TDirectory* motherdir = GetMotherDir();
155  fSeekParent = f->GetSeekDir();
156  Int_t nbytes = TDirectoryFile::Sizeof();
157  TKey *key = new TKey(fName,fTitle,cl,nbytes,motherdir);
158  fNbytesName = key->GetKeylen();
159  fSeekDir = key->GetSeekKey();
160  if (fSeekDir == 0) return;
161  char *buffer = key->GetBuffer();
162  TDirectoryFile::FillBuffer(buffer);
163  Int_t cycle = motherdir ? motherdir->AppendKey(key) : 0;
164  key->WriteFile(cycle);
165  } else {
166  fSeekParent = 0;
167  fNbytesName = 0;
168  fSeekDir = f->DirCreateEntry(this);
169  if (fSeekDir == 0) return;
170  }
171 
172 }
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// Destructor.
176 
177 TDirectoryFile::~TDirectoryFile()
178 {
179  if (fKeys) {
180  fKeys->Delete("slow");
181  SafeDelete(fKeys);
182  }
183 
184  TDirectoryFile::CleanTargets();
185 
186  // Delete our content before we become somewhat invalid
187  // since some those objects (TTree for example) needs information
188  // from this object. Note that on some platform after the end
189  // of the body (i.e. thus during ~TDirectory which is also
190  // contains this code) the execution of 'this->GetFile()' fails
191  // to return the 'proper' value (because it uses the wrong
192  // virtual function).
193  if (fList) {
194  fList->Delete("slow");
195  SafeDelete(fList);
196  }
197 
198  if (gDebug) {
199  Info("~TDirectoryFile", "dtor called for %s", GetName());
200  }
201 }
202 
203 ////////////////////////////////////////////////////////////////////////////////
204 /// Append object to this directory.
205 ///
206 /// If replace is true:
207 /// remove any existing objects with the same same (if the name is not ""
208 
209 void TDirectoryFile::Append(TObject *obj, Bool_t replace /* = kFALSE */)
210 {
211  if (!obj || !fList) return;
212 
213  TDirectory::Append(obj,replace);
214 
215  if (!fMother) return;
216  if (fMother->IsA() == TMapFile::Class()) {
217  TMapFile *mfile = (TMapFile*)fMother;
218  mfile->Add(obj);
219  }
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 /// Insert key in the linked list of keys of this directory.
224 
225 Int_t TDirectoryFile::AppendKey(TKey *key)
226 {
227  if (!fKeys) {
228  Error("AppendKey","TDirectoryFile not initialized yet.");
229  return 0;
230  }
231 
232  fModified = kTRUE;
233 
234  key->SetMotherDir(this);
235 
236  // This is a fast hash lookup in case the key does not already exist
237  TKey *oldkey = (TKey*)fKeys->FindObject(key->GetName());
238  if (!oldkey) {
239  fKeys->Add(key);
240  return 1;
241  }
242 
243  // If the key name already exists we have to make a scan for it
244  // and insert the new key ahead of the current one
245  TObjLink *lnk = fKeys->FirstLink();
246  while (lnk) {
247  oldkey = (TKey*)lnk->GetObject();
248  if (!strcmp(oldkey->GetName(), key->GetName()))
249  break;
250  lnk = lnk->Next();
251  }
252 
253  fKeys->AddBefore(lnk, key);
254  return oldkey->GetCycle() + 1;
255 }
256 
257 ////////////////////////////////////////////////////////////////////////////////
258 /// Browse the content of the directory.
259 
260 void TDirectoryFile::Browse(TBrowser *b)
261 {
262  TString name;
263 
264  if (b) {
265  TObject *obj = nullptr;
266  TIter nextin(fList);
267  TKey *key = nullptr, *keyo = nullptr;
268  TIter next(fKeys);
269 
270  cd();
271 
272  //Add objects that are only in memory
273  while ((obj = nextin())) {
274  if (fKeys->FindObject(obj->GetName())) continue;
275  b->Add(obj, obj->GetName());
276  }
277 
278  //Add keys
279  while ((key = (TKey *) next())) {
280  int skip = 0;
281  if (!keyo || (keyo && strcmp(keyo->GetName(), key->GetName()))) {
282  skip = 0;
283  obj = fList->FindObject(key->GetName());
284 
285  if (obj) {
286  b->Add(obj, obj->GetName());
287  if (obj->IsFolder() && !obj->InheritsFrom("TTree"))
288  skip = 1;
289  }
290  }
291 
292  if (!skip) {
293  name.Form("%s;%d", key->GetName(), key->GetCycle());
294  b->Add(key, name);
295  }
296 
297  keyo = key;
298  }
299  }
300 }
301 
302 ////////////////////////////////////////////////////////////////////////////////
303 /// Initialise directory to defaults.
304 
305 void TDirectoryFile::BuildDirectoryFile(TFile* motherFile, TDirectory* motherDir)
306 {
307  // If directory is created via default ctor (when dir is read from file)
308  // don't add it here to the directory since its name is not yet known.
309  // It will be added to the directory in TKey::ReadObj().
310 
311  if (motherDir && strlen(GetName()) != 0) motherDir->Append(this);
312 
313  fModified = kTRUE;
314  fWritable = kFALSE;
315  fDatimeC.Set();
316  fDatimeM.Set();
317  fNbytesKeys = 0;
318  fSeekDir = 0;
319  fSeekParent = 0;
320  fSeekKeys = 0;
321  fList = new THashList(100,50);
322  fKeys = new THashList(100,50);
323  fList->UseRWLock();
324  fMother = motherDir;
325  fFile = motherFile ? motherFile : TFile::CurrentFile();
326  SetBit(kCanDelete);
327 }
328 
329 ////////////////////////////////////////////////////////////////////////////////
330 /// Change current directory to "this" directory.
331 /// Using path one can
332 /// change the current directory to "path". The absolute path syntax is:
333 ///
334 /// file.root:/dir1/dir2
335 ///
336 /// where file.root is the file and /dir1/dir2 the desired subdirectory
337 /// in the file. Relative syntax is relative to "this" directory. E.g:
338 /// ../aa. Returns kTRUE in case of success.
339 
340 Bool_t TDirectoryFile::cd(const char *path)
341 {
342  Bool_t ok = TDirectory::cd(path);
343  if (ok) TFile::CurrentFile() = fFile;
344  return ok;
345 }
346 
347 ////////////////////////////////////////////////////////////////////////////////
348 /// Clean the pointers to this object (gDirectory, TContext, etc.)
349 
350 void TDirectoryFile::CleanTargets()
351 {
352  // After CleanTargets either gFile was changed appropriately
353  // by a cd() or needs to be set to zero.
354  if (gFile == this) {
355  gFile = nullptr;
356  }
357 
358  TDirectory::CleanTargets();
359 }
360 
361 ////////////////////////////////////////////////////////////////////////////////
362 /// Make a clone of an object using the Streamer facility.
363 ///
364 /// If the object derives from TNamed, this function is called
365 /// by TNamed::Clone. TNamed::Clone uses the optional argument newname to set
366 /// a new name to the newly created object.
367 ///
368 /// If autoadd is true and if the object class has a
369 /// DirectoryAutoAdd function, it will be called at the end of the
370 /// function with the parameter gDirectory. This usually means that
371 /// the object will be appended to the current ROOT directory.
372 
373 TObject *TDirectoryFile::CloneObject(const TObject *obj, Bool_t autoadd /* = kTRUE */)
374 {
375  // if no default ctor return immediately (error issued by New())
376  char *pobj = (char*)obj->IsA()->New();
377  if (!pobj) return nullptr;
378 
379  Int_t baseOffset = obj->IsA()->GetBaseClassOffset(TObject::Class());
380  if (baseOffset==-1) {
381  // cl does not inherit from TObject.
382  // Since this is not supported in this function, the only reason we could reach this code
383  // is because something is screwed up in the ROOT code.
384  Fatal("CloneObject","Incorrect detection of the inheritance from TObject for class %s.\n",
385  obj->IsA()->GetName());
386  }
387  TObject *newobj = (TObject*)(pobj+baseOffset);
388 
389  //create a buffer where the object will be streamed
390  {
391  // NOTE: do we still need to make this change to gFile?
392  // NOTE: This can not be 'gDirectory=0' as at least roofit expect gDirectory to not be null
393  // during the streaming ....
394  TFile *filsav = gFile;
395  gFile = nullptr;
396  const Int_t bufsize = 10000;
397  TBufferFile buffer(TBuffer::kWrite,bufsize);
398  buffer.MapObject(obj); //register obj in map to handle self reference
399  {
400  Bool_t isRef = obj->TestBit(kIsReferenced);
401  ((TObject*)obj)->ResetBit(kIsReferenced);
402 
403  ((TObject*)obj)->Streamer(buffer);
404 
405  if (isRef) ((TObject*)obj)->SetBit(kIsReferenced);
406  }
407 
408  // read new object from buffer
409  buffer.SetReadMode();
410  buffer.ResetMap();
411  buffer.SetBufferOffset(0);
412  buffer.MapObject(newobj); //register obj in map to handle self reference
413  newobj->Streamer(buffer);
414  newobj->ResetBit(kIsReferenced);
415  newobj->ResetBit(kCanDelete);
416  gFile = filsav;
417  }
418 
419  if (autoadd) {
420  ROOT::DirAutoAdd_t func = obj->IsA()->GetDirectoryAutoAdd();
421  if (func) {
422  func(newobj,this);
423  }
424  }
425  return newobj;
426 }
427 
428 ////////////////////////////////////////////////////////////////////////////////
429 /// Scan the memory lists of all files for an object with name
430 
431 TObject *TDirectoryFile::FindObjectAnyFile(const char *name) const
432 {
433  TFile *f;
434  R__LOCKGUARD(gROOTMutex);
435  TIter next(gROOT->GetListOfFiles());
436  while ((f = (TFile*)next())) {
437  TObject *obj = f->GetList()->FindObject(name);
438  if (obj) return obj;
439  }
440  return nullptr;
441 }
442 
443 ////////////////////////////////////////////////////////////////////////////////
444 /// Find a directory named "apath".
445 ///
446 /// It apath is null or empty, returns "this" directory.
447 /// Otherwise use the name "apath" to find a directory.
448 /// The absolute path syntax is:
449 ///
450 /// file.root:/dir1/dir2
451 ///
452 /// where file.root is the file and /dir1/dir2 the desired subdirectory
453 /// in the file. Relative syntax is relative to "this" directory. E.g:
454 /// ../aa.
455 /// Returns 0 in case path does not exist.
456 /// If printError is true, use Error with 'funcname' to issue an error message.
457 
458 TDirectory *TDirectoryFile::GetDirectory(const char *apath,
459  Bool_t printError, const char *funcname)
460 {
461  Int_t nch = 0;
462  if (apath) nch = strlen(apath);
463  if (!nch) {
464  return this;
465  }
466 
467  if (funcname==0 || strlen(funcname)==0) funcname = "GetDirectory";
468 
469  TDirectory *result = this;
470 
471  char *path = new char[nch+1]; path[0] = 0;
472  if (nch) strlcpy(path,apath,nch+1);
473  char *s = (char*)strchr(path, ':');
474  if (s) {
475  *s = '\0';
476  R__LOCKGUARD(gROOTMutex);
477  TDirectory *f = (TDirectory *)gROOT->GetListOfFiles()->FindObject(path);
478  // Check if this is a duplicate (2nd opening) on this file and prefer
479  // this file.
480  if (GetFile()) {
481  auto url = GetFile()->GetEndpointUrl();
482  if (f && 0 == url->Compare(f->GetFile()->GetEndpointUrl()))
483  return GetDirectory(s+1,printError,funcname);
484  }
485  if (!f && !strcmp(gROOT->GetName(), path)) f = gROOT;
486  if (s) *s = ':';
487  if (f) {
488  result = f;
489  if (s && *(s+1)) result = f->GetDirectory(s+1,printError,funcname);
490  delete [] path; return result;
491  } else {
492  if (printError) Error(funcname, "No such file %s", path);
493  delete [] path; return nullptr;
494  }
495  }
496 
497  // path starts with a slash (assumes current file)
498  if (path[0] == '/') {
499  TDirectory *td = fFile;
500  if (!fFile) td = gROOT;
501  result = td->GetDirectory(path+1,printError,funcname);
502  delete [] path; return result;
503  }
504 
505  TDirectoryFile *obj;
506  char *slash = (char*)strchr(path,'/');
507  if (!slash) { // we are at the lowest level
508  if (!strcmp(path, "..")) {
509  result = GetMotherDir();
510  delete [] path; return result;
511  }
512  GetObject(path,obj);
513  if (!obj) {
514  if (printError) Error(funcname,"Unknown directory %s", path);
515  delete [] path; return nullptr;
516  }
517 
518  delete [] path; return obj;
519  }
520 
521  TString subdir(path);
522  slash = (char*)strchr(subdir.Data(),'/');
523  *slash = 0;
524  //Get object with path from current directory/file
525  if (!strcmp(subdir, "..")) {
526  TDirectory* mom = GetMotherDir();
527  if (mom)
528  result = mom->GetDirectory(slash+1,printError,funcname);
529  delete [] path; return result;
530  }
531  GetObject(subdir,obj);
532  if (!obj) {
533  if (printError) Error(funcname,"Unknown directory %s", subdir.Data());
534  delete [] path; return nullptr;
535  }
536 
537  result = ((TDirectory*)obj)->GetDirectory(slash+1,printError,funcname);
538  delete [] path; return result;
539 }
540 
541 ////////////////////////////////////////////////////////////////////////////////
542 /// Delete all objects from memory and directory structure itself.
543 
544 void TDirectoryFile::Close(Option_t *option)
545 {
546  if (!fList || !fSeekDir) {
547  return;
548  }
549 
550  // Save the directory key list and header
551  Save();
552 
553  Bool_t nodelete = option ? (!strcmp(option, "nodelete") ? kTRUE : kFALSE) : kFALSE;
554 
555  if (!nodelete) {
556  Bool_t fast = kTRUE;
557  TObjLink *lnk = fList->FirstLink();
558  while (lnk) {
559  if (lnk->GetObject()->IsA() == TDirectoryFile::Class()) {fast = kFALSE;break;}
560  lnk = lnk->Next();
561  }
562  // Delete objects from directory list, this in turn, recursively closes all
563  // sub-directories (that were allocated on the heap)
564  // if this dir contains subdirs, we must use the slow option for Delete!
565  // we must avoid "slow" as much as possible, in particular Delete("slow")
566  // with a large number of objects (eg >10^5) would take for ever.
567  {
568  if (fast) fList->Delete();
569  else fList->Delete("slow");
570  }
571  }
572 
573  // Delete keys from key list (but don't delete the list header)
574  if (fKeys) {
575  fKeys->Delete("slow");
576  }
577 
578  TDirectoryFile::CleanTargets();
579 }
580 
581 ////////////////////////////////////////////////////////////////////////////////
582 /// Delete Objects or/and keys in a directory
583 ///
584 /// Properties of the namecycle string:
585 /// - namecycle has the format name;cycle
586 /// - namecycle = "" is same as namecycle ="T*"
587 /// - name = * means all
588 /// - cycle = * means all cycles (memory and keys)
589 /// - cycle = "" or cycle = 9999 ==> apply to a memory object
590 /// When name=* use T* to delete subdirectories also
591 ///
592 /// To delete one directory, you must specify the directory cycle,
593 /// eg. file.Delete("dir1;1");
594 ///
595 /// Examples:
596 /// | Pattern | Description |
597 /// |---------|-------------|
598 /// | foo | delete object named foo in memory |
599 /// | foo* | delete all objects with a name starting with foo |
600 /// | foo;1 | delete cycle 1 of foo on file |
601 /// | foo;* | delete all cycles of foo on file and also from memory |
602 /// | *;2 | delete all objects on file having the cycle 2 |
603 /// | *;* | delete all objects from memory and file |
604 /// | T*;* | delete all objects from memory and file and all subdirectories |
605 ///
606 /// ## WARNING
607 /// If the key to be deleted contains special characters ("+","^","?", etc
608 /// that have a special meaning for the regular expression parser (see TRegexp)
609 /// then you must specify 2 backslash characters to escape the regular expression.
610 /// For example, if the key to be deleted is namecycle = "C++", you must call
611 ///
612 /// mydir.Delete("C\\+\\+"));
613 ///
614 
615 void TDirectoryFile::Delete(const char *namecycle)
616 {
617  if (gDebug)
618  Info("Delete","Call for this = %s namecycle = %s",
619  GetName(), (namecycle ? namecycle : "null"));
620 
621  TDirectory::TContext ctxt(this);
622  Short_t cycle;
623  char name[kMaxLen];
624  const char *nmcy = (namecycle) ? namecycle : "";
625  DecodeNameCycle(nmcy, name, cycle, kMaxLen);
626 
627  Int_t deleteall = 0;
628  Int_t deletetree = 0;
629  if(strcmp(name,"*") == 0) deleteall = 1;
630  if(strcmp(name,"*T") == 0){ deleteall = 1; deletetree = 1;}
631  if(strcmp(name,"T*") == 0){ deleteall = 1; deletetree = 1;}
632  if(namecycle==0 || !namecycle[0]){ deleteall = 1; deletetree = 1;}
633  TRegexp re(name,kTRUE);
634  TString s;
635  Int_t deleteOK = 0;
636 
637 //*-*---------------------Case of Object in memory---------------------
638 // ========================
639  if (cycle >= 9999 ) {
640  TNamed *idcur;
641  TIter next(fList);
642  while ((idcur = (TNamed *) next())) {
643  deleteOK = 0;
644  s = idcur->GetName();
645  if (deleteall || s.Index(re) != kNPOS) {
646  deleteOK = 1;
647  if (idcur->IsA() == TDirectoryFile::Class()) {
648  deleteOK = 2;
649  if (!deletetree && deleteall) deleteOK = 0;
650  }
651  }
652  if (deleteOK != 0) {
653  fList->Remove(idcur);
654  if (deleteOK==2) {
655  // read subdirectories to correctly delete them
656  if (deletetree)
657  ((TDirectory*) idcur)->ReadAll("dirs");
658  idcur->Delete(deletetree ? "T*;*" : "*");
659  delete idcur;
660  } else
661  idcur->Delete(name);
662  }
663  }
664 // if (deleteOK == 2) {
665 // Info("Delete","Dir:%lx %s", fList->FindObject(name), name);
666 // delete fList->FindObject(name); //deleting a TDirectory
667 // }
668  }
669 //*-*---------------------Case of Key---------------------
670 // ===========
671  if (cycle != 9999 ) {
672  if (IsWritable()) {
673  TKey *key;
674  TIter nextkey(GetListOfKeys());
675  while ((key = (TKey *) nextkey())) {
676  deleteOK = 0;
677  s = key->GetName();
678  if (deleteall || s.Index(re) != kNPOS) {
679  if (cycle == key->GetCycle()) deleteOK = 1;
680  if (cycle > 9999) deleteOK = 1;
681  //if (!strcmp(key->GetClassName(),"TDirectory")) {
682  if (strstr(key->GetClassName(),"TDirectory")) {
683  deleteOK = 2;
684  if (!deletetree && deleteall) deleteOK = 0;
685  if (cycle == key->GetCycle()) deleteOK = 2;
686  }
687  }
688  if (deleteOK) {
689  if (deleteOK==2) {
690  // read directory with subdirectories to correctly delete and free key structure
691  TDirectory* dir = GetDirectory(key->GetName(), kTRUE, "Delete");
692  if (dir!=0) {
693  dir->Delete("T*;*");
694  fList->Remove(dir);
695  delete dir;
696  }
697  }
698 
699  key->Delete();
700  fKeys->Remove(key);
701  fModified = kTRUE;
702  delete key;
703  }
704  }
705  TFile* f = GetFile();
706  if (fModified && (f!=0)) {
707  WriteKeys(); //*-* Write new keys structure
708  WriteDirHeader(); //*-* Write new directory header
709  f->WriteFree(); //*-* Write new free segments list
710  f->WriteHeader(); //*-* Write new file header
711  }
712  }
713  }
714 }
715 
716 ////////////////////////////////////////////////////////////////////////////////
717 /// Encode directory header into output buffer
718 
719 void TDirectoryFile::FillBuffer(char *&buffer)
720 {
721  Version_t version = TDirectoryFile::Class_Version();
722  if (fSeekDir > TFile::kStartBigFile ||
723  fSeekParent > TFile::kStartBigFile ||
724  fSeekKeys > TFile::kStartBigFile )
725  {
726  // One of the address is larger than 2GB we need to use longer onfile
727  // integer, thus we increase the version number.
728  // Note that fSeekDir and fSeekKey are not necessarily correlated, if
729  // some object are 'removed' from the file and the holes are reused.
730  version += 1000;
731  }
732  tobuf(buffer, version);
733  const bool reproducible = TestBit(TFile::kReproducible) || (fFile && fFile->TestBit(TFile::kReproducible));
734  if (reproducible) {
735  TDatime((UInt_t) 1).FillBuffer(buffer);
736  TDatime((UInt_t) 1).FillBuffer(buffer);
737  } else {
738  fDatimeC.FillBuffer(buffer);
739  fDatimeM.FillBuffer(buffer);
740  }
741  tobuf(buffer, fNbytesKeys);
742  tobuf(buffer, fNbytesName);
743  if (version > 1000) {
744  tobuf(buffer, fSeekDir);
745  tobuf(buffer, fSeekParent);
746  tobuf(buffer, fSeekKeys);
747  } else {
748  tobuf(buffer, (Int_t)fSeekDir);
749  tobuf(buffer, (Int_t)fSeekParent);
750  tobuf(buffer, (Int_t)fSeekKeys);
751  }
752  if (reproducible)
753  TUUID("00000000-0000-0000-0000-000000000000").FillBuffer(buffer);
754  else
755  fUUID.FillBuffer(buffer);
756  if (fFile && fFile->GetVersion() < 40000) return;
757  if (version <=1000) for (Int_t i=0;i<3;i++) tobuf(buffer,Int_t(0));
758 }
759 
760 ////////////////////////////////////////////////////////////////////////////////
761 /// Find key with name keyname in the current directory
762 
763 TKey *TDirectoryFile::FindKey(const char *keyname) const
764 {
765  Short_t cycle;
766  char name[kMaxLen];
767 
768  DecodeNameCycle(keyname, name, cycle, kMaxLen);
769  return GetKey(name,cycle);
770 }
771 
772 ////////////////////////////////////////////////////////////////////////////////
773 /// Find key with name keyname in the current directory or
774 /// its subdirectories.
775 ///
776 /// NOTE: that If a key is found, the directory containing the key becomes
777 /// the current directory
778 
779 TKey *TDirectoryFile::FindKeyAny(const char *keyname) const
780 {
781  TDirectory *dirsav = gDirectory;
782  Short_t cycle;
783  char name[kMaxLen];
784 
785  DecodeNameCycle(keyname, name, cycle, kMaxLen);
786 
787  TIter next(GetListOfKeys());
788  TKey *key;
789  while ((key = (TKey *) next())) {
790  if (!strcmp(name, key->GetName()))
791  if ((cycle == 9999) || (cycle >= key->GetCycle())) {
792  const_cast<TDirectoryFile*>(this)->cd(); // may be we should not make cd ???
793  return key;
794  }
795  }
796  //try with subdirectories
797  next.Reset();
798  while ((key = (TKey *) next())) {
799  //if (!strcmp(key->GetClassName(),"TDirectory")) {
800  if (strstr(key->GetClassName(),"TDirectory")) {
801  TDirectory* subdir =
802  const_cast<TDirectoryFile*>(this)->GetDirectory(key->GetName(), kTRUE, "FindKeyAny");
803  TKey *k = subdir ? subdir->FindKeyAny(keyname) : nullptr;
804  if (k) return k;
805  }
806  }
807  if (dirsav) dirsav->cd();
808  return nullptr;
809 }
810 
811 ////////////////////////////////////////////////////////////////////////////////
812 /// Find object by name in the list of memory objects of the current
813 /// directory or its sub-directories.
814 ///
815 /// After this call the current directory is not changed.
816 /// To automatically set the current directory where the object is found,
817 /// use FindKeyAny(aname)->ReadObj().
818 
819 TObject *TDirectoryFile::FindObjectAny(const char *aname) const
820 {
821  //object may be already in the list of objects in memory
822  TObject *obj = TDirectory::FindObjectAny(aname);
823  if (obj) return obj;
824 
825  TDirectory *dirsav = gDirectory;
826  Short_t cycle;
827  char name[kMaxLen];
828 
829  DecodeNameCycle(aname, name, cycle, kMaxLen);
830 
831  TIter next(GetListOfKeys());
832  TKey *key;
833  //may be a key in the current directory
834  while ((key = (TKey *) next())) {
835  if (!strcmp(name, key->GetName())) {
836  if (cycle == 9999) return key->ReadObj();
837  if (cycle >= key->GetCycle()) return key->ReadObj();
838  }
839  }
840  //try with subdirectories
841  next.Reset();
842  while ((key = (TKey *) next())) {
843  //if (!strcmp(key->GetClassName(),"TDirectory")) {
844  if (strstr(key->GetClassName(),"TDirectory")) {
845  TDirectory* subdir =
846  ((TDirectory*)this)->GetDirectory(key->GetName(), kTRUE, "FindKeyAny");
847  TKey *k = subdir ? subdir->FindKeyAny(aname) : nullptr;
848  if (k) { if (dirsav) dirsav->cd(); return k->ReadObj();}
849  }
850  }
851  if (dirsav) dirsav->cd();
852  return nullptr;
853 }
854 
855 ////////////////////////////////////////////////////////////////////////////////
856 /// Return pointer to object identified by namecycle.
857 ///
858 /// Properties:
859 /// - namecycle has the format name;cycle
860 /// - name = * is illegal, cycle = * is illegal
861 /// - cycle = "" or cycle = 9999 ==> apply to a memory object
862 ///
863 /// Examples:
864 /// | Pattern | Explanation |
865 /// |---------|-------------|
866 /// | foo | get object named foo in memory if object is not in memory, try with highest cycle from file |
867 /// | foo;1 | get cycle 1 of foo on file |
868 ///
869 /// The retrieved object should in principle derive from TObject.
870 /// If not, the function TDirectoryFile::Get<T> should be called.
871 /// However, this function will still work for a non-TObject, provided that
872 /// the calling application cast the return type to the correct type (which
873 /// is the actual type of the object).
874 ///
875 /// ### The Get<T> Method
876 /// The method Get<T> offers better protection and avoids the need for any
877 /// cast:
878 /// ~~~{.cpp}
879 /// auto objPtr = directory->Get<MyClass>("some object");
880 /// if (objPtr) { ... the object exist and inherits from MyClass ... }
881 /// ~~~
882 ///
883 /// ### Very important note about inheritance
884 /// In case the class of this object derives from TObject but not
885 /// as a first inheritance, one must use dynamic_cast<>().
886 ///
887 /// #### Example 1 - Normal case:
888 ///
889 /// class MyClass : public TObject, public AnotherClass
890 ///
891 /// then on return, one can adopt a C style cast:
892 ///
893 /// auto objPtr = (MyClass*)directory->Get("some object of MyClass");
894 ///
895 /// #### Example 2 - Special case:
896 ///
897 /// class MyClass : public AnotherClass, public TObject
898 ///
899 /// then on return, one must do:
900 ///
901 /// auto objPtr = dynamic_cast<MyClass*>(directory->Get("some object of MyClass"));
902 ///
903 /// Of course, dynamic_cast<> can also be used in the example 1.
904 ///
905 
906 TObject *TDirectoryFile::Get(const char *namecycle)
907 {
908  Short_t cycle;
909  char name[kMaxLen];
910 
911  DecodeNameCycle(namecycle, name, cycle, kMaxLen);
912  Int_t nch = strlen(name);
913  for (Int_t i = nch-1; i > 0; i--) {
914  if (name[i] == '/') {
915  name[i] = 0;
916  TDirectory* dirToSearch=GetDirectory(name);
917  const char *subnamecycle = namecycle + i + 1;
918  name[i] = '/';
919  return dirToSearch?dirToSearch->Get(subnamecycle):0;
920  }
921  }
922  const char *namobj = name;
923 
924 //*-*---------------------Case of Object in memory---------------------
925 // ========================
926  TObject *idcur = fList ? fList->FindObject(namobj) : nullptr;
927  if (idcur) {
928  if (idcur==this && strlen(namobj)!=0) {
929  // The object has the same name has the directory and
930  // that's what we picked-up! We just need to ignore
931  // it ...
932  idcur = nullptr;
933  } else if (cycle == 9999) {
934  return idcur;
935  } else {
936  if (idcur->InheritsFrom(TCollection::Class()))
937  idcur->Delete(); // delete also list elements
938  delete idcur;
939  idcur = nullptr;
940  }
941  }
942 
943 //*-*---------------------Case of Key---------------------
944 // ===========
945  TKey *key;
946  TIter nextkey(GetListOfKeys());
947  while ((key = (TKey *) nextkey())) {
948  if (strcmp(namobj,key->GetName()) == 0) {
949  if ((cycle == 9999) || (cycle == key->GetCycle())) {
950  TDirectory::TContext ctxt(this);
951  idcur = key->ReadObj();
952  break;
953  }
954  }
955  }
956 
957  return idcur;
958 }
959 
960 ////////////////////////////////////////////////////////////////////////////////
961 /// Return pointer to object identified by namecycle.
962 ///
963 /// The returned object may or may not derive from TObject.
964 ///
965 /// - namecycle has the format name;cycle
966 /// - name = * is illegal, cycle = * is illegal
967 /// - cycle = "" or cycle = 9999 ==> apply to a memory object
968 ///
969 /// ## Very important note
970 /// The calling application must cast the returned object to
971 /// the final type, e.g.
972 ///
973 /// auto objPtr = (MyClass*)directory->GetObject("some object of MyClass");
974 
975 void *TDirectoryFile::GetObjectUnchecked(const char *namecycle)
976 {
977  return GetObjectChecked(namecycle,(TClass*)nullptr);
978 }
979 
980 ////////////////////////////////////////////////////////////////////////////////
981 /// See documentation of TDirectoryFile::GetObjectCheck(const char *namecycle, const TClass *cl)
982 
983 void *TDirectoryFile::GetObjectChecked(const char *namecycle, const char* classname)
984 {
985  return GetObjectChecked(namecycle,TClass::GetClass(classname));
986 }
987 
988 
989 ////////////////////////////////////////////////////////////////////////////////
990 /// Return pointer to object identified by namecycle if and only if the actual
991 /// object is a type suitable to be stored as a pointer to a "expectedClass"
992 /// If expectedClass is null, no check is performed.
993 ///
994 /// - namecycle has the format name;cycle
995 /// - name = * is illegal, cycle = * is illegal
996 /// - cycle = "" or cycle = 9999 ==> apply to a memory object
997 ///
998 /// ### Very important note
999 /// The calling application must cast the returned pointer to
1000 /// the type described by the 2 arguments (i.e. cl):
1001 ///
1002 /// auto objPtr = (MyClass*)directory->GetObjectChecked("some object of MyClass","MyClass"));
1003 ///
1004 /// Note: We recommend using the method TDirectoryFile::Get<T>:
1005 /// ~~~{.cpp}
1006 /// auto objPtr = directory->Get<MyClass>("some object inheriting from MyClass");
1007 /// if (objPtr) { ... we found what we are looking for ... }
1008 /// ~~~
1009 
1010 void *TDirectoryFile::GetObjectChecked(const char *namecycle, const TClass* expectedClass)
1011 {
1012 
1013  // If the name is invalid, issue an error message and return a nullptr
1014  if (!namecycle || '\0' == namecycle[0]) {
1015  Error("GetObjectChecked", "The provided key name is invalid.");
1016  return nullptr;
1017  }
1018 
1019  Short_t cycle;
1020  char name[kMaxLen];
1021 
1022  DecodeNameCycle(namecycle, name, cycle, kMaxLen);
1023  Int_t nch = strlen(name);
1024  for (Int_t i = nch-1; i > 0; i--) {
1025  if (name[i] == '/') {
1026  name[i] = 0;
1027  TDirectory* dirToSearch=GetDirectory(name);
1028  const char *subnamecycle = namecycle + i + 1;
1029  name[i] = '/';
1030  if (dirToSearch) {
1031  return dirToSearch->GetObjectChecked(subnamecycle, expectedClass);
1032  } else {
1033  return nullptr;
1034  }
1035  }
1036  }
1037  const char *namobj = name;
1038 
1039 //*-*---------------------Case of Object in memory---------------------
1040 // ========================
1041  if (expectedClass==0 || expectedClass->IsTObject()) {
1042  TObject *objcur = fList ? fList->FindObject(namobj) : nullptr;
1043  if (objcur) {
1044  if (objcur==this && strlen(namobj)!=0) {
1045  // The object has the same name has the directory and
1046  // that's what we picked-up! We just need to ignore
1047  // it ...
1048  objcur = nullptr;
1049  } else if (cycle == 9999) {
1050  // Check type
1051  if (expectedClass && objcur->IsA()->GetBaseClassOffset(expectedClass) == -1) return nullptr;
1052  else return objcur;
1053  } else {
1054  if (objcur->InheritsFrom(TCollection::Class()))
1055  objcur->Delete(); // delete also list elements
1056  delete objcur;
1057  objcur = nullptr;
1058  }
1059  }
1060  }
1061 
1062 //*-*---------------------Case of Key---------------------
1063 // ===========
1064  void *idcur = nullptr;
1065  TKey *key;
1066  TIter nextkey(GetListOfKeys());
1067  while ((key = (TKey *) nextkey())) {
1068  if (strcmp(namobj,key->GetName()) == 0) {
1069  if ((cycle == 9999) || (cycle == key->GetCycle())) {
1070  TDirectory::TContext ctxt(this);
1071  idcur = key->ReadObjectAny(expectedClass);
1072  break;
1073  }
1074  }
1075  }
1076 
1077  return idcur;
1078 }
1079 
1080 ////////////////////////////////////////////////////////////////////////////////
1081 /// Return the buffer size to create new TKeys.
1082 ///
1083 /// If the stored fBufferSize is null, the value returned is the average
1084 /// buffer size of objects in the file so far.
1085 
1086 Int_t TDirectoryFile::GetBufferSize() const
1087 {
1088  if (fBufferSize <= 0) return fFile->GetBestBuffer();
1089  else return fBufferSize;
1090 }
1091 
1092 
1093 ////////////////////////////////////////////////////////////////////////////////
1094 /// Return pointer to key with name,cycle
1095 ///
1096 /// if cycle = 9999 returns highest cycle
1097 
1098 TKey *TDirectoryFile::GetKey(const char *name, Short_t cycle) const
1099 {
1100  if (!fKeys) return nullptr;
1101 
1102  // TIter::TIter() already checks for null pointers
1103  TIter next( ((THashList *)(GetListOfKeys()))->GetListForObject(name) );
1104 
1105  TKey *key;
1106  while (( key = (TKey *)next() )) {
1107  if (!strcmp(name, key->GetName())) {
1108  if ((cycle == 9999) || (cycle >= key->GetCycle()))
1109  return key;
1110  }
1111  }
1112 
1113  return nullptr;
1114 }
1115 
1116 ////////////////////////////////////////////////////////////////////////////////
1117 /// List Directory contents
1118 ///
1119 /// Indentation is used to identify the directory tree
1120 /// Subdirectories are listed first, then objects in memory, then objects on the file
1121 ///
1122 /// The option can has the following format: <b>[-d |-m][<regexp>]</b>
1123 /// Options:
1124 /// - -d: only list objects in the file
1125 /// - -m: only list objects in memory
1126 /// The <regexp> will be used to match the name of the objects.
1127 /// By default memory and disk objects are listed.
1128 
1129 void TDirectoryFile::ls(Option_t *option) const
1130 {
1131  TROOT::IndentLevel();
1132  std::cout <<ClassName()<<"*\t\t"<<GetName()<<"\t"<<GetTitle()<<std::endl;
1133  TROOT::IncreaseDirLevel();
1134 
1135  TString opta = option;
1136  TString opt = opta.Strip(TString::kBoth);
1137  Bool_t memobj = kTRUE;
1138  Bool_t diskobj = kTRUE;
1139  TString reg = "*";
1140  if (opt.BeginsWith("-m")) {
1141  diskobj = kFALSE;
1142  if (opt.Length() > 2)
1143  reg = opt(2,opt.Length());
1144  } else if (opt.BeginsWith("-d")) {
1145  memobj = kFALSE;
1146  if (opt.Length() > 2)
1147  reg = opt(2,opt.Length());
1148  } else if (!opt.IsNull())
1149  reg = opt;
1150 
1151  TRegexp re(reg, kTRUE);
1152 
1153  if (memobj) {
1154  TObject *obj;
1155  TIter nextobj(fList);
1156  while ((obj = (TObject *) nextobj())) {
1157  TString s = obj->GetName();
1158  if (s.Index(re) == kNPOS) continue;
1159  obj->ls(option); //*-* Loop on all the objects in memory
1160  }
1161  }
1162 
1163  if (diskobj) {
1164  TKey *key;
1165  TIter next(GetListOfKeys());
1166  while ((key = (TKey *) next())) {
1167  TString s = key->GetName();
1168  if (s.Index(re) == kNPOS) continue;
1169  key->ls(); //*-* Loop on all the keys
1170  }
1171  }
1172  TROOT::DecreaseDirLevel();
1173 }
1174 
1175 ////////////////////////////////////////////////////////////////////////////////
1176 /// Interface to TFile::Open
1177 
1178 TFile *TDirectoryFile::OpenFile(const char *name, Option_t *option,const char *ftitle, Int_t compress, Int_t netopt)
1179 {
1180  return TFile::Open(name,option,ftitle,compress,netopt);
1181 
1182 }
1183 
1184 ////////////////////////////////////////////////////////////////////////////////
1185 /// Create a sub-directory "a" or a hierarchy of sub-directories "a/b/c/...".
1186 ///
1187 /// Returns 0 in case of error or if a sub-directory (hierarchy) with the requested
1188 /// name already exists.
1189 /// returnExistingDirectory returns a pointer to an already existing sub-directory instead of 0.
1190 /// Returns a pointer to the created sub-directory or to the top sub-directory of
1191 /// the hierarchy (in the above example, the returned TDirectory * always points
1192 /// to "a").
1193 
1194 TDirectory *TDirectoryFile::mkdir(const char *name, const char *title, Bool_t returnExistingDirectory)
1195 {
1196  if (!name || !title || !name[0]) return nullptr;
1197  if (!title[0]) title = name;
1198  if (GetKey(name)) {
1199  if (returnExistingDirectory)
1200  return (TDirectoryFile*) GetDirectory(name);
1201  else {
1202  Error("mkdir","An object with name %s exists already",name);
1203  return nullptr;
1204  }
1205  }
1206  TDirectoryFile *newdir = nullptr;
1207  if (const char *slash = strchr(name,'/')) {
1208  TString workname(name, Long_t(slash-name));
1209  TDirectoryFile *tmpdir = nullptr;
1210  GetObject(workname.Data(), tmpdir);
1211  if (!tmpdir) {
1212  tmpdir = (TDirectoryFile*)mkdir(workname.Data(),title);
1213  if (!tmpdir) return nullptr;
1214  }
1215  if (!newdir) newdir = tmpdir;
1216  tmpdir->mkdir(slash+1);
1217  return newdir;
1218  }
1219 
1220  TDirectory::TContext ctxt(this);
1221 
1222  newdir = new TDirectoryFile(name, title, "", this);
1223 
1224  return newdir;
1225 }
1226 
1227 ////////////////////////////////////////////////////////////////////////////////
1228 /// Purge lowest key cycles in a directory.
1229 ///
1230 /// By default, only the highest cycle of a key is kept. Keys for which
1231 /// the "KEEP" flag has been set are not removed. See TKey::Keep().
1232 
1233 void TDirectoryFile::Purge(Short_t)
1234 {
1235  if (!IsWritable()) return;
1236 
1237  TDirectory::TContext ctxt(this);
1238 
1239  TKey *key;
1240  TIter prev(GetListOfKeys(), kIterBackward);
1241 
1242  while ((key = (TKey*)prev())) { // reverse loop on keys
1243  TKey *keyprev = (TKey*)GetListOfKeys()->Before(key);
1244  if (!keyprev) break;
1245  if (key->GetKeep() == 0) {
1246  if (strcmp(key->GetName(), keyprev->GetName()) == 0) {
1247  key->Delete(); // Remove from the file.
1248  delete key; // Remove from memory.
1249  }
1250  }
1251  }
1252  TFile *f = GetFile();
1253  if (fModified && f) {
1254  WriteKeys(); // Write new keys structure
1255  WriteDirHeader(); // Write new directory header
1256  f->WriteFree(); // Write new free segments list
1257  f->WriteHeader(); // Write new file header
1258  }
1259 }
1260 
1261 ////////////////////////////////////////////////////////////////////////////////
1262 /// Read objects from a ROOT file directory into memory.
1263 ///
1264 /// If an object is already in memory, the memory copy is deleted
1265 /// and the object is again read from the file.
1266 /// If opt=="dirs", only subdirectories will be read
1267 /// If opt=="dirs*" complete directory tree will be read
1268 
1269 void TDirectoryFile::ReadAll(Option_t* opt)
1270 {
1271  TDirectory::TContext ctxt(this);
1272 
1273  TKey *key;
1274  TIter next(GetListOfKeys());
1275 
1276  Bool_t readdirs = ((opt!=0) && ((strcmp(opt,"dirs")==0) || (strcmp(opt,"dirs*")==0)));
1277 
1278  if (readdirs)
1279  while ((key = (TKey *) next())) {
1280 
1281  //if (strcmp(key->GetClassName(),"TDirectory")!=0) continue;
1282  if (strstr(key->GetClassName(),"TDirectory")==0) continue;
1283 
1284  TDirectory *dir = GetDirectory(key->GetName(), kTRUE, "ReadAll");
1285 
1286  if ((dir!=0) && (strcmp(opt,"dirs*")==0)) dir->ReadAll("dirs*");
1287  }
1288  else
1289  while ((key = (TKey *) next())) {
1290  TObject *thing = GetList()->FindObject(key->GetName());
1291  if (thing) { delete thing; }
1292  key->ReadObj();
1293  }
1294 }
1295 
1296 ////////////////////////////////////////////////////////////////////////////////
1297 /// Read the linked list of keys.
1298 ///
1299 /// Every directory has a linked list (fKeys). This linked list has been
1300 /// written on the file via WriteKeys as a single data record.
1301 ///
1302 /// It is interesting to call this function in the following situation.
1303 /// Assume another process1 is connecting this directory in Update mode
1304 /// - Process1 is adding/updating objects in this directory
1305 /// - You want to see the latest status from process1.
1306 /// Example Process1:
1307 /// ~~~{.cpp}
1308 /// obj1.Write();
1309 /// obj2.Write();
1310 /// gDirectory->SaveSelf();
1311 /// ~~~
1312 ///
1313 /// Example Process2:
1314 /// ~~~{.cpp}
1315 /// gDirectory->ReadKeys();
1316 /// obj1->Draw();
1317 /// ~~~
1318 /// This is an efficient way (without opening/closing files) to view
1319 /// the latest updates of a file being modified by another process
1320 /// as it is typically the case in a data acquisition system.
1321 
1322 Int_t TDirectoryFile::ReadKeys(Bool_t forceRead)
1323 {
1324  if (!fFile || !fKeys) return 0;
1325 
1326  if (!fFile->IsBinary())
1327  return fFile->DirReadKeys(this);
1328 
1329  TDirectory::TContext ctxt(this);
1330 
1331  char *buffer;
1332  if (forceRead) {
1333  fKeys->Delete();
1334  //In case directory was updated by another process, read new
1335  //position for the keys
1336  Int_t nbytes = fNbytesName + TDirectoryFile::Sizeof();
1337  char *header = new char[nbytes];
1338  buffer = header;
1339  fFile->Seek(fSeekDir);
1340  if ( fFile->ReadBuffer(buffer,nbytes) ) {
1341  // ReadBuffer return kTRUE in case of failure.
1342  delete [] header;
1343  return 0;
1344  }
1345  buffer += fNbytesName;
1346  Version_t versiondir;
1347  frombuf(buffer,&versiondir);
1348  fDatimeC.ReadBuffer(buffer);
1349  fDatimeM.ReadBuffer(buffer);
1350  frombuf(buffer, &fNbytesKeys);
1351  frombuf(buffer, &fNbytesName);
1352  if (versiondir > 1000) {
1353  frombuf(buffer, &fSeekDir);
1354  frombuf(buffer, &fSeekParent);
1355  frombuf(buffer, &fSeekKeys);
1356  } else {
1357  Int_t sdir,sparent,skeys;
1358  frombuf(buffer, &sdir); fSeekDir = (Long64_t)sdir;
1359  frombuf(buffer, &sparent); fSeekParent = (Long64_t)sparent;
1360  frombuf(buffer, &skeys); fSeekKeys = (Long64_t)skeys;
1361  }
1362  delete [] header;
1363  }
1364 
1365  Int_t nkeys = 0;
1366  Long64_t fsize = fFile->GetSize();
1367  if ( fSeekKeys > 0) {
1368  TKey *headerkey = new TKey(fSeekKeys, fNbytesKeys, this);
1369  headerkey->ReadFile();
1370  buffer = headerkey->GetBuffer();
1371  headerkey->ReadKeyBuffer(buffer);
1372 
1373  TKey *key;
1374  frombuf(buffer, &nkeys);
1375  for (Int_t i = 0; i < nkeys; i++) {
1376  key = new TKey(this);
1377  key->ReadKeyBuffer(buffer);
1378  if (key->GetSeekKey() < 64 || key->GetSeekKey() > fsize) {
1379  Error("ReadKeys","reading illegal key, exiting after %d keys",i);
1380  fKeys->Remove(key);
1381  nkeys = i;
1382  break;
1383  }
1384  if (key->GetSeekPdir() < 64 || key->GetSeekPdir() > fsize) {
1385  Error("ReadKeys","reading illegal key, exiting after %d keys",i);
1386  fKeys->Remove(key);
1387  nkeys = i;
1388  break;
1389  }
1390  fKeys->Add(key);
1391  }
1392  delete headerkey;
1393  }
1394 
1395  return nkeys;
1396 }
1397 
1398 
1399 ////////////////////////////////////////////////////////////////////////////////
1400 /// Read object with keyname from the current directory
1401 ///
1402 /// Read contents of object with specified name from the current directory.
1403 /// First the key with keyname is searched in the current directory,
1404 /// next the key buffer is deserialized into the object.
1405 /// The object must have been created before via the default constructor.
1406 /// See TObject::Write().
1407 
1408 Int_t TDirectoryFile::ReadTObject(TObject *obj, const char *keyname)
1409 {
1410  if (!fFile) { Error("Read","No file open"); return 0; }
1411  TKey *key = nullptr;
1412  TIter nextkey(GetListOfKeys());
1413  while ((key = (TKey *) nextkey())) {
1414  if (strcmp(keyname,key->GetName()) == 0) {
1415  return key->Read(obj);
1416  }
1417  }
1418  Error("Read","Key not found");
1419  return 0;
1420 }
1421 
1422 ////////////////////////////////////////////////////////////////////////////////
1423 /// Reset the TDirectory after its content has been merged into another
1424 /// Directory.
1425 ///
1426 /// This returns the TDirectoryFile object back to its state
1427 /// before any data has been written to the file.
1428 /// The object in the in-memory list are assumed to also have been reset.
1429 
1430 void TDirectoryFile::ResetAfterMerge(TFileMergeInfo *info)
1431 {
1432  // There is nothing to reset in the base class (TDirectory) since
1433  // we do want to key the list of in-memory object as is.
1434  fModified = kFALSE;
1435  // Does not change: fWritable
1436  fDatimeC.Set();
1437  fDatimeM.Set();
1438  fNbytesKeys = 0; // updated when the keys are written
1439  fNbytesName = 0; // updated by Init
1440  // Does not change (user customization): fBufferSize;
1441  fSeekDir = 0; // updated by Init
1442  fSeekParent = 0; // updated by Init
1443  fSeekKeys = 0; // updated by Init
1444  // Does not change: fFile
1445  TKey *key = fKeys ? (TKey*)fKeys->FindObject(fName) : nullptr;
1446  TClass *cl = IsA();
1447  if (key) {
1448  cl = TClass::GetClass(key->GetClassName());
1449  }
1450  // NOTE: We should check that the content is really mergeable and in
1451  // the in-mmeory list, before deleting the keys.
1452  if (fKeys) {
1453  fKeys->Delete("slow");
1454  }
1455 
1456  InitDirectoryFile(cl);
1457 
1458  // Do the same with the sub-directories.
1459  TIter next(GetList());
1460  TObject *idcur;
1461  while ((idcur = next())) {
1462  if (idcur->IsA() == TDirectoryFile::Class()) {
1463  ((TDirectoryFile*)idcur)->ResetAfterMerge(info);
1464  }
1465  }
1466 
1467 }
1468 
1469 ////////////////////////////////////////////////////////////////////////////////
1470 /// Removes subdirectory from the directory
1471 ///
1472 /// When directory is deleted, all keys in all subdirectories will be
1473 /// read first and deleted from file (if exists)
1474 /// Equivalent call is Delete("name;*");
1475 
1476 void TDirectoryFile::rmdir(const char *name)
1477 {
1478  if (!name || (*name==0)) return;
1479 
1480  TString mask(name);
1481  mask += ";*";
1482  Delete(mask);
1483 }
1484 
1485 ////////////////////////////////////////////////////////////////////////////////
1486 /// Save recursively all directory keys and headers
1487 
1488 void TDirectoryFile::Save()
1489 {
1490  TDirectory::TContext ctxt(this);
1491 
1492  SaveSelf();
1493 
1494  // recursively save all sub-directories
1495  if (fList && fList->FirstLink()) {
1496  auto lnk = fList->FirstLink()->shared_from_this();
1497  while (lnk) {
1498  TObject *idcur = lnk->GetObject();
1499  if (idcur && idcur->InheritsFrom(TDirectoryFile::Class())) {
1500  TDirectoryFile *dir = (TDirectoryFile *)idcur;
1501  dir->Save();
1502  }
1503  lnk = lnk->NextSP();
1504  }
1505  }
1506 }
1507 
1508 ////////////////////////////////////////////////////////////////////////////////
1509 /// Save object in filename.
1510 ///
1511 /// If filename is 0 or "", a file with "objectname.root" is created.
1512 /// The name of the key is the object name.
1513 /// If the operation is successful, it returns the number of bytes written to the file
1514 /// otherwise it returns 0.
1515 /// By default a message is printed. Use option "q" to not print the message.
1516 /// If filename contains ".json" extension, JSON representation of the object
1517 /// will be created and saved in the text file. Such file can be used in
1518 /// JavaScript ROOT (https://root.cern.ch/js/) to display object in web browser
1519 /// When creating JSON file, option string may contain compression level from 0 to 3 (default 0)
1520 
1521 Int_t TDirectoryFile::SaveObjectAs(const TObject *obj, const char *filename, Option_t *option) const
1522 {
1523  if (!obj) return 0;
1524  TDirectory *dirsav = gDirectory;
1525  TString fname = filename;
1526  if (!filename || !filename[0]) {
1527  fname.Form("%s.root",obj->GetName());
1528  }
1529  Int_t nbytes = 0;
1530  if (fname.Index(".json") > 0) {
1531  nbytes = TBufferJSON::ExportToFile(fname, obj, option);
1532  } else {
1533  TFile *local = TFile::Open(fname.Data(),"recreate");
1534  if (!local) return 0;
1535  nbytes = obj->Write();
1536  delete local;
1537  if (dirsav) dirsav->cd();
1538  }
1539  TString opt = option;
1540  opt.ToLower();
1541  if (!opt.Contains("q")) {
1542  if (!gSystem->AccessPathName(fname.Data())) obj->Info("SaveAs", "ROOT file %s has been created", fname.Data());
1543  }
1544  return nbytes;
1545 }
1546 
1547 ////////////////////////////////////////////////////////////////////////////////
1548 /// Save Directory keys and header
1549 ///
1550 /// If the directory has been modified (fModified set), write the keys
1551 /// and the directory header. This function assumes the cd is correctly set.
1552 ///
1553 /// It is recommended to use this function in the following situation:
1554 /// Assume a process1 using a directory in Update mode
1555 /// - New objects or modified objects have been written to the directory.
1556 /// - You do not want to close the file.
1557 /// - You want your changes be visible from another process2 already connected
1558 /// to this directory in read mode.
1559 /// - Call this function.
1560 /// - In process2, use TDirectoryFile::ReadKeys to refresh the directory.
1561 
1562 void TDirectoryFile::SaveSelf(Bool_t force)
1563 {
1564  if (IsWritable() && (fModified || force) && fFile) {
1565  Bool_t dowrite = kTRUE;
1566  if (fFile->GetListOfFree())
1567  dowrite = fFile->GetListOfFree()->First() != nullptr;
1568  if (dowrite) {
1569  TDirectory *dirsav = gDirectory;
1570  if (dirsav != this) cd();
1571  WriteKeys(); //*-*- Write keys record
1572  WriteDirHeader(); //*-*- Update directory record
1573  if (dirsav && dirsav != this) dirsav->cd();
1574  }
1575  }
1576 }
1577 
1578 ////////////////////////////////////////////////////////////////////////////////
1579 /// Set the default buffer size when creating new TKeys.
1580 ///
1581 /// See also TDirectoryFile::GetBufferSize
1582 
1583 void TDirectoryFile::SetBufferSize(Int_t bufsize)
1584 {
1585  fBufferSize = bufsize;
1586 }
1587 
1588 ////////////////////////////////////////////////////////////////////////////////
1589 /// Find the action to be executed in the dictionary of the parent class
1590 /// and store the corresponding exec number into fBits.
1591 ///
1592 /// This function searches a data member in the class of parent with an
1593 /// offset corresponding to this.
1594 /// If a comment "TEXEC:" is found in the comment field of the data member,
1595 /// the function stores the exec identifier of the exec statement
1596 /// following this keyword.
1597 
1598 void TDirectoryFile::SetTRefAction(TObject *ref, TObject *parent)
1599 {
1600  Int_t offset = (char*)ref - (char*)parent;
1601  TClass *cl = parent->IsA();
1602  cl->BuildRealData(parent);
1603  TStreamerInfo *info = (TStreamerInfo*)cl->GetStreamerInfo();
1604  TIter next(info->GetElements());
1605  TStreamerElement *element;
1606  while((element = (TStreamerElement*)next())) {
1607  if (element->GetOffset() != offset) continue;
1608  Int_t execid = element->GetExecID();
1609  if (execid > 0) ref->SetBit(execid << 8);
1610  return;
1611  }
1612 }
1613 
1614 ////////////////////////////////////////////////////////////////////////////////
1615 /// Set the new value of fWritable recursively
1616 
1617 void TDirectoryFile::SetWritable(Bool_t writable)
1618 {
1619  TDirectory::TContext ctxt(this);
1620 
1621  fWritable = writable;
1622 
1623  // recursively set all sub-directories
1624  if (fList) {
1625  TObject *idcur;
1626  TIter next(fList);
1627  while ((idcur = next())) {
1628  if (idcur->InheritsFrom(TDirectoryFile::Class())) {
1629  TDirectoryFile *dir = (TDirectoryFile*)idcur;
1630  dir->SetWritable(writable);
1631  }
1632  }
1633  }
1634 }
1635 
1636 
1637 ////////////////////////////////////////////////////////////////////////////////
1638 /// Return the size in bytes of the directory header
1639 
1640 Int_t TDirectoryFile::Sizeof() const
1641 {
1642  Int_t nbytes = 22;
1643 
1644  nbytes += fDatimeC.Sizeof();
1645  nbytes += fDatimeM.Sizeof();
1646  nbytes += fUUID.Sizeof();
1647  //assume that the file may be above 2 Gbytes if file version is > 4
1648  if (fFile && fFile->GetVersion() >= 40000) nbytes += 12;
1649  return nbytes;
1650 }
1651 
1652 
1653 ////////////////////////////////////////////////////////////////////////////////
1654 /// Stream a class object
1655 
1656 void TDirectoryFile::Streamer(TBuffer &b)
1657 {
1658  Version_t v,version;
1659  if (b.IsReading()) {
1660  BuildDirectoryFile((TFile*)b.GetParent(), nullptr);
1661  if (fFile && fFile->IsWritable()) fWritable = kTRUE;
1662 
1663  if (fFile && !fFile->IsBinary()) {
1664  Version_t R__v = b.ReadVersion(0, 0);
1665 
1666  TClass* dirclass = (R__v < 5) ? TDirectory::Class() : TDirectoryFile::Class();
1667 
1668  b.ClassBegin(dirclass, R__v);
1669 
1670  TString sbuf;
1671 
1672  b.ClassMember("CreateTime","TString");
1673  sbuf.Streamer(b);
1674  TDatime timeC(sbuf.Data());
1675  fDatimeC = timeC;
1676 
1677  b.ClassMember("ModifyTime","TString");
1678  sbuf.Streamer(b);
1679  TDatime timeM(sbuf.Data());
1680  fDatimeM = timeM;
1681 
1682  b.ClassMember("UUID","TString");
1683  sbuf.Streamer(b);
1684  TUUID id(sbuf.Data());
1685  fUUID = id;
1686 
1687  b.ClassEnd(dirclass);
1688 
1689  fSeekKeys = 0; // read keys later in the TKeySQL class
1690  } else {
1691  b >> version;
1692  fDatimeC.Streamer(b);
1693  fDatimeM.Streamer(b);
1694  b >> fNbytesKeys;
1695  b >> fNbytesName;
1696  if (version > 1000) {
1697  SetBit(kIsBigFile);
1698  b >> fSeekDir;
1699  b >> fSeekParent;
1700  b >> fSeekKeys;
1701  } else {
1702  Int_t sdir,sparent,skeys;
1703  b >> sdir; fSeekDir = (Long64_t)sdir;
1704  b >> sparent; fSeekParent = (Long64_t)sparent;
1705  b >> skeys; fSeekKeys = (Long64_t)skeys;
1706  }
1707  v = version%1000;
1708  if (v == 2) {
1709  fUUID.StreamerV1(b);
1710  } else if (v > 2) {
1711  fUUID.Streamer(b);
1712  }
1713  }
1714  fList->UseRWLock();
1715  R__LOCKGUARD(gROOTMutex);
1716  gROOT->GetUUIDs()->AddUUID(fUUID,this);
1717  if (fSeekKeys) ReadKeys();
1718  } else {
1719  if (fFile && !fFile->IsBinary()) {
1720  b.WriteVersion(TDirectoryFile::Class());
1721 
1722  TString sbuf;
1723 
1724  b.ClassBegin(TDirectoryFile::Class());
1725 
1726  b.ClassMember("CreateTime","TString");
1727  sbuf = fDatimeC.AsSQLString();
1728  sbuf.Streamer(b);
1729 
1730  b.ClassMember("ModifyTime","TString");
1731  fDatimeM.Set();
1732  sbuf = fDatimeM.AsSQLString();
1733  sbuf.Streamer(b);
1734 
1735  b.ClassMember("UUID","TString");
1736  sbuf = fUUID.AsString();
1737  sbuf.Streamer(b);
1738 
1739  b.ClassEnd(TDirectoryFile::Class());
1740  } else {
1741  version = TDirectoryFile::Class_Version();
1742  if (fFile && fFile->GetEND() > TFile::kStartBigFile) version += 1000;
1743  b << version;
1744  fDatimeC.Streamer(b);
1745  fDatimeM.Streamer(b);
1746  b << fNbytesKeys;
1747  b << fNbytesName;
1748  if (version > 1000) {
1749  b << fSeekDir;
1750  b << fSeekParent;
1751  b << fSeekKeys;
1752  } else {
1753  b << (Int_t)fSeekDir;
1754  b << (Int_t)fSeekParent;
1755  b << (Int_t)fSeekKeys;
1756  }
1757  fUUID.Streamer(b);
1758  if (version <=1000) for (Int_t i=0;i<3;i++) b << Int_t(0);
1759  }
1760  }
1761 }
1762 
1763 ////////////////////////////////////////////////////////////////////////////////
1764 /// Write all objects in memory to disk.
1765 ///
1766 /// Loop on all objects in memory (including subdirectories).
1767 /// A new key is created in the keys linked list for each object.
1768 /// For allowed options see TObject::Write().
1769 /// The directory header info is rewritten on the directory header record.
1770 
1771 Int_t TDirectoryFile::Write(const char *, Int_t opt, Int_t bufsize)
1772 {
1773  if (!IsWritable()) return 0;
1774  TDirectory::TContext ctxt(this);
1775 
1776  // Loop on all objects (including subdirs)
1777  TIter next(fList);
1778  TObject *obj;
1779  Int_t nbytes = 0;
1780  while ((obj=next())) {
1781  nbytes += obj->Write(0,opt,bufsize);
1782  }
1783  SaveSelf(kTRUE); // force save itself
1784 
1785  return nbytes;
1786 }
1787 
1788 ////////////////////////////////////////////////////////////////////////////////
1789 /// One can not save a const TDirectory object.
1790 
1791 Int_t TDirectoryFile::Write(const char *n, Int_t opt, Int_t bufsize) const
1792 {
1793  Error("Write const","A const TDirectory object should not be saved. We try to proceed anyway.");
1794  return const_cast<TDirectoryFile*>(this)->Write(n, opt, bufsize);
1795 }
1796 
1797 ////////////////////////////////////////////////////////////////////////////////
1798 /// Write object obj to this directory.
1799 ///
1800 /// The data structure corresponding to this object is serialized.
1801 /// The corresponding buffer is written to this directory
1802 /// with an associated key with name "name".
1803 ///
1804 /// Writing an object to a file involves the following steps:
1805 /// - Creation of a support TKey object in the directory. The TKey object
1806 /// creates a TBuffer object.
1807 /// - The TBuffer object is filled via the class::Streamer function.
1808 /// - If the file is compressed (default) a second buffer is created to hold
1809 /// the compressed buffer.
1810 /// - Reservation of the corresponding space in the file by looking in the
1811 /// TFree list of free blocks of the file.
1812 /// - The buffer is written to the file.
1813 ///
1814 /// By default, the buffersize will be taken from the average buffer size
1815 /// of all objects written to the current file so far.
1816 /// Use TDirectoryFile::SetBufferSize to force a given buffer size.
1817 ///
1818 /// If a name is specified, it will be the name of the key.
1819 /// If name is not given, the name of the key will be the name as returned
1820 /// by obj->GetName().
1821 ///
1822 /// The option can be a combination of:
1823 /// - "SingleKey"
1824 /// - "Overwrite"
1825 /// - "WriteDelete"
1826 /// Using the "Overwrite" option a previous key with the same name is
1827 /// overwritten. The previous key is deleted before writing the new object.
1828 /// Using the "WriteDelete" option a previous key with the same name is
1829 /// deleted only after the new object has been written. This option
1830 /// is safer than kOverwrite but it is slower.
1831 /// The "SingleKey" option is only used by TCollection::Write() to write
1832 /// a container with a single key instead of each object in the container
1833 /// with its own key.
1834 /// An object is read from this directory via TDirectoryFile::Get.
1835 /// The function returns the total number of bytes written to the directory.
1836 /// It returns 0 if the object cannot be written.
1837 ///
1838 /// WARNING: avoid special characters like '^','$','.' in the name as they
1839 /// are used by the regular expression parser (see TRegexp).
1840 
1841 Int_t TDirectoryFile::WriteTObject(const TObject *obj, const char *name, Option_t *option, Int_t bufsize)
1842 {
1843  TDirectory::TContext ctxt(this);
1844 
1845  if (fFile==0) {
1846  const char *objname = "no name specified";
1847  if (name) objname = name;
1848  else if (obj) objname = obj->GetName();
1849  Error("WriteTObject","The current directory (%s) is not associated with a file. The object (%s) has not been written.",GetName(),objname);
1850  return 0;
1851  }
1852 
1853  if (!fFile->IsWritable()) {
1854  if (!fFile->TestBit(TFile::kWriteError)) {
1855  // Do not print the error if the file already had a SysError.
1856  Error("WriteTObject","Directory %s is not writable", fFile->GetName());
1857  }
1858  return 0;
1859  }
1860 
1861  if (!obj) return 0;
1862 
1863  TString opt = option;
1864  opt.ToLower();
1865 
1866  TKey *key=0, *oldkey=0;
1867  Int_t bsize = GetBufferSize();
1868  if (bufsize > 0) bsize = bufsize;
1869 
1870  const char *oname;
1871  if (name && *name)
1872  oname = name;
1873  else
1874  oname = obj->GetName();
1875 
1876  // Remove trailing blanks in object name
1877  Int_t nch = strlen(oname);
1878  char *newName = nullptr;
1879  if (nch && oname[nch-1] == ' ') {
1880  newName = new char[nch+1];
1881  strlcpy(newName,oname,nch+1);
1882  for (Int_t i=0;i<nch;i++) {
1883  if (newName[nch-i-1] != ' ') break;
1884  newName[nch-i-1] = 0;
1885  }
1886  oname = newName;
1887  }
1888 
1889  if (opt.Contains("overwrite")) {
1890  //One must use GetKey. FindObject would return the lowest cycle of the key!
1891  //key = (TKey*)gDirectory->GetListOfKeys()->FindObject(oname);
1892  key = GetKey(oname);
1893  if (key) {
1894  key->Delete();
1895  delete key;
1896  }
1897  }
1898  if (opt.Contains("writedelete")) {
1899  oldkey = GetKey(oname);
1900  }
1901  key = fFile->CreateKey(this, obj, oname, bsize);
1902  if (newName) delete [] newName;
1903 
1904  if (!key->GetSeekKey()) {
1905  fKeys->Remove(key);
1906  delete key;
1907  if (bufsize) fFile->SetBufferSize(bufsize);
1908  return 0;
1909  }
1910  fFile->SumBuffer(key->GetObjlen());
1911  Int_t nbytes = key->WriteFile(0);
1912  if (fFile->TestBit(TFile::kWriteError)) {
1913  if (bufsize) fFile->SetBufferSize(bufsize);
1914  return 0;
1915  }
1916  if (oldkey) {
1917  oldkey->Delete();
1918  delete oldkey;
1919  }
1920  if (bufsize) fFile->SetBufferSize(bufsize);
1921 
1922  return nbytes;
1923 }
1924 
1925 ////////////////////////////////////////////////////////////////////////////////
1926 /// Write object from pointer of class classname in this directory.
1927 ///
1928 /// obj may not derive from TObject. See TDirectoryFile::WriteTObject for comments
1929 ///
1930 /// ## Very important note
1931 /// The value passed as 'obj' needs to be from a pointer to the type described by classname.
1932 /// For example:
1933 /// ~~~{.cpp}
1934 /// TopClass *top;
1935 /// BottomClass *bottom;
1936 /// top = bottom;
1937 /// ~~~
1938 /// you can do:
1939 /// ~~~{.cpp}
1940 /// directory->WriteObjectAny(top,"top","name of object");
1941 /// directory->WriteObjectAny(bottom,"bottom","name of object");
1942 /// ~~~
1943 /// <b>BUT YOU CAN NOT DO</b> the following since it will fail with multiple inheritance:
1944 /// ~~~{.cpp}
1945 /// directory->WriteObjectAny(top,"bottom","name of object");
1946 /// ~~~
1947 /// We <b>STRONGLY</b> recommend to use
1948 /// ~~~{.cpp}
1949 /// TopClass *top = ....;
1950 /// directory->WriteObject(top,"name of object")
1951 /// ~~~
1952 /// See also remarks in TDirectoryFile::WriteTObject
1953 
1954 Int_t TDirectoryFile::WriteObjectAny(const void *obj, const char *classname, const char *name, Option_t *option, Int_t bufsize)
1955 {
1956  TClass *cl = TClass::GetClass(classname);
1957  if (!cl) {
1958  TObject *info_obj = *(TObject**)obj;
1959  TVirtualStreamerInfo *info = dynamic_cast<TVirtualStreamerInfo*>(info_obj);
1960  if (!info) {
1961  Error("WriteObjectAny","Unknown class: %s",classname);
1962  return 0;
1963  } else {
1964  cl = info->GetClass();
1965  }
1966  }
1967  return WriteObjectAny(obj,cl,name,option,bufsize);
1968 }
1969 
1970 ////////////////////////////////////////////////////////////////////////////////
1971 /// Write object of class with dictionary cl in this directory.
1972 ///
1973 /// obj may not derive from TObject
1974 /// To get the TClass* cl pointer, one can use
1975 ///
1976 /// TClass *cl = TClass::GetClass("classname");
1977 ///
1978 /// An alternative is to call the function WriteObjectAny above.
1979 /// see TDirectoryFile::WriteTObject for comments
1980 
1981 Int_t TDirectoryFile::WriteObjectAny(const void *obj, const TClass *cl, const char *name, Option_t *option, Int_t bufsize)
1982 {
1983  TDirectory::TContext ctxt(this);
1984 
1985  if (!fFile) return 0;
1986 
1987  if (!cl) {
1988  Error("WriteObject","Unknown type for %s, it can not be written.",name);
1989  return 0;
1990  }
1991 
1992  if (!fFile->IsWritable()) {
1993  if (!fFile->TestBit(TFile::kWriteError)) {
1994  // Do not print the error if the file already had a SysError.
1995  Error("WriteObject","File %s is not writable", fFile->GetName());
1996  }
1997  return 0;
1998  }
1999 
2000  if (!obj) return 0;
2001 
2002  const char *className = cl->GetName();
2003  const char *oname;
2004  if (name && *name)
2005  oname = name;
2006  else
2007  oname = className;
2008 
2009  if (cl && cl->GetCollectionProxy() && dynamic_cast<TEmulatedCollectionProxy*>(cl->GetCollectionProxy())) {
2010  Error("WriteObjectAny",
2011  "The class requested (%s) for the key name \"%s\""
2012  " is an instance of an stl collection and does not have a compiled CollectionProxy."
2013  " Please generate the dictionary for this collection (%s). No data will be written.",
2014  className, oname, className);
2015  return 0;
2016  }
2017 
2018  TKey *key, *oldkey = nullptr;
2019  Int_t bsize = GetBufferSize();
2020  if (bufsize > 0) bsize = bufsize;
2021 
2022  TString opt = option;
2023  opt.ToLower();
2024 
2025  // Remove trailing blanks in object name
2026  Int_t nch = strlen(oname);
2027  char *newName = nullptr;
2028  if (nch && oname[nch-1] == ' ') {
2029  newName = new char[nch+1];
2030  strlcpy(newName,oname,nch+1);
2031  for (Int_t i=0;i<nch;i++) {
2032  if (newName[nch-i-1] != ' ') break;
2033  newName[nch-i-1] = 0;
2034  }
2035  oname = newName;
2036  }
2037 
2038  if (opt.Contains("overwrite")) {
2039  //One must use GetKey. FindObject would return the lowest cycle of the key!
2040  //key = (TKey*)gDirectory->GetListOfKeys()->FindObject(oname);
2041  key = GetKey(oname);
2042  if (key) {
2043  key->Delete();
2044  delete key;
2045  }
2046  }
2047  if (opt.Contains("writedelete")) {
2048  oldkey = GetKey(oname);
2049  }
2050  key = fFile->CreateKey(this, obj, cl, oname, bsize);
2051  if (newName) delete [] newName;
2052 
2053  if (!key->GetSeekKey()) {
2054  fKeys->Remove(key);
2055  delete key;
2056  return 0;
2057  }
2058  fFile->SumBuffer(key->GetObjlen());
2059  Int_t nbytes = key->WriteFile(0);
2060  if (fFile->TestBit(TFile::kWriteError)) return 0;
2061 
2062  if (oldkey) {
2063  oldkey->Delete();
2064  delete oldkey;
2065  }
2066 
2067  return nbytes;
2068 }
2069 
2070 ////////////////////////////////////////////////////////////////////////////////
2071 /// Overwrite the Directory header record.
2072 
2073 void TDirectoryFile::WriteDirHeader()
2074 {
2075  TFile* f = GetFile();
2076  if (!f) return;
2077 
2078  if (!f->IsBinary()) {
2079  fDatimeM.Set();
2080  f->DirWriteHeader(this);
2081  return;
2082  }
2083 
2084  Int_t nbytes = TDirectoryFile::Sizeof(); //Warning ! TFile has a Sizeof()
2085  char *header = new char[nbytes];
2086  char *buffer = header;
2087  fDatimeM.Set();
2088  TDirectoryFile::FillBuffer(buffer);
2089  Long64_t pointer = fSeekDir + fNbytesName; // do not overwrite the name/title part
2090  fModified = kFALSE;
2091  f->Seek(pointer);
2092  f->WriteBuffer(header, nbytes);
2093  if (f->MustFlush()) f->Flush();
2094  delete [] header;
2095 }
2096 
2097 ////////////////////////////////////////////////////////////////////////////////
2098 /// Write Keys linked list on the file.
2099 ///
2100 /// The linked list of keys (fKeys) is written as a single data record
2101 
2102 void TDirectoryFile::WriteKeys()
2103 {
2104  TFile* f = GetFile();
2105  if (!f) return;
2106 
2107  if (!f->IsBinary()) {
2108  f->DirWriteKeys(this);
2109  return;
2110  }
2111 
2112 //*-* Delete the old keys structure if it exists
2113  if (fSeekKeys != 0) {
2114  f->MakeFree(fSeekKeys, fSeekKeys + fNbytesKeys -1);
2115  }
2116 //*-* Write new keys record
2117  TIter next(fKeys);
2118  TKey *key;
2119  Int_t nkeys = fKeys->GetSize();
2120  Int_t nbytes = sizeof nkeys; //*-* Compute size of all keys
2121  if (f->GetEND() > TFile::kStartBigFile) nbytes += 8;
2122  while ((key = (TKey*)next())) {
2123  nbytes += key->Sizeof();
2124  }
2125  TKey *headerkey = new TKey(fName,fTitle,IsA(),nbytes,this);
2126  if (headerkey->GetSeekKey() == 0) {
2127  delete headerkey;
2128  return;
2129  }
2130  char *buffer = headerkey->GetBuffer();
2131  next.Reset();
2132  tobuf(buffer, nkeys);
2133  while ((key = (TKey*)next())) {
2134  key->FillBuffer(buffer);
2135  }
2136 
2137  fSeekKeys = headerkey->GetSeekKey();
2138  fNbytesKeys = headerkey->GetNbytes();
2139  headerkey->WriteFile();
2140  delete headerkey;
2141 }