Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TBufferIO.cxx
Go to the documentation of this file.
1 // @(#)root/io:$Id$
2 // Author: Sergey Linev 21/02/2018
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2018, 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 \file TBufferIO.cxx
14 \class TBufferIO
15 \ingroup IO
16 
17 Direct subclass of TBuffer, implements common methods for TBufferFile and TBufferText classes
18 */
19 
20 #include "TBufferIO.h"
21 
22 #include "TExMap.h"
23 #include "TClass.h"
24 #include "TFile.h"
25 #include "TError.h"
26 #include "TClonesArray.h"
27 #include "TStreamerInfo.h"
28 #include "TStreamerElement.h"
29 #include "TArrayC.h"
30 #include "TRefTable.h"
31 #include "TProcessID.h"
32 #include "TVirtualMutex.h"
33 #include "TInterpreter.h"
34 #include "TROOT.h"
35 
36 Int_t TBufferIO::fgMapSize = kMapSize;
37 
38 ClassImp(TBufferIO);
39 
40 ////////////////////////////////////////////////////////////////////////////////
41 /// constructor
42 
43 TBufferIO::TBufferIO(TBuffer::EMode mode) : TBuffer(mode)
44 {
45  fMapSize = fgMapSize;
46 }
47 
48 ////////////////////////////////////////////////////////////////////////////////
49 /// constructor
50 
51 TBufferIO::TBufferIO(TBuffer::EMode mode, Int_t bufsiz) : TBuffer(mode, bufsiz)
52 {
53  fMapSize = fgMapSize;
54 }
55 
56 ////////////////////////////////////////////////////////////////////////////////
57 /// constructor
58 
59 TBufferIO::TBufferIO(TBuffer::EMode mode, Int_t bufsiz, void *buf, Bool_t adopt, ReAllocCharFun_t reallocfunc)
60  : TBuffer(mode, bufsiz, buf, adopt, reallocfunc)
61 {
62  fMapSize = fgMapSize;
63 }
64 
65 ////////////////////////////////////////////////////////////////////////////////
66 /// destructor
67 
68 TBufferIO::~TBufferIO()
69 {
70  delete fMap;
71  delete fClassMap;
72 }
73 
74 ////////////////////////////////////////////////////////////////////////////////
75 /// Return the version number of the owner file.
76 
77 Int_t TBufferIO::GetVersionOwner() const
78 {
79  TFile *file = (TFile *)GetParent();
80  if (file)
81  return file->GetVersion();
82  else
83  return 0;
84 }
85 
86 ////////////////////////////////////////////////////////////////////////////////
87 /// Set the initial size of the map used to store object and class
88 /// references during reading. The default size is TBufferFile::kMapSize.
89 /// Increasing the default has the benefit that when reading many
90 /// small objects the map does not need to be resized too often
91 /// (the system is always dynamic, even with the default everything
92 /// will work, only the initial resizing will cost some time).
93 /// This method can only be called directly after the creation of
94 /// the TBuffer, before any reading is done. Globally this option
95 /// can be changed using SetGlobalReadParam().
96 
97 void TBufferIO::SetReadParam(Int_t mapsize)
98 {
99  R__ASSERT(IsReading());
100  R__ASSERT(fMap == nullptr);
101 
102  fMapSize = mapsize;
103 }
104 
105 ////////////////////////////////////////////////////////////////////////////////
106 /// Set the initial size of the hashtable used to store object and class
107 /// references during writing. The default size is TBufferFile::kMapSize.
108 /// Increasing the default has the benefit that when writing many
109 /// small objects the hashtable does not get too many collisions
110 /// (the system is always dynamic, even with the default everything
111 /// will work, only a large number of collisions will cost performance).
112 /// For optimal performance hashsize should always be a prime.
113 /// This method can only be called directly after the creation of
114 /// the TBuffer, before any writing is done. Globally this option
115 /// can be changed using SetGlobalWriteParam().
116 
117 void TBufferIO::SetWriteParam(Int_t mapsize)
118 {
119  R__ASSERT(IsWriting());
120  R__ASSERT(fMap == nullptr);
121 
122  fMapSize = mapsize;
123 }
124 
125 ////////////////////////////////////////////////////////////////////////////////
126 /// Create the fMap container and initialize them
127 /// with the null object.
128 
129 void TBufferIO::InitMap()
130 {
131  if (IsWriting()) {
132  if (!fMap) {
133  fMap = new TExMap(fMapSize);
134  // No need to keep track of the class in write mode
135  // fClassMap = new TExMap(fMapSize);
136  fMapCount = 0;
137  }
138  } else {
139  if (!fMap) {
140  fMap = new TExMap(fMapSize);
141  fMap->Add(0, kNullTag); // put kNullTag in slot 0
142  fMapCount = 1;
143  } else if (fMapCount == 0) {
144  fMap->Add(0, kNullTag); // put kNullTag in slot 0
145  fMapCount = 1;
146  }
147  if (!fClassMap) {
148  fClassMap = new TExMap(fMapSize);
149  fClassMap->Add(0, kNullTag); // put kNullTag in slot 0
150  }
151  }
152 }
153 
154 ////////////////////////////////////////////////////////////////////////////////
155 /// Add object to the fMap container.
156 ///
157 /// If obj is not 0 add object to the map (in read mode also add 0 objects to
158 /// the map). This method may only be called outside this class just before
159 /// calling obj->Streamer() to prevent self reference of obj, in case obj
160 /// contains (via via) a pointer to itself. In that case offset must be 1
161 /// (default value for offset).
162 
163 void TBufferIO::MapObject(const TObject *obj, UInt_t offset)
164 {
165  if (IsWriting()) {
166  if (!fMap)
167  InitMap();
168 
169  if (obj) {
170  CheckCount(offset);
171  ULong_t hash = Void_Hash(obj);
172  fMap->Add(hash, (Long_t)obj, offset);
173  // No need to keep track of the class in write mode
174  // fClassMap->Add(hash, (Long_t)obj, (Long_t)((TObject*)obj)->IsA());
175  fMapCount++;
176  }
177  } else {
178  if (!fMap || !fClassMap)
179  InitMap();
180 
181  fMap->Add(offset, (Long_t)obj);
182  fClassMap->Add(offset, (obj && obj != (TObject *)-1) ? (Long_t)((TObject *)obj)->IsA() : 0);
183  fMapCount++;
184  }
185 }
186 
187 ////////////////////////////////////////////////////////////////////////////////
188 /// Add object to the fMap container.
189 ///
190 /// If obj is not 0 add object to the map (in read mode also add 0 objects to
191 /// the map). This method may only be called outside this class just before
192 /// calling obj->Streamer() to prevent self reference of obj, in case obj
193 /// contains (via via) a pointer to itself. In that case offset must be 1
194 /// (default value for offset).
195 
196 void TBufferIO::MapObject(const void *obj, const TClass *cl, UInt_t offset)
197 {
198  if (IsWriting()) {
199  if (!fMap)
200  InitMap();
201 
202  if (obj) {
203  CheckCount(offset);
204  ULong_t hash = Void_Hash(obj);
205  fMap->Add(hash, (Long_t)obj, offset);
206  // No need to keep track of the class in write mode
207  // fClassMap->Add(hash, (Long_t)obj, (Long_t)cl);
208  fMapCount++;
209  }
210  } else {
211  if (!fMap || !fClassMap)
212  InitMap();
213 
214  fMap->Add(offset, (Long_t)obj);
215  fClassMap->Add(offset, (Long_t)cl);
216  fMapCount++;
217  }
218 }
219 
220 ////////////////////////////////////////////////////////////////////////////////
221 /// Check if the specified object is already in the buffer.
222 /// Returns kTRUE if object already in the buffer, kFALSE otherwise
223 /// (also if obj is 0 or TBuffer not in writing mode).
224 
225 Bool_t TBufferIO::CheckObject(const TObject *obj)
226 {
227  return CheckObject(obj, TObject::Class());
228 }
229 
230 ////////////////////////////////////////////////////////////////////////////////
231 /// Check if the specified object of the specified class is already in
232 /// the buffer. Returns kTRUE if object already in the buffer,
233 /// kFALSE otherwise (also if obj is 0 ).
234 
235 Bool_t TBufferIO::CheckObject(const void *obj, const TClass *ptrClass)
236 {
237  if (!obj || !fMap || !ptrClass)
238  return kFALSE;
239 
240  TClass *clActual = ptrClass->GetActualClass(obj);
241 
242  ULong_t idx;
243 
244  if (clActual && (ptrClass != clActual)) {
245  const char *temp = (const char *)obj;
246  temp -= clActual->GetBaseClassOffset(ptrClass);
247  idx = (ULong_t)fMap->GetValue(Void_Hash(temp), (Long_t)temp);
248  } else {
249  idx = (ULong_t)fMap->GetValue(Void_Hash(obj), (Long_t)obj);
250  }
251 
252  return idx ? kTRUE : kFALSE;
253 }
254 
255 ////////////////////////////////////////////////////////////////////////////////
256 /// Retrieve the object stored in the buffer's object map at 'tag'
257 /// Set ptr and ClassPtr respectively to the address of the object and
258 /// a pointer to its TClass.
259 
260 void TBufferIO::GetMappedObject(UInt_t tag, void *&ptr, TClass *&ClassPtr) const
261 {
262  // original code in TBufferFile is wrong, fMap->GetSize() is just number of entries, cannot be used for tag checks
263 
264  // if (tag > (UInt_t)fMap->GetSize()) {
265  // ptr = nullptr;
266  // ClassPtr = nullptr;
267  // } else {
268  ptr = (void *)(Long_t)fMap->GetValue(tag);
269  ClassPtr = (TClass *)(Long_t)fClassMap->GetValue(tag);
270  // }
271 }
272 
273 ////////////////////////////////////////////////////////////////////////////////////
274 /// Returns tag for specified object from objects map (if exists)
275 /// Returns 0 if object not included into objects map
276 
277 Long64_t TBufferIO::GetObjectTag(const void *obj)
278 {
279  if (!obj || !fMap)
280  return 0;
281 
282  return fMap->GetValue(Void_Hash(obj), (Long_t)obj);
283 }
284 
285 ////////////////////////////////////////////////////////////////////////////////
286 /// Delete existing fMap and reset map counter.
287 
288 void TBufferIO::ResetMap()
289 {
290  if (fMap)
291  fMap->Delete();
292  if (fClassMap)
293  fClassMap->Delete();
294  fMapCount = 0;
295  fDisplacement = 0;
296 
297  // reset user bits
298  ResetBit(kUser1);
299  ResetBit(kUser2);
300  ResetBit(kUser3);
301 }
302 
303 ////////////////////////////////////////////////////////////////////////////////
304 /// Reset buffer object. Resets map and buffer offset
305 void TBufferIO::Reset()
306 {
307  SetBufferOffset();
308  ResetMap();
309 }
310 
311 ////////////////////////////////////////////////////////////////////////////////
312 /// This offset is used when a key (or basket) is transfered from one
313 /// file to the other. In this case the TRef and TObject might have stored a
314 /// pid index (to retrieve TProcessIDs) which referred to their order on the original
315 /// file, the fPidOffset is to be added to those values to correctly find the
316 /// TProcessID. This fPidOffset needs to be increment if the key/basket is copied
317 /// and need to be zero for new key/basket.
318 
319 void TBufferIO::SetPidOffset(UShort_t offset)
320 {
321  fPidOffset = offset;
322 }
323 
324 //---- Utilities for TStreamerInfo ----------------------------------------------
325 
326 ////////////////////////////////////////////////////////////////////////////////
327 /// force writing the TStreamerInfo to the file
328 
329 void TBufferIO::ForceWriteInfo(TVirtualStreamerInfo *info, Bool_t force)
330 {
331  if (info)
332  info->ForceWriteInfo((TFile *)GetParent(), force);
333 }
334 
335 ////////////////////////////////////////////////////////////////////////////////
336 /// Make sure TStreamerInfo is not optimized, otherwise it will not be
337 /// possible to support schema evolution in read mode.
338 /// In case the StreamerInfo has already been computed and optimized,
339 /// one must disable the option BypassStreamer.
340 
341 void TBufferIO::ForceWriteInfoClones(TClonesArray *a)
342 {
343  TStreamerInfo *sinfo = (TStreamerInfo *)a->GetClass()->GetStreamerInfo();
344  ForceWriteInfo(sinfo, kFALSE);
345 }
346 
347 ////////////////////////////////////////////////////////////////////////////////
348 /// Mark the classindex of the current file as using this TStreamerInfo
349 
350 void TBufferIO::TagStreamerInfo(TVirtualStreamerInfo *info)
351 {
352  TFile *file = (TFile *)GetParent();
353  if (file) {
354  TArrayC *cindex = file->GetClassIndex();
355  Int_t nindex = cindex->GetSize();
356  Int_t number = info->GetNumber();
357  if (number < 0 || number >= nindex) {
358  Error("TagStreamerInfo", "StreamerInfo: %s number: %d out of range[0,%d] in file: %s", info->GetName(), number,
359  nindex, file->GetName());
360  return;
361  }
362  if (cindex->fArray[number] == 0) {
363  cindex->fArray[0] = 1;
364  cindex->fArray[number] = 1;
365  }
366  }
367 }
368 
369 ////////////////////////////////////////////////////////////////////////////////
370 /// Interface to TStreamerInfo::ReadBufferClones.
371 
372 Int_t TBufferIO::ReadClones(TClonesArray *a, Int_t nobjects, Version_t objvers)
373 {
374  char **arr = (char **)a->GetObjectRef(0);
375  char **end = arr + nobjects;
376  // a->GetClass()->GetStreamerInfo()->ReadBufferClones(*this,a,nobjects,-1,0);
377  TStreamerInfo *info = (TStreamerInfo *)a->GetClass()->GetStreamerInfo(objvers);
378  // return info->ReadBuffer(*this,arr,-1,nobjects,0,1);
379  return ApplySequenceVecPtr(*(info->GetReadMemberWiseActions(kTRUE)), arr, end);
380 }
381 
382 ////////////////////////////////////////////////////////////////////////////////
383 /// Interface to TStreamerInfo::WriteBufferClones.
384 
385 Int_t TBufferIO::WriteClones(TClonesArray *a, Int_t nobjects)
386 {
387  char **arr = reinterpret_cast<char **>(a->GetObjectRef(0));
388  // a->GetClass()->GetStreamerInfo()->WriteBufferClones(*this,(TClonesArray*)a,nobjects,-1,0);
389  TStreamerInfo *info = (TStreamerInfo *)a->GetClass()->GetStreamerInfo();
390  // return info->WriteBufferAux(*this,arr,-1,nobjects,0,1);
391  char **end = arr + nobjects;
392  // No need to tell call ForceWriteInfo as it by ForceWriteInfoClones.
393  return ApplySequenceVecPtr(*(info->GetWriteMemberWiseActions(kTRUE)), arr, end);
394 }
395 
396 ////////////////////////////////////////////////////////////////////////////////
397 /// Return the last TProcessID in the file.
398 
399 TProcessID *TBufferIO::GetLastProcessID(TRefTable *reftable) const
400 {
401  TFile *file = (TFile *)GetParent();
402  // warn if the file contains > 1 PID (i.e. if we might have ambiguity)
403  if (file && !reftable->TestBit(TRefTable::kHaveWarnedReadingOld) && file->GetNProcessIDs() > 1) {
404  Warning("ReadBuffer", "The file was written during several processes with an "
405  "older ROOT version; the TRefTable entries might be inconsistent.");
406  reftable->SetBit(TRefTable::kHaveWarnedReadingOld);
407  }
408 
409  // the file's last PID is the relevant one, all others might have their tables overwritten
410  TProcessID *fileProcessID = TProcessID::GetProcessID(0);
411  if (file && file->GetNProcessIDs() > 0) {
412  // take the last loaded PID
413  fileProcessID = (TProcessID *)file->GetListOfProcessIDs()->Last();
414  }
415  return fileProcessID;
416 }
417 
418 ////////////////////////////////////////////////////////////////////////////////
419 /// The TProcessID with number pidf is read from file.
420 /// If the object is not already entered in the gROOT list, it is added.
421 
422 TProcessID *TBufferIO::ReadProcessID(UShort_t pidf)
423 {
424  TFile *file = (TFile *)GetParent();
425  if (!file) {
426  if (!pidf)
427  return TProcessID::GetPID(); // may happen when cloning an object
428  return nullptr;
429  }
430 
431  TProcessID *pid = nullptr;
432  {
433  R__LOCKGUARD_IMT(gInterpreterMutex); // Lock for parallel TTree I/O
434  pid = file->ReadProcessID(pidf);
435  }
436 
437  return pid;
438 }
439 
440 ////////////////////////////////////////////////////////////////////////////////
441 /// Return the exec id stored in the current TStreamerInfo element.
442 /// The execid has been saved in the unique id of the TStreamerElement
443 /// being read by TStreamerElement::Streamer.
444 /// The current element (fgElement) is set as a static global
445 /// by TStreamerInfo::ReadBuffer (Clones) when reading this TRef.
446 
447 UInt_t TBufferIO::GetTRefExecId()
448 {
449  return TStreamerInfo::GetCurrentElement()->GetUniqueID();
450 }
451 
452 ////////////////////////////////////////////////////////////////////////////////
453 /// Check if the ProcessID pid is already in the file.
454 /// If not, add it and return the index number in the local file list.
455 
456 UShort_t TBufferIO::WriteProcessID(TProcessID *pid)
457 {
458  TFile *file = (TFile *)GetParent();
459  if (!file)
460  return 0;
461  return file->WriteProcessID(pid);
462 }
463 
464 ////////////////////////////////////////////////////////////////////////////////
465 
466 namespace {
467 struct DynamicType {
468  // Helper class to enable typeid on any address
469  // Used in code similar to:
470  // typeid( * (DynamicType*) void_ptr );
471  virtual ~DynamicType() {}
472 };
473 } // namespace
474 
475 ////////////////////////////////////////////////////////////////////////////////
476 /// Write object to I/O buffer.
477 ///
478 /// This function assumes that the value in 'obj' is the value stored in
479 /// a pointer to a "ptrClass". The actual type of the object pointed to
480 /// can be any class derived from "ptrClass".
481 /// Return:
482 /// - 0: failure
483 /// - 1: success
484 /// - 2: truncated success (i.e actual class is missing. Only ptrClass saved.)
485 ///
486 /// If 'cacheReuse' is true (default) upon seeing an object address a second time,
487 /// we record the offset where its was written the first time rather than streaming
488 /// the object a second time.
489 /// If 'cacheReuse' is false, we always stream the object. This allows the (re)use
490 /// of temporary object to store different data in the same buffer.
491 
492 Int_t TBufferIO::WriteObjectAny(const void *obj, const TClass *ptrClass, Bool_t cacheReuse /* = kTRUE */)
493 {
494  if (!obj) {
495  WriteObjectClass(nullptr, nullptr, kTRUE);
496  return 1;
497  }
498 
499  if (!ptrClass) {
500  Error("WriteObjectAny", "ptrClass argument may not be 0");
501  return 0;
502  }
503 
504  TClass *clActual = ptrClass->GetActualClass(obj);
505 
506  if (clActual == 0 || clActual->GetState() == TClass::kForwardDeclared) {
507  // The ptrClass is a class with a virtual table and we have no
508  // TClass with the actual type_info in memory.
509 
510  DynamicType *d_ptr = (DynamicType *)obj;
511  Warning("WriteObjectAny", "An object of type %s (from type_info) passed through a %s pointer was truncated (due "
512  "a missing dictionary)!!!",
513  typeid(*d_ptr).name(), ptrClass->GetName());
514  WriteObjectClass(obj, ptrClass, cacheReuse);
515  return 2;
516  } else if (clActual && (clActual != ptrClass)) {
517  const char *temp = (const char *)obj;
518  temp -= clActual->GetBaseClassOffset(ptrClass);
519  WriteObjectClass(temp, clActual, cacheReuse);
520  return 1;
521  } else {
522  WriteObjectClass(obj, ptrClass, cacheReuse);
523  return 1;
524  }
525 }
526 
527 ////////////////////////////////////////////////////////////////////////////////
528 /// Write object to I/O buffer.
529 
530 void TBufferIO::WriteObject(const TObject *obj, Bool_t cacheReuse)
531 {
532  WriteObjectAny(obj, TObject::Class(), cacheReuse);
533 }
534 
535 //---- Static functions --------------------------------------------------------
536 
537 ////////////////////////////////////////////////////////////////////////////////
538 /// Set the initial size of the map used to store object and class
539 /// references during reading.
540 ///
541 /// The default size is kMapSize.
542 /// Increasing the default has the benefit that when reading many
543 /// small objects the array does not need to be resized too often
544 /// (the system is always dynamic, even with the default everything
545 /// will work, only the initial resizing will cost some time).
546 /// Per TBuffer object this option can be changed using SetReadParam().
547 
548 void TBufferIO::SetGlobalReadParam(Int_t mapsize)
549 {
550  fgMapSize = mapsize;
551 }
552 
553 ////////////////////////////////////////////////////////////////////////////////
554 /// Set the initial size of the map used to store object and class
555 /// references during reading.
556 ///
557 /// The default size is kMapSize.
558 /// Increasing the default has the benefit that when reading many
559 /// small objects the array does not need to be resized too often
560 /// (the system is always dynamic, even with the default everything
561 /// will work, only the initial resizing will cost some time).
562 /// Per TBuffer object this option can be changed using SetReadParam().
563 
564 void TBufferIO::SetGlobalWriteParam(Int_t mapsize)
565 {
566  fgMapSize = mapsize;
567 }
568 
569 ////////////////////////////////////////////////////////////////////////////////
570 /// Get default read map size.
571 
572 Int_t TBufferIO::GetGlobalReadParam()
573 {
574  return fgMapSize;
575 }
576 
577 ////////////////////////////////////////////////////////////////////////////////
578 /// Get default write map size.
579 
580 Int_t TBufferIO::GetGlobalWriteParam()
581 {
582  return fgMapSize;
583 }