Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TKey.cxx
Go to the documentation of this file.
1 // @(#)root/io:$Id$
2 // Author: Rene Brun 28/12/94
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 /**
13 \class TKey
14 \ingroup IO
15 
16  Book space in a file, create I/O buffers, to fill them, (un)compress them.
17 
18  The TKey class includes functions to book space in a file, to create I/O
19  buffers, to fill these buffers, to compress/uncompress data buffers.
20  Before saving (making persistent) an object in a file, a key must
21  be created. The key structure contains all the information to
22  uniquely identify a persistent object in a file.
23  | Data Member | Explanation |
24  |-------------|-------------|
25  | fNbytes | Number of bytes for the compressed object and key. |
26  | fObjlen | Length of uncompressed object. |
27  | fDatime | Date/Time when the object was written. |
28  | fKeylen | Number of bytes for the key structure. |
29  | fCycle | Cycle number of the object. |
30  | fSeekKey | Address of the object on file (points to fNbytes). This is a redundant information used to cross-check the data base integrity. |
31  | fSeekPdir | Pointer to the directory supporting this object.|
32  | fClassName | Object class name. |
33  | fName | Name of the object. |
34  | fTitle | Title of the object. |
35 
36  In the 16 highest bits of fSeekPdir is encoded a pid offset. This
37  offset is to be added to the pid index stored in the TRef object
38  and the referenced TObject.
39 
40  The TKey class is used by ROOT to:
41  - Write an object in the current directory
42  - Write a new ntuple buffer
43 
44  The structure of a file is shown in TFile::TFile.
45  The structure of a directory is shown in TDirectoryFile::TDirectoryFile.
46  The TKey class is used by the TBasket class.
47  See also TTree.
48 */
49 
50 #include <atomic>
51 
52 #include "Riostream.h"
53 #include "TROOT.h"
54 #include "TClass.h"
55 #include "TDirectoryFile.h"
56 #include "TFile.h"
57 #include "TKey.h"
58 #include "TBufferFile.h"
59 #include "TFree.h"
60 #include "TBrowser.h"
61 #include "Bytes.h"
62 #include "TInterpreter.h"
63 #include "TError.h"
64 #include "TVirtualStreamerInfo.h"
65 #include "TSchemaRuleSet.h"
66 
67 #include "RZip.h"
68 
69 const Int_t kTitleMax = 32000;
70 #if 0
71 const Int_t kMAXFILEBUFFER = 262144;
72 #endif
73 
74 #if !defined(_MSC_VER) || (_MSC_VER>1300)
75 const ULong64_t kPidOffsetMask = 0xffffffffffffULL;
76 #else
77 const ULong64_t kPidOffsetMask = 0xffffffffffffUL;
78 #endif
79 const UChar_t kPidOffsetShift = 48;
80 
81 TString &gTDirectoryString() {
82  TTHREAD_TLS_DECL_ARG(TString,gTDirectoryString,"TDirectory");
83  return gTDirectoryString;
84 }
85 std::atomic<UInt_t> keyAbsNumber{0};
86 
87 ClassImp(TKey);
88 
89 ////////////////////////////////////////////////////////////////////////////////
90 /// TKey default constructor.
91 
92 TKey::TKey() : TNamed(), fDatime((UInt_t)0)
93 {
94  Build(0, "", 0);
95 
96  fKeylen = Sizeof();
97 
98  keyAbsNumber++; SetUniqueID(keyAbsNumber);
99 }
100 
101 ////////////////////////////////////////////////////////////////////////////////
102 /// TKey default constructor.
103 
104 TKey::TKey(TDirectory* motherDir) : TNamed(), fDatime((UInt_t)0)
105 {
106  Build(motherDir, "", 0);
107 
108  fKeylen = Sizeof();
109 
110  keyAbsNumber++; SetUniqueID(keyAbsNumber);
111 }
112 
113 ////////////////////////////////////////////////////////////////////////////////
114 /// Copy a TKey from its original directory to the new 'motherDir'
115 
116 TKey::TKey(TDirectory* motherDir, const TKey &orig, UShort_t pidOffset) : TNamed(), fDatime((UInt_t)0)
117 {
118  fMotherDir = motherDir;
119 
120  fPidOffset = orig.fPidOffset + pidOffset;
121  fNbytes = orig.fNbytes;
122  fObjlen = orig.fObjlen;
123  fClassName = orig.fClassName;
124  fName = orig.fName;
125  fTitle = orig.fTitle;
126 
127  fCycle = fMotherDir->AppendKey(this);
128  fSeekPdir = 0;
129  fSeekKey = 0;
130  fLeft = 0;
131 
132  fVersion = TKey::Class_Version();
133  Long64_t filepos = GetFile()->GetEND();
134  if (filepos > TFile::kStartBigFile || fPidOffset) fVersion += 1000;
135 
136  fKeylen = Sizeof(); // fVersion must be set.
137 
138  UInt_t bufferDecOffset = 0;
139  UInt_t bufferIncOffset = 0;
140  UInt_t alloc = fNbytes + sizeof(Int_t); // The extra Int_t is for any free space information.
141  if (fKeylen < orig.fKeylen) {
142  bufferDecOffset = orig.fKeylen - fKeylen;
143  fNbytes -= bufferDecOffset;
144  } else if (fKeylen > orig.fKeylen) {
145  bufferIncOffset = fKeylen - orig.fKeylen;
146  alloc += bufferIncOffset;
147  fNbytes += bufferIncOffset;
148  }
149 
150  fBufferRef = new TBufferFile(TBuffer::kWrite, alloc);
151  fBuffer = fBufferRef->Buffer();
152 
153  // Steal the data from the old key.
154 
155  TFile* f = orig.GetFile();
156  if (f) {
157  Int_t nsize = orig.fNbytes;
158  f->Seek(orig.fSeekKey);
159  if( f->ReadBuffer(fBuffer+bufferIncOffset,nsize) )
160  {
161  Error("ReadFile", "Failed to read data.");
162  return;
163  }
164  if (gDebug) {
165  std::cout << "TKey Reading "<<nsize<< " bytes at address "<<fSeekKey<<std::endl;
166  }
167  }
168  fBuffer += bufferDecOffset; // Reset the buffer to be appropriate for this key.
169  Int_t nout = fNbytes - fKeylen;
170  Create(nout);
171  fBufferRef->SetBufferOffset(bufferDecOffset);
172  Streamer(*fBufferRef); //write key itself again
173 }
174 
175 ////////////////////////////////////////////////////////////////////////////////
176 /// Create a TKey object to read keys.
177 /// Constructor called by TDirectoryFile::ReadKeys and by TFile::TFile.
178 /// A TKey object is created to read the keys structure itself.
179 
180 TKey::TKey(Long64_t pointer, Int_t nbytes, TDirectory* motherDir) : TNamed()
181 {
182  Build(motherDir, "", pointer);
183 
184  fSeekKey = pointer;
185  fNbytes = nbytes;
186  fBuffer = new char[nbytes];
187  keyAbsNumber++; SetUniqueID(keyAbsNumber);
188 }
189 
190 ////////////////////////////////////////////////////////////////////////////////
191 /// Create a TKey object with the specified name, title for the given class.
192 ///
193 /// WARNING: in name avoid special characters like '^','$','.' that are used
194 /// by the regular expression parser (see TRegexp).
195 
196 TKey::TKey(const char *name, const char *title, const TClass *cl, Int_t nbytes, TDirectory* motherDir)
197  : TNamed(name,title)
198 {
199  Build(motherDir, cl->GetName(), -1);
200 
201  fKeylen = Sizeof();
202  fObjlen = nbytes;
203  Create(nbytes);
204 }
205 
206 ////////////////////////////////////////////////////////////////////////////////
207 /// Create a TKey object with the specified name, title for the given class.
208 ///
209 /// WARNING: in name avoid special characters like '^','$','.' that are used
210 /// by the regular expression parser (see TRegexp).
211 
212 TKey::TKey(const TString &name, const TString &title, const TClass *cl, Int_t nbytes, TDirectory* motherDir)
213  : TNamed(name,title)
214 {
215  Build(motherDir, cl->GetName(), -1);
216 
217  fKeylen = Sizeof();
218  fObjlen = nbytes;
219  Create(nbytes);
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 /// Create a TKey object for a TObject* and fill output buffer
224 ///
225 /// WARNING: in name avoid special characters like '^','$','.' that are used
226 /// by the regular expression parser (see TRegexp).
227 
228 TKey::TKey(const TObject *obj, const char *name, Int_t bufsize, TDirectory* motherDir)
229  : TNamed(name, obj->GetTitle())
230 {
231  R__ASSERT(obj);
232 
233  if (!obj->IsA()->HasDefaultConstructor()) {
234  Warning("TKey", "since %s has no public constructor\n"
235  "\twhich can be called without argument, objects of this class\n"
236  "\tcan not be read with the current library. You will need to\n"
237  "\tadd a default constructor before attempting to read it.",
238  obj->ClassName());
239  }
240 
241  Build(motherDir, obj->ClassName(), -1);
242 
243  Int_t lbuf, nout, noutot, bufmax, nzip;
244  fBufferRef = new TBufferFile(TBuffer::kWrite, bufsize);
245  fBufferRef->SetParent(GetFile());
246  fCycle = fMotherDir->AppendKey(this);
247 
248  Streamer(*fBufferRef); //write key itself
249  fKeylen = fBufferRef->Length();
250  fBufferRef->MapObject(obj); //register obj in map in case of self reference
251  ((TObject*)obj)->Streamer(*fBufferRef); //write object
252  lbuf = fBufferRef->Length();
253  fObjlen = lbuf - fKeylen;
254 
255  Int_t cxlevel = GetFile() ? GetFile()->GetCompressionLevel() : 0;
256  ROOT::RCompressionSetting::EAlgorithm::EValues cxAlgorithm = static_cast<ROOT::RCompressionSetting::EAlgorithm::EValues>(GetFile() ? GetFile()->GetCompressionAlgorithm() : 0);
257  if (cxlevel > 0 && fObjlen > 256) {
258  Int_t nbuffers = 1 + (fObjlen - 1)/kMAXZIPBUF;
259  Int_t buflen = TMath::Max(512,fKeylen + fObjlen + 9*nbuffers + 28); //add 28 bytes in case object is placed in a deleted gap
260  fBuffer = new char[buflen];
261  char *objbuf = fBufferRef->Buffer() + fKeylen;
262  char *bufcur = &fBuffer[fKeylen];
263  noutot = 0;
264  nzip = 0;
265  for (Int_t i = 0; i < nbuffers; ++i) {
266  if (i == nbuffers - 1) bufmax = fObjlen - nzip;
267  else bufmax = kMAXZIPBUF;
268  R__zipMultipleAlgorithm(cxlevel, &bufmax, objbuf, &bufmax, bufcur, &nout, cxAlgorithm);
269  if (nout == 0 || nout >= fObjlen) { //this happens when the buffer cannot be compressed
270  fBuffer = fBufferRef->Buffer();
271  Create(fObjlen);
272  fBufferRef->SetBufferOffset(0);
273  Streamer(*fBufferRef); //write key itself again
274  return;
275  }
276  bufcur += nout;
277  noutot += nout;
278  objbuf += kMAXZIPBUF;
279  nzip += kMAXZIPBUF;
280  }
281  Create(noutot);
282  fBufferRef->SetBufferOffset(0);
283  Streamer(*fBufferRef); //write key itself again
284  memcpy(fBuffer,fBufferRef->Buffer(),fKeylen);
285  delete fBufferRef; fBufferRef = 0;
286  } else {
287  fBuffer = fBufferRef->Buffer();
288  Create(fObjlen);
289  fBufferRef->SetBufferOffset(0);
290  Streamer(*fBufferRef); //write key itself again
291  }
292 }
293 
294 ////////////////////////////////////////////////////////////////////////////////
295 /// Create a TKey object for any object obj of class cl d and fill
296 /// output buffer.
297 ///
298 /// WARNING: in name avoid special characters like '^','$','.' that are used
299 /// by the regular expression parser (see TRegexp).
300 
301 TKey::TKey(const void *obj, const TClass *cl, const char *name, Int_t bufsize, TDirectory* motherDir)
302  : TNamed(name, "object title")
303 {
304  R__ASSERT(obj && cl);
305 
306  if (!cl->HasDefaultConstructor()) {
307  Warning("TKey", "since %s has no public constructor\n"
308  "\twhich can be called without argument, objects of this class\n"
309  "\tcan not be read with the current library. You will need to\n"
310  "\tadd a default constructor before attempting to read it.",
311  cl->GetName());
312  }
313 
314  TClass *clActual = cl->GetActualClass(obj);
315  const void* actualStart;
316  if (clActual) {
317  const char *temp = (const char*) obj;
318  // clActual->GetStreamerInfo();
319  Int_t offset = (cl != clActual) ?
320  clActual->GetBaseClassOffset(cl) : 0;
321  temp -= offset;
322  actualStart = temp;
323  } else {
324  // We could not determine the real type of this object,
325  // let's assume it is the one given by the caller.
326  clActual = const_cast<TClass*>(cl);
327  actualStart = obj;
328  }
329 
330  Build(motherDir, clActual->GetName(), -1);
331 
332  fBufferRef = new TBufferFile(TBuffer::kWrite, bufsize);
333  fBufferRef->SetParent(GetFile());
334  fCycle = fMotherDir->AppendKey(this);
335 
336  Streamer(*fBufferRef); //write key itself
337  fKeylen = fBufferRef->Length();
338 
339  Int_t lbuf, nout, noutot, bufmax, nzip;
340 
341  fBufferRef->MapObject(actualStart,clActual); //register obj in map in case of self reference
342  clActual->Streamer((void*)actualStart, *fBufferRef); //write object
343  lbuf = fBufferRef->Length();
344  fObjlen = lbuf - fKeylen;
345 
346  Int_t cxlevel = GetFile() ? GetFile()->GetCompressionLevel() : 0;
347  ROOT::RCompressionSetting::EAlgorithm::EValues cxAlgorithm = static_cast<ROOT::RCompressionSetting::EAlgorithm::EValues>(GetFile() ? GetFile()->GetCompressionAlgorithm() : 0);
348  if (cxlevel > 0 && fObjlen > 256) {
349  Int_t nbuffers = 1 + (fObjlen - 1)/kMAXZIPBUF;
350  Int_t buflen = TMath::Max(512,fKeylen + fObjlen + 9*nbuffers + 28); //add 28 bytes in case object is placed in a deleted gap
351  fBuffer = new char[buflen];
352  char *objbuf = fBufferRef->Buffer() + fKeylen;
353  char *bufcur = &fBuffer[fKeylen];
354  noutot = 0;
355  nzip = 0;
356  for (Int_t i = 0; i < nbuffers; ++i) {
357  if (i == nbuffers - 1) bufmax = fObjlen - nzip;
358  else bufmax = kMAXZIPBUF;
359  R__zipMultipleAlgorithm(cxlevel, &bufmax, objbuf, &bufmax, bufcur, &nout, cxAlgorithm);
360  if (nout == 0 || nout >= fObjlen) { //this happens when the buffer cannot be compressed
361  fBuffer = fBufferRef->Buffer();
362  Create(fObjlen);
363  fBufferRef->SetBufferOffset(0);
364  Streamer(*fBufferRef); //write key itself again
365  return;
366  }
367  bufcur += nout;
368  noutot += nout;
369  objbuf += kMAXZIPBUF;
370  nzip += kMAXZIPBUF;
371  }
372  Create(noutot);
373  fBufferRef->SetBufferOffset(0);
374  Streamer(*fBufferRef); //write key itself again
375  memcpy(fBuffer,fBufferRef->Buffer(),fKeylen);
376  delete fBufferRef; fBufferRef = 0;
377  } else {
378  fBuffer = fBufferRef->Buffer();
379  Create(fObjlen);
380  fBufferRef->SetBufferOffset(0);
381  Streamer(*fBufferRef); //write key itself again
382  }
383 }
384 
385 ////////////////////////////////////////////////////////////////////////////////
386 /// Method used in all TKey constructor to initialize basic data fields.
387 ///
388 /// The member filepos is used to calculate correct version number of key
389 /// if filepos==-1, end of file position is used.
390 
391 void TKey::Build(TDirectory* motherDir, const char* classname, Long64_t filepos)
392 {
393  fMotherDir = motherDir;
394 
395  fPidOffset = 0;
396  fNbytes = 0;
397  fBuffer = 0;
398  fKeylen = 0;
399  fObjlen = 0;
400  fBufferRef = 0;
401  fCycle = 0;
402  fSeekPdir = 0;
403  fSeekKey = 0;
404  fLeft = 0;
405 
406  fClassName = classname;
407  //the following test required for forward and backward compatibility
408  if (fClassName == "TDirectoryFile") SetBit(kIsDirectoryFile);
409 
410  fVersion = TKey::Class_Version();
411 
412  if ((filepos==-1) && GetFile()) filepos = GetFile()->GetEND();
413  if (filepos > TFile::kStartBigFile) fVersion += 1000;
414 
415  if (fTitle.Length() > kTitleMax) fTitle.Resize(kTitleMax);
416 
417  if (GetFile() && GetFile()->TestBit(TFile::kReproducible))
418  SetBit(TKey::kReproducible);
419 }
420 
421 ////////////////////////////////////////////////////////////////////////////////
422 /// Read object from disk and call its Browse() method.
423 ///
424 /// If object with same name already exist in memory delete it (like
425 /// TDirectoryFile::Get() is doing), except when the key references a
426 /// folder in which case we don't want to re-read the folder object
427 /// since it might contain new objects not yet saved.
428 
429 void TKey::Browse(TBrowser *b)
430 {
431  if (fMotherDir==0) return;
432 
433  TClass *objcl = TClass::GetClass(GetClassName());
434 
435  void* obj = fMotherDir->GetList()->FindObject(GetName());
436  if (obj && objcl->IsTObject()) {
437  TObject *tobj = (TObject*) objcl->DynamicCast(TObject::Class(), obj);
438  if (!tobj->IsFolder()) {
439  if (tobj->InheritsFrom(TCollection::Class()))
440  tobj->Delete(); // delete also collection elements
441  delete tobj;
442  obj = 0;
443  }
444  }
445 
446  if (!obj)
447  obj = ReadObj();
448 
449  if (b && obj) {
450  objcl->Browse(obj,b);
451  b->SetRefreshFlag(kTRUE);
452  }
453 }
454 
455 ////////////////////////////////////////////////////////////////////////////////
456 /// Create a TKey object of specified size.
457 ///
458 /// If externFile!=0, key will be allocated in specified file, otherwise file
459 /// of mother directory will be used.
460 
461 void TKey::Create(Int_t nbytes, TFile* externFile)
462 {
463  keyAbsNumber++; SetUniqueID(keyAbsNumber);
464 
465  TFile *f = externFile;
466  if (!f) f = GetFile();
467  if (!f) {
468  Error("Create","Cannot create key without file");
469  return;
470  }
471 
472  Int_t nsize = nbytes + fKeylen;
473  TList *lfree = f->GetListOfFree();
474  TFree *f1 = (TFree*)lfree->First();
475 //*-*-------------------find free segment
476 //*-* =================
477  TFree *bestfree = f1->GetBestFree(lfree,nsize);
478  if (bestfree == 0) {
479  Error("Create","Cannot allocate %d bytes for ID = %s Title = %s",
480  nsize,GetName(),GetTitle());
481  return;
482  }
483 
484  if (f->TestBit(TFile::kReproducible))
485  SetBit(TKey::kReproducible);
486 
487  fDatime.Set();
488  fSeekKey = bestfree->GetFirst();
489 //*-*----------------- Case Add at the end of the file
490  if (fSeekKey >= f->GetEND()) {
491  f->SetEND(fSeekKey+nsize);
492  bestfree->SetFirst(fSeekKey+nsize);
493  if (f->GetEND() > bestfree->GetLast()) {
494  bestfree->SetLast(bestfree->GetLast() + 1000000000);
495  }
496  fLeft = -1;
497  if (!fBuffer) fBuffer = new char[nsize];
498  } else {
499  fLeft = Int_t(bestfree->GetLast() - fSeekKey - nsize + 1);
500  }
501 //*-*----------------- Case where new object fills exactly a deleted gap
502  fNbytes = nsize;
503  if (fLeft == 0) {
504  if (!fBuffer) {
505  fBuffer = new char[nsize];
506  }
507  lfree->Remove(bestfree);
508  delete bestfree;
509  }
510 //*-*----------------- Case where new object is placed in a deleted gap larger than itself
511  if (fLeft > 0) { // found a bigger segment
512  if (!fBuffer) {
513  fBuffer = new char[nsize+sizeof(Int_t)];
514  }
515  char *buffer = fBuffer+nsize;
516  Int_t nbytesleft = -fLeft; // set header of remaining record
517  tobuf(buffer, nbytesleft);
518  bestfree->SetFirst(fSeekKey+nsize);
519  }
520 
521  fSeekPdir = externFile ? externFile->GetSeekDir() : fMotherDir->GetSeekDir();
522 }
523 
524 ////////////////////////////////////////////////////////////////////////////////
525 /// TKey default destructor.
526 
527 TKey::~TKey()
528 {
529  // delete [] fBuffer; fBuffer = 0;
530  // delete fBufferRef; fBufferRef = 0;
531 
532  DeleteBuffer();
533 }
534 
535 ////////////////////////////////////////////////////////////////////////////////
536 /// Delete an object from the file.
537 ///
538 /// Note: the key is not deleted. You still have to call "delete key".
539 /// This is different from the behaviour of TObject::Delete()!
540 
541 void TKey::Delete(Option_t *option)
542 {
543  if (option && option[0] == 'v') printf("Deleting key: %s at address %lld, nbytes = %d\n",GetName(),fSeekKey,fNbytes);
544  Long64_t first = fSeekKey;
545  Long64_t last = fSeekKey + fNbytes -1;
546  if (GetFile()) GetFile()->MakeFree(first, last); // release space used by this key
547  fMotherDir->GetListOfKeys()->Remove(this);
548 }
549 
550 ////////////////////////////////////////////////////////////////////////////////
551 /// Delete key buffer(s).
552 
553 void TKey::DeleteBuffer()
554 {
555  if (fBufferRef) {
556  delete fBufferRef;
557  fBufferRef = 0;
558  } else {
559  // We only need to delete fBuffer if fBufferRef is zero because
560  // if fBufferRef exists, we delegate ownership of fBuffer to fBufferRef.
561  if (fBuffer) {
562  delete [] fBuffer;
563  }
564  }
565  fBuffer = 0;
566 }
567 
568 ////////////////////////////////////////////////////////////////////////////////
569 /// Return cycle number associated to this key.
570 
571 Short_t TKey::GetCycle() const
572 {
573  return ((fCycle >0) ? fCycle : -fCycle);
574 }
575 
576 ////////////////////////////////////////////////////////////////////////////////
577 /// Returns file to which key belong.
578 
579 TFile *TKey::GetFile() const
580 {
581  return fMotherDir!=0 ? fMotherDir->GetFile() : gFile;
582 }
583 
584 ////////////////////////////////////////////////////////////////////////////////
585 /// Returns the "KEEP" status.
586 
587 Short_t TKey::GetKeep() const
588 {
589  return ((fCycle >0) ? 0 : 1);
590 }
591 
592 ////////////////////////////////////////////////////////////////////////////////
593 /// Encode key header into output buffer.
594 
595 void TKey::FillBuffer(char *&buffer)
596 {
597  tobuf(buffer, fNbytes);
598  Version_t version = fVersion;
599  tobuf(buffer, version);
600 
601  tobuf(buffer, fObjlen);
602  if (TestBit(TKey::kReproducible))
603  TDatime((UInt_t) 1).FillBuffer(buffer);
604  else
605  fDatime.FillBuffer(buffer);
606  tobuf(buffer, fKeylen);
607  tobuf(buffer, fCycle);
608  if (fVersion > 1000) {
609  tobuf(buffer, fSeekKey);
610 
611  // We currently store in the 16 highest bit of fSeekPdir the value of
612  // fPidOffset. This offset is used when a key (or basket) is transfered from one
613  // file to the other. In this case the TRef and TObject might have stored a
614  // pid index (to retrieve TProcessIDs) which refered to their order on the original
615  // file, the fPidOffset is to be added to those values to correctly find the
616  // TProcessID. This fPidOffset needs to be increment if the key/basket is copied
617  // and need to be zero for new key/basket.
618  Long64_t pdir = (((Long64_t)fPidOffset)<<kPidOffsetShift) | (kPidOffsetMask & fSeekPdir);
619  tobuf(buffer, pdir);
620  } else {
621  tobuf(buffer, (Int_t)fSeekKey);
622  tobuf(buffer, (Int_t)fSeekPdir);
623  }
624  if (TestBit(kIsDirectoryFile)) {
625  // We want to record "TDirectory" instead of TDirectoryFile so that the file can be read by ancient version of ROOT.
626  gTDirectoryString().FillBuffer(buffer);
627  } else {
628  fClassName.FillBuffer(buffer);
629  }
630  fName.FillBuffer(buffer);
631  fTitle.FillBuffer(buffer);
632 }
633 
634 ////////////////////////////////////////////////////////////////////////////////
635 /// Increment fPidOffset by 'offset'.
636 ///
637 /// This offset is used when a key (or basket) is transfered from one file to
638 /// the other. In this case the TRef and TObject might have stored a pid
639 /// index (to retrieve TProcessIDs) which refered to their order on the
640 /// original file, the fPidOffset is to be added to those values to correctly
641 /// find the TProcessID. This fPidOffset needs to be increment if the
642 /// key/basket is copied and need to be zero for new key/basket.
643 
644 void TKey::IncrementPidOffset(UShort_t offset)
645 {
646  fPidOffset += offset;
647  if (fPidOffset) {
648  // We currently store fPidOffset in the 16 highest bit of fSeekPdir, which
649  // need to be store as a 64 bit integer. So we require this key to be
650  // a 'large file' key.
651  if (fVersion<1000) fVersion += 1000;
652  }
653 }
654 
655 ////////////////////////////////////////////////////////////////////////////////
656 /// Check if object referenced by the key is a folder.
657 
658 Bool_t TKey::IsFolder() const
659 {
660  Bool_t ret = kFALSE;
661 
662  TClass *classPtr = TClass::GetClass((const char *) fClassName);
663  if (classPtr && classPtr->GetState() > TClass::kEmulated && classPtr->IsTObject()) {
664  TObject *obj = (TObject *) classPtr->DynamicCast(TObject::Class(), classPtr->New(TClass::kDummyNew));
665  if (obj) {
666  ret = obj->IsFolder();
667  delete obj;
668  }
669  }
670 
671  return ret;
672 }
673 
674 ////////////////////////////////////////////////////////////////////////////////
675 /// Set the "KEEP" status.
676 ///
677 /// When the KEEP flag is set to 1 the object cannot be purged.
678 
679 void TKey::Keep()
680 {
681  if (fCycle >0) fCycle = -fCycle;
682 }
683 
684 ////////////////////////////////////////////////////////////////////////////////
685 /// List Key contents.
686 
687 void TKey::ls(Option_t *) const
688 {
689  TROOT::IndentLevel();
690  std::cout <<"KEY: "<<fClassName<<"\t"<<GetName()<<";"<<GetCycle()<<"\t"<<GetTitle()<<std::endl;
691 }
692 
693 ////////////////////////////////////////////////////////////////////////////////
694 /// Print key contents.
695 
696 void TKey::Print(Option_t *) const
697 {
698  printf("TKey Name = %s, Title = %s, Cycle = %d\n",GetName(),GetTitle(),GetCycle());
699 }
700 
701 ////////////////////////////////////////////////////////////////////////////////
702 /// To read a TObject* from the file.
703 ///
704 /// The object associated to this key is read from the file into memory
705 /// Once the key structure is read (via Streamer) the class identifier
706 /// of the object is known.
707 /// Using the class identifier we find the TClass object for this class.
708 /// A TClass object contains a full description (i.e. dictionary) of the
709 /// associated class. In particular the TClass object can create a new
710 /// object of the class type it describes. This new object now calls its
711 /// Streamer function to rebuilt itself.
712 ///
713 /// Use TKey::ReadObjectAny to read any object non-derived from TObject
714 ///
715 /// ### Note
716 /// A C style cast can only be used in the case where the final class
717 /// of this object derives from TObject as a first inheritance, otherwise
718 /// one must use a dynamic_cast.
719 ///
720 /// #### Example1: simplified case
721 /// class MyClass : public TObject, public AnotherClass
722 /// then on return, one get away with using:
723 /// MyClass *obj = (MyClass*)key->ReadObj();
724 ///
725 /// #### Example2: Usual case (recommended unless performance is critical)
726 /// MyClass *obj = dynamic_cast<MyClass*>(key->ReadObj());
727 /// which support also the more complex inheritance like:
728 /// class MyClass : public AnotherClass, public TObject
729 ///
730 /// Of course, dynamic_cast<> can also be used in the example 1.
731 
732 TObject *TKey::ReadObj()
733 {
734  TClass *cl = TClass::GetClass(fClassName.Data());
735  if (!cl) {
736  Error("ReadObj", "Unknown class %s", fClassName.Data());
737  return 0;
738  }
739  if (!cl->IsTObject()) {
740  // in principle user should call TKey::ReadObjectAny!
741  return (TObject*)ReadObjectAny(0);
742  }
743 
744  fBufferRef = new TBufferFile(TBuffer::kRead, fObjlen+fKeylen);
745  if (!fBufferRef) {
746  Error("ReadObj", "Cannot allocate buffer: fObjlen = %d", fObjlen);
747  return 0;
748  }
749  if (GetFile()==0) return 0;
750  fBufferRef->SetParent(GetFile());
751  fBufferRef->SetPidOffset(fPidOffset);
752 
753  if (fObjlen > fNbytes-fKeylen) {
754  fBuffer = new char[fNbytes];
755  if( !ReadFile() ) //Read object structure from file
756  {
757  delete fBufferRef;
758  delete [] fBuffer;
759  fBufferRef = 0;
760  fBuffer = 0;
761  return 0;
762  }
763  memcpy(fBufferRef->Buffer(),fBuffer,fKeylen);
764  } else {
765  fBuffer = fBufferRef->Buffer();
766  if( !ReadFile() ) { //Read object structure from file
767  delete fBufferRef;
768  fBufferRef = 0;
769  fBuffer = 0;
770  return 0;
771  }
772  }
773 
774  // get version of key
775  fBufferRef->SetBufferOffset(sizeof(fNbytes));
776  Version_t kvers = fBufferRef->ReadVersion();
777 
778  fBufferRef->SetBufferOffset(fKeylen);
779  TObject *tobj = 0;
780  // Create an instance of this class
781 
782  char *pobj = (char*)cl->New();
783  if (!pobj) {
784  Error("ReadObj", "Cannot create new object of class %s", fClassName.Data());
785  return 0;
786  }
787  Int_t baseOffset = cl->GetBaseClassOffset(TObject::Class());
788  if (baseOffset==-1) {
789  // cl does not inherit from TObject.
790  // Since this is not possible yet, the only reason we could reach this code
791  // is because something is screw up in the ROOT code.
792  Fatal("ReadObj","Incorrect detection of the inheritance from TObject for class %s.\n",
793  fClassName.Data());
794  }
795  tobj = (TObject*)(pobj+baseOffset);
796  if (kvers > 1)
797  fBufferRef->MapObject(pobj,cl); //register obj in map to handle self reference
798 
799  if (fObjlen > fNbytes-fKeylen) {
800  char *objbuf = fBufferRef->Buffer() + fKeylen;
801  UChar_t *bufcur = (UChar_t *)&fBuffer[fKeylen];
802  Int_t nin, nout = 0, nbuf;
803  Int_t noutot = 0;
804  while (1) {
805  Int_t hc = R__unzip_header(&nin, bufcur, &nbuf);
806  if (hc!=0) break;
807  R__unzip(&nin, bufcur, &nbuf, (unsigned char*) objbuf, &nout);
808  if (!nout) break;
809  noutot += nout;
810  if (noutot >= fObjlen) break;
811  bufcur += nin;
812  objbuf += nout;
813  }
814  if (nout) {
815  tobj->Streamer(*fBufferRef); //does not work with example 2 above
816  delete [] fBuffer;
817  } else {
818  delete [] fBuffer;
819  // Even-though we have a TObject, if the class is emulated the virtual
820  // table may not be 'right', so let's go via the TClass.
821  cl->Destructor(pobj);
822  pobj = 0;
823  tobj = 0;
824  goto CLEAR;
825  }
826  } else {
827  tobj->Streamer(*fBufferRef);
828  }
829 
830  if (gROOT->GetForceStyle()) tobj->UseCurrentStyle();
831 
832  if (cl->InheritsFrom(TDirectoryFile::Class())) {
833  TDirectory *dir = static_cast<TDirectoryFile*>(tobj);
834  dir->SetName(GetName());
835  dir->SetTitle(GetTitle());
836  dir->SetMother(fMotherDir);
837  fMotherDir->Append(dir);
838  }
839 
840  // Append the object to the directory if requested:
841  {
842  ROOT::DirAutoAdd_t addfunc = cl->GetDirectoryAutoAdd();
843  if (addfunc) {
844  addfunc(pobj, fMotherDir);
845  }
846  }
847 
848 CLEAR:
849  delete fBufferRef;
850  fBufferRef = 0;
851  fBuffer = 0;
852 
853  return tobj;
854 }
855 
856 ////////////////////////////////////////////////////////////////////////////////
857 /// To read a TObject* from bufferRead.
858 ///
859 /// This function is identical to TKey::ReadObj, but it reads directly from
860 /// bufferRead instead of reading from a file.
861 /// The object associated to this key is read from the buffer into memory
862 /// Using the class identifier we find the TClass object for this class.
863 /// A TClass object contains a full description (i.e. dictionary) of the
864 /// associated class. In particular the TClass object can create a new
865 /// object of the class type it describes. This new object now calls its
866 /// Streamer function to rebuilt itself.
867 ///
868 /// ### Note
869 /// This function is called only internally by ROOT classes.
870 /// Although being public it is not supposed to be used outside ROOT.
871 /// If used, you must make sure that the bufferRead is large enough to
872 /// accomodate the object being read.
873 
874 TObject *TKey::ReadObjWithBuffer(char *bufferRead)
875 {
876 
877  TClass *cl = TClass::GetClass(fClassName.Data());
878  if (!cl) {
879  Error("ReadObjWithBuffer", "Unknown class %s", fClassName.Data());
880  return 0;
881  }
882  if (!cl->IsTObject()) {
883  // in principle user should call TKey::ReadObjectAny!
884  return (TObject*)ReadObjectAny(0);
885  }
886 
887  fBufferRef = new TBufferFile(TBuffer::kRead, fObjlen+fKeylen);
888  if (!fBufferRef) {
889  Error("ReadObjWithBuffer", "Cannot allocate buffer: fObjlen = %d", fObjlen);
890  return 0;
891  }
892  if (GetFile()==0) return 0;
893  fBufferRef->SetParent(GetFile());
894  fBufferRef->SetPidOffset(fPidOffset);
895 
896  if (fObjlen > fNbytes-fKeylen) {
897  fBuffer = bufferRead;
898  memcpy(fBufferRef->Buffer(),fBuffer,fKeylen);
899  } else {
900  fBuffer = fBufferRef->Buffer();
901  ReadFile(); //Read object structure from file
902  }
903 
904  // get version of key
905  fBufferRef->SetBufferOffset(sizeof(fNbytes));
906  Version_t kvers = fBufferRef->ReadVersion();
907 
908  fBufferRef->SetBufferOffset(fKeylen);
909  TObject *tobj = 0;
910  // Create an instance of this class
911 
912  char *pobj = (char*)cl->New();
913  if (!pobj) {
914  Error("ReadObjWithBuffer", "Cannot create new object of class %s", fClassName.Data());
915  return 0;
916  }
917  Int_t baseOffset = cl->GetBaseClassOffset(TObject::Class());
918  if (baseOffset==-1) {
919  // cl does not inherit from TObject.
920  // Since this is not possible yet, the only reason we could reach this code
921  // is because something is screw up in the ROOT code.
922  Fatal("ReadObjWithBuffer","Incorrect detection of the inheritance from TObject for class %s.\n",
923  fClassName.Data());
924  }
925  tobj = (TObject*)(pobj+baseOffset);
926 
927  if (kvers > 1)
928  fBufferRef->MapObject(pobj,cl); //register obj in map to handle self reference
929 
930  if (fObjlen > fNbytes-fKeylen) {
931  char *objbuf = fBufferRef->Buffer() + fKeylen;
932  UChar_t *bufcur = (UChar_t *)&fBuffer[fKeylen];
933  Int_t nin, nout = 0, nbuf;
934  Int_t noutot = 0;
935  while (1) {
936  Int_t hc = R__unzip_header(&nin, bufcur, &nbuf);
937  if (hc!=0) break;
938  R__unzip(&nin, bufcur, &nbuf, (unsigned char*) objbuf, &nout);
939  if (!nout) break;
940  noutot += nout;
941  if (noutot >= fObjlen) break;
942  bufcur += nin;
943  objbuf += nout;
944  }
945  if (nout) {
946  tobj->Streamer(*fBufferRef); //does not work with example 2 above
947  } else {
948  // Even-though we have a TObject, if the class is emulated the virtual
949  // table may not be 'right', so let's go via the TClass.
950  cl->Destructor(pobj);
951  pobj = 0;
952  tobj = 0;
953  goto CLEAR;
954  }
955  } else {
956  tobj->Streamer(*fBufferRef);
957  }
958 
959  if (gROOT->GetForceStyle()) tobj->UseCurrentStyle();
960 
961  if (cl->InheritsFrom(TDirectoryFile::Class())) {
962  TDirectory *dir = static_cast<TDirectoryFile*>(tobj);
963  dir->SetName(GetName());
964  dir->SetTitle(GetTitle());
965  dir->SetMother(fMotherDir);
966  fMotherDir->Append(dir);
967  }
968 
969  // Append the object to the directory if requested:
970  {
971  ROOT::DirAutoAdd_t addfunc = cl->GetDirectoryAutoAdd();
972  if (addfunc) {
973  addfunc(pobj, fMotherDir);
974  }
975  }
976 
977 CLEAR:
978  delete fBufferRef;
979  fBufferRef = 0;
980  fBuffer = 0;
981 
982  return tobj;
983 }
984 
985 ////////////////////////////////////////////////////////////////////////////////
986 /// To read an object (non deriving from TObject) from the file.
987 ///
988 /// If expectedClass is not null, we checked that that actual class of the
989 /// object stored is suitable to be stored in a pointer pointing to an object
990 /// of class 'expectedClass'. We also adjust the value of the returned address
991 /// so that it is suitable to be cast (C-Style)
992 /// a pointer pointing to an object of class 'expectedClass'.
993 ///
994 /// So for example if the class Bottom inherits from Top and the object
995 /// stored is of type Bottom you can safely do:
996 /// ~~~{.cpp}
997 /// auto TopClass = TClass::GetClass("Top");
998 /// auto ptr = (Top*) key->ReadObjectAny( TopClass );
999 /// if (ptr==0) printError("the object stored in the key is not of the expected type\n");
1000 /// ~~~
1001 /// The object associated to this key is read from the file into memory.
1002 /// Once the key structure is read (via Streamer) the class identifier
1003 /// of the object is known.
1004 /// Using the class identifier we find the TClass object for this class.
1005 /// A TClass object contains a full description (i.e. dictionary) of the
1006 /// associated class. In particular the TClass object can create a new
1007 /// object of the class type it describes. This new object now calls its
1008 /// Streamer function to rebuilt itself.
1009 
1010 void *TKey::ReadObjectAny(const TClass* expectedClass)
1011 {
1012  fBufferRef = new TBufferFile(TBuffer::kRead, fObjlen+fKeylen);
1013  if (!fBufferRef) {
1014  Error("ReadObj", "Cannot allocate buffer: fObjlen = %d", fObjlen);
1015  return 0;
1016  }
1017  if (GetFile()==0) return 0;
1018  fBufferRef->SetParent(GetFile());
1019  fBufferRef->SetPidOffset(fPidOffset);
1020 
1021  if (fObjlen > fNbytes-fKeylen) {
1022  fBuffer = new char[fNbytes];
1023  ReadFile(); //Read object structure from file
1024  memcpy(fBufferRef->Buffer(),fBuffer,fKeylen);
1025  } else {
1026  fBuffer = fBufferRef->Buffer();
1027  ReadFile(); //Read object structure from file
1028  }
1029 
1030  // get version of key
1031  fBufferRef->SetBufferOffset(sizeof(fNbytes));
1032  Version_t kvers = fBufferRef->ReadVersion();
1033 
1034  fBufferRef->SetBufferOffset(fKeylen);
1035  TClass *cl = TClass::GetClass(fClassName.Data());
1036  TClass *clOnfile = 0;
1037  if (!cl) {
1038  Error("ReadObjectAny", "Unknown class %s", fClassName.Data());
1039  return 0;
1040  }
1041  Int_t baseOffset = 0;
1042  if (expectedClass) {
1043  // baseOffset will be -1 if cl does not inherit from expectedClass
1044  baseOffset = cl->GetBaseClassOffset(expectedClass);
1045  if (baseOffset == -1) {
1046  // The 2 classes are unrelated, maybe there is a converter between the 2.
1047 
1048  if (!expectedClass->GetSchemaRules() ||
1049  !expectedClass->GetSchemaRules()->HasRuleWithSourceClass(cl->GetName()))
1050  {
1051  // There is no converter
1052  return 0;
1053  }
1054  baseOffset = 0; // For now we do not support requesting from a class that is the base of one of the class for which there is transformation to ....
1055  clOnfile = cl;
1056  cl = const_cast<TClass*>(expectedClass);
1057  Info("ReadObjectAny","Using Converter StreamerInfo from %s to %s",clOnfile->GetName(),expectedClass->GetName());
1058  }
1059  if (cl->GetState() > TClass::kEmulated && expectedClass->GetState() <= TClass::kEmulated) {
1060  //we cannot mix a compiled class with an emulated class in the inheritance
1061  Warning("ReadObjectAny",
1062  "Trying to read an emulated class (%s) to store in a compiled pointer (%s)",
1063  cl->GetName(),expectedClass->GetName());
1064  }
1065  }
1066  // Create an instance of this class
1067 
1068  void *pobj = cl->New();
1069  if (!pobj) {
1070  Error("ReadObjectAny", "Cannot create new object of class %s", fClassName.Data());
1071  return 0;
1072  }
1073 
1074  if (kvers > 1)
1075  fBufferRef->MapObject(pobj,cl); //register obj in map to handle self reference
1076 
1077  if (fObjlen > fNbytes-fKeylen) {
1078  char *objbuf = fBufferRef->Buffer() + fKeylen;
1079  UChar_t *bufcur = (UChar_t *)&fBuffer[fKeylen];
1080  Int_t nin, nout = 0, nbuf;
1081  Int_t noutot = 0;
1082  while (1) {
1083  Int_t hc = R__unzip_header(&nin, bufcur, &nbuf);
1084  if (hc!=0) break;
1085  R__unzip(&nin, bufcur, &nbuf, (unsigned char*) objbuf, &nout);
1086  if (!nout) break;
1087  noutot += nout;
1088  if (noutot >= fObjlen) break;
1089  bufcur += nin;
1090  objbuf += nout;
1091  }
1092  if (nout) {
1093  cl->Streamer((void*)pobj, *fBufferRef, clOnfile); //read object
1094  delete [] fBuffer;
1095  } else {
1096  delete [] fBuffer;
1097  cl->Destructor(pobj);
1098  pobj = 0;
1099  goto CLEAR;
1100  }
1101  } else {
1102  cl->Streamer((void*)pobj, *fBufferRef, clOnfile); //read object
1103  }
1104 
1105  if (cl->IsTObject()) {
1106  auto tobjBaseOffset = cl->GetBaseClassOffset(TObject::Class());
1107  if (tobjBaseOffset == -1) {
1108  Fatal("ReadObj","Incorrect detection of the inheritance from TObject for class %s.\n",
1109  fClassName.Data());
1110  }
1111  TObject *tobj = (TObject*)( ((char*)pobj) + tobjBaseOffset);
1112 
1113  // See similar adjustments in ReadObj
1114  if (gROOT->GetForceStyle()) tobj->UseCurrentStyle();
1115 
1116  if (cl->InheritsFrom(TDirectoryFile::Class())) {
1117  TDirectory *dir = static_cast<TDirectoryFile*>(tobj);
1118  dir->SetName(GetName());
1119  dir->SetTitle(GetTitle());
1120  dir->SetMother(fMotherDir);
1121  fMotherDir->Append(dir);
1122  }
1123  }
1124 
1125  {
1126  // Append the object to the directory if requested:
1127  ROOT::DirAutoAdd_t addfunc = cl->GetDirectoryAutoAdd();
1128  if (addfunc) {
1129  addfunc(pobj, fMotherDir);
1130  }
1131  }
1132 
1133  CLEAR:
1134  delete fBufferRef;
1135  fBufferRef = 0;
1136  fBuffer = 0;
1137 
1138  return ( ((char*)pobj) + baseOffset );
1139 }
1140 
1141 ////////////////////////////////////////////////////////////////////////////////
1142 /// To read an object from the file.
1143 ///
1144 /// The object associated to this key is read from the file into memory.
1145 /// Before invoking this function, obj has been created via the
1146 /// default constructor.
1147 
1148 Int_t TKey::Read(TObject *obj)
1149 {
1150  if (!obj || (GetFile()==0)) return 0;
1151 
1152  fBufferRef = new TBufferFile(TBuffer::kRead, fObjlen+fKeylen);
1153  fBufferRef->SetParent(GetFile());
1154  fBufferRef->SetPidOffset(fPidOffset);
1155 
1156  if (fVersion > 1)
1157  fBufferRef->MapObject(obj); //register obj in map to handle self reference
1158 
1159  if (fObjlen > fNbytes-fKeylen) {
1160  fBuffer = new char[fNbytes];
1161  ReadFile(); //Read object structure from file
1162  memcpy(fBufferRef->Buffer(),fBuffer,fKeylen);
1163  } else {
1164  fBuffer = fBufferRef->Buffer();
1165  ReadFile(); //Read object structure from file
1166  }
1167  fBufferRef->SetBufferOffset(fKeylen);
1168  if (fObjlen > fNbytes-fKeylen) {
1169  char *objbuf = fBufferRef->Buffer() + fKeylen;
1170  UChar_t *bufcur = (UChar_t *)&fBuffer[fKeylen];
1171  Int_t nin, nout = 0, nbuf;
1172  Int_t noutot = 0;
1173  while (1) {
1174  Int_t hc = R__unzip_header(&nin, bufcur, &nbuf);
1175  if (hc!=0) break;
1176  R__unzip(&nin, bufcur, &nbuf, (unsigned char*) objbuf, &nout);
1177  if (!nout) break;
1178  noutot += nout;
1179  if (noutot >= fObjlen) break;
1180  bufcur += nin;
1181  objbuf += nout;
1182  }
1183  if (nout) obj->Streamer(*fBufferRef);
1184  delete [] fBuffer;
1185  } else {
1186  obj->Streamer(*fBufferRef);
1187  }
1188 
1189  // Append the object to the directory if requested:
1190  {
1191  ROOT::DirAutoAdd_t addfunc = obj->IsA()->GetDirectoryAutoAdd();
1192  if (addfunc) {
1193  addfunc(obj, fMotherDir);
1194  }
1195  }
1196 
1197  delete fBufferRef;
1198  fBufferRef = 0;
1199  fBuffer = 0;
1200  return fNbytes;
1201 }
1202 
1203 ////////////////////////////////////////////////////////////////////////////////
1204 /// Decode input buffer.
1205 ///
1206 /// In some situation will add key to gDirectory.
1207 
1208 void TKey::ReadBuffer(char *&buffer)
1209 {
1210  ReadKeyBuffer(buffer);
1211 
1212  if (!gROOT->ReadingObject() && gDirectory) {
1213  if (fSeekPdir != gDirectory->GetSeekDir()) gDirectory->AppendKey(this);
1214  }
1215 }
1216 
1217 ////////////////////////////////////////////////////////////////////////////////
1218 /// Decode input buffer.
1219 
1220 void TKey::ReadKeyBuffer(char *&buffer)
1221 {
1222  frombuf(buffer, &fNbytes);
1223  Version_t version;
1224  frombuf(buffer,&version);
1225  fVersion = (Int_t)version;
1226  frombuf(buffer, &fObjlen);
1227  fDatime.ReadBuffer(buffer);
1228  frombuf(buffer, &fKeylen);
1229  frombuf(buffer, &fCycle);
1230  if (fVersion > 1000) {
1231  frombuf(buffer, &fSeekKey);
1232 
1233  // We currently store in the 16 highest bit of fSeekPdir the value of
1234  // fPidOffset. This offset is used when a key (or basket) is transfered from one
1235  // file to the other. In this case the TRef and TObject might have stored a
1236  // pid index (to retrieve TProcessIDs) which refered to their order on the original
1237  // file, the fPidOffset is to be added to those values to correctly find the
1238  // TProcessID. This fPidOffset needs to be increment if the key/basket is copied
1239  // and need to be zero for new key/basket.
1240  Long64_t pdir;
1241  frombuf(buffer, &pdir);
1242  fPidOffset = pdir >> kPidOffsetShift;
1243  fSeekPdir = pdir & kPidOffsetMask;
1244  } else {
1245  UInt_t seekkey,seekdir;
1246  frombuf(buffer, &seekkey); fSeekKey = (Long64_t)seekkey;
1247  frombuf(buffer, &seekdir); fSeekPdir= (Long64_t)seekdir;
1248  }
1249  fClassName.ReadBuffer(buffer);
1250  //the following test required for forward and backward compatibility
1251  if (fClassName == "TDirectory") {
1252  fClassName = "TDirectoryFile";
1253  SetBit(kIsDirectoryFile);
1254  }
1255 
1256  fName.ReadBuffer(buffer);
1257  fTitle.ReadBuffer(buffer);
1258 }
1259 
1260 ////////////////////////////////////////////////////////////////////////////////
1261 /// Read the key structure from the file
1262 
1263 Bool_t TKey::ReadFile()
1264 {
1265  TFile* f = GetFile();
1266  if (f==0) return kFALSE;
1267 
1268  Int_t nsize = fNbytes;
1269  f->Seek(fSeekKey);
1270 #if 0
1271  for (Int_t i = 0; i < nsize; i += kMAXFILEBUFFER) {
1272  int nb = kMAXFILEBUFFER;
1273  if (i+nb > nsize) nb = nsize - i;
1274  f->ReadBuffer(fBuffer+i,nb);
1275  }
1276 #else
1277  if( f->ReadBuffer(fBuffer,nsize) )
1278  {
1279  Error("ReadFile", "Failed to read data.");
1280  return kFALSE;
1281  }
1282 #endif
1283  if (gDebug) {
1284  std::cout << "TKey Reading "<<nsize<< " bytes at address "<<fSeekKey<<std::endl;
1285  }
1286  return kTRUE;
1287 }
1288 
1289 ////////////////////////////////////////////////////////////////////////////////
1290 /// Set parent in key buffer.
1291 
1292 void TKey::SetParent(const TObject *parent)
1293 {
1294  if (fBufferRef) fBufferRef->SetParent((TObject*)parent);
1295 }
1296 
1297 ////////////////////////////////////////////////////////////////////////////////
1298 /// Reset the key as it had not been 'filled' yet.
1299 
1300 void TKey::Reset()
1301 {
1302  fPidOffset = 0;
1303  fNbytes = 0;
1304  fBuffer = 0;
1305  fObjlen = 0;
1306  fCycle = 0;
1307  fSeekPdir = 0;
1308  fSeekKey = 0;
1309  fLeft = 0;
1310  fDatime = (UInt_t)0;
1311 
1312  // fBufferRef and fKeylen intentionally not reset/changed
1313 
1314  keyAbsNumber++; SetUniqueID(keyAbsNumber);
1315 }
1316 
1317 ////////////////////////////////////////////////////////////////////////////////
1318 /// Return the size in bytes of the key header structure.
1319 ///
1320 /// An explaination about the nbytes (Int_t nbytes) variable used in the
1321 /// function. The size of fSeekKey and fSeekPdir is 8 instead of 4 if version is
1322 /// greater than 1000.
1323 /// | Component | Sizeof |
1324 /// |-------------------|--------|
1325 /// | fNbytes | 4 |
1326 /// | sizeof(Version_t) | 2 |
1327 /// | fObjlen | 4 |
1328 /// | fKeylen | 2 |
1329 /// | fCycle | 2 |
1330 /// | fSeekKey | 4 or 8 |
1331 /// | fSeekPdir | 4 or 8 |
1332 /// | **TOTAL** | 22 |
1333 
1334 Int_t TKey::Sizeof() const
1335 {
1336  Int_t nbytes = 22; if (fVersion > 1000) nbytes += 8;
1337  nbytes += fDatime.Sizeof();
1338  if (TestBit(kIsDirectoryFile)) {
1339  nbytes += 11; // strlen("TDirectory")+1
1340  } else {
1341  nbytes += fClassName.Sizeof();
1342  }
1343  nbytes += fName.Sizeof();
1344  nbytes += fTitle.Sizeof();
1345  return nbytes;
1346 }
1347 
1348 ////////////////////////////////////////////////////////////////////////////////
1349 /// Stream a class object.
1350 
1351 void TKey::Streamer(TBuffer &b)
1352 {
1353  Version_t version;
1354  if (b.IsReading()) {
1355  b >> fNbytes;
1356  b >> version; fVersion = (Int_t)version;
1357  b >> fObjlen;
1358  fDatime.Streamer(b);
1359  b >> fKeylen;
1360  b >> fCycle;
1361  if (fVersion > 1000) {
1362  b >> fSeekKey;
1363 
1364  // We currently store in the 16 highest bit of fSeekPdir the value of
1365  // fPidOffset. This offset is used when a key (or basket) is transfered from one
1366  // file to the other. In this case the TRef and TObject might have stored a
1367  // pid index (to retrieve TProcessIDs) which refered to their order on the original
1368  // file, the fPidOffset is to be added to those values to correctly find the
1369  // TProcessID. This fPidOffset needs to be increment if the key/basket is copied
1370  // and need to be zero for new key/basket.
1371  Long64_t pdir;
1372  b >> pdir;
1373  fPidOffset = pdir >> kPidOffsetShift;
1374  fSeekPdir = pdir & kPidOffsetMask;
1375  } else {
1376  UInt_t seekkey, seekdir;
1377  b >> seekkey; fSeekKey = (Long64_t)seekkey;
1378  b >> seekdir; fSeekPdir= (Long64_t)seekdir;
1379  }
1380  fClassName.Streamer(b);
1381  //the following test required for forward and backward compatibility
1382  if (fClassName == "TDirectory") {
1383  fClassName = "TDirectoryFile";
1384  SetBit(kIsDirectoryFile);
1385  }
1386  fName.Streamer(b);
1387  fTitle.Streamer(b);
1388  if (fKeylen < 0) {
1389  Error("Streamer","The value of fKeylen is incorrect (%d) ; trying to recover by setting it to zero",fKeylen);
1390  MakeZombie();
1391  fKeylen = 0;
1392  }
1393  if (fObjlen < 0) {
1394  Error("Streamer","The value of fObjlen is incorrect (%d) ; trying to recover by setting it to zero",fObjlen);
1395  MakeZombie();
1396  fObjlen = 0;
1397  }
1398  if (fNbytes < 0) {
1399  Error("Streamer","The value of fNbytes is incorrect (%d) ; trying to recover by setting it to zero",fNbytes);
1400  MakeZombie();
1401  fNbytes = 0;
1402  }
1403 
1404  } else {
1405  b << fNbytes;
1406  version = (Version_t)fVersion;
1407  b << version;
1408  b << fObjlen;
1409  if (fDatime.Get() == 0) fDatime.Set();
1410  if (TestBit(TKey::kReproducible))
1411  TDatime((UInt_t) 1).Streamer(b);
1412  else
1413  fDatime.Streamer(b);
1414  b << fKeylen;
1415  b << fCycle;
1416  if (fVersion > 1000) {
1417  b << fSeekKey;
1418 
1419  // We currently store in the 16 highest bit of fSeekPdir the value of
1420  // fPidOffset. This offset is used when a key (or basket) is transfered from one
1421  // file to the other. In this case the TRef and TObject might have stored a
1422  // pid index (to retrieve TProcessIDs) which refered to their order on the original
1423  // file, the fPidOffset is to be added to those values to correctly find the
1424  // TProcessID. This fPidOffset needs to be increment if the key/basket is copied
1425  // and need to be zero for new key/basket.
1426  Long64_t pdir = (((Long64_t)fPidOffset)<<kPidOffsetShift) | (kPidOffsetMask & fSeekPdir);
1427  b << pdir;
1428  } else {
1429  b << (Int_t)fSeekKey;
1430  b << (Int_t)fSeekPdir;
1431  }
1432  if (TestBit(kIsDirectoryFile)) {
1433  // We want to record "TDirectory" instead of TDirectoryFile so that the file can be read by ancient version of ROOT.
1434  gTDirectoryString().Streamer(b);
1435  } else {
1436  fClassName.Streamer(b);
1437  }
1438  fName.Streamer(b);
1439  fTitle.Streamer(b);
1440  }
1441 }
1442 
1443 ////////////////////////////////////////////////////////////////////////////////
1444 /// Write the encoded object supported by this key.
1445 /// The function returns the number of bytes committed to the file.
1446 /// If a write error occurs, the number of bytes returned is -1.
1447 
1448 Int_t TKey::WriteFile(Int_t cycle, TFile* f)
1449 {
1450  if (!f) f = GetFile();
1451  if (!f) return -1;
1452 
1453  Int_t nsize = fNbytes;
1454  char *buffer = fBuffer;
1455  if (cycle) {
1456  fCycle = cycle;
1457  FillBuffer(buffer);
1458  buffer = fBuffer;
1459  }
1460 
1461  if (fLeft > 0) nsize += sizeof(Int_t);
1462  f->Seek(fSeekKey);
1463 #if 0
1464  for (Int_t i=0;i<nsize;i+=kMAXFILEBUFFER) {
1465  Int_t nb = kMAXFILEBUFFER;
1466  if (i+nb > nsize) nb = nsize - i;
1467  f->WriteBuffer(buffer,nb);
1468  buffer += nb;
1469  }
1470 #else
1471  Bool_t result = f->WriteBuffer(buffer,nsize);
1472 #endif
1473  //f->Flush(); Flushing takes too much time.
1474  // Let user flush the file when they want.
1475  if (gDebug) {
1476  std::cout <<" TKey Writing "<<nsize<< " bytes at address "<<fSeekKey
1477  <<" for ID= " <<GetName()<<" Title= "<<GetTitle()<<std::endl;
1478  }
1479 
1480  DeleteBuffer();
1481  return result==kTRUE ? -1 : nsize;
1482 }
1483 
1484 ////////////////////////////////////////////////////////////////////////////////
1485 /// Write the encoded object supported by this key.
1486 /// The function returns the number of bytes committed to the file.
1487 /// If a write error occurs, the number of bytes returned is -1.
1488 
1489 Int_t TKey::WriteFileKeepBuffer(TFile *f)
1490 {
1491  if (!f) f = GetFile();
1492  if (!f) return -1;
1493 
1494  Int_t nsize = fNbytes;
1495  char *buffer = fBuffer;
1496 
1497  if (fLeft > 0) nsize += sizeof(Int_t);
1498  f->Seek(fSeekKey);
1499 #if 0
1500  for (Int_t i=0;i<nsize;i+=kMAXFILEBUFFER) {
1501  Int_t nb = kMAXFILEBUFFER;
1502  if (i+nb > nsize) nb = nsize - i;
1503  f->WriteBuffer(buffer,nb);
1504  buffer += nb;
1505  }
1506 #else
1507  Bool_t result = f->WriteBuffer(buffer,nsize);
1508 #endif
1509  //f->Flush(); Flushing takes too much time.
1510  // Let user flush the file when they want.
1511  if (gDebug) {
1512  std::cout <<" TKey Writing "<<nsize<< " bytes at address "<<fSeekKey
1513  <<" for ID= " <<GetName()<<" Title= "<<GetTitle()<<std::endl;
1514  }
1515 
1516  return result==kTRUE ? -1 : nsize;
1517 }
1518 
1519 ////////////////////////////////////////////////////////////////////////////////
1520 /// Title can keep 32x32 xpm thumbnail/icon of the parent object.
1521 
1522 const char *TKey::GetIconName() const
1523 {
1524  return (!fTitle.IsNull() && fTitle.BeginsWith("/* ") ? fTitle.Data() : 0);
1525 }
1526 
1527 ////////////////////////////////////////////////////////////////////////////////
1528 /// Returns title (title can contain 32x32 xpm thumbnail/icon).
1529 
1530 const char *TKey::GetTitle() const
1531 {
1532  if (!fTitle.IsNull() && fTitle.BeginsWith("/* ")) { // title contains xpm thumbnail
1533  static TString ret;
1534  int start = fTitle.Index("/*") + 3;
1535  int stop = fTitle.Index("*/") - 1;
1536  ret = fTitle(start, stop - start);
1537  return ret.Data();
1538  }
1539  return fTitle.Data();
1540 }