Logo ROOT  
Reference Guide
TStreamerInfo.cxx
Go to the documentation of this file.
1// @(#)root/io:$Id$
2// Author: Rene Brun 12/10/2000
3
4/*************************************************************************
5 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. *
6 * All rights reserved. *
7 * *
8 * For the licensing terms see $ROOTSYS/LICENSE. *
9 * For the list of contributors see $ROOTSYS/README/CREDITS. *
10 *************************************************************************/
11
12/** \class TStreamerInfo
13 \ingroup IO
14
15Describes a persistent version of a class.
16
17A ROOT file contains the list of TStreamerInfo objects for all the
18class versions written to this file.
19When reading a file, all the TStreamerInfo objects are read back in
20memory and registered to the TClass list of TStreamerInfo.
21One can see the list and contents of the TStreamerInfo on a file
22with, e.g.,
23~~~{.cpp}
24 TFile f("myfile.root");
25 f.ShowStreamerInfo();
26~~~
27A TStreamerInfo is a list of TStreamerElement objects (one per data
28member or base class).
29When streaming an object, the system (TClass) loops on all the
30TStreamerElement objects and calls the appropriate function for each
31element type.
32*/
33
34#include "TStreamerInfo.h"
35#include "TFile.h"
36#include "TROOT.h"
37#include "TClonesArray.h"
38#include "TStreamerElement.h"
39#include "TClass.h"
40#include "TClassEdit.h"
41#include "TClassTable.h"
42#include "TDataMember.h"
43#include "TDataType.h"
44#include "TRealData.h"
45#include "TBaseClass.h"
46#include "TBuffer.h"
47#include "TArrayC.h"
48#include "TArrayI.h"
49#include "TArrayF.h"
50#include "TArrayD.h"
51#include "TArrayS.h"
52#include "TArrayL.h"
53#include "TError.h"
54#include "TEnum.h"
55#include "TRef.h"
56#include "TProcessID.h"
57#include "TSystem.h"
58#include "TObjString.h"
59#include "snprintf.h"
60
61#include "TStreamer.h"
65#include "TInterpreter.h"
66
67#include "TMemberInspector.h"
68
69#include "TMakeProject.h"
70
71#include "TSchemaRuleSet.h"
72#include "TSchemaRule.h"
73
74#include "TVirtualMutex.h"
75
77
78#include <memory>
79#include <array>
80
81std::atomic<Int_t> TStreamerInfo::fgCount{0};
82
83const Int_t kMaxLen = 1024;
84
86
87static void R__TObjArray_InsertAt(TObjArray *arr, TObject *obj, Int_t at)
88{
89 // Slide by one.
90 Int_t last = arr->GetLast();
91 arr->AddAtAndExpand(arr->At(last),last+1);
92 for(Int_t ind = last-1; ind >= at; --ind) {
93 arr->AddAt( arr->At(ind), ind+1);
94 };
95 arr->AddAt( obj, at);
96}
97
98static void R__TObjArray_InsertAt(TObjArray *arr, std::vector<TStreamerArtificial*> &objs, Int_t at)
99{
100 // Slide by enough.
101 Int_t offset = objs.size();
102 Int_t last = arr->GetLast();
103 arr->AddAtAndExpand(arr->At(last),last+offset);
104 for(Int_t ind = last-1; ind >= at; --ind) {
105 arr->AddAt( arr->At(ind), ind+offset);
106 };
107 for(size_t ins = 0; ins < objs.size(); ++ins) {
108 arr->AddAt(objs[ins], at+ins);
109 }
110}
111
112static void R__TObjArray_InsertAfter(TObjArray *arr, TObject *newobj, TObject *oldobj)
113{
114 // Slide by one.
115 Int_t last = arr->GetLast();
116 Int_t at = 0;
117 while (at<last && arr->At(at) != oldobj) {
118 ++at;
119 }
120 ++at; // we found the object, insert after it
121 R__TObjArray_InsertAt(arr, newobj, at);
122}
123
124static void R__TObjArray_InsertBefore(TObjArray *arr, TObject *newobj, TObject *oldobj)
125{
126 // Slide by one.
127 Int_t last = arr->GetLast();
128 Int_t at = 0;
129 while (at<last && arr->At(at) != oldobj) {
130 ++at;
131 }
132 R__TObjArray_InsertAt(arr, newobj, at);
133}
134
135enum class EUniquePtrOffset : char
136 {
137 kNA = 0,
138 kZero = 1,
139 kNonZero = 2
140 };
141
142////////////////////////////////////////////////////////////////////////////////
143/// Default ctor.
144
146{
147 fNumber = -2;
148 fClass = 0;
149 fElements = 0;
150 fComp = 0;
151 fCompFull = 0;
152 fCompOpt = 0;
153 fCheckSum = 0;
154 fNdata = 0;
155 fNfulldata= 0;
156 fNslots = 0;
157 fSize = 0;
158 fClassVersion = 0;
160 fOldVersion = Class()->GetClassVersion();
162 fVirtualInfoLoc = 0;
163
164 fReadObjectWise = 0;
165 fReadMemberWise = 0;
167 fReadText = 0;
171 fWriteText = 0;
172}
173
174////////////////////////////////////////////////////////////////////////////////
175/// Create a TStreamerInfo object.
176
179{
180 fgCount++;
182 fClass = cl;
183 fElements = new TObjArray();
184 fComp = 0;
185 fCompFull = 0;
186 fCompOpt = 0;
187 fCheckSum = 0;
188 fNdata = 0;
189 fNfulldata= 0;
190 fNslots = 0;
191 fSize = 0;
194 fOldVersion = Class()->GetClassVersion();
196 fVirtualInfoLoc = 0;
197
198 fReadObjectWise = 0;
199 fReadMemberWise = 0;
201 fReadText = 0;
205 fWriteText = 0;
206}
207
208////////////////////////////////////////////////////////////////////////////////
209/// TStreamerInfo dtor.
210
212{
213 // Note: If a StreamerInfo is loaded from a file and is the same information
214 // as an existing one, it is assigned the same "unique id" and we need to double
215 // check before removing it from the global list.
216 if (fNumber >=0 && gROOT->GetListOfStreamerInfo()->At(fNumber) == this)
217 gROOT->GetListOfStreamerInfo()->RemoveAt(fNumber);
218
219 delete [] fComp; fComp = 0;
220 delete [] fCompFull; fCompFull = 0;
221 delete [] fCompOpt; fCompOpt = 0;
222 delete [] fVirtualInfoLoc; fVirtualInfoLoc =0;
223
224 delete fReadObjectWise;
225 delete fReadMemberWise;
227 delete fReadText;
228 delete fWriteObjectWise;
229 delete fWriteMemberWise;
231 delete fWriteText;
232
233 if (!fElements) return;
234 fElements->Delete();
235 delete fElements; fElements=0;
236}
237
238////////////////////////////////////////////////////////////////////////////////
239/// Makes sure kBuildOldUsed set once Build or BuildOld finishes.
240/// Makes sure kBuildRunning reset once Build finishes.
241
242namespace {
243 struct TPreventRecursiveBuildGuard {
244 TPreventRecursiveBuildGuard(TStreamerInfo* info): fInfo(info) {
245 fInfo->SetBit(TStreamerInfo::kBuildRunning);
246 fInfo->SetBit(TStreamerInfo::kBuildOldUsed);
247 }
248 ~TPreventRecursiveBuildGuard() {
249 fInfo->ResetBit(TStreamerInfo::kBuildOldUsed);
250 fInfo->ResetBit(TStreamerInfo::kBuildRunning);
251 }
252 TStreamerInfo* fInfo;
253 };
254
255}
256
257////////////////////////////////////////////////////////////////////////////////
258/// Build the I/O data structure for the current class version.
259///
260/// A list of TStreamerElement derived classes is built by scanning
261/// one by one the list of data members of the analyzed class.
263{
264 // Did another thread already do the work?
265 if (fIsCompiled) return;
266
268
269 // Did another thread already do the work while we were waiting ..
270 if (fIsCompiled) return;
271
272 // Has Build already been run?
273 if (fIsBuilt) return;
274
275 // Are we recursing on ourself?
277
278 // This is used to avoid unwanted recursive call to Build or BuildOld.
279 TPreventRecursiveBuildGuard buildGuard(this);
280
281 if (fClass->GetCollectionProxy()) {
283 TString title;
284 if (proxy->GetValueClass()) {
285 title.Form("<%s%s> Used to call the proper TStreamerInfo case",proxy->GetValueClass()->GetName(),proxy->HasPointers() ? "*" : "");
286 } else {
287 title .Form("<%s%s> Used to call the proper TStreamerInfo case",TDataType::GetTypeName(proxy->GetType()),proxy->HasPointers() ? "*" : "");
288 }
289 TStreamerElement* element = new TStreamerSTL("This", title.Data(), 0, fClass->GetName(), *proxy, 0);
290 fElements->Add(element);
291 Compile();
293 fIsBuilt = kTRUE;
294 return;
295 }
296
297 // Warn on read/write of RVec (see 6.24 release notes)
298 if (strncmp(GetName(), "ROOT::VecOps::RVec<", 19) == 0) {
299 Warning("Build", "Due to some major, backward-incompatible improvements planned for ROOT::RVec, direct I/O of "
300 "ROOT::RVec objects will break between v6.24 and v6.26. Please use std::vectors instead. See "
301 "the release notes of v6.24 for more information.");
302 }
303
304 TStreamerElement::Class()->IgnoreTObjectStreamer();
305
306 fClass->BuildRealData(nullptr, isTransient);
307
309
310 Bool_t needAllocClass = kFALSE;
311 Bool_t wasCompiled = fComp != 0;
312 ROOT::TSchemaRuleSet::TMatches rules;
313 if (fClass->GetSchemaRules()) {
315 }
316
317 //
318 // Iterate over base classes.
319 //
320
321 // ROOT-9808: Here we skip the investigations of the base classes in case
322 // this is a pair, otherwise, on some STL implementations, it can happen that
323 // pair has mother classes which are an internal implementation detail and
324 // would result in bogus messages printed on screen.
326 const bool isCollection = fClass->GetCollectionProxy();
327 const bool isString = !strcmp(fClass->GetName(), "string");
328 TBaseClass* base = 0;
329 TIter nextb(fClass->GetListOfBases());
330 while ((base = (TBaseClass*)nextb())) {
331 TStreamerElement* element = 0;
332 Int_t offset = base->GetDelta();
333 if (offset == kMissing) {
334 continue;
335 }
336 if (offset == kNeedObjectForVirtualBaseClass) {
337 if (!isTransient)
338 Error("Build()", "Cannot stream virtual base %s of class %s",
339 base->GetName(), fClass->GetName());
340 continue;
341 }
342 const char* bname = base->GetName();
343 const char* btitle = base->GetTitle();
344 // this case appears with STL collections as base class.
345 if (!strcmp(bname, "string")) {
346 element = new TStreamerSTLstring(bname, btitle, offset, bname, kFALSE);
347 } else if (base->IsSTLContainer()) {
349 if (proxy) element = new TStreamerSTL(bname, btitle, offset, bname, *proxy, kFALSE);
350 else element = new TStreamerSTL(bname, btitle, offset, bname, 0, kFALSE);
351 if (fClass->IsLoaded() && ((TStreamerSTL*)element)->GetSTLtype() != ROOT::kSTLvector) {
352 if (!element->GetClassPointer()->IsLoaded()) {
353 if (!isTransient)
354 Error("Build","The class \"%s\" is compiled and its base class \"%s\" is a collection and we do not have a dictionary for it, we will not be able to read or write this base class.",GetName(),bname);
355 delete element;
356 continue;
357 }
358 }
359 } else {
360 element = new TStreamerBase(bname, btitle, offset, isTransient);
361 TClass* clm = element->GetClassPointer();
362 if (!clm) {
363 // We have no information about the class yet, except that since it
364 // is a base class, we know it is a class. So let's create it (in v5
365 // it would have been created as a side effect of the dictionary of
366 // for the derived class having a forward declaration of the base class).
367 clm = new TClass(bname,1,TClass::kForwardDeclared, true /*silent*/);
368 Warning("Build", "%s: base class %s has no streamer or dictionary it will not be saved", GetName(), clm->GetName());
369 element->Init(0);
370 } else {
371 // Now part of the TStreamerBase constructor.
372 // clm->GetStreamerInfo();
373 if ((clm == TObject::Class()) && fClass->CanIgnoreTObjectStreamer()) {
374 // -- An ignored TObject base class.
375 // Note: The TClass kIgnoreTObjectStreamer == BIT(15), but
376 // the TStreamerInfo kIgnoreTobjectStreamer == BIT(13) which
377 // is confusing.
379 // Flag the element to be ignored by setting its type to -1.
380 // This flag will be used later by Compile() to prevent this
381 // element from being inserted into the compiled info.
382 element->SetType(-1);
383 }
384 if (!isTransient && !clm->IsLoaded() && !(isCollection || isString)) {
385 // Don't complain about the base classes of collections nor of
386 // std::string.
387 Warning("Build", "%s: base class %s has no streamer or dictionary it will not be saved", GetName(), clm->GetName());
388 }
389 }
390 }
391 if (element) {
392 fElements->Add(element);
393 }
394 } // end of base class loop
395 }
396
397 //
398 // Iterate over data members.
399 //
400
401 Int_t dsize;
402 TDataMember* dm = 0;
403 std::string typeNameBuf;
404 std::string trueTypeNameBuf;
406 while ((dm = (TDataMember*) nextd())) {
407 if (fClass->GetClassVersion() == 0) {
408 continue;
409 }
410 if (!dm->IsPersistent()) {
411 continue;
412 }
413 if (dm->Property() & kIsUnionMember) {
414 continue;
415 }
416 TMemberStreamer* streamer = 0;
417 Int_t offset = GetDataMemberOffset(dm, streamer);
418 if (offset == kMissing) {
419 continue;
420 }
421 TStreamerElement* element = 0;
422 dsize = 0;
423
424 // Save some useful variables
425 const char* dmName = dm->GetName();
426 const char* dmTitle = dm->GetTitle();
427 const char* dmType = dm->GetTypeName();
428 const char* dmFull = dm->GetTrueTypeName(); // Used to be GetFullTypeName ...
429 Bool_t dmIsPtr = dm->IsaPointer();
430 TDataType* dt(nullptr);
431 Int_t ndim = dm->GetArrayDim();
432 std::array<Int_t, 5> maxIndices; // 5 is the maximum supported in TStreamerElement::SetMaxIndex
433 Bool_t isStdArray(kFALSE);
434
435 // Let's treat the unique_ptr case
436 bool nameChanged;
437 trueTypeNameBuf = typeNameBuf = TClassEdit::GetNameForIO(dmFull, TClassEdit::EModType::kNone, &nameChanged);
438 if (nameChanged) {
439 if (TClassEdit::IsUniquePtr(dmFull)) {
440 dmIsPtr = true;
441 }
442 while(typeNameBuf.back() == '*') typeNameBuf.pop_back();
443 dmFull = trueTypeNameBuf.c_str();
444 dmType = typeNameBuf.c_str();
445 }
446 if ((isStdArray = TClassEdit::IsStdArray(dmType))){ // We tackle the std array case
448 typeNameBuf,
449 maxIndices,
450 ndim);
451 trueTypeNameBuf = typeNameBuf;
452 while(typeNameBuf.back() == '*') typeNameBuf.pop_back();
453 dmFull = dmType = typeNameBuf.c_str();
454 dt = gROOT->GetType(dmType);
455 }
456
457 TDataMember* dmCounter = 0;
458 if (dmIsPtr) {
459 //
460 // look for a pointer data member with a counter
461 // in the comment string, like so:
462 //
463 // int n;
464 // double* MyArray; //[n]
465 //
466 const char* lbracket = TVirtualStreamerInfo::GetElementCounterStart(dmTitle);
467 const char* rbracket = ::strchr(dmTitle, ']');
468 if (lbracket && rbracket) {
469 const char* counterName = dm->GetArrayIndex();
470 TRealData* rdCounter = (TRealData*) fClass->GetListOfRealData()->FindObject(counterName);
471 if (!rdCounter || rdCounter->TestBit(TRealData::kTransient)) {
472 if (!isTransient)
473 Error("Build", "%s, discarding: %s %s, illegal %s\n", GetName(), dmFull, dmName, dmTitle);
474 continue;
475 }
476 dmCounter = rdCounter->GetDataMember();
477 TDataType* dtCounter = dmCounter->GetDataType();
478 Bool_t isInteger = dtCounter && ((dtCounter->GetType() == 3) || (dtCounter->GetType() == 13));
479 if (!dtCounter || !isInteger) {
480 if (!isTransient)
481 Error("Build", "%s, discarding: %s %s, illegal [%s] (must be Int_t)\n", GetName(), dmFull, dmName, counterName);
482 continue;
483 }
484 TStreamerBasicType* bt = TStreamerInfo::GetElementCounter(counterName, dmCounter->GetClass());
485 if (!bt) {
486 if (dmCounter->GetClass()->Property() & kIsAbstract) {
487 continue;
488 }
489 if (!isTransient)
490 Error("Build", "%s, discarding: %s %s, illegal [%s] must be placed before \n", GetName(), dmFull, dmName, counterName);
491 continue;
492 }
493 }
494 }
495 if (!dt && !isStdArray) dt = dm->GetDataType();
496 if (dt) {
497 // found a basic type
498 Int_t dtype = dt->GetType();
499 dsize = dt->Size();
500 if (!dmCounter && (strstr(dmFull, "char*") || strstr(dmFull, "Char_t*"))) {
501 dtype = kCharStar;
502 dsize = sizeof(char*);
503 }
504 if (dtype == kOther_t || dtype == kNoType_t) {
505 if (!isTransient)
506 Error("Build", "%s, unknown type: %s %s", GetName(), dmFull, dmName);
507 continue;
508 } else if (dmIsPtr && (dtype != kCharStar)) {
509 if (dmCounter) {
510 // data member is pointer to an array of basic types
511 element = new TStreamerBasicPointer(dmName, dmTitle, offset, dtype, dm->GetArrayIndex(), dmCounter->GetClass()->GetName(), dmCounter->GetClass()->GetClassVersion(), dmFull);
512 } else {
513 if ((fName == "TString") || (fName == "TClass")) {
514 continue;
515 }
516 if (!isTransient)
517 Error("Build", "%s, discarding: %s %s, no [dimension]\n", GetName(), dmFull, dmName);
518 continue;
519 }
520 } else {
521 // data member is a basic type
522 if ((fClass == TObject::Class()) && !strcmp(dmName, "fBits")) {
523 //printf("found fBits, changing dtype from %d to 15\n", dtype);
524 dtype = kBits;
525 }
526 // Here we treat data members such as int, float, double[4]
527 element = new TStreamerBasicType(dmName, dmTitle, offset, dtype, dmFull);
528 }
529 } else {
530 // try STL container or string
531 static const char* full_string_name = "basic_string<char,char_traits<char>,allocator<char> >";
532 if (!strcmp(dmType, "string") || !strcmp(dmType, "std::string") || !strcmp(dmType, full_string_name)) {
533 element = new TStreamerSTLstring(dmName, dmTitle, offset, dmFull, dmIsPtr);
534 } else if (dm->IsSTLContainer()) {
535 TVirtualCollectionProxy *proxy = TClass::GetClass(dmType /* the underlying type */)->GetCollectionProxy();
536 if (proxy) element = new TStreamerSTL(dmName, dmTitle, offset, dmFull, *proxy, dmIsPtr);
537 else element = new TStreamerSTL(dmName, dmTitle, offset, dmFull, dmFull, dmIsPtr);
538 bool hasCustomAlloc = proxy ? proxy->GetProperties() & TVirtualCollectionProxy::kCustomAlloc : kFALSE;
539 if (((TStreamerSTL*)element)->GetSTLtype() != ROOT::kSTLvector || hasCustomAlloc) {
540 auto printErrorMsg = [&](const char* category)
541 {
542 if (!isTransient)
543 Error("Build","The class \"%s\" is %s and for its data member \"%s\" we do not have a dictionary for the collection \"%s\". Because of this, we will not be able to read or write this data member.",GetName(), category, dmName, dmType);
544 };
545 if (fClass->IsLoaded()) {
546 if (!element->GetClassPointer()->IsLoaded()) {
547 printErrorMsg("compiled");
548 delete element;
549 continue;
550 }
551 } else if (fClass->GetState() == TClass::kInterpreted) {
553 printErrorMsg("interpreted");
554 delete element;
555 continue;
556 }
557 }
558 }
559 } else {
560 TClass* clm = TClass::GetClass(dmType);
561 if (!clm) {
562 if (!isTransient)
563 Error("Build", "%s, unknown type: %s %s\n", GetName(), dmFull, dmName);
564 continue;
565 }
566 if (isStdArray) {
567 // We do not want to rebuild the streamerinfo of an std::array<T,N> asking the dm->GetUnitSize(), but rather of T only.
568
569 dsize = clm->Size();
570 }
571 if (dmIsPtr) {
572 // a pointer to a class
573 if (dmCounter) {
574 element = new TStreamerLoop(dmName, dmTitle, offset, dm->GetArrayIndex(), dmCounter->GetClass()->GetName(), dmCounter->GetClass()->GetClassVersion(), dmFull);
575 } else {
576 if (clm->IsTObject()) {
577 element = new TStreamerObjectPointer(dmName, dmTitle, offset, dmFull);
578 } else {
579 element = new TStreamerObjectAnyPointer(dmName, dmTitle, offset, dmFull);
580 if (!isTransient && !streamer && !clm->GetStreamer() && !clm->IsLoaded() && !clm->fIsSyntheticPair) {
581 Error("Build", "%s: %s has no streamer or dictionary, data member %s will not be saved",
582 GetName(), dmFull, dmName);
583 }
584 }
585 }
586 } else if (clm->IsTObject()) {
587 element = new TStreamerObject(dmName, dmTitle, offset, dmFull);
588 } else if ((clm == TString::Class()) && !dmIsPtr) {
589 element = new TStreamerString(dmName, dmTitle, offset);
590 } else {
591 element = new TStreamerObjectAny(dmName, dmTitle, offset, dmFull);
592 if (!isTransient && !streamer && !clm->GetStreamer() && !clm->IsLoaded() && !clm->fIsSyntheticPair) {
593 Warning("Build", "%s: %s has no streamer or dictionary, data member \"%s\" will not be saved",
594 GetName(), dmFull, dmName);
595 }
596 }
597 }
598 }
599 if (!element) {
600 // If we didn't make an element, there is nothing to do.
601 continue;
602 }
603 if (!dsize) {
604 dsize = dm->GetUnitSize();
605 }
606 for (Int_t i = 0; i < ndim; ++i) {
607 auto maxIndex = 0;
608 if (isStdArray) maxIndex = maxIndices[i];
609 else maxIndex = dm->GetMaxIndex(i);
610 element->SetMaxIndex(i, maxIndex);
611 }
612 element->SetArrayDim(ndim);
613 // If the datamember was a int[4] this is 4, if double[3][2] 3*2=6
614 Int_t narr = element->GetArrayLength();
615 if (!narr) {
616 narr = 1;
617 }
618 element->SetSize(dsize*narr);
619 element->SetStreamer(streamer);
620 if (!streamer) {
621 Int_t k = element->GetType();
622 if (k == kStreamer) {
623 //if ((k == kSTL) || (k == kSTL + kOffsetL) || (k == kStreamer) || (k == kStreamLoop))
624 element->SetType(-1);
625 }
626 }
627
628 if ( !wasCompiled && (rules && rules.HasRuleWithSource( element->GetName(), kTRUE )) ) {
629 needAllocClass = kTRUE;
630
631 // If this is optimized to re-use TStreamerElement(s) in case of variable renaming,
632 // then we must revisit the code in TBranchElement::InitInfo that recalculate the
633 // fID (i.e. the index of the TStreamerElement to be used for streaming).
634
635 TStreamerElement *cached = element;
636 // Now that we are caching the unconverted element, we do not assign it to the real type even if we could have!
637 if (element->GetNewType()>0 /* intentionally not including base class for now */
638 && rules && !rules.HasRuleWithTarget( element->GetName(), kTRUE ) )
639 {
640 TStreamerElement *copy = (TStreamerElement*)element->Clone();
641 fElements->Add(copy);
643 cached = copy;
644
645 // Warning("BuildOld","%s::%s is not set from the version %d of %s (You must add a rule for it)\n",GetName(), element->GetName(), GetClassVersion(), GetName() );
646 } else {
647 // If the element is just cached and not repeat, we need to inject an element
648 // to insure the writing.
649 TStreamerElement *writecopy = (TStreamerElement*)element->Clone();
650 fElements->Add(element);
652 writecopy->SetNewType( writecopy->GetType() );
653 writecopy->SetOffset( element->GetOffset() );
654 // Put the write element after the read element (that does caching).
655 element = writecopy;
656 }
658 cached->SetNewType( cached->GetType() );
659 }
660
661 fElements->Add(element);
662 } // end of member loop
663
664 // Now add artificial TStreamerElement (i.e. rules that creates new members or set transient members).
666
667 if (needAllocClass) {
669 if (!infoalloc) {
670 if (!isTransient)
671 Error("Build","Could you create a TStreamerInfo for %s\n",TString::Format("%s@@%d",GetName(),GetClassVersion()).Data());
672 } else {
673 // Tell clone we should rerun BuildOld
674 infoalloc->SetBit(kBuildOldUsed,false);
675 // Temporarily mark it as built to avoid the BuildCheck from removing
676 // Technically we only need to do this for the 'current' StreamerInfo
677 fIsBuilt = kTRUE;
678 infoalloc->BuildCheck();
679 infoalloc->BuildOld();
681 TClass *allocClass = infoalloc->GetClass();
682
683 {
684 TIter next(fElements);
685 TStreamerElement* element;
686 while ((element = (TStreamerElement*) next())) {
687 if (element->TestBit(TStreamerElement::kRepeat) && element->IsaPointer()) {
688 TStreamerElement *other = (TStreamerElement*) infoalloc->GetElements()->FindObject(element->GetName());
689 if (other) {
691 }
692 }
693 }
694 infoalloc->GetElements()->Compress();
695 }
696 {
697 TIter next(fElements);
698 TStreamerElement* element;
699 while ((element = (TStreamerElement*) next())) {
700 if (element->TestBit(TStreamerElement::kCache)) {
701 element->SetOffset(infoalloc->GetOffset(element->GetName()));
702 }
703 }
704 }
705
706 TStreamerElement *el = new TStreamerArtificial("@@alloc","", 0, TStreamerInfo::kCacheNew, allocClass->GetName());
708
709 el = new TStreamerArtificial("@@dealloc","", 0, TStreamerInfo::kCacheDelete, allocClass->GetName());
710 fElements->Add( el );
711 }
712 }
713
714 //
715 // Make a more compact version.
716 //
717 Compile();
718 fIsBuilt = kTRUE;
719}
720
721////////////////////////////////////////////////////////////////////////////////
722/// Check if built and consistent with the class dictionary.
723/// This method is called by TFile::ReadStreamerInfo.
724
725void TStreamerInfo::BuildCheck(TFile *file /* = 0 */, Bool_t load /* = kTRUE */)
726{
728
730 if (!fClass) {
731 // fClassVersion should have been a Version_t and/or Version_t
732 // should have been an Int_t. Changing the on-file format
733 // of the StreamerInfo is 'hard' (for forward compatibility), so
734 // leave it as is for now.
737
738 // Case of a custom collection (the user provided a CollectionProxy
739 // for a class that is not an STL collection).
740 if (GetElements()->GetEntriesFast() == 1) {
741 TObject *element = GetElements()->UncheckedAt(0);
742 Bool_t isstl = element && strcmp("This",element->GetName())==0;
743 if (isstl) {
744 if (element->GetTitle()[0] == '<') {
745 // We know the content.
746 TString content = element->GetTitle();
747 Int_t level = 1;
748 for(Int_t c = 1; c < content.Length(); ++c) {
749 if (content[c] == '<') ++level;
750 else if (content[c] == '>') --level;
751 if (level == 0) {
752 content.Remove(c+1);
753 break;
754 }
755 }
756 content.Prepend("vector");
757 TClass *clequiv = TClass::GetClass(content);
759 if (gDebug > 1)
760 Info("BuildCheck",
761 "Update the collection proxy of the class \"%s\" \n"
762 "\tto be similar to \"%s\".",
763 GetName(),content.Data());
764 fClass->CopyCollectionProxy( *proxy );
765 } else {
766 Warning("BuildCheck", "\n\
767 The class %s had a collection proxy when written but it is not an STL\n \
768 collection and we did not record the type of the content of the collection.\n \
769 We will claim the content is a bool (i.e. no data will be read).",
770 GetName());
771 }
772 }
773 }
774
775 } else {
777 const bool isOldRVec = fClass->GetCollectionType() == ROOT::kROOTRVec && (fElements->GetEntries() == 1) &&
778 !strcmp(fElements->At(0)->GetName(), "fData");
779 if (!isOldRVec && TClassEdit::IsSTLCont(fClass->GetName())) {
780 // We have a collection that is indeed an STL collection,
781 // we know we don't need its streamerInfo.
783 return;
784 }
785 }
787 // The format never change, no need to import the old StreamerInfo
788 // (which anyway would have issue when being setup due to the lack
789 // of TDataMember in the TClass.
791 return;
792 }
793
794 if (0 == strcmp("string",fClass->GetName())) {
795 // We know we do not need any offset check for a string
797 return;
798 }
799
800 const TObjArray *array = fClass->GetStreamerInfos();
801 TStreamerInfo* info = 0;
802
803 if (fClass->TestBit(TClass::kIsEmulation) && array->IsEmpty()) {
804 // We have an emulated class that has no TStreamerInfo, this
805 // means it was created to insert a (default) rule. Consequently
806 // the error message about the missing dictionary was not printed.
807 // For consistency, let's print it now!
808
809 ::Warning("TClass::TClass", "no dictionary for class %s is available", GetName());
810 }
811
812 // Case of a custom collection (the user provided a CollectionProxy
813 // for a class that is not an STL collection).
814 if (GetElements()->GetEntriesFast() == 1) {
815 TObject *element = GetElements()->UncheckedAt(0);
816 Bool_t isstl = element && strcmp("This",element->GetName())==0;
817 if (isstl && !fClass->GetCollectionProxy()) {
818 if (element->GetTitle()[0] == '<') {
819 // We know the content.
820 TString content = element->GetTitle();
821 Int_t level = 1;
822 for(Int_t c = 1; c < content.Length(); ++c) {
823 if (content[c] == '<') ++level;
824 else if (content[c] == '>') --level;
825 if (level == 0) {
826 content.Remove(c+1);
827 break;
828 }
829 }
830 content.Prepend("vector");
831 TClass *clequiv = TClass::GetClass(content);
833 if (gDebug > 1)
834 Info("BuildCheck",
835 "Update the collection proxy of the class \"%s\" \n"
836 "\tto be similar to \"%s\".",
837 GetName(),content.Data());
838 fClass->CopyCollectionProxy( *proxy );
839 } else {
840 Warning("BuildCheck", "\n\
841 The class %s had a collection proxy when written but it is not an STL\n \
842 collection and we did not record the type of the content of the collection.\n \
843 We will claim the content is a bool (i.e. no data will be read).",
844 GetName());
845 }
847 return;
848 }
849 }
850
851 // If the user has not specified a class version (this _used to_
852 // always be the case when the class is Foreign) or if the user
853 // has specified a version to be explicitly 1. [We can not
854 // distinguish the two cases using the information in the "on
855 // file" StreamerInfo.]
856
857 Bool_t searchOnChecksum = kFALSE;
858 if (fClass->IsLoaded() && fClass->GetClassVersion() >= 2) {
859 // We know for sure that the user specified the version.
860
861 if (fOnFileClassVersion >= 2) {
862 // The class version was specified when the object was
863 // written
864
865 searchOnChecksum = kFALSE;
866
867 } else {
868 // The class version was not specified when the object was
869 // written OR it was specified to be 1.
870
871 searchOnChecksum = kTRUE;
872 }
873 } else if (fClass->IsLoaded() && !fClass->IsForeign()) {
874 // We are in the case where the class has a Streamer function.
875 // and fClass->GetClassVersion is 1, we still assume that the
876 // Class Version is specified (to be one).
877
878 searchOnChecksum = kFALSE;
879
880 } else if (fClass->IsLoaded() /* implied: && fClass->IsForeign() */ ) {
881 // We are in the case of a Foreign class with no specified
882 // class version.
883
884 searchOnChecksum = kTRUE;
885
886 }
887 else {
888 // We are in the case of an 'emulated' class.
889
890 if (fOnFileClassVersion >= 2) {
891 // The class version was specified when the object was
892 // written
893
894 searchOnChecksum = kFALSE;
895
896 } else {
897 // The class version was not specified when the object was
898 // written OR it was specified to be 1.
899
900 searchOnChecksum = kTRUE;
901
902 TStreamerInfo* v1 = (TStreamerInfo*) array->At(1);
903 if (v1) {
904 if (fCheckSum != v1->GetCheckSum()) {
905 fClassVersion = array->GetLast() + 1;
906 }
907 }
908 }
909 }
910
911 if (!searchOnChecksum) {
912 if (fClassVersion < (array->GetEntriesFast() - 1)) {
913 info = (TStreamerInfo*) array->At(fClassVersion);
914 }
915 } else {
916 Int_t ninfos = array->GetEntriesFast() - 1;
917 for (Int_t i = -1; i < ninfos; ++i) {
918 info = (TStreamerInfo*) array->UncheckedAt(i);
919 if (!info) {
920 continue;
921 }
922 if (fCheckSum == info->GetCheckSum() && (info->GetOnFileClassVersion()==1 || info->GetOnFileClassVersion()==0)) {
923 // We must match on the same checksum, an existing TStreamerInfo
924 // for one of the 'unversioned' class layout (i.e. version was 1).
925 fClassVersion = i;
926 break;
927 }
928 info = 0;
929 }
930 if (info==0) {
931 // Find an empty slot.
932 ninfos = array->GetEntriesFast() - 1;
933 Int_t slot = 1; // Start of Class version 1.
934 while ((slot < ninfos) && (array->UncheckedAt(slot) != 0)) {
935 ++slot;
936 }
937 fClassVersion = slot;
938 }
939 }
940
941 // NOTE: Should we check if the already existing info is the same as
942 // the current one? Yes
943 // In case a class (eg Event.h) has a TClonesArray of Tracks, it could be
944 // that the old info does not have the class name (Track) in the data
945 // member title. Set old title to new title
946 if (info) {
947 // We found an existing TStreamerInfo for our ClassVersion
948 Bool_t match = kTRUE;
949 Bool_t done = kFALSE;
950 Bool_t oldIsNonVersioned = kFALSE;
951 if (fClassVersion!=0 && !fClass->TestBit(TClass::kWarned) && (fClassVersion == info->GetClassVersion()) && (fCheckSum != info->GetCheckSum())) {
952 // The TStreamerInfo's checksum is different from the checksum for the compile class.
953
954 match = kFALSE;
955 oldIsNonVersioned = info->fOnFileClassVersion==1 && info->fClassVersion != 1;
956
958 // In the case where the read-in TStreamerInfo does not
959 // match in the 'current' in memory TStreamerInfo for
960 // a non foreign class (we can not get here if this is
961 // a foreign class so we do not need to test it),
962 // we need to add this one more test since the CINT behaviour
963 // with enums changed over time, so verify the checksum ignoring
964 // members of type enum. We also used to not count the //[xyz] comment
965 // in the checksum, so test for that too.
968 )
969 {
970 match = kTRUE;
971 }
972 if (fOldVersion <= 2) {
973 // Names of STL base classes was modified in vers==3. Allocators removed
974 // (We could be more specific (see test for the same case below)
975 match = kTRUE;
976 }
977 if (!match && CompareContent(0,info,kFALSE,kFALSE,file)) {
978 match = kTRUE;
979 }
980#ifdef TEST_FOR_BACKWARD_COMPATIBILITY_ABSTRACT_CLASSES
981 if (!match && file->GetVersion() < 51800 && fClass && (fClass->Property() & kIsAbstract)
983 {
984 // In some instances of old files (v5.17 and less), some StreamerInfo for
985 // an abstract class where not written correctly, and add no
986 // data member listed. If in addition one of the data member
987 // was declared using a typedef _and_ the current class definition
988 // uses a different typedef, we are unable to recalculate the
989 // checksum as it was, because the information is missing from
990 // the StreamerInfo, and for the same reason CompareContent can
991 // not know whether this is okay or not ...
992 //
993 // Since this is such an unlikely scenario, let's complain
994 // about it anyway (The class layout *may* have changed, we
995 // don't know).
996
997 // if (this has only base classes) {
998 // match = kTRUE;
999 // }
1000 }
1001#endif
1002 } else {
1003 // The on-file TStreamerInfo's checksum differs from the checksum of a TStreamerInfo on another file.
1004
1005 match = kFALSE;
1006 oldIsNonVersioned = info->fOnFileClassVersion==1 && info->fClassVersion != 1;
1007
1008 // In the case where the read-in TStreamerInfo does not
1009 // match in the 'current' in memory TStreamerInfo for
1010 // a non foreign class (we can not get here if this is
1011 // a foreign class so we do not need to test it),
1012 // we need to add this one more test since the CINT behaviour
1013 // with enums changed over time, so verify the checksum ignoring
1014 // members of type enum. We also used to not count the //[xyz] comment
1015 // in the checksum, so test for that too.
1021 {
1022 match = kTRUE;
1023 }
1024 if (fOldVersion <= 2) {
1025 // Names of STL base classes was modified in vers==3. Allocators removed
1026 // (We could be more specific (see test for the same case below)
1027 match = kTRUE;
1028 }
1029 if (!match && CompareContent(0,info,kFALSE,kFALSE,file)) {
1030 match = kTRUE;
1031 }
1032 }
1033 }
1034 if (info->IsBuilt()) {
1036 fNumber = info->GetNumber();
1038 TObjArray* elems = info->GetElements();
1039 TStreamerElement* e1 = 0;
1040 TStreamerElement* e2 = 0;
1041 for (Int_t i = 0; i < nel; ++i) {
1043 e2 = (TStreamerElement*) elems->At(i);
1044 if (!e1 || !e2) {
1045 continue;
1046 }
1047 if (strlen(e1->GetTitle()) != strlen(e2->GetTitle())) {
1048 e2->SetTitle(e1->GetTitle());
1049 }
1050 }
1051
1052 done = kTRUE;
1053 } else {
1055 info = 0;
1056 }
1057 TString origin;
1058 if (!match && !fClass->TestBit(TClass::kWarned)) {
1059 if (oldIsNonVersioned) {
1060 if (file) {
1061 Warning("BuildCheck", "\n\
1062 The class %s transitioned from not having a specified class version\n\
1063 to having a specified class version (the current class version is %d).\n\
1064 However too many different non-versioned layouts of the class have been\n\
1065 loaded so far. This prevent the proper reading of objects written with\n\
1066 the class layout version %d, in particular from the file:\n\
1067 %s.\n\
1068 To work around this issue, load fewer 'old' files in the same ROOT session.",
1070 } else {
1071 Warning("BuildCheck", "\n\
1072 The class %s transitioned from not having a specified class version\n\
1073 to having a specified class version (the current class version is %d).\n\
1074 However too many different non-versioned layouts of the class have been\n\
1075 loaded so far. This prevent the proper reading of objects written with\n\
1076 the class layout version %d.\n\
1077 To work around this issue, load fewer 'old' files in the same ROOT session.",
1079 }
1080 } else {
1081 if (file) {
1082 if (done) {
1083 Warning("BuildCheck", "\n\
1084 The StreamerInfo for version %d of class %s read from the file %s\n\
1085 has a different checksum than the previously loaded StreamerInfo.\n\
1086 Reading objects of type %s from the file %s \n\
1087 (and potentially other files) might not work correctly.\n\
1088 Most likely the version number of the class was not properly\n\
1089 updated [See ClassDef(%s,%d)].",
1090 fClassVersion, GetName(), file->GetName(), GetName(), file->GetName(), GetName(), fClassVersion);
1091 } else {
1092 Warning("BuildCheck", "\n\
1093 The StreamerInfo from %s does not match existing one (%s:%d)\n\
1094 The existing one has not been used yet and will be discarded.\n\
1095 Reading the file %s will work properly, however writing object of\n\
1096 type %s will not work properly. Most likely the version number\n\
1097 of the class was not properly updated [See ClassDef(%s,%d)].",
1098 file->GetName(), GetName(), fClassVersion,file->GetName(),GetName(), GetName(), fClassVersion);
1099 }
1100 } else {
1101 if (done) {
1102 Warning("BuildCheck", "\n\
1103 The StreamerInfo for version %d of class %s\n\
1104 has a different checksum than the previously loaded StreamerInfo.\n\
1105 Reading objects of type %s\n\
1106 (and potentially other files) might not work correctly.\n\
1107 Most likely the version number of the class was not properly\n\
1108 updated [See ClassDef(%s,%d)].",
1110 } else {
1111 Warning("BuildCheck", "\n\
1112 The StreamerInfo does not match existing one (%s:%d)\n\
1113 The existing one has not been used yet and will be discarded.\n\
1114 Reading should work properly, however writing object of\n\
1115 type %s will not work properly. Most likely the version number\n\
1116 of the class was not properly updated [See ClassDef(%s,%d)].",
1118 }
1119 }
1120 }
1123 }
1124 if (done) {
1125 return;
1126 }
1127 }
1128 // The slot was free, however it might still be reserved for the current
1129 // loaded version of the class
1130 if (fClass->IsLoaded()
1132 && (fClassVersion != 0) // We don't care about transient classes
1134 && (fCheckSum != fClass->GetCheckSum())) {
1135
1136 // If the old TStreamerInfo matches the in-memory one when we either
1137 // - ignore the members of type enum
1138 // or
1139 // - ignore the comments annotation (//[xyz])
1140 // we can accept the old TStreamerInfo.
1141
1143
1145 if (warn) {
1147 }
1148#ifdef TEST_FOR_BACKWARD_COMPATIBILITY_ABSTRACT_CLASSES
1149 if (warn && file->GetVersion() < 51800 && fClass && (fClass->Property() & kIsAbstract)
1151 {
1152 // In some instances of old files (v5.17 and less), some StreamerInfo for
1153 // an abstract class where not written correctly, and add no
1154 // data member listed. If in addition one of the data member
1155 // was declared using a typedef _and_ the current class definition
1156 // uses a different typedef, we are unable to recalculate the
1157 // checksum as it was, because the information is missing from
1158 // the StreamerInfo, and for the same reason CompareContent can
1159 // not know whether this is okay or not ...
1160 //
1161 // Since this is such an unlikely scenario, let's complain
1162 // about it anyway (The class layout *may* have changed, we
1163 // don't know).
1164
1165 // if (this has only base classes) {
1166 // warn = kFALSE;
1167 // }
1168 }
1169#endif // TEST_FOR_BACKWARD_COMPATIBILITY
1170 if (warn && (fOldVersion <= 2)) {
1171 // Names of STL base classes was modified in vers==3. Allocators removed
1172 //
1173 TIter nextBC(fClass->GetListOfBases());
1174 TBaseClass* bc = 0;
1175 while ((bc = (TBaseClass*) nextBC())) {
1176 if (bc->GetClassPointer()->GetCollectionType()) {
1177 warn = kFALSE;
1178 }
1179 }
1180 }
1181 if (warn) {
1182 if (file) {
1183 Warning("BuildCheck", "\n\
1184 The StreamerInfo of class %s read from file %s\n\
1185 has the same version (=%d) as the active class but a different checksum.\n\
1186 You should update the version to ClassDef(%s,%d).\n\
1187 Do not try to write objects with the current class definition,\n\
1188 the files will not be readable.\n", GetName(), file->GetName(), fClassVersion, GetName(), fClassVersion + 1);
1189 } else {
1190 Warning("BuildCheck", "\n\
1191 The StreamerInfo of class %s \n\
1192 has the same version (=%d) as the active class but a different checksum.\n\
1193 You should update the version to ClassDef(%s,%d).\n\
1194 Do not try to write objects with the current class definition,\n\
1195 the files will not be readable.\n", GetName(), fClassVersion, GetName(), fClassVersion + 1);
1196 }
1199 }
1200 } else {
1201 if (!fClass->IsVersioned()) {
1202 Fatal("BuildCheck", "\n\
1203 The StreamerInfo of unversioned class %s \n\
1204 has the same version (=%d) as the active class but an old checksum.\n\
1205 This should not happen. An assert will follow.\n", GetName(), fClassVersion);
1206 }
1207 }
1208 }
1209 if (!fClass->IsLoaded() && this->fOnFileClassVersion>1)
1210 {
1211 ROOT::ResetClassVersion(fClass,(const char*)-1, this->fClassVersion);
1212 }
1213 }
1214 // FIXME: This code can never execute because Build() calls
1215 // TStreamerElement::Class()->IgnoreTObjectStreamer()
1216 // so our bits are never saved to the file.
1219 }
1220 if ((fClassVersion < -1) || (fClassVersion > 65000)) {
1221 printf("ERROR reading TStreamerInfo: %s fClassVersion=%d\n", GetName(), fClassVersion);
1223 fNumber = -1;
1224 return;
1225 }
1226
1229 && GetCheckSum() != fClass->GetCheckSum()
1231 // We got here, thus we are a perfect alias for the current streamerInfo,
1232 // but we might had odd v5 style name spelling, so let's prefer the
1233 // current one.
1234 auto maininfo = fClass->GetStreamerInfo();
1235 if (maininfo) {
1236 fNumber = maininfo->GetNumber(); // For ReadStreamerInfo to record the expected slot.
1237 }
1239 return;
1240 }
1241
1243 ++fgCount;
1244 fNumber = fgCount;
1245
1246 // Since we just read this streamerInfo from file, it has already been built.
1247 fIsBuilt = kTRUE;
1248
1249 //add to the global list of StreamerInfo
1250 TObjArray* infos = (TObjArray*) gROOT->GetListOfStreamerInfo();
1251 infos->AddAtAndExpand(this, fNumber);
1252}
1253
1254////////////////////////////////////////////////////////////////////////////////
1255/// Create an Emulation TStreamerInfo object.
1256
1258{
1260
1261 TString duName;
1262 R__ASSERT(file);
1263 Int_t fv = file->GetVersion()%100000;
1264 R__ASSERT(fv < 30000);
1265 fClassVersion = -1;
1266 fCheckSum = 2001;
1267 TObjArray *elements = GetElements();
1268 Int_t ndata = elements ? elements->GetEntriesFast() : 0;
1269 for (Int_t i=0;i < ndata;i++) {
1270 TStreamerElement *element = (TStreamerElement*)elements->UncheckedAt(i);
1271 if (!element) break;
1272 int ty = element->GetType();
1273 if (ty < kChar || ty >kULong+kOffsetL) continue;
1274 if (ty == kLong) element->SetType(kInt);
1275 if (ty == kULong) element->SetType(kUInt);
1276 if (ty == kLong + kOffsetL) element->SetType(kInt + kOffsetL);
1277 if (ty == kULong + kOffsetL) element->SetType(kUInt + kOffsetL);
1278 if (ty <= kULong) continue;
1279 duName = element->GetName();
1280 duName.Append("QWERTY");
1281 TStreamerBasicType *bt = new TStreamerBasicType(duName, "", 0, kInt,"Int_t");
1282 {for (int j=ndata-1;j>=i;j--) {elements->AddAtAndExpand(elements->At(j),j+1);}}
1283 elements->AddAt(bt,i);
1284 ndata++;
1285 i++;
1286 }
1287 BuildOld();
1288}
1289
1290////////////////////////////////////////////////////////////////////////////////
1291/// Check if we can build this for foreign class - do we have some rules
1292/// to do that.
1293
1295{
1297
1298 if( !in_memory_cl || !in_memory_cl->GetSchemaRules() ) {
1299 return kFALSE;
1300 }
1301
1302 auto rules = in_memory_cl->GetSchemaRules()->FindRules( GetName(), fOnFileClassVersion, fCheckSum );
1303
1304 if( rules.empty() && !in_memory_cl->GetCollectionType() ) {
1305 Warning( "BuildFor", "The build of %s streamer info for %s has been requested, but no matching conversion rules were specified", GetName(), in_memory_cl->GetName() );
1306 return kFALSE;
1307 }
1308
1309 fClass = const_cast<TClass*>(in_memory_cl);
1310
1311 return kTRUE;
1312}
1313
1314
1315namespace {
1316////////////////////////////////////////////////////////////////////////////////
1317/// Helper function for BuildOld
1318 Bool_t ClassWasMovedToNamespace(TClass *oldClass, TClass *newClass)
1319 {
1320 // Returns true if oldClass is the same as newClass but newClass is in a
1321 // namespace (and oldClass was not in a namespace).
1322
1323 if (oldClass == 0 || newClass == 0) return kFALSE;
1324
1325 UInt_t newlen = strlen(newClass->GetName());
1326 UInt_t oldlen = strlen(oldClass->GetName());
1327
1328 const char *oldname = oldClass->GetName();
1329 for (UInt_t i = oldlen, done = false, nest = 0; (i>0) && !done ; --i) {
1330 switch (oldClass->GetName()[i-1]) {
1331 case '>' : ++nest; break;
1332 case '<' : if (nest==0) return kFALSE; // the name is not well formed, give up.
1333 --nest; break;
1334 case ':' : if (nest == 0) oldname= &(oldClass->GetName()[i]); done = kTRUE; break;
1335 }
1336 }
1337 oldlen = strlen(oldname);
1338 if (!(strlen(newClass->GetName()) > strlen(oldClass->GetName()))) {
1339 return kFALSE;
1340 }
1341
1342 const char* newEnd = & (newClass->GetName()[newlen-oldlen]);
1343
1344 if (0 != strcmp(newEnd, oldname)) {
1345 return kFALSE;
1346 }
1347
1348 Int_t oldv = oldClass->GetStreamerInfo()->GetClassVersion();
1349
1350 if (newClass->GetStreamerInfos() && oldv < newClass->GetStreamerInfos()->GetSize() && newClass->GetStreamerInfos()->At(oldv) && strcmp(newClass->GetStreamerInfos()->At(oldv)->GetName(), oldClass->GetName()) != 0) {
1351 // The new class has already a TStreamerInfo for the the same version as
1352 // the old class and this was not the result of an import. So we do not
1353 // have a match
1354 return kFALSE;
1355 }
1356 return kTRUE;
1357 }
1358
1359////////////////////////////////////////////////////////////////////////////////
1360/// Import the streamerInfo from oldClass to newClass.
1361///
1362/// In case of conflict, returns the version number of the StreamerInfo
1363/// with the conflict.
1364/// Return 0 in case of success
1365 Int_t ImportStreamerInfo(TClass *oldClass, TClass *newClass) {
1366
1367 TIter next(oldClass->GetStreamerInfos());
1368 TStreamerInfo *info;
1369 while ((info = (TStreamerInfo*)next())) {
1370 info = (TStreamerInfo*)info->Clone();
1371 if (!info) {
1372 Error("ImportStreamerInfo","Unable to clone the StreamerInfo for %s.",(*next)->GetName());
1373 } else {
1374 info->SetClass(newClass);
1375 Int_t oldv = info->GetClassVersion();
1376 if (oldv > newClass->GetStreamerInfos()->GetSize() || newClass->GetStreamerInfos()->At(oldv) == 0) {
1377 // All is good.
1378 newClass->RegisterStreamerInfo(info);
1379 } else {
1380 // We verify that we are consistent and that
1381 // newcl->GetStreamerInfos()->UncheckedAt(info->GetClassVersion)
1382 // is already the same as info.
1383 if (strcmp(newClass->GetStreamerInfos()->At(oldv)->GetName(),
1384 oldClass->GetName()) != 0) {
1385 // The existing StreamerInfo does not already come from OldClass.
1386 // This is a real problem!
1387 return oldv;
1388 }
1389 }
1390 }
1391 }
1392 return 0;
1393 }
1394
1395 Bool_t ContainerMatchTClonesArray(TClass *newClass)
1396 {
1397 // Return true if newClass is a likely valid conversion from
1398 // a TClonesArray
1399
1400 return newClass->GetCollectionProxy()
1401 && newClass->GetCollectionProxy()->GetValueClass()
1402 && !newClass->GetCollectionProxy()->HasPointers();
1403 }
1404
1405 Bool_t CollectionMatch(const TClass *oldClass, const TClass* newClass)
1406 {
1407 // Return true if oldClass and newClass points to 2 compatible collection.
1408 // i.e. they contains the exact same type.
1409
1410 TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy();
1411 TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy();
1412
1413 TClass *oldContent = oldProxy->GetValueClass();
1414 TClass *newContent = newProxy->GetValueClass();
1415
1416 Bool_t contentMatch = kFALSE;
1417 if (oldContent) {
1418 if (oldContent == newContent) {
1419 contentMatch = kTRUE;
1420 } else if (newContent) {
1421 TString oldFlatContent( TMakeProject::UpdateAssociativeToVector(oldContent->GetName()) );
1422 TString newFlatContent( TMakeProject::UpdateAssociativeToVector(newContent->GetName()) );
1423 if (oldFlatContent == newFlatContent) {
1424 contentMatch = kTRUE;
1425 }
1426 } else {
1427 contentMatch = kFALSE;
1428 }
1429 } else {
1430 contentMatch = (newContent==0);
1431 }
1432
1433 if (contentMatch) {
1434 if ((oldContent==0 && oldProxy->GetType() == newProxy->GetType())
1435 ||(oldContent && oldProxy->HasPointers() == newProxy->HasPointers())) {
1436 // We have compatibles collections (they have the same content)!
1437 return kTRUE;
1438 }
1439 }
1440 return kFALSE;
1441 }
1442
1443 Bool_t CollectionMatchFloat16(const TClass *oldClass, const TClass* newClass)
1444 {
1445 // Return true if oldClass and newClass points to 2 compatible collection.
1446 // i.e. they contains the exact same type.
1447
1448 TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy();
1449 TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy();
1450
1451 if (oldProxy->GetValueClass() == 0 && newProxy->GetValueClass() == 0
1452 && (oldProxy->GetType() == kFloat_t || oldProxy->GetType() == kFloat16_t)
1453 && (newProxy->GetType() == kFloat_t || newProxy->GetType() == kFloat16_t )) {
1454 // We have compatibles collections (they have the same content)!
1455 return (oldClass->GetCollectionType() == newClass->GetCollectionType());
1456 }
1457 return kFALSE;
1458 }
1459
1460 Bool_t CollectionMatchDouble32(const TClass *oldClass, const TClass* newClass)
1461 {
1462 // Return true if oldClass and newClass points to 2 compatible collection.
1463 // i.e. they contains the exact same type.
1464
1465 TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy();
1466 TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy();
1467
1468 if (oldProxy->GetValueClass() == 0 && newProxy->GetValueClass() == 0
1469 && (oldProxy->GetType() == kDouble_t || oldProxy->GetType() == kDouble32_t)
1470 && (newProxy->GetType() == kDouble_t || newProxy->GetType() == kDouble32_t )) {
1471 // We have compatibles collections (they have the same content)!
1472 return (oldClass->GetCollectionType() == newClass->GetCollectionType());
1473 }
1474 return kFALSE;
1475 }
1476
1477 Bool_t CollectionMatchLong64(const TClass *oldClass, const TClass* newClass)
1478 {
1479 // Return true if oldClass and newClass points to 2 compatible collection.
1480 // i.e. they contains the exact same type.
1481
1482 TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy();
1483 TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy();
1484
1485 if (oldProxy->GetValueClass() == 0 && newProxy->GetValueClass() == 0
1486 && (oldProxy->GetType() == kLong_t || oldProxy->GetType() == kLong64_t)
1487 && (newProxy->GetType() == kLong_t || newProxy->GetType() == kLong64_t )) {
1488 // We have compatibles collections (they have the same content)!
1489 return (oldClass->GetCollectionType() == newClass->GetCollectionType());
1490 }
1491 return kFALSE;
1492 }
1493
1494 Bool_t CollectionMatchULong64(const TClass *oldClass, const TClass* newClass)
1495 {
1496 // Return true if oldClass and newClass points to 2 compatible collection.
1497 // i.e. they contains the exact same type.
1498
1499 TVirtualCollectionProxy *oldProxy = oldClass->GetCollectionProxy();
1500 TVirtualCollectionProxy *newProxy = newClass->GetCollectionProxy();
1501
1502 if (oldProxy->GetValueClass() == 0 && newProxy->GetValueClass() == 0
1503 && (oldProxy->GetType() == kULong_t || oldProxy->GetType() == kULong64_t)
1504 && (newProxy->GetType() == kULong_t || newProxy->GetType() == kULong64_t )) {
1505 // We have compatibles collections (they have the same content)!
1506 return (oldClass->GetCollectionType() == newClass->GetCollectionType());
1507 }
1508 return kFALSE;
1509 }
1510
1511 TClass *FindAlternate(TClass *context, const std::string &i_name, std::string& newName)
1512 {
1513 // Return a class whose has the name as oldClass and can be found
1514 // within the scope of the class 'context'.
1515
1516 // First strip any 'const ' prefix or trailing '*'.
1517 std::string name(i_name);
1518 newName.clear();
1519 if (name.compare(0,6,"const ")==0) {
1520 newName = "const ";
1521 name.erase(0,6);
1522 }
1523 std::string suffix;
1524 UInt_t nstars = 0;
1525 while(name[name.length()-nstars-1]=='*') {
1526 ++nstars;
1527 suffix.append("*");
1528 }
1529 if (nstars) {
1530 name.erase(name.length()-nstars,nstars);
1531 }
1532
1533 std::string alternate(context->GetName());
1534 alternate.append("::");
1535 alternate.append(name);
1536
1537 TClass *altcl = TClass::GetClass(alternate.c_str(),/*load=*/ false,true);
1538 if (altcl) {
1539 newName.append(altcl->GetName());
1540 newName.append(suffix);
1541 return altcl;
1542 }
1543
1544 size_t ctxt_cursor = strlen(context->GetName());
1545 for (size_t level = 0; ctxt_cursor != 0; --ctxt_cursor) {
1546 switch (context->GetName()[ctxt_cursor]) {
1547 case '<': --level; break;
1548 case '>': ++level; break;
1549 case ':': if (level == 0) {
1550 // we encountered a scope not within a template
1551 // parameter.
1552 alternate.clear();
1553 alternate.append(context->GetName(),ctxt_cursor+1);
1554 alternate.append(name);
1555 altcl = TClass::GetClass(alternate.c_str(),/*load=*/ false,true);
1556 if (altcl) {
1557 newName.append(altcl->GetName());
1558 newName.append(suffix);
1559 return altcl;
1560 }
1561 }
1562 }
1563 }
1564 newName.clear();
1565 return 0;
1566 }
1567
1568 TClass *FixCollectionV5(TClass *context, TClass *oldClass, TClass *newClass)
1569 {
1570 assert(oldClass->GetCollectionProxy() && newClass->GetCollectionProxy());
1571
1573 TVirtualCollectionProxy *current = newClass->GetCollectionProxy();
1574 Int_t stlkind = old->GetCollectionType();
1575
1576 if (stlkind == ROOT::kSTLmap || stlkind == ROOT::kSTLmultimap) {
1577
1578 if (current->GetValueClass() == nullptr) {
1579 // This should really never happen (the content of map should always
1580 // be a pair and thus have a TClass ... so let's just give up ...
1581 // It actually happens in the case where one of the member is an
1582 // enum that is part of dictionary payload that is not yet
1583 // auto-loaded.
1584 return nullptr;
1585 }
1587 if (info->GetElements()->GetEntriesFast() != 2) {
1588 return oldClass;
1589 }
1592
1593 // Since we do not create TClass for pair of unknown types, old->GetValueClass can
1594 // be nullptr even-though the type used be known. An example of such change
1595 // is `RooExpensiveObjectCache::ExpensiveObject` which used to be recorded
1596 // as `ExpensiveObject` in the name of the map ... making it unknown
1597 // (and this is precisely the type of change we are trying to handle here/below!)
1598 info = old->GetValueClass() ? old->GetValueClass()->GetStreamerInfo() : nullptr;
1599 assert(!info || info->GetElements()->GetEntriesFast() == 2);
1600 TStreamerElement *of = info ? (TStreamerElement*) info->GetElements()->At(0) : nullptr;
1601 TStreamerElement *os = info ? (TStreamerElement*) info->GetElements()->At(1) : nullptr;
1602
1603 TClass *firstNewCl = f ? f->GetClass() : 0;
1604 TClass *secondNewCl = s ? s->GetClass() : 0;
1605
1606 TClass *firstOldCl = of ? of->GetClass() : 0;
1607 TClass *secondOldCl = os ? os->GetClass() : 0;
1608
1609 if ((firstNewCl && !firstOldCl) || (secondNewCl && !secondOldCl))
1610 {
1611 std::vector<std::string> inside;
1612 int nestedLoc;
1613 TClassEdit::GetSplit( oldClass->GetName(), inside, nestedLoc, TClassEdit::kLong64 );
1614
1615 TClass *firstAltCl = firstOldCl;
1616 TClass *secondAltCl = secondOldCl;
1617 std::string firstNewName;
1618 std::string secondNewName;
1619 if (!info && !firstOldCl) {
1620 firstOldCl = TClass::GetClass(inside[1].c_str(), kTRUE, kTRUE);
1621 }
1622 if (!info && !secondOldCl) {
1623 secondOldCl = TClass::GetClass(inside[2].c_str(), kTRUE, kTRUE);
1624 }
1625 if (firstNewCl && !firstOldCl) {
1626 firstAltCl = FindAlternate(context, inside[1], firstNewName);
1627 } else if (firstAltCl) {
1628 firstNewName = firstAltCl->GetName();
1629 } else {
1630 firstNewName = inside[1];
1631 }
1632 if (secondNewCl && !secondOldCl) {
1633 secondAltCl = FindAlternate(context, inside[2], secondNewName);
1634 } else if (secondAltCl) {
1635 secondNewName = secondAltCl->GetName();
1636 } else {
1637 secondNewName = inside[2];
1638 }
1639 if ((firstNewCl && firstAltCl != firstOldCl) ||
1640 (secondNewCl && secondAltCl != secondOldCl) ) {
1641
1642 // Need to produce new name.
1643 std::string alternate = inside[0];
1644 alternate.append("<");
1645 alternate.append(firstNewName);
1646 alternate.append(",");
1647 alternate.append(secondNewName);
1648 // We are intentionally dropping any further arguments,
1649 // they would be using the wrong typename and would also be
1650 // somewhat superflous since this is for the old layout.
1651 if (alternate[alternate.length()-1]=='>') {
1652 alternate.append(" ");
1653 }
1654 alternate.append(">");
1655 return TClass::GetClass(alternate.c_str(),true,true);
1656 }
1657 }
1658
1659 } else if (current->GetValueClass() && !old->GetValueClass()
1660 && old->GetType() == kInt_t) {
1661
1662 // The old CollectionProxy claims it contains int (or enums) while
1663 // the new one claims to contain a class. It is likely that we have
1664 // in the collection name a class (typedef) name that is missing its
1665 // scope. Let's try to check.
1666
1667 std::vector<std::string> inside;
1668 int nestedLoc;
1669 TClassEdit::GetSplit( oldClass->GetName(), inside, nestedLoc, TClassEdit::kLong64 );
1670
1671 // Now let's if we can find this missing type.
1672 std::string newName;
1673 TClass *altcl = FindAlternate(context, inside[1], newName);
1674
1675 if (altcl) {
1676 std::string alternate = inside[0];
1677 alternate.append("<");
1678 alternate.append(newName);
1679 // We are intentionally dropping any further arguments,
1680 // they would be using the wrong typename and would also be
1681 // somewhat superflous since this is for the old layout.
1682 if (alternate[alternate.length()-1]=='>') {
1683 alternate.append(" ");
1684 }
1685 alternate.append(">");
1686 return TClass::GetClass(alternate.c_str(),true,true);
1687 }
1688 }
1689 return 0;
1690 }
1691
1692 // Makes sure kBuildOldUsed set once BuildOld finishes
1693 struct TBuildOldGuard {
1694 TBuildOldGuard(TStreamerInfo* info): fInfo(info) {
1695 fInfo->SetBit(TStreamerInfo::kBuildRunning);
1696 }
1697 ~TBuildOldGuard() {
1698 fInfo->ResetBit(TStreamerInfo::kBuildRunning);
1699 fInfo->SetBit(TStreamerInfo::kBuildOldUsed);
1700 }
1701 TStreamerInfo* fInfo;
1702 };
1703}
1704
1705////////////////////////////////////////////////////////////////////////////////
1706/// rebuild the TStreamerInfo structure
1707
1709{
1711
1712 if ( TestBit(kBuildOldUsed) ) return;
1713
1714 // Are we recursing on ourself?
1716
1717 // This is used to avoid unwanted recursive call to Build and make sure
1718 // that we record the execution of BuildOld.
1719 TBuildOldGuard buildOldGuard(this);
1720
1721 if (gDebug > 0) {
1722 printf("\n====>Rebuilding TStreamerInfo for class: %s, version: %d\n", GetName(), fClassVersion);
1723 }
1724
1725 Bool_t wasCompiled = IsCompiled();
1726
1729 {
1730 // Handle emulated classes and STL containers specially.
1731 // in this case BuildRealData would call BuildOld for this same
1732 // TStreamerInfo to be able to build the real data on it.
1733 } else {
1735 }
1736 }
1737 else {
1738 // This is to support the following case
1739 // Shared library: Event v2
1740 // calling cl->GetStreamerInfo(1)->BuildOld(); (or equivalent)
1741 // which calls cl->BuildReadData()
1742 // which set fRealData to some value
1743 // then call Event()
1744 // which call cl->GetStreamerInfo()
1745 // which call cl->BuildRealData();
1746 // which returns immediately (upon seeing fRealData!=0)
1747 // then the main StreamerInfo build using the partial content of fRealData
1748 // then BuildRealData returns
1749 // then GetStreamerInfo() returns
1750 // then Event() returns
1751 // then fRealData is finished being populated
1752 // then this function continue,
1753 // then it uses the main streamerInfo
1754 // .... which is incomplete.
1755 //
1756 // Instead we force the creation of the main streamerInfo object
1757 // before the creation of fRealData.
1759 }
1760
1761 TIter next(fElements);
1762 TStreamerElement* element;
1763 Int_t offset = 0;
1764 TMemberStreamer* streamer = 0;
1765
1766 constexpr size_t kSizeOfPtr = sizeof(void*);
1767
1768 int nBaze = 0;
1769
1770 if ((fElements->GetEntriesFast() == 1) && !strcmp(fElements->At(0)->GetName(), "This")) {
1771 if (fClass->GetCollectionProxy()) {
1772 element = (TStreamerElement*)next();
1773 element->SetNewType( element->GetType() );
1774 element->SetNewClass( fClass );
1775 } else if (((TStreamerElement*)fElements->At(0))->GetType() == TStreamerInfo::kSTL &&
1776 strcmp( ((TStreamerElement*)fElements->At(0))->GetTypeName(),GetName()) != 0) {
1777 // We have a collection that was proxied but does not have a collection proxy,
1778 // let's put one in place just for fun ... humm however we have no clue what is the value
1779 // type ....
1780
1781 // For now wild guess ....
1782
1783 }
1784 }
1785
1786 TClass *allocClass = 0;
1787 TStreamerInfo *infoalloc = 0;
1788
1789 //---------------------------------------------------------------------------
1790 // Get schema rules for this class
1791 /////////////////////////////////////////////////////////////////////////////
1792
1793 ROOT::TSchemaRuleSet::TMatches rules;
1794 const ROOT::TSchemaRuleSet* ruleSet = fClass->GetSchemaRules();
1795
1796 if (ruleSet) rules = ruleSet->FindRules( GetName(), fOnFileClassVersion, fCheckSum );
1797
1799 Int_t virtualInfoLocAlloc = 0;
1800 fNVirtualInfoLoc = 0;
1801 delete [] fVirtualInfoLoc;
1802 fVirtualInfoLoc = 0;
1803
1804 while ((element = (TStreamerElement*) next())) {
1805 if (element->IsA()==TStreamerArtificial::Class()
1806 || element->TestBit(TStreamerElement::kCache) )
1807 {
1808 // Prevent BuildOld from modifying existing ArtificialElement (We need to review when and why BuildOld
1809 // needs to be re-run; it might be needed if the 'current' class change (for example from being an onfile
1810 // version to being a version loaded from a shared library) and we thus may have to remove the artificial
1811 // element at the beginning of BuildOld)
1812
1813 continue;
1814 };
1815
1816 element->SetNewType(element->GetType());
1817 if (element->IsBase()) {
1818 //---------------------------------------------------------------------
1819 // Dealing with nonSTL bases
1820 ///////////////////////////////////////////////////////////////////////
1821
1822 if (element->IsA() == TStreamerBase::Class()) {
1823 TStreamerBase* base = (TStreamerBase*) element;
1824#if defined(PROPER_IMPLEMEMANTION_OF_BASE_CLASS_RENAMING)
1825 TClassRef baseclass = fClass->GetBaseClass( base->GetName() );
1826#else
1827 // Currently the base class renaming does not work, so we use the old
1828 // version of the code which essentially disable the next if(!baseclass ..
1829 // statement.
1830 // During the TStreamerElement's Init an emulated TClass might be replaced
1831 // by one from the dictionary, we use a TClassRef to be informed of the change.
1832 TClassRef baseclass = base->GetClassPointer();
1833#endif
1834
1835 //------------------------------------------------------------------
1836 // We do not have this base class - check if we're renaming
1837 ////////////////////////////////////////////////////////////////////
1838
1839 if( !baseclass && !fClass->TestBit( TClass::kIsEmulation ) ) {
1840 const ROOT::TSchemaRule* rule = (rules ? rules.GetRuleWithSource( base->GetName() ) : 0);
1841
1842 //---------------------------------------------------------------
1843 // No renaming, sorry
1844 /////////////////////////////////////////////////////////////////
1845
1846 if( !rule ) {
1847 Error("BuildOld", "Could not find base class: %s for %s and could not find any matching rename rule\n", base->GetName(), GetName());
1848 continue;
1849 }
1850
1851 //----------------------------------------------------------------
1852 // Find a new target class
1853 /////////////////////////////////////////////////////////////////
1854
1855 const TObjArray* targets = rule->GetTarget();
1856 if( !targets ) {
1857 Error("BuildOld", "Could not find base class: %s for %s, renaming rule was found but is malformed\n", base->GetName(), GetName());
1858 }
1859 TString newBaseClass = ((TObjString*)targets->At(0))->GetString();
1860 baseclass = TClass::GetClass( newBaseClass );
1861 base->SetNewBaseClass( baseclass );
1862 }
1863 //-------------------------------------------------------------------
1864 // No base class in emulated mode
1865 ////////////////////////////////////////////////////////////////////
1866
1867 else if( !baseclass ) {
1868 baseclass = base->GetClassPointer();
1869 if (!baseclass) {
1870 Warning("BuildOld", "Missing base class: %s skipped", base->GetName());
1871 // FIXME: Why is the version number 1 here? Answer: because we don't know any better at this point
1872 baseclass = new TClass(element->GetName(), 1, 0, 0, -1, -1);
1873 element->Update(0, baseclass);
1874 }
1875 }
1876 baseclass->BuildRealData();
1877
1878 // Calculate the offset using the 'real' base class name (as opposed to the
1879 // '@@emulated' in the case of the emulation of an abstract base class.
1880 Int_t baseOffset = fClass->GetBaseClassOffset(baseclass);
1881
1882 // Deal with potential schema evolution (renaming) of the base class.
1883 if (baseOffset < 0) {
1884
1885 // See if this base element can be converted into one of
1886 // the existing base class.
1887 TList* listOfBases = fClass->GetListOfBases();
1888 if (listOfBases) {
1889 TBaseClass* bc = 0;
1890 TIter nextBC(fClass->GetListOfBases());
1891 while ((bc = (TBaseClass*) nextBC())) {
1892 TClass *in_memory_bcl = bc->GetClassPointer();
1893 if (in_memory_bcl && in_memory_bcl->GetSchemaRules()) {
1894 auto baserule = in_memory_bcl->GetSchemaRules()->FindRules( base->GetName(), base->GetBaseVersion(), base->GetBaseCheckSum() );
1895 if (!baserule.empty()) {
1896 base->SetNewBaseClass(in_memory_bcl);
1897 baseOffset = bc->GetDelta();
1898
1899 }
1900 }
1901 }
1902 }
1903 }
1904 // We need to initialize the element now, as we need the
1905 // correct StreamerInfo next.
1906 element->Init(this);
1907
1908 // Force the StreamerInfo "Compilation" of the base classes first. This is necessary in
1909 // case the base class contains a member used as an array dimension in the derived classes.
1910 TStreamerInfo* infobase;
1911 if (fClass->TestBit(TClass::kIsEmulation) && (baseclass->Property() & kIsAbstract)) {
1912 Int_t version = base->GetBaseVersion();
1913 if (version >= 0 || base->GetBaseCheckSum() == 0) {
1914 infobase = (TStreamerInfo*)baseclass->GetStreamerInfoAbstractEmulated(version);
1915 } else {
1916 infobase = (TStreamerInfo*)baseclass->FindStreamerInfoAbstractEmulated(base->GetBaseCheckSum());
1917 }
1918 if (infobase) baseclass = infobase->GetClass();
1919 }
1920 else {
1921 infobase = (TStreamerInfo*)base->GetBaseStreamerInfo();
1922 }
1923
1924 if (infobase && infobase->fComp == 0) {
1925 infobase->BuildOld();
1926 }
1927
1928 if (infobase && shouldHaveInfoLoc && baseclass->TestBit(TClass::kIsEmulation) ) {
1929 if ( (fNVirtualInfoLoc + infobase->fNVirtualInfoLoc) > virtualInfoLocAlloc ) {
1930 ULong_t *store = fVirtualInfoLoc;
1931 virtualInfoLocAlloc = 16 * ( (fNVirtualInfoLoc + infobase->fNVirtualInfoLoc) / 16 + 1);
1932 fVirtualInfoLoc = new ULong_t[virtualInfoLocAlloc];
1933 if (store) {
1934 memcpy(fVirtualInfoLoc, store, sizeof(ULong_t)*fNVirtualInfoLoc);
1935 delete [] store;
1936 }
1937 }
1938 for (int nloc = 0; nloc < infobase->fNVirtualInfoLoc; ++nloc) {
1939 fVirtualInfoLoc[ fNVirtualInfoLoc + nloc ] = baseOffset + infobase->fVirtualInfoLoc[nloc];
1940 }
1942 }
1943
1944
1945 {
1946 if (baseOffset < 0) {
1947 element->SetNewType(-1);
1948 }
1949 }
1950 element->SetOffset(baseOffset);
1951 offset += baseclass->Size();
1952
1953 continue;
1954 } else {
1955 // Not a base elem but still base, string or STL as a base
1956 nBaze++;
1957 TList* listOfBases = fClass->GetListOfBases();
1958 Int_t baseOffset = -1;
1959 Int_t asize = 0;
1960 if (listOfBases) {
1961 // Do a search for the classname and some of its alternatives spelling.
1962
1963 TBaseClass* bc = 0;
1964 TIter nextBC(fClass->GetListOfBases());
1965 while ((bc = (TBaseClass*) nextBC())) {
1966 if (strchr(bc->GetName(), '<') || !strcmp(bc->GetName(),"string")) {
1969 if (bcName == elName) {
1970 break;
1971 }
1972 }
1973 }
1974
1975 if (!bc) {
1976 // Error("BuildOld", "Could not find STL base class: %s for %s\n", element->GetName(), GetName());
1977 offset = kMissing;
1978 element->SetOffset(kMissing);
1979 element->SetNewType(-1);
1980 continue;
1981 } else if (bc->GetClassPointer()->GetCollectionProxy()
1982 && !bc->GetClassPointer()->IsLoaded()
1984 Error("BuildOld","The class \"%s\" is compiled and its base class \"%s\" is a collection and we do not have a dictionary for it, we will not be able to read or write this base class.",GetName(),bc->GetName());
1985 offset = kMissing;
1986 element->SetOffset(kMissing);
1987 element->SetNewType(-1);
1988 continue;
1989 }
1990 baseOffset = bc->GetDelta();
1991 asize = bc->GetClassPointer()->Size();
1992
1993 } else if (fClass->TestBit( TClass::kIsEmulation )) {
1994 // Do a search for the classname and some of its alternatives spelling.
1995
1997 if (newInfo == this) {
1998 baseOffset = offset;
1999 asize = element->GetSize();
2000 } else if (newInfo) {
2001 TIter newElems( newInfo->GetElements() );
2002 TStreamerElement *newElement;
2003 while( (newElement = (TStreamerElement*)newElems()) ) {
2004 const char *newElName = newElement->GetName();
2005 if (newElement->IsBase() && (strchr(newElName,'<') || !strcmp(newElName,"string")) ) {
2006 TString bcName(TClassEdit::ShortType(newElName, TClassEdit::kDropStlDefault).c_str());
2008 if (bcName == elName) {
2009 break;
2010 }
2011 }
2012 }
2013 if (!newElement) {
2014 Error("BuildOld", "Could not find STL base class: %s for %s\n", element->GetName(), GetName());
2015 continue;
2016 }
2017 baseOffset = newElement->GetOffset();
2018 asize = newElement->GetSize();
2019 }
2020 }
2021 if (baseOffset == -1) {
2022 TClass* cb = element->GetClassPointer();
2023 if (!cb) {
2024 element->SetNewType(-1);
2025 continue;
2026 }
2027 asize = cb->Size();
2028 baseOffset = fClass->GetBaseClassOffset(cb);
2029 }
2030
2031 // we know how to read but do we know where to read?
2032 if (baseOffset < 0) {
2033 element->SetNewType(-1);
2034 continue;
2035 }
2036 element->SetOffset(baseOffset);
2037 offset += asize;
2038 element->Init(this);
2039 continue;
2040 } // if element is of type TStreamerBase or not.
2041 } // if (element->IsBase())
2042
2043 // If we get here, this means that we looked at all the base classes.
2044 if (shouldHaveInfoLoc && fNVirtualInfoLoc==0) {
2045 fNVirtualInfoLoc = 1;
2046 fVirtualInfoLoc = new ULong_t[1]; // To allow for a single delete statement.
2047 fVirtualInfoLoc[0] = offset;
2048 offset += sizeof(TStreamerInfo*);
2049 }
2050
2051 TDataMember* dm = 0;
2052
2053 std::string typeNameBuf;
2054 const char* dmType = nullptr;
2055 Bool_t dmIsPtr = false;
2056 TDataType* dt(nullptr);
2057 Int_t ndim = 0 ; //dm->GetArrayDim();
2058 std::array<Int_t, 5> maxIndices; // 5 is the maximum supported in TStreamerElement::SetMaxIndex
2059 Bool_t isStdArray(kFALSE);
2060
2061 // First set the offset and sizes.
2062 if (fClass->GetState() <= TClass::kEmulated) {
2063 // Note the initilization in this case are
2064 // delayed until __after__ the schema evolution
2065 // section, just in case the info has changed.
2066
2067 // We are in the emulated case
2068 streamer = 0;
2069 element->Init(this);
2070 } else {
2071 // The class is known to Cling (and thus is not emulated)
2072 // and we need to use the real offsets.
2073 // However we may not have a 'proper' TClass for it
2074 // (in which case IsLoaded will be false and GetImplFileLine will be -1)
2075
2076 // First look for the data member in the current class
2078 if (dm && dm->IsPersistent()) {
2080 streamer = 0;
2081 offset = GetDataMemberOffset(dm, streamer);
2082 element->SetOffset(offset);
2083 element->Init(this);
2084
2085 // Treat unique pointers and std arrays
2086 dmType = dm->GetTypeName();
2087 dmIsPtr = dm->IsaPointer();
2088 Bool_t nameChanged;
2089 typeNameBuf = TClassEdit::GetNameForIO(dmType, TClassEdit::EModType::kNone, &nameChanged);
2090 if (nameChanged) {
2091 dmIsPtr = TClassEdit::IsUniquePtr(dmType);
2092 dmType = typeNameBuf.c_str();
2093 }
2094 if ((isStdArray = TClassEdit::IsStdArray(dmType))){ // We tackle the std array case
2096 typeNameBuf,
2097 maxIndices,
2098 ndim);
2099 dmType = typeNameBuf.c_str();
2100 dt = gROOT->GetType(dmType);
2101 }
2102
2103 // We have a loaded class, let's make sure that if we have a collection
2104 // it is also loaded.
2105 TString dmClassName = TClassEdit::ShortType(dmType,TClassEdit::kDropStlDefault).c_str();
2106 dmClassName = dmClassName.Strip(TString::kTrailing, '*');
2107 if (dmClassName.Index("const ")==0) dmClassName.Remove(0,6);
2108 TClass *elemDm = ! (dt || dm->IsBasic()) ? TClass::GetClass(dmClassName.Data()) : 0;
2109 if (elemDm && elemDm->GetCollectionProxy()
2110 && !elemDm->IsLoaded()
2112 Error("BuildOld","The class \"%s\" is compiled and for its data member \"%s\", we do not have a dictionary for the collection \"%s\", we will not be able to read or write this data member.",GetName(),dm->GetName(),elemDm->GetName());
2113 offset = kMissing;
2114 element->SetOffset(kMissing);
2115 element->SetNewType(-1);
2116 }
2117 element->SetStreamer(streamer);
2118 int narr = element->GetArrayLength();
2119 if (!narr) {
2120 narr = 1;
2121 }
2122 int dsize = dm->GetUnitSize();
2123 element->SetSize(dsize*narr);
2124 } else {
2125 // We did not find it, let's look for it in the base classes via TRealData
2126 TRealData* rd = fClass->GetRealData(element->GetName());
2127 if (rd && rd->GetDataMember()) {
2128 element->SetOffset(rd->GetThisOffset());
2129 element->Init(this);
2130 dm = rd->GetDataMember();
2131 dmType = dm->GetTypeName();
2132 dmIsPtr = dm->IsaPointer();
2133 int narr = element->GetArrayLength();
2134 if (!narr) {
2135 narr = 1;
2136 }
2137 int dsize = dm->GetUnitSize();
2138 element->SetSize(dsize*narr);
2139 } else if (strcmp(element->GetName(), "fData") == 0 && strncmp(GetName(), "ROOT::VecOps::RVec", 18) == 0) {
2140 Error("BuildCheck", "Reading RVecs that were written directly to file before ROOT v6.24 is not "
2141 "supported, the program will likely crash.");
2142 element->SetOffset(0);
2143 element->Init(this);
2144 dmType = element->GetTypeName();
2145 dmIsPtr = false;
2146 }
2147 }
2148 } // Class corresponding to StreamerInfo is emulated or not.
2149
2150 // Now let's deal with Schema evolution
2151 Int_t newType = -1;
2152 TClassRef newClass;
2153
2154 if (dm && dm->IsPersistent()) {
2155 auto theType = isStdArray ? dt : dm->GetDataType();
2156 if (theType) {
2157 Bool_t isArray = isStdArray || element->GetArrayLength() >= 1;
2158 Bool_t hasCount = element->HasCounter();
2159 // data member is a basic type
2160 if ((fClass == TObject::Class()) && !strcmp(dm->GetName(), "fBits")) {
2161 //printf("found fBits, changing dtype from %d to 15\n", dtype);
2162 newType = kBits;
2163 } else {
2164 // All the values of EDataType have the same semantic in EReadWrite
2165 newType = (EReadWrite)theType->GetType();
2166 }
2167 if ((newType == ::kChar_t) && dmIsPtr && !isArray && !hasCount) {
2168 newType = ::kCharStar;
2169 } else if (dmIsPtr) {
2170 newType += kOffsetP;
2171 } else if (isArray) {
2172 newType += kOffsetL;
2173 }
2174 }
2175 if (newType == -1) {
2176 newClass = TClass::GetClass(dmType);
2177 }
2178 } else {
2179 // Either the class is not loaded or the data member is gone
2180 if (!fClass->IsLoaded()) {
2182 if (newInfo && (newInfo != this)) {
2183 TStreamerElement* newElems = (TStreamerElement*) newInfo->GetElements()->FindObject(element->GetName());
2184 newClass = newElems ? newElems->GetClassPointer() : 0;
2185 if (newClass == 0) {
2186 newType = newElems ? newElems->GetType() : -1;
2187 if (!(newType < kObject)) {
2188 // sanity check.
2189 newType = -1;
2190 }
2191 }
2192 } else {
2193 newClass = element->GetClassPointer();
2194 if (newClass.GetClass() == 0) {
2195 newType = element->GetType();
2196 if (!(newType < kObject)) {
2197 // sanity check.
2198 newType = -1;
2199 }
2200 }
2201 }
2202 }
2203 }
2204
2205 if (newType > 0) {
2206 // Case of a numerical type
2207 if (element->GetType() >= TStreamerInfo::kObject) {
2208 // Old type was not a numerical type.
2209 element->SetNewType(-2);
2210 } else if (element->GetType() != newType) {
2211 element->SetNewType(newType);
2212 if (gDebug > 0) {
2213 // coverity[mixed_enums] - All the values of EDataType have the same semantic in EReadWrite
2214 Info("BuildOld", "element: %s %s::%s has new type: %s/%d", element->GetTypeName(), GetName(), element->GetName(), dm ? dm->GetFullTypeName() : TDataType::GetTypeName((EDataType)newType), newType);
2215 }
2216 }
2217 } else if (newClass.GetClass()) {
2218 // Sometime BuildOld is called again.
2219 // In that case we might already have fix up the streamer element.
2220 // So we need to go back to the original information!
2221 newClass.Reset();
2223 if (oldClass == newClass.GetClass()) {
2224 // Nothing to do, also in the unique_ptr case :)
2225 } else if (ClassWasMovedToNamespace(oldClass, newClass.GetClass())) {
2226 Int_t oldv;
2227 if (0 != (oldv = ImportStreamerInfo(oldClass, newClass.GetClass()))) {
2228 Warning("BuildOld", "Can not properly load the TStreamerInfo from %s into %s due to a conflict for the class version %d", oldClass->GetName(), newClass->GetName(), oldv);
2229 } else {
2230 element->SetTypeName(newClass->GetName());
2231 if (gDebug > 0) {
2232 Warning("BuildOld", "element: %s::%s %s has new type %s", GetName(), element->GetTypeName(), element->GetName(), newClass->GetName());
2233 }
2234 }
2235 } else if (oldClass == TClonesArray::Class()) {
2236 if (ContainerMatchTClonesArray(newClass.GetClass())) {
2237 Int_t elemType = element->GetType();
2238 Bool_t isPrealloc = (elemType == kObjectp) || (elemType == kAnyp) || (elemType == (kObjectp + kOffsetL)) || (elemType == (kAnyp + kOffsetL));
2239 element->Update(oldClass, newClass.GetClass());
2241 TConvertClonesArrayToProxy *ms = new TConvertClonesArrayToProxy(cp, element->IsaPointer(), isPrealloc);
2242 element->SetStreamer(ms);
2243
2244 // When the type is kObject, the TObject::Streamer is used instead
2245 // of the TStreamerElement's streamer. So let force the usage
2246 // of our streamer
2247 if (element->GetType() == kObject) {
2248 element->SetNewType(kAny);
2249 element->SetType(kAny);
2250 }
2251 if (gDebug > 0) {
2252 Warning("BuildOld","element: %s::%s %s has new type %s", GetName(), element->GetTypeName(), element->GetName(), newClass->GetName());
2253 }
2254 } else {
2255 element->SetNewType(-2);
2256 }
2257 } else if (oldClass && oldClass->GetCollectionProxy() && newClass->GetCollectionProxy()) {
2258 {
2259 TClass *oldFixedClass = FixCollectionV5(GetClass(),oldClass,newClass);
2260 if (oldFixedClass && oldFixedClass != oldClass) {
2261 element->Update(oldClass,oldFixedClass);
2262 oldClass = oldFixedClass;
2263 }
2264 }
2265 if (CollectionMatch(oldClass, newClass)) {
2266 Int_t oldkind = oldClass->GetCollectionType();
2267 Int_t newkind = newClass->GetCollectionType();
2268
2269 if ( (oldkind==ROOT::kSTLmap || oldkind==ROOT::kSTLmultimap) &&
2270 (newkind!=ROOT::kSTLmap && newkind!=ROOT::kSTLmultimap) ) {
2271
2272 Int_t elemType = element->GetType();
2273 Bool_t isPrealloc = (elemType == kObjectp) || (elemType == kAnyp) || (elemType == (kObjectp + kOffsetL)) || (elemType == (kAnyp + kOffsetL));
2274
2275 TClassStreamer *streamer2 = newClass->GetStreamer();
2276 if (streamer2) {
2277 TConvertMapToProxy *ms = new TConvertMapToProxy(streamer2, element->IsaPointer(), isPrealloc);
2278 if (ms && ms->IsValid()) {
2279 element->SetStreamer(ms);
2280 switch( element->GetType() ) {
2281 //case TStreamerInfo::kSTLvarp: // Variable size array of STL containers.
2282 case TStreamerInfo::kSTLp: // Pointer to container with no virtual table (stl) and no comment
2283 case TStreamerInfo::kSTLp + TStreamerInfo::kOffsetL: // array of pointers to container with no virtual table (stl) and no comment
2284 element->SetNewType(-2);
2285 break;
2286 case TStreamerInfo::kSTL: // container with no virtual table (stl) and no comment
2287 case TStreamerInfo::kSTL + TStreamerInfo::kOffsetL: // array of containers with no virtual table (stl) and no comment
2288 break;
2289 }
2290 } else {
2291 delete ms;
2292 }
2293 }
2294 element->Update(oldClass, newClass.GetClass());
2295
2296 } else if ( (newkind==ROOT::kSTLmap || newkind==ROOT::kSTLmultimap) &&
2297 (oldkind!=ROOT::kSTLmap && oldkind!=ROOT::kSTLmultimap) ) {
2298 element->SetNewType(-2);
2299 } else {
2300 element->Update(oldClass, newClass.GetClass());
2301 }
2302 // Is this needed ? : element->SetSTLtype(newelement->GetSTLtype());
2303 if (gDebug > 0) {
2304 Warning("BuildOld","element: %s::%s %s has new type %s", GetName(), element->GetTypeName(), element->GetName(), newClass->GetName());
2305 }
2306 } else if (CollectionMatchFloat16(oldClass,newClass)) {
2307 // Actually nothing to do, since both are the same collection of double in memory.
2308 } else if (CollectionMatchDouble32(oldClass,newClass)) {
2309 // Actually nothing to do, since both are the same collection of double in memory.
2310 } else if (CollectionMatchLong64(oldClass,newClass)) {
2311 // Not much to do since both are the same collection of 8 bits entities on file.
2312 element->Update(oldClass, newClass.GetClass());
2313 } else if (CollectionMatchULong64(oldClass,newClass)) {
2314 // Not much to do since both are the same collection of 8 bits unsigned entities on file
2315 element->Update(oldClass, newClass.GetClass());
2316 } else if (newClass->GetSchemaRules()->HasRuleWithSourceClass( oldClass->GetName() )) {
2317 //------------------------------------------------------------------------
2318 // We can convert one type to another (at least for some of the versions).
2319 /////////////////////////////////////////////////////////////////
2320
2321 element->SetNewClass( newClass );
2322 } else {
2323 element->SetNewType(-2);
2324 }
2325
2326 } else if(oldClass &&
2327 newClass.GetClass() &&
2328 newClass->GetSchemaRules() &&
2329 newClass->GetSchemaRules()->HasRuleWithSourceClass( oldClass->GetName() ) ) {
2330 //------------------------------------------------------------------------
2331 // We can convert one type to another (at least for some of the versions).
2332 ////////////////////////////////////////////////////////////////////
2333
2334 element->SetNewClass( newClass );
2335 } else {
2336 element->SetNewType(-2);
2337 }
2338 // Humm we still need to make sure we have the same 'type' (pointer, embedded object, array, etc..)
2339 Bool_t cannotConvert = kFALSE;
2340 if (element->GetNewType() != -2) {
2341 if (dm) {
2342 if (dmIsPtr) {
2343 if (strncmp(dm->GetTitle(),"->",2)==0) {
2344 // We are fine, nothing to do.
2345 if (newClass->IsTObject()) {
2346 newType = kObjectp;
2347 } else if (newClass->GetCollectionProxy()) {
2348 newType = kSTLp;
2349 } else {
2350 newType = kAnyp;
2351 }
2352 } else {
2353 if (TClass::GetClass(dm->GetTypeName())->IsTObject()) {
2354 newType = kObjectP;
2355 } else if (newClass->GetCollectionProxy()) {
2356 newType = kSTLp;
2357 } else {
2358 newType = kAnyP;
2359 }
2360 }
2361 } else {
2362 if (newClass->GetCollectionProxy()) {
2363 newType = kSTL;
2364 } else if (newClass == TString::Class()) {
2365 newType = kTString;
2366 } else if (newClass == TObject::Class()) {
2367 newType = kTObject;
2368 } else if (newClass == TNamed::Class()) {
2369 newType = kTNamed;
2370 } else if (newClass->IsTObject()) {
2371 newType = kObject;
2372 } else {
2373 newType = kAny;
2374 }
2375 }
2376 if ((!dmIsPtr || newType==kSTLp) && (isStdArray ? ndim : dm->GetArrayDim()) > 0) {
2377 newType += kOffsetL;
2378 }
2379 } else if (!fClass->IsLoaded()) {
2381 if (newInfo && (newInfo != this)) {
2382 TStreamerElement* newElems = (TStreamerElement*) newInfo->GetElements()->FindObject(element->GetName());
2383 if (newElems) {
2384 newType = newElems->GetType();
2385 }
2386 } else {
2387 newType = element->GetType();
2388 }
2389 }
2390 if (element->GetType() == kSTL
2391 || ((element->GetType() == kObject || element->GetType() == kAny || element->GetType() == kObjectp || element->GetType() == kAnyp)
2392 && oldClass == TClonesArray::Class()))
2393 {
2394 cannotConvert = (newType != kSTL && newType != kObject && newType != kAny && newType != kSTLp && newType != kObjectp && newType != kAnyp);
2395
2396 } else if (element->GetType() == kSTLp || ((element->GetType() == kObjectP || element->GetType() == kAnyP) && oldClass == TClonesArray::Class()) )
2397 {
2398 cannotConvert = (newType != kSTL && newType != kObject && newType != kAny && newType != kSTLp && newType != kObjectP && newType != kAnyP);
2399
2400 } else if (element->GetType() == kSTL + kOffsetL
2401 || ((element->GetType() == kObject + kOffsetL|| element->GetType() == kAny + kOffsetL|| element->GetType() == kObjectp+ kOffsetL || element->GetType() == kAnyp+ kOffsetL)
2402 && oldClass == TClonesArray::Class()))
2403 {
2404 cannotConvert = (newType != kSTL + kOffsetL && newType != kObject+ kOffsetL && newType != kAny+ kOffsetL && newType != kSTLp+ kOffsetL && newType != kObjectp+ kOffsetL && newType != kAnyp+ kOffsetL);
2405
2406 } else if (element->GetType() == kSTLp + kOffsetL || ((element->GetType() == kObjectP+ kOffsetL || element->GetType() == kAnyP+ kOffsetL) && oldClass == TClonesArray::Class()) )
2407 {
2408 cannotConvert = (newType != kSTL+ kOffsetL && newType != kObject+ kOffsetL && newType != kAny+ kOffsetL && newType != kSTLp + kOffsetL&& newType != kObjectP+ kOffsetL && newType != kAnyP+ kOffsetL);
2409
2410 } else if ((element->GetType() == kObjectp || element->GetType() == kAnyp
2411 || element->GetType() == kObject || element->GetType() == kAny
2412 || element->GetType() == kTObject || element->GetType() == kTNamed || element->GetType() == kTString )) {
2413 // We had Type* ... ; //-> or Type ...;
2414 // this is completely compatible with the same and with a embedded object.
2415 if (newType != -1) {
2416 if (newType == kObjectp || newType == kAnyp
2417 || newType == kObject || newType == kAny
2418 || newType == kTObject || newType == kTNamed || newType == kTString) {
2419 // We are fine, no transformation to make
2420 element->SetNewType(newType);
2421 } else {
2422 // We do not support this yet.
2423 cannotConvert = kTRUE;
2424 }
2425 } else {
2426 // We have no clue
2427 printf("%s We have no clue\n", dm->GetName());
2428 cannotConvert = kTRUE;
2429 }
2430 } else if (element->GetType() == kObjectP || element->GetType() == kAnyP) {
2431 if (newType != -1) {
2432 if (newType == kObjectP || newType == kAnyP ) {
2433 // nothing to do}
2434 } else {
2435 cannotConvert = kTRUE;
2436 }
2437 } else {
2438 // We have no clue
2439 cannotConvert = kTRUE;
2440 }
2441 }
2442 }
2443 if (cannotConvert) {
2444 element->SetNewType(-2);
2445 if (gDebug > 0) {
2446 // coverity[mixed_enums] - All the values of EDataType have the same semantic in EReadWrite
2447 Info("BuildOld", "element: %s %s::%s has new type: %s/%d", element->GetTypeName(), GetName(), element->GetName(), dm ? dm->GetFullTypeName() : TDataType::GetTypeName((EDataType)newType), newType);
2448 }
2449 }
2450 } else {
2451 element->SetNewType(-1);
2452 offset = kMissing;
2453 element->SetOffset(kMissing);
2454 }
2455
2456 if (offset != kMissing && fClass->GetState() <= TClass::kEmulated && !fClass->fIsSyntheticPair) {
2457 // Note the initialization in this case are
2458 // delayed until __after__ the schema evolution
2459 // section, just in case the info has changed.
2460
2461 // The class is NOT known to Cling, i.e. is emulated,
2462 // and we need to use the calculated offset.
2463
2464 Int_t asize;
2465 if (element->GetType() == TStreamerInfo::kSTL &&
2466 strcmp(element->GetName(),"This") == 0 &&
2467 strcmp(element->GetTypeName(),GetName()) == 0 &&
2469 // Humm .. we are missing the collection Proxy
2470 // for a proxied (custom) collection ... avoid
2471 // an infinite recursion and take a wild guess
2472 asize = sizeof(std::vector<int>);
2473 } else {
2474 // Regular case
2475 asize = element->GetSize();
2476 }
2477 // align the non-basic data types (required on alpha and IRIX!!)
2478 if ((offset % kSizeOfPtr) != 0) {
2479 offset = offset - (offset % kSizeOfPtr) + kSizeOfPtr;
2480 }
2481 element->SetOffset(offset);
2482 offset += asize;
2483 }
2484
2485 if (!wasCompiled && rules) {
2486 if (rules.HasRuleWithSource( element->GetName(), kTRUE ) ) {
2487
2488 if (allocClass == 0) {
2489 infoalloc = (TStreamerInfo *)Clone(TString::Format("%s@@%d",GetName(),GetOnFileClassVersion()));
2490 if (!infoalloc) {
2491 Error("BuildOld","Unable to create the StreamerInfo for %s.",TString::Format("%s@@%d",GetName(),GetOnFileClassVersion()).Data());
2492 } else {
2493 infoalloc->SetBit(kBuildOldUsed,false);
2494 infoalloc->BuildCheck();
2495 infoalloc->BuildOld();
2496 allocClass = infoalloc->GetClass();
2497 }
2498 }
2499
2500 // Now that we are caching the unconverted element, we do not assign it to the real type even if we could have!
2501 if (element->GetNewType()>0 /* intentionally not including base class for now */
2502 && !rules.HasRuleWithTarget( element->GetName(), kTRUE ) ) {
2503
2504 TStreamerElement *copy = (TStreamerElement*)element->Clone();
2505 R__TObjArray_InsertBefore( fElements, copy, element );
2506 next(); // move the cursor passed the insert object.
2508 element = copy;
2509
2510 // Warning("BuildOld","%s::%s is not set from the version %d of %s (You must add a rule for it)\n",GetName(), element->GetName(), GetClassVersion(), GetName() );
2511 } else {
2512 // If the element is just cached and not repeat, we need to inject an element
2513 // to insure the writing.
2514 TStreamerElement *writecopy = (TStreamerElement*)element->Clone();
2515 R__TObjArray_InsertAfter( fElements, writecopy, element );
2516 next(); // move the cursor passed the insert object.
2517 writecopy->SetBit(TStreamerElement::kWrite);
2518 writecopy->SetNewType( writecopy->GetType() );
2519 writecopy->SetOffset(element->GetOffset());
2520 }
2522 element->SetNewType( element->GetType() );
2523 element->SetOffset(infoalloc ? infoalloc->GetOffset(element->GetName()) : 0);
2524 } else if (rules.HasRuleWithTarget( element->GetName(), kTRUE ) ) {
2525 // The data member exist in the onfile StreamerInfo and there is a rule
2526 // that has the same member 'only' has a target ... so this means we are
2527 // asked to ignore the input data ...
2528 if (element->GetType() == kCounter) {
2529 // If the element is a counter, we will need its value to read
2530 // other data member, so let's do so (by not disabling it) even
2531 // if the value will be over-written by a rule.
2532 } else {
2533 element->SetOffset(kMissing);
2534 }
2535 }
2536 } else if (rules && rules.HasRuleWithTarget( element->GetName(), kTRUE ) ) {
2537 // The data member exist in the onfile StreamerInfo and there is a rule
2538 // that has the same member 'only' has a target ... so this means we are
2539 // asked to ignore the input data ...
2540 if (element->GetType() == kCounter) {
2541 // If the element is a counter, we will need its value to read
2542 // other data member, so let's do so (by not disabling it) even
2543 // if the value will be over-written by a rule.
2544 } else {
2545 element->SetOffset(kMissing);
2546 }
2547 }
2548
2549 if (element->GetNewType() == -2) {
2550 Warning("BuildOld", "Cannot convert %s::%s from type: %s to type: %s, skip element", GetName(), element->GetName(), element->GetTypeName(), newClass ? newClass->GetName() : (dm ? dm->GetFullTypeName() : "unknown") );
2551 }
2552 }
2553
2554 // If we get here, this means that there no data member after the last base class
2555 // (or no base class at all).
2556 if (shouldHaveInfoLoc && fNVirtualInfoLoc==0) {
2557 fNVirtualInfoLoc = 1;
2558 fVirtualInfoLoc = new ULong_t[1]; // To allow for a single delete statement.
2559 fVirtualInfoLoc[0] = offset;
2560 offset += sizeof(TStreamerInfo*);
2561 }
2562
2563 // change order , move "bazes" to the end. Workaround old bug
2564 if ((fOldVersion <= 2) && nBaze) {
2566 TObjArray& arr = *fElements;
2567 TObjArray tai(nBaze);
2568 int narr = arr.GetLast() + 1;
2569 int iel;
2570 int jel = 0;
2571 int kel = 0;
2572 for (iel = 0; iel < narr; ++iel) {
2573 element = (TStreamerElement*) arr[iel];
2574 if (element->IsBase() && (element->IsA() != TStreamerBase::Class())) {
2575 tai[kel++] = element;
2576 } else {
2577 arr[jel++] = element;
2578 }
2579 }
2580 for (kel = 0; jel < narr;) {
2581 arr[jel++] = tai[kel++];
2582 }
2583 }
2584
2585 // Now add artificial TStreamerElement (i.e. rules that creates new members or set transient members).
2586 if (!wasCompiled) InsertArtificialElements(rules);
2587
2588 if (!wasCompiled && allocClass) {
2589
2590 TStreamerElement *el = new TStreamerArtificial("@@alloc","", 0, TStreamerInfo::kCacheNew, allocClass->GetName());
2592
2593 el = new TStreamerArtificial("@@dealloc","", 0, TStreamerInfo::kCacheDelete, allocClass->GetName());
2594 fElements->Add( el );
2595 }
2596
2597 Compile();
2598}
2599
2600////////////////////////////////////////////////////////////////////////////////
2601/// If opt contains 'built', reset this StreamerInfo as if Build or BuildOld
2602/// was never called on it (useful to force their re-running).
2603
2605{
2606 TString opt = option;
2607 opt.ToLower();
2608
2609 if (opt.Contains("build")) {
2611
2612 delete [] fComp; fComp = 0;
2613 delete [] fCompFull; fCompFull= 0;
2614 delete [] fCompOpt; fCompOpt = 0;
2615 fNdata = 0;
2616 fNfulldata = 0;
2617 fNslots= 0;
2618 fSize = 0;
2621
2625 if (fReadText) fReadText->fActions.clear();
2629 if (fWriteText) fWriteText->fActions.clear();
2630 }
2631}
2632
2633namespace {
2634 // TMemberInfo
2635 // Local helper class to be able to compare data member represented by
2636 // 2 distinct TStreamerInfos
2637 class TMemberInfo {
2638 public:
2639 TClass *fParent;
2640 TString fName;
2641 TString fClassName;
2642 TString fComment;
2643 Int_t fDataType;
2644
2645 TMemberInfo(TClass *parent) : fParent(parent) {};
2646
2647 void SetDataType(Int_t datatype) {
2648 fDataType = datatype;
2649 }
2650
2651 void SetName(const char *name) {
2652 fName = name;
2653 }
2654 void SetClassName(const char *name) {
2656 }
2657 void SetComment(const char *title) {
2658 const char *left = strstr(title,"[");
2659 if (left) {
2660 const char *right = strstr(left,"]");
2661 if (right) {
2662 ++left;
2663 fComment.Append(left,right-left);
2664 }
2665 }
2666 }
2667 void Clear() {
2668 fName.Clear();
2669 fClassName.Clear();
2670 fComment.Clear();
2671 }
2672 /* Hide this not yet used implementation to suppress warnings message
2673 from icc 11
2674 Bool_t operator==(const TMemberInfo &other) {
2675 return fName==other.fName
2676 && fClassName == other.fClassName
2677 && fComment == other.fComment;
2678 }
2679 */
2680 Bool_t operator!=(const TMemberInfo &other) {
2681 if (fName != other.fName) return kTRUE;
2682 if (fDataType < TStreamerInfo::kObject) {
2683 // For simple type, let compare the data type
2684 if (fDataType != other.fDataType) {
2685 if ( (fDataType == 4 && other.fDataType == 16)
2686 || (fDataType == 16 && other.fDataType == 4) ) {
2687 // long and 'long long' have the same file format
2688 } else if ( (fDataType == 14 && other.fDataType == 17)
2689 || (fDataType == 17 && other.fDataType == 14) ) {
2690 // unsigned long and 'unsigned long long' have the same file format
2691 } else if ( (fDataType == 3 && other.fDataType == 6)
2692 ||(fDataType == 6 && other.fDataType == 3) ){
2693 // Int_t and kCounter. As the switch from Int_t (3) to
2694 // kCounter (6) might be triggered by a derived class using
2695 // the field as an array size, the class itself has no
2696 // control on what the field type really use.
2697 } else {
2698 return kTRUE;
2699 }
2700 }
2701 } else if (fClassName != other.fClassName) {
2702 if ( (fClassName == "long" && (other.fClassName == "long long" || other.fClassName == "Long64_t"))
2703 || ( (fClassName == "long long" || fClassName == "Long64_t") && other.fClassName == "long") ) {
2704 // This is okay both have the same on file format.
2705 } else if ( (fClassName == "unsigned long" && (other.fClassName == "unsigned long long" || other.fClassName == "ULong64_t"))
2706 || ( (fClassName == "unsigned long long" || fClassName == "ULong64_t") && other.fClassName == "unsigned long") ) {
2707 // This is okay both have the same on file format.
2708 } else if (TClassEdit::IsSTLCont(fClassName)) {
2710 TString othername = TClassEdit::ShortType( other.fClassName, TClassEdit::kDropStlDefault );
2711 if (name != othername) {
2713 TClass *otherCl = TClass::GetClass(othername);
2714 if (!CollectionMatch(cl,otherCl)) {
2715 TClass *oldFixedClass = FixCollectionV5(fParent,cl,otherCl);
2716 if (!oldFixedClass || !CollectionMatch(oldFixedClass,otherCl)) {
2717 return kTRUE;
2718 }
2719 }
2720 }
2721 } else {
2722 return kTRUE;
2723 }
2724 }
2725 return fComment != other.fComment;
2726 }
2727 };
2728}
2729
2730////////////////////////////////////////////////////////////////////////////////
2731/// Emulated a call ShowMembers() on the obj of this class type, passing insp and parent.
2732
2733void TStreamerInfo::CallShowMembers(const void* obj, TMemberInspector &insp, Bool_t isTransient) const
2734{
2735 TIter next(fElements);
2736 TStreamerElement* element = (TStreamerElement*) next();
2737
2738 TString elementName;
2739
2740 for (; element; element = (TStreamerElement*) next()) {
2741
2742 // Skip elements which have not been allocated memory.
2743 if (element->GetOffset() == kMissing) {
2744 continue;
2745 }
2746
2747 char* eaddr = ((char*)obj) + element->GetOffset();
2748
2749 if (element->IsBase()) {
2750 // Nothing to do this round.
2751 } else if (element->IsaPointer()) {
2752 elementName.Form("*%s",element->GetFullName());
2753 insp.Inspect(fClass, insp.GetParent(), elementName.Data(), eaddr, isTransient);
2754 } else {
2755 insp.Inspect(fClass, insp.GetParent(), element->GetFullName(), eaddr, isTransient);
2756 Int_t etype = element->GetType();
2757 switch(etype) {
2758 case kObject:
2759 case kAny:
2760 case kTObject:
2761 case kTString:
2762 case kTNamed:
2763 case kSTL:
2764 {
2765 TClass *ecl = element->GetClassPointer();
2766 if (ecl && (fClass!=ecl /* This happens 'artificially for stl container see the use of "This" */)) {
2767 insp.InspectMember(ecl, eaddr, TString(element->GetName()) + ".", isTransient);
2768 }
2769 break;
2770 }
2771 } // switch(etype)
2772 } // if IsaPointer()
2773 } // Loop over elements
2774
2775 // And now do the base classes
2776 next.Reset();
2777 element = (TStreamerElement*) next();
2778 for (; element; element = (TStreamerElement*) next()) {
2779 if (element->IsBase()) {
2780 // Skip elements which have not been allocated memory.
2781 if (element->GetOffset() == kMissing) {
2782 continue;
2783 }
2784
2785 char* eaddr = ((char*)obj) + element->GetOffset();
2786
2787 TClass *ecl = element->GetClassPointer();
2788 if (ecl) {
2789 ecl->CallShowMembers(eaddr, insp, isTransient);
2790 }
2791 } // If is a abse
2792 } // Loop over elements
2793}
2794
2795////////////////////////////////////////////////////////////////////////////////
2796/// Make a clone of an object using the Streamer facility.
2797/// If newname is specified, this will be the name of the new object.
2798
2799TObject *TStreamerInfo::Clone(const char *newname) const
2800{
2801 TStreamerInfo *newinfo = (TStreamerInfo*)TNamed::Clone(newname);
2802 if (newname && newname[0] && fName != newname) {
2803 TObjArray *newelems = newinfo->GetElements();
2804 Int_t ndata = newelems->GetEntriesFast();
2805 for(Int_t i = 0; i < ndata; ++i) {
2806 TObject *element = newelems->UncheckedAt(i);
2807 if (element->IsA() == TStreamerLoop::Class()) {
2808 TStreamerLoop *eloop = (TStreamerLoop*)element;
2809 if (fName == eloop->GetCountClass()) {
2810 eloop->SetCountClass(newname);
2811 eloop->Init();
2812 }
2813 } else if (element->IsA() == TStreamerBasicPointer::Class()) {
2815 if (fName == eptr->GetCountClass()) {
2816 eptr->SetCountClass(newname);
2817 eptr->Init();
2818 }
2819 }
2820 }
2821 }
2822 ++fgCount;
2823 newinfo->fNumber = fgCount;
2824 return newinfo;
2825}
2826
2827////////////////////////////////////////////////////////////////////////////////
2828/// Return True if the current StreamerInfo in cl or info is equivalent to this TStreamerInfo.
2829///
2830/// In this context 'Equivalent' means the same number of persistent data member which the same actual C++ type and
2831/// the same name.
2832/// If 'warn' is true, Warning message are printed to explicit the differences.
2833/// If 'complete' is false, stop at the first error, otherwise continue until all members have been checked.
2834
2836{
2837 Bool_t result = kTRUE;
2838 R__ASSERT( (cl==0 || info==0) && (cl!=0 || info!=0) /* must compare to only one thing! */);
2839
2840 TString name;
2841 TString type;
2842 TStreamerElement *el;
2843 TStreamerElement *infoel = 0;
2844
2845 TIter next(GetElements());
2846 TIter infonext((TList*)0);
2847 TIter basenext((TList*)0);
2848 TIter membernext((TList*)0);
2849 if (info) {
2850 infonext = info->GetElements();
2851 }
2852 if (cl) {
2853 TList *tlb = cl->GetListOfBases();
2854 if (tlb) { // Loop over bases
2855 basenext = tlb;
2856 }
2857 tlb = cl->GetListOfDataMembers();
2858 if (tlb) {
2859 membernext = tlb;
2860 }
2861 }
2862
2863 // First let's compare base classes
2864 Bool_t done = kFALSE;
2865 TString localClass;
2866 TString otherClass;
2867 while(!done) {
2868 localClass.Clear();
2869 otherClass.Clear();
2870 el = (TStreamerElement*)next();
2871 if (el && el->IsBase()) {
2872 localClass = el->GetName();
2873 } else {
2874 el = 0;
2875 }
2876 if (cl) {
2877 TBaseClass *tbc = (TBaseClass*)basenext();
2878 if (tbc) {
2879 otherClass = tbc->GetName();
2880 } else if (el==0) {
2881 done = kTRUE;
2882 break;
2883 }
2884 } else {
2885 infoel = (TStreamerElement*)infonext();
2886 if (infoel && infoel->IsBase()) {
2887 otherClass = infoel->GetName();
2888 } else if (el==0) {
2889 done = kTRUE;
2890 break;
2891 }
2892 }
2893 if (TClassEdit::IsSTLCont(localClass)) {
2894 localClass = TClassEdit::ShortType( localClass, TClassEdit::kDropStlDefault );
2895 otherClass = TClassEdit::ShortType( otherClass, TClassEdit::kDropStlDefault );
2896 }
2897 // Need to normalize the name
2898 if (localClass != otherClass) {
2899 if (warn) {
2900 if (el==0) {
2901 Warning("CompareContent",
2902 "The in-memory layout version %d for class '%s' has a base class (%s) that the on-file layout version %d does not have.",
2903 GetClassVersion(), GetName(), otherClass.Data(), GetClassVersion());
2904 } else if (otherClass.Length()==0) {
2905 Warning("CompareContent",
2906 "The on-file layout version %d for class '%s' has a base class (%s) that the in-memory layout version %d does not have",
2907 GetClassVersion(), GetName(), localClass.Data(), GetClassVersion());
2908 } else {
2909 Warning("CompareContent",
2910 "One base class of the on-file layout version %d and of the in memory layout version %d for '%s' is different: '%s' vs '%s'",
2911 GetClassVersion(), GetClassVersion(), GetName(), localClass.Data(), otherClass.Data());
2912 }
2913 }
2914 if (!complete) return kFALSE;
2915 result = result && kFALSE;
2916 }
2917 if (cl) {
2918 TStreamerBase *localBase = dynamic_cast<TStreamerBase*>(el);
2919 if (!localBase) continue;
2920 // We already have localBaseClass == otherBaseClass
2921 TClass *otherBaseClass = localBase->GetClassPointer();
2922 if (!otherBaseClass) continue;
2923 if (otherBaseClass->IsVersioned() && localBase->GetBaseVersion() != otherBaseClass->GetClassVersion()) {
2924 TString msg;
2925 msg.Form(" The StreamerInfo of class %s read from %s%s\n"
2926 " has the same version (=%d) as the active class but a different checksum.\n"
2927 " You should update the version to ClassDef(%s,%d).\n"
2928 " The objects on this file might not be readable because:\n"
2929 " The in-memory layout version %d for class '%s' has a base class (%s) with version %d but the on-file layout version %d recorded the version number %d for this base class (%s).",
2930 GetName(), file ? "file " : "", file ? file->GetName() : "", fClassVersion, GetName(), fClassVersion + 1,
2931 GetClassVersion(), GetName(), otherClass.Data(), otherBaseClass->GetClassVersion(),
2932 GetClassVersion(), localBase->GetBaseVersion(), localClass.Data());
2933 TStreamerBase *otherBase = (TStreamerBase*)cl->GetStreamerInfo()->GetElements()->FindObject(otherClass);
2934 otherBase->SetErrorMessage(msg);
2935
2936 } else if (!otherBaseClass->IsVersioned() && localBase->GetBaseCheckSum() != otherBaseClass->GetCheckSum()) {
2937 TVirtualStreamerInfo *localBaseInfo = otherBaseClass->FindStreamerInfo(localBase->GetBaseCheckSum());
2938 if (!localBaseInfo) {
2939 // We are likely in the situation where the base class comes after the derived
2940 // class in the TFile's list of StreamerInfo, so it has not yet been loaded,
2941 // let's see if it is there.
2942 const TList *list = file->GetStreamerInfoCache();
2943 localBaseInfo = list ? (TStreamerInfo*)list->FindObject(localBase->GetName()) : 0;
2944 }
2945 if (!localBaseInfo) {
2946 TString msg;
2947 msg.Form(" The StreamerInfo of the base class %s (of class %s) read from %s%s\n"
2948 " refers to a checksum (%x) that can not be found neither in memory nor in the file.\n",
2949 otherBaseClass->GetName(), localClass.Data(),
2950 file ? "file " : "", file ? file->GetName() : "",
2951 localBase->GetBaseCheckSum()
2952 );
2953 TStreamerBase *otherBase = (TStreamerBase*)cl->GetStreamerInfo()->GetElements()->FindObject(otherClass);
2954 otherBase->SetErrorMessage(msg);
2955 continue;
2956 }
2957 if (localBaseInfo->CompareContent(otherBaseClass,0,kFALSE,kFALSE,file) ) {
2958 // They are equivalent, no problem.
2959 continue;
2960 }
2961 TString msg;
2962 msg.Form(" The StreamerInfo of class %s read from %s%s\n"
2963 " has the same version (=%d) as the active class but a different checksum.\n"
2964 " You should update the version to ClassDef(%s,%d).\n"
2965 " The objects on this file might not be readable because:\n"
2966 " The in-memory layout version %d for class '%s' has a base class (%s) with checksum %x but the on-file layout version %d recorded the checksum value %x for this base class (%s).",
2967 GetName(), file ? "file " : "", file ? file->GetName() : "", fClassVersion, GetName(), fClassVersion + 1,
2968 GetClassVersion(), GetName(), otherClass.Data(), otherBaseClass->GetCheckSum(),
2969 GetClassVersion(), localBase->GetBaseCheckSum(), localClass.Data());
2970 TStreamerBase *otherBase = (TStreamerBase*)cl->GetStreamerInfo()->GetElements()->FindObject(otherClass);
2971 otherBase->SetErrorMessage(msg);
2972 }
2973 } else {
2974 TStreamerBase *localBase = dynamic_cast<TStreamerBase*>(el);
2975 TStreamerBase *otherBase = dynamic_cast<TStreamerBase*>(infoel);
2976 if (!localBase || !otherBase) continue;
2977
2978 // We already have localBaseClass == otherBaseClass
2979 TClass *otherBaseClass = localBase->GetClassPointer();
2980 if (otherBaseClass->IsVersioned() && localBase->GetBaseVersion() != otherBase->GetBaseVersion()) {
2981 TString msg;
2982 msg.Form(" The StreamerInfo of class %s read from %s%s\n"
2983 " has the same version (=%d) as the active class but a different checksum.\n"
2984 " You should update the version to ClassDef(%s,%d).\n"
2985 " The objects on this file might not be readable because:\n"
2986 " The in-memory layout version %d for class '%s' has a base class (%s) with version %d but the on-file layout version %d recorded the version number %d for this base class (%s).",
2987 GetName(), file ? "file " : "", file ? file->GetName() : "", fClassVersion, GetName(), fClassVersion + 1,
2988 GetClassVersion(), GetName(), otherClass.Data(), otherBase->GetBaseVersion(),
2989 GetClassVersion(), localBase->GetBaseVersion(), localClass.Data());
2990 otherBase->SetErrorMessage(msg);
2991
2992 } else if (!otherBaseClass->IsVersioned() && localBase->GetBaseCheckSum() != otherBase->GetBaseCheckSum())
2993 {
2994 TVirtualStreamerInfo *localBaseInfo = otherBaseClass->FindStreamerInfo(localBase->GetBaseCheckSum());
2995 TVirtualStreamerInfo *otherBaseInfo = otherBaseClass->FindStreamerInfo(otherBase->GetBaseCheckSum());
2996 if (localBaseInfo == otherBaseInfo ||
2997 localBaseInfo->CompareContent(0,otherBaseInfo,kFALSE,kFALSE,file) ) {
2998 // They are equivalent, no problem.
2999 continue;
3000 }
3001 TString msg;
3002 msg.Form(" The StreamerInfo of class %s read from %s%s\n"
3003 " has the same version (=%d) as the active class but a different checksum.\n"
3004 " You should update the version to ClassDef(%s,%d).\n"
3005 " The objects on this file might not be readable because:\n"
3006 " The in-memory layout version %d for class '%s' has a base class (%s) with checksum %x but the on-file layout version %d recorded the checksum value %x for this base class (%s).",
3007 GetName(), file ? "file " : "", file ? file->GetName() : "", fClassVersion, GetName(), fClassVersion + 1,
3008 GetClassVersion(), GetName(), otherClass.Data(), otherBase->GetBaseCheckSum(),
3009 GetClassVersion(), localBase->GetBaseCheckSum(), localClass.Data());
3010 otherBase->SetErrorMessage(msg);
3011 }
3012 }
3013 }
3014 if (!result && !complete) {
3015 return result;
3016 }
3017 // Next the datamembers
3018 done = kFALSE;
3019 next.Reset();
3020 infonext.Reset();
3021
3022 TMemberInfo local(GetClass());
3023 TMemberInfo other(cl ? cl : info->GetClass());
3024 UInt_t idx = 0;
3025 while(!done) {
3026 local.Clear();
3027 other.Clear();
3028 el = (TStreamerElement*)next();
3029 while (el && (el->IsBase() || el->IsA() == TStreamerArtificial::Class())) {
3030 el = (TStreamerElement*)next();
3031 ++idx;
3032 }
3033 if (el) {
3034 local.SetName( el->GetName() );
3035 local.SetClassName( el->GetTypeName() );
3036 local.SetComment( el->GetTitle() );
3037 local.SetDataType( el->GetType() );
3038 }
3039 if (cl) {
3040 TDataMember *tdm = (TDataMember*)membernext();
3041 while(tdm && ( (!tdm->IsPersistent()) || (tdm->Property()&kIsStatic) || (el && local.fName != tdm->GetName()) )) {
3042 tdm = (TDataMember*)membernext();
3043 }
3044 if (tdm) {
3045 other.SetName( tdm->GetName() );
3046 other.SetClassName( tdm->GetTrueTypeName() );
3047 other.SetComment( tdm->GetTitle() );
3048 if (tdm->GetDataType()) {
3049 // Need to update the type for arrays.
3050 if (tdm->IsaPointer()) {
3051 if (tdm->GetDataType()->GetType() == TVirtualStreamerInfo::kChar && !tdm->GetArrayDim() && tdm->GetArrayIndex()[0]==0) {
3052 other.SetDataType( TVirtualStreamerInfo::kCharStar );
3053 } else {
3054 other.SetDataType( tdm->GetDataType()->GetType() + TVirtualStreamerInfo::kOffsetP);
3055 }
3056 } else {
3057 if (tdm->GetArrayDim()) {
3058 other.SetDataType( tdm->GetDataType()->GetType() + TVirtualStreamerInfo::kOffsetL);
3059 } else {
3060 other.SetDataType( tdm->GetDataType()->GetType() );
3061 }
3062 }
3063 }
3064 } else if (el==0) {
3065 done = kTRUE;
3066 break;
3067 }
3068 } else {
3069 infoel = (TStreamerElement*)infonext();
3070 while (infoel && (infoel->IsBase() || infoel->IsA() == TStreamerArtificial::Class())) {
3071 infoel = (TStreamerElement*)infonext();
3072 }
3073 if (infoel) {
3074 other.SetName( infoel->GetName() );
3075 other.SetClassName( infoel->GetTypeName() );
3076 other.SetComment( infoel->GetTitle() );
3077 other.SetDataType( infoel->GetType() );
3078 } else if (el==0) {
3079 done = kTRUE;
3080 break;
3081 }
3082 }
3083 if (local!=other) {
3084 if (warn) {
3085 if (!el) {
3086 Warning("CompareContent","The following data member of\nthe in-memory layout version %d of class '%s' is missing from \nthe on-file layout version %d:\n"
3087 " %s %s; //%s"
3089 ,other.fClassName.Data(),other.fName.Data(),other.fComment.Data());
3090
3091 } else if (other.fName.Length()==0) {
3092 Warning("CompareContent","The following data member of\nthe in-memory layout version %d of class '%s' is missing from \nthe on-file layout version %d:\n"
3093 " %s %s; //%s"
3095 ,local.fClassName.Data(),local.fName.Data(),local.fComment.Data());
3096 } else {
3097 Warning("CompareContent","The following data member of\nthe on-file layout version %d of class '%s' differs from \nthe in-memory layout version %d:\n"
3098 " %s %s; //%s\n"
3099 "vs\n"
3100 " %s %s; //%s"
3102 ,local.fClassName.Data(),local.fName.Data(),local.fComment.Data()
3103 ,other.fClassName.Data(),other.fName.Data(),other.fComment.Data());
3104 }
3105 }
3106 result = result && kFALSE;
3107 if (!complete) return result;
3108 }
3109 ++idx;
3110 }
3111 return result;
3112}
3113
3114
3115////////////////////////////////////////////////////////////////////////////////
3116/// Compute total size of all persistent elements of the class
3117
3119{
3120 if (this == fClass->GetCurrentStreamerInfo()) {
3123 return;
3124 }
3125 }
3126
3128 //faster and more precise to use last element offset +size
3129 //on 64 bit machines, offset may be forced to be a multiple of 8 bytes
3130 fSize = element ? element->GetOffset() + element->GetSize() : 0;
3131 if (fNVirtualInfoLoc > 0 && (fVirtualInfoLoc[0]+sizeof(TStreamerInfo*)) >= (ULong_t)fSize) {
3132 fSize = fVirtualInfoLoc[0] + sizeof(TStreamerInfo*);
3133 }
3134
3135 // On some platform and in some case of layout non-basic data types needs
3136 // to be aligned. So let's be on the safe side and align on the size of
3137 // the pointers. (Question: is that the right thing on x32 ABI ?)
3138 constexpr size_t kSizeOfPtr = sizeof(void*);
3139 if ((fSize % kSizeOfPtr) != 0) {
3140 fSize = fSize - (fSize % kSizeOfPtr) + kSizeOfPtr;
3141 }
3142}
3143
3144////////////////////////////////////////////////////////////////////////////////
3145/// Recursively mark streamer infos for writing to a file.
3146///
3147/// Will force this TStreamerInfo to the file and also
3148/// all the dependencies.
3149/// If argument force > 0 the loop on class dependencies is forced.
3150/// This function is called when streaming a class that contains
3151/// a null pointer. In this case, the TStreamerInfo for the class
3152/// with the null pointer must be written to the file and also all
3153/// the TStreamerInfo of all the classes referenced by the class.
3154/// We must be given a file to write to.
3155
3157{
3158 if (!file || fNumber < 0) {
3159 return;
3160 }
3161 // Get the given file's list of streamer infos marked for writing.
3162 TArrayC* cindex = file->GetClassIndex();
3163 //the test below testing fArray[fNumber]>1 is to avoid a recursivity
3164 //problem in some cases like:
3165 // class aProblemChild: public TNamed {
3166 // aProblemChild *canBeNull;
3167 // };
3168 if ( // -- Done if already marked, and we are not forcing, or forcing is blocked.
3169 (cindex->fArray[fNumber] && !force) || // Streamer info is already marked, and not forcing, or
3170 (cindex->fArray[fNumber] > 1) // == 2 means ignore forcing to prevent infinite recursion.
3171 ) {
3172 return;
3173 }
3174 // We do not want to write streamer info to the file
3175 // for std::string.
3176 static TClassRef string_classref("string");
3177 if (fClass == string_classref) { // We are std::string.
3178 return;
3179 }
3180 // We do not want to write streamer info to the file
3181 // for STL containers.
3182 if (fClass==0) {
3183 // Build or BuildCheck has not been called yet.
3184 // Let's use another means of checking.
3185 if (fElements && fElements->GetEntriesFast()==1 && strcmp("This",fElements->UncheckedAt(0)->GetName())==0) {
3186 // We are an STL collection.
3187 return;
3188 }
3189 } else if (fClass->GetCollectionProxy()) { // We are an STL collection.
3190 return;
3191 }
3192 // Mark ourselves for output, and block
3193 // forcing to prevent infinite recursion.
3194 cindex->fArray[fNumber] = 2;
3195 // Signal the file that the marked streamer info list has changed.
3196 cindex->fArray[0] = 1;
3197 // Recursively mark the streamer infos for
3198 // all of our elements.
3199 TIter next(fElements);
3200 TStreamerElement* element = (TStreamerElement*) next();
3201 for (; element; element = (TStreamerElement*) next()) {
3202 if (element->IsTransient()) continue;
3203 TClass* cl = element->GetClassPointer();
3204 if (cl) {
3205 TVirtualStreamerInfo* si = 0;
3206 if (cl->Property() & kIsAbstract) {
3207 // If the class of the element is abstract, register the
3208 // TStreamerInfo only if it has already been built.
3209 // Otherwise call cl->GetStreamerInfo() would generate an
3210 // incorrect StreamerInfo.
3211 si = cl->GetCurrentStreamerInfo();
3212 } else {
3213 si = cl->GetStreamerInfo();
3214 }
3215 if (si) {
3216 si->ForceWriteInfo(file, force);
3217 }
3218 }
3219 }
3220}
3221
3222////////////////////////////////////////////////////////////////////////////////
3223/// Assuming that obj points to (the part of) an object that is of the
3224/// type described by this streamerInfo, return the actual type of the
3225/// object (i.e. the type described by this streamerInfo is a base class
3226/// of the actual type of the object.
3227/// This routine should only be called if the class described by this
3228/// StreamerInfo is 'emulated'.
3229
3231{
3233
3234 if (fNVirtualInfoLoc != 0) {
3235 TStreamerInfo *allocator = *(TStreamerInfo**)( (const char*)obj + fVirtualInfoLoc[0] );
3236 if (allocator) return allocator->GetClass();
3237 }
3238 return (TClass*)fClass;
3239}
3240
3241////////////////////////////////////////////////////////////////////////////////
3242/// Return true if the checksum passed as argument is one of the checksum
3243/// value produced by the older checksum calculation algorithm.
3244
3246{
3247 for(UInt_t i = 1; i < TClass::kLatestCheckSum; ++i) {
3248 if ( checksum == GetCheckSum( (TClass::ECheckSum) i) ) return kTRUE;
3249 }
3250 return kFALSE;
3251}
3252
3253////////////////////////////////////////////////////////////////////////////////
3254/// Recalculate the checksum of this TStreamerInfo based on its code.
3255///
3256/// The class ckecksum is used by the automatic schema evolution algorithm
3257/// to uniquely identify a class version.
3258/// The check sum is built from the names/types of base classes and
3259/// data members.
3260/// The valid range of code is determined by ECheckSum.
3261/// - kNoEnum: data members of type enum are not counted in the checksum
3262/// - kNoRange: return the checksum of data members and base classes, not including the ranges and array size found in comments.
3263/// - kWithTypeDef: use the sugared type name in the calculation.
3264///
3265/// This is needed for backward compatibility.
3266/// ### WARNING
3267/// This function must be kept in sync with TClass::GetCheckSum.
3268/// They are both used to handle backward compatibility and should both return the same values.
3269/// TStreamerInfo uses the information in TStreamerElement while TClass uses the information
3270/// from TClass::GetListOfBases and TClass::GetListOfDataMembers.
3271/// Original algorithm from Victor Perevovchikov (perev@bnl.gov).
3272
3274{
3275 // kCurrentCheckSum (0) should be kept for backward compatibility, to be
3276 // able to use the inequality checks, we need to set the code to the largest
3277 // value.
3279
3280 UInt_t id = 0;
3281
3282 int il;
3283 TString name = GetName();
3284 TString type;
3285 il = name.Length();
3286 for (int i=0; i<il; i++) id = id*3+name[i];
3287
3288 TIter next(GetElements());
3289 TStreamerElement *el;
3290 // Here we skip he base classes in case this is a pair or STL collection,
3291 // otherwise, on some STL implementations, it can happen that pair has
3292 // base classes which are an internal implementation detail.
3294 while ( (el=(TStreamerElement*)next())) { // loop over bases
3295 if (el->IsBase()) {
3296 name = el->GetName();
3297 il = name.Length();
3298 for (int i=0; i<il; i++) id = id*3+name[i];
3299 if (code > TClass::kNoBaseCheckSum && el->IsA() == TStreamerBase::Class()) {
3300 TStreamerBase *base = (TStreamerBase*)el;
3301 id = id*3 + base->GetBaseCheckSum();
3302 }
3303 }
3304 } /* End of Base Loop */
3305 }
3306
3307 next.Reset();
3308 while ( (el=(TStreamerElement*)next()) ) {
3309 if (el->IsBase()) continue;
3310
3311 // humm can we tell if a TStreamerElement is an enum?
3312 // Maybe something like:
3313 Bool_t isenum = kFALSE;
3314 if ( el->GetType()==3 && gROOT->GetType(el->GetTypeName())==0) {
3315 // If the type is not an enum but a typedef to int then
3316 // el->GetTypeName() should be return 'int'
3317 isenum = kTRUE;
3318 }
3319 if ( (code > TClass::kNoEnum) && isenum) id = id*3 + 1;
3320
3321 name = el->GetName(); il = name.Length();
3322
3323 int i;
3324 for (i=0; i<il; i++) id = id*3+name[i];
3325
3326 if (code == TClass::kReflex || code == TClass::kReflexNoComment) {
3327 // With TClass::kReflexV5 we do not want the Long64 in the name
3328 // nor any typedef.
3330
3331 } else if (code <= TClass::kWithTypeDef) {
3332 // humm ... In the streamerInfo we only have the desugared/normalized
3333 // names, so we are unable to calculate the name with typedefs ...
3334 // except for the case of the ROOT typedef (Int_t, etc.) which are
3335 // kept by TClassEdit::ResolveTypedef(typeName) but not by TCling's
3336 // normalization ...
3337 //
3338 type = el->GetTypeName();
3339 } else {
3341 }
3344 }
3345 if (code == TClass::kReflex || code == TClass::kReflexNoComment) {
3346 type.ReplaceAll("ULong64_t","unsigned long long");
3347 type.ReplaceAll("Long64_t","long long");
3348 type.ReplaceAll("signed char","char");
3349 type.ReplaceAll("<signed char","<char");
3350 type.ReplaceAll(",signed char",",char");
3351 if (type=="signed char") type = "char";
3352 }
3353
3354 il = type.Length();
3355 for (i=0; i<il; i++) id = id*3+type[i];
3356
3357 int dim = el->GetArrayDim();
3358 if (dim) {
3359 for (i=0;i<dim;i++) id = id*3+el->GetMaxIndex(i);
3360 }
3361
3362
3363 if (code > TClass::kNoRange) {
3364 const char *left;
3365 if (code > TClass::kNoRangeCheck)
3367 else
3368 left = strstr(el->GetTitle(),"[");
3369 if (left) {
3370 const char *right = strstr(left,"]");
3371 if (right) {
3372 ++left;
3373 while (left != right) {
3374 id = id*3 + *left;
3375 ++left;
3376 }
3377 }
3378 }
3379 }
3380 }
3381 return id;
3382}
3383
3384////////////////////////////////////////////////////////////////////////////////
3385
3386static void R__WriteConstructorBody(FILE *file, TIter &next)
3387{
3388 TStreamerElement *element = 0;
3389 next.Reset();
3390 while ((element = (TStreamerElement*)next())) {
3395 if(element->GetArrayLength() <= 1) {
3396 fprintf(file," %s = 0;\n",element->GetName());
3397 } else {
3398 fprintf(file," memset(%s,0,%d);\n",element->GetName(),element->GetSize());
3399 }
3400 }
3401 if (TVirtualStreamerInfo::kOffsetP <= element->GetType() && element->GetType() < TVirtualStreamerInfo::kObject ) {
3402 fprintf(file," %s = 0;\n",element->GetName());
3403 }
3404 }
3405}
3406
3407static constexpr int str_length(const char* str)
3408{
3409 return *str ? 1 + str_length(str + 1) : 0;
3410}
3411
3412////////////////////////////////////////////////////////////////////////////////
3413/// Return true if the element is auto_ptr or unique_ptr
3414
3415static bool R__IsUniquePtr(TStreamerElement *element) {
3416
3417 constexpr auto auto_ptr_len = str_length("auto_ptr<");
3418 constexpr auto unique_ptr_len = str_length("unique_ptr<");
3419
3420 const char *name = element->GetTypeNameBasic();
3421
3422 return ((strncmp(name, "auto_ptr<", auto_ptr_len) == 0)
3423 || (strncmp(name, "unique_ptr<", unique_ptr_len) == 0));
3424}
3425
3426////////////////////////////////////////////////////////////////////////////////
3427/// Write down the body of the 'move' constructor.
3428
3429static void R__WriteMoveConstructorBody(FILE *file, const TString &protoname, TIter &next)
3430{
3431 TStreamerElement *element = 0;
3432 next.Reset();
3433 Bool_t atstart = kTRUE;
3434 while ((element = (TStreamerElement*)next())) {
3435 if (element->IsBase()) {
3436 if (atstart) { fprintf(file," : "); atstart = kFALSE; }
3437 else fprintf(file," , ");
3438 fprintf(file, "%s(const_cast<%s &>( rhs ))\n", element->GetName(),protoname.Data());
3439 } else {
3440 if (element->GetArrayLength() <= 1) {
3441 if (atstart) { fprintf(file," : "); atstart = kFALSE; }
3442 else fprintf(file," , ");
3443 if (R__IsUniquePtr(element)) {
3444 fprintf(file, "%s(const_cast<%s &>( rhs ).%s.release() )\n",element->GetName(),protoname.Data(),element->GetName());
3445 } else {
3446 fprintf(file, "%s(const_cast<%s &>( rhs ).%s)\n",element->GetName(),protoname.Data(),element->GetName());
3447 }
3448 }
3449 }
3450 }
3451 fprintf(file,"{\n");
3452 fprintf(file," // This is NOT a copy constructor. This is actually a move constructor (for stl container's sake).\n");
3453 fprintf(file," // Use at your own risk!\n");
3454 fprintf(file," (void)rhs; // avoid warning about unused parameter\n");
3455 next.Reset();
3456 Bool_t defMod = kFALSE;
3457 while ((element = (TStreamerElement*)next())) {
3461 {
3462 if (!defMod) { fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE; };
3463 const char *ename = element->GetName();
3464 const char *colon2 = strstr(ename,"::");
3465 if (colon2) ename = colon2+2;
3466 if(element->GetArrayLength() <= 1) {
3467 fprintf(file," modrhs.%s = 0;\n",ename);
3468 } else {
3469 fprintf(file," memset(modrhs.%s,0,%d);\n",ename,element->GetSize());
3470 }
3471 } else {
3472 const char *ename = element->GetName();
3473 if (element->GetType() == kCharStar) {
3474 if (!defMod) {
3475 fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE;
3476 };
3477 fprintf(file," modrhs.%s = 0;\n",ename);
3478 } else if (TVirtualStreamerInfo::kOffsetP <= element->GetType() && element->GetType() < TVirtualStreamerInfo::kObject ) {
3479 if (!defMod) {
3480 fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE;
3481 };
3482 fprintf(file," modrhs.%s = 0;\n",ename);
3483 } else if (element->GetArrayLength() > 1) {
3484 // FIXME: Need to add support for variable length array.
3485 if (element->GetArrayDim() == 1) {
3486 fprintf(file," for (Int_t i=0;i<%d;i++) %s[i] = rhs.%s[i];\n",element->GetArrayLength(),ename,ename);
3487 } else if (element->GetArrayDim() >= 2) {
3488 fprintf(file," for (Int_t i=0;i<%d;i++) (&(%s",element->GetArrayLength(),ename);
3489 for (Int_t d = 0; d < element->GetArrayDim(); ++d) {
3490 fprintf(file,"[0]");
3491 }
3492 fprintf(file,"))[i] = (&(rhs.%s",ename);
3493 for (Int_t d = 0; d < element->GetArrayDim(); ++d) {
3494 fprintf(file,"[0]");
3495 }
3496 fprintf(file,"))[i];\n");
3497 }
3498 } else if (element->GetType() == TVirtualStreamerInfo::kSTLp) {
3499 if (!defMod) { fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE; };
3500 fprintf(file," modrhs.%s = 0;\n",ename);
3501 } else if (element->GetType() == TVirtualStreamerInfo::kSTL) {
3502 if (!defMod) {
3503 fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE;
3504 }
3505 TClass *cle = element->GetClassPointer();
3506 TVirtualCollectionProxy *proxy = cle ? element->GetClassPointer()->GetCollectionProxy() : 0;
3507 std::string method_name = "clear";
3508 if (!element->TestBit(TStreamerElement::kDoNotDelete) && proxy && (((TStreamerSTL*)element)->GetSTLtype() == ROOT::kSTLbitset)) {
3509 method_name = "reset";
3510 }
3511 if (element->IsBase()) {
3512 fprintf(file," modrhs.%s();\n", method_name.c_str());
3513 } else {
3514 fprintf(file," modrhs.%s.%s();\n",ename, method_name.c_str());
3515 }
3516 }
3517 }
3518 }
3519}
3520
3521////////////////////////////////////////////////////////////////////////////////
3522
3523static void R__WriteDestructorBody(FILE *file, TIter &next)
3524{
3525 TStreamerElement *element = 0;
3526 next.Reset();
3527 while ((element = (TStreamerElement*)next())) {
3531 {
3532 const char *ename = element->GetName();
3533 const char *colon2 = strstr(ename,"::");
3534 if (colon2) ename = colon2+2;
3536 if(element->GetArrayLength() <= 1) {
3537 fprintf(file," %s = 0;\n",ename);
3538 } else {
3539 fprintf(file," memset(%s,0,%d);\n",ename,element->GetSize());
3540 }
3541 } else {
3542 if(element->GetArrayLength() <= 1) {
3543 fprintf(file," delete %s; %s = 0;\n",ename,ename);
3544 } else {
3545 fprintf(file," for (Int_t i=0;i<%d;i++) delete %s[i]; memset(%s,0,%d);\n",element->GetArrayLength(),ename,ename,element->GetSize());
3546 }
3547 }
3548 }
3549 if (element->GetType() == TVirtualStreamerInfo::kCharStar) {
3550 const char *ename = element->GetName();
3552 fprintf(file," %s = 0;\n",ename);
3553 } else {
3554 fprintf(file," delete [] %s; %s = 0;\n",ename,ename);
3555 }
3556 }
3557 if (TVirtualStreamerInfo::kOffsetP <= element->GetType() && element->GetType() < TVirtualStreamerInfo::kObject ) {
3558 const char *ename = element->GetName();
3560 fprintf(file," %s = 0;\n",ename);
3561 } else if (element->HasCounter()) {
3562 fprintf(file," delete %s; %s = 0;\n",ename,ename);
3563 } else {
3564 fprintf(file," delete [] %s; %s = 0;\n",ename,ename);
3565 }
3566 }
3567 if (element->GetType() == TVirtualStreamerInfo::kSTL || element->GetType() == TVirtualStreamerInfo::kSTLp) {
3568 const char *ename = element->GetName();
3569 const char *prefix = "";
3570 if ( element->GetType() == TVirtualStreamerInfo::kSTLp ) {
3571 prefix = "*";
3572 } else if ( element->IsBase() ) {
3573 ename = "this";
3574 }
3575 TClass *cle = element->GetClassPointer();
3576 TVirtualCollectionProxy *proxy = cle ? element->GetClassPointer()->GetCollectionProxy() : 0;
3577 if (!element->TestBit(TStreamerElement::kDoNotDelete) && proxy) {
3578 Int_t stltype = ((TStreamerSTL*)element)->GetSTLtype();
3579
3580 if (proxy->HasPointers()) {
3581 fprintf(file," std::for_each( (%s %s).rbegin(), (%s %s).rend(), DeleteObjectFunctor() );\n",prefix,ename,prefix,ename);
3582 //fprintf(file," %s::iterator iter;\n");
3583 //fprintf(file," %s::iterator begin = (%s %s).begin();\n");
3584 //fprintf(file," %s::iterator end (%s %s).end();\n");
3585 //fprintf(file," for( iter = begin; iter != end; ++iter) { delete *iter; }\n");
3586 } else {
3587 if (stltype == ROOT::kSTLmap || stltype == ROOT::kSTLmultimap) {
3589 std::vector<std::string> inside;
3590 int nestedLoc;
3591 TClassEdit::GetSplit(enamebasic, inside, nestedLoc, TClassEdit::kLong64);
3592 if ((!inside[1].empty() && inside[1][inside[1].size()-1]=='*')
3593 || (!inside[2].empty() && inside[2][inside[2].size()-1]=='*')) {
3594 fprintf(file," std::for_each( (%s %s).rbegin(), (%s %s).rend(), DeleteObjectFunctor() );\n",prefix,ename,prefix,ename);
3595 }
3596 }
3597 }
3598 }
3599 if ( prefix[0] ) {
3600 fprintf(file," delete %s; %s = 0;\n",ename,ename);
3601 }
3602 }
3603 }
3604}
3605
3606////////////////////////////////////////////////////////////////////////////////
3607/// Write the Declaration of class.
3608
3609void TStreamerInfo::GenerateDeclaration(FILE *fp, FILE *sfp, const TList *subClasses, Bool_t top)
3610{
3611 if (fClassVersion == -3) {
3612 return;
3613 }
3614
3615 Bool_t needGenericTemplate = fElements==0 || fElements->IsEmpty();
3616 Bool_t isTemplate = kFALSE;
3617 const char *clname = GetName();
3618 TString template_protoname;
3619 if (strchr(clname, ':')) {
3620 // We might have a namespace in front of the classname.
3621 Int_t len = strlen(clname);
3622 const char *name = clname;
3623 UInt_t nest = 0;
3624 UInt_t pr_pos = 0;
3625 for (Int_t cur = 0; cur < len; ++cur) {
3626 switch (clname[cur]) {
3627 case '<':
3628 ++nest;
3629 pr_pos = cur;
3630 isTemplate = kTRUE;
3631 break;
3632 case '>':
3633 if (nest == 0) { cur = len; continue; } // the name is not well formed, give up.
3634 --nest;
3635 break;
3636 case ':': {
3637 if (nest == 0 && clname[cur+1] == ':') {
3638 // We have a scope
3639 isTemplate = kFALSE;
3640 name = clname + cur + 2;
3641 }
3642 break;
3643 }
3644 }
3645 }
3646 if (isTemplate) {
3647 template_protoname.Append(clname,pr_pos);
3648 }
3649 clname = name;
3650 } else {
3651 const char *where = strstr(clname, "<");
3652 isTemplate = where != 0;
3653 if (isTemplate) {
3654 template_protoname.Append(clname,where-clname);
3655 }
3656 }
3657
3658 if (needGenericTemplate && isTemplate) {
3659 TString templateName(TMakeProject::GetHeaderName("template "+template_protoname,0));
3660 fprintf(fp, "#ifndef %s_h\n", templateName.Data());
3661 fprintf(fp, "#define %s_h\n", templateName.Data());
3662 }
3663
3664 TString protoname;
3665 UInt_t numberOfNamespaces = TMakeProject::GenerateClassPrefix(fp, GetName(), top, protoname, 0, kFALSE, needGenericTemplate);
3666
3667 // Generate class statement with base classes.
3668 TStreamerElement *element;
3669 TIter next(fElements);
3670 Int_t nbase = 0;
3671 while ((element = (TStreamerElement*)next())) {
3672 if (!element->IsBase()) continue;
3673 nbase++;
3674 const char *ename = element->GetName();
3675 if (nbase == 1) fprintf(fp," : public %s",ename);
3676 else fprintf(fp," , public %s",ename);
3677 }
3678 fprintf(fp," {\n");
3679
3680 // Generate forward declaration nested classes.
3681 if (subClasses && subClasses->GetEntries()) {
3682 Bool_t needheader = true;
3683
3684 TIter subnext(subClasses);
3685 TStreamerInfo *subinfo;
3686 Int_t len = strlen(GetName());
3687 while ((subinfo = (TStreamerInfo*)subnext())) {
3688 if (strncmp(GetName(),subinfo->GetName(),len)==0 && (subinfo->GetName()[len]==':') ) {
3689 if (subinfo->GetName()[len+1]==':' && strstr(subinfo->GetName()+len+2,":")==0) {
3690 if (needheader) {
3691 fprintf(fp,"\npublic:\n");
3692 fprintf(fp,"// Nested classes forward declaration.\n");
3693 needheader = false;
3694 }
3695 TString sub_protoname;
3696 UInt_t sub_numberOfClasses = 0;
3697 UInt_t sub_numberOfNamespaces;
3698 if (subinfo->GetClassVersion() == -3) {
3699 sub_numberOfNamespaces = TMakeProject::GenerateClassPrefix(fp, subinfo->GetName() + len+2, kFALSE, sub_protoname, &sub_numberOfClasses, 3);
3700 } else {
3701 sub_numberOfNamespaces = TMakeProject::GenerateClassPrefix(fp, subinfo->GetName() + len+2, kFALSE, sub_protoname, &sub_numberOfClasses, kFALSE);
3702 fprintf(fp, ";\n");
3703 }
3704
3705 for (UInt_t i = 0;i < sub_numberOfClasses;++i) {
3706 fprintf(fp, "}; // end of class.\n");
3707 }
3708 if (sub_numberOfNamespaces > 0) {
3709 Error("GenerateDeclaration","Nested classes %s thought to be inside a namespace inside the class %s",subinfo->GetName(),GetName());
3710 }
3711 }
3712 }
3713 }
3714 }
3715
3716 fprintf(fp,"\npublic:\n");
3717 fprintf(fp,"// Nested classes declaration.\n");
3718
3719 // Generate nested classes.
3720 if (subClasses && subClasses->GetEntries()) {
3721 TIter subnext(subClasses,kIterBackward);
3722 TStreamerInfo *subinfo;
3723 Int_t len = strlen(GetName());
3724 while ((subinfo = (TStreamerInfo*)subnext())) {
3725 if (strncmp(GetName(),subinfo->GetName(),len)==0 && (subinfo->GetName()[len]==':')) {
3726 if (subinfo->GetName()[len+1]==':' && strstr(subinfo->GetName()+len+2,":")==0) {
3727 subinfo->GenerateDeclaration(fp, sfp, subClasses, kFALSE);
3728 }
3729 }
3730 }
3731 }
3732
3733 fprintf(fp,"\npublic:\n");
3734 fprintf(fp,"// Data Members.\n");
3735
3736 {
3737 // Generate data members.
3738 TString name(128);
3739 Int_t ltype = 12;
3740 Int_t ldata = 10;
3741 Int_t lt,ld,is;
3742 TString line;
3743 line.Resize(kMaxLen);
3744 next.Reset();
3745 while ((element = (TStreamerElement*)next())) {
3746
3747 if (element->IsBase()) continue;
3748 const char *ename = element->GetName();
3749
3750 name = ename;
3751 for (Int_t i=0;i < element->GetArrayDim();i++) {
3752 name += TString::Format("[%d]",element->GetMaxIndex(i));
3753 }
3754 name += ";";
3755 ld = name.Length();
3756
3757 TString enamebasic = element->GetTypeNameBasic();
3758 if (element->IsA() == TStreamerSTL::Class()) {
3759 // If we have a map, multimap, set or multiset,
3760 // and the key is a class, we need to replace the
3761 // container by a vector since we don't have the
3762 // comparator function.
3763 Int_t stltype = ((TStreamerSTL*)element)->GetSTLtype();
3764 switch (stltype) {
3765 case ROOT::kSTLmap:
3766 case ROOT::kSTLmultimap:
3767 case ROOT::kSTLset:
3768 case ROOT::kSTLmultiset:
3771 {
3772 enamebasic = TMakeProject::UpdateAssociativeToVector(enamebasic);
3773 }
3774 default:
3775 // nothing to do.
3776 break;
3777 }
3778 } else if (strncmp(enamebasic.Data(), "auto_ptr<", strlen("auto_ptr<")) == 0) {
3779 enamebasic = TMakeProject::UpdateAssociativeToVector(enamebasic);
3780 }
3781
3782 lt = enamebasic.Length();
3783
3784 line = " ";
3785 line += enamebasic;
3786 if (lt>=ltype) ltype = lt+1;
3787
3788 for (is = 3+lt; is < (3+ltype); ++is) line += ' ';
3789
3790 line += name;
3791 if (element->IsaPointer() && !strchr(line,'*')) line[2+ltype] = '*';
3792
3793 if (ld>=ldata) ldata = ld+1;
3794 for (is = 3+ltype+ld; is < (3+ltype+ldata); ++is) line += ' ';
3795
3796 line += " //";
3797 line += element->GetTitle();
3798 fprintf(fp,"%s\n",line.Data());
3799 }
3800 }
3801 if (needGenericTemplate && isTemplate) {
3802 // Generate default functions, ClassDef and trailer.
3803 fprintf(fp,"\n %s() {\n",protoname.Data());
3804 R__WriteConstructorBody(fp,next);
3805 fprintf(fp," }\n");
3806 fprintf(fp," %s(%s && ) = default;\n",protoname.Data(),protoname.Data());
3807 fprintf(fp," %s(const %s & rhs )\n",protoname.Data(),protoname.Data());
3808 R__WriteMoveConstructorBody(fp,protoname,next);
3809 fprintf(fp," }\n");
3810 fprintf(fp," virtual ~%s() {\n",protoname.Data());
3811 R__WriteDestructorBody(fp,next);
3812 fprintf(fp," }\n\n");
3813
3814 } else {
3815 // Generate default functions, ClassDef and trailer.
3816 fprintf(fp,"\n %s();\n",protoname.Data());
3817 fprintf(fp," %s(%s && ) = default;\n",protoname.Data(),protoname.Data());
3818 fprintf(fp," %s(const %s & );\n",protoname.Data(),protoname.Data());
3819 fprintf(fp," virtual ~%s();\n\n",protoname.Data());
3820
3821 // Add the implementations to the source.cxx file.
3823 fprintf(sfp,"#ifndef %s_cxx\n",guard.Data());
3824 fprintf(sfp,"#define %s_cxx\n",guard.Data());
3825 fprintf(sfp,"%s::%s() {\n",GetName(),protoname.Data());
3826 R__WriteConstructorBody(sfp,next);
3827 fprintf(sfp,"}\n");
3828
3829 fprintf(sfp,"%s::%s(const %s & rhs)\n",GetName(),protoname.Data(),protoname.Data());
3830 R__WriteMoveConstructorBody(sfp,protoname,next);
3831 fprintf(sfp,"}\n");
3832
3833 fprintf(sfp,"%s::~%s() {\n",GetName(),protoname.Data());
3834 R__WriteDestructorBody(sfp,next);
3835 fprintf(sfp,"}\n");
3836 fprintf(sfp,"#endif // %s_cxx\n\n",guard.Data());
3837 }
3838
3839 TClass *cl = gROOT->GetClass(GetName());
3840 if (fClassVersion > 1 || (cl && cl->IsTObject()) ) {
3841 // add 1 to class version in case we didn't manage reproduce the class layout to 100%.
3842 if (fClassVersion == 0) {
3843 // If the class was declared 'transient', keep it that way.
3844 fprintf(fp," ClassDef(%s,%d); // Generated by MakeProject.\n",protoname.Data(),0);
3845 } else {
3846 fprintf(fp," ClassDef(%s,%d); // Generated by MakeProject.\n",protoname.Data(),fClassVersion + 1);
3847 }
3848 }
3849 fprintf(fp,"};\n");
3850
3851 for(UInt_t i=0;i<numberOfNamespaces;++i) {
3852 fprintf(fp,"} // namespace\n");
3853 }
3854
3855 if (needGenericTemplate && isTemplate) {
3856 fprintf(fp,"#endif // generic template declaration\n");
3857 }
3858}
3859
3860////////////////////////////////////////////////////////////////////////////////
3861/// Add to the header file, the \#include need for this class.
3862
3863UInt_t TStreamerInfo::GenerateIncludes(FILE *fp, char *inclist, const TList *extrainfos)
3864{
3865 if (inclist[0]==0) {
3866 // Always have this include for ClassDef.
3867 TMakeProject::AddInclude( fp, "Rtypes.h", kFALSE, inclist);
3868 }
3869 UInt_t ninc = 0;
3870
3871 const char *clname = GetName();
3872 if (strchr(clname,'<')) {
3873 // This is a template, we need to check the template parameter.
3874 ninc += TMakeProject::GenerateIncludeForTemplate(fp, clname, inclist, kFALSE, extrainfos);
3875 }
3876
3877 TString name(1024);
3878 Int_t ltype = 10;
3879 Int_t ldata = 10;
3880 Int_t lt;
3881 Int_t ld;
3882 TIter next(fElements);
3883 TStreamerElement *element;
3884 Bool_t incRiostream = kFALSE;
3885 while ((element = (TStreamerElement*)next())) {
3886 //if (element->IsA() == TStreamerBase::Class()) continue;
3887 const char *ename = element->GetName();
3888 const char *colon2 = strstr(ename,"::");
3889 if (colon2) ename = colon2+2;
3890 name = ename;
3891 for (Int_t i=0;i < element->GetArrayDim();i++) {
3892 name += TString::Format("[%d]",element->GetMaxIndex(i));
3893 }
3894 ld = name.Length();
3895 lt = strlen(element->GetTypeName());
3896 if (ltype < lt) ltype = lt;
3897 if (ldata < ld) ldata = ld;
3898
3899 //must include Riostream.h in case of an STL container
3900 if (!incRiostream && element->InheritsFrom(TStreamerSTL::Class())) {
3901 incRiostream = kTRUE;
3902 TMakeProject::AddInclude( fp, "Riostream.h", kFALSE, inclist);
3903 }
3904
3905 //get include file name if any
3906 const char *include = element->GetInclude();
3907 if (!include[0]) continue;
3908
3909 Bool_t greater = (include[0]=='<');
3910 include++;
3911
3912 if (strncmp(include,"include/",8)==0) {
3913 include += 8;
3914 }
3915 if (strncmp(include,"include\\",9)==0) {
3916 include += 9;
3917 }
3918 if (TClassEdit::IsStdPair(element->GetTypeName())) {
3919 TMakeProject::AddInclude( fp, "utility", kTRUE, inclist);
3920 } else if (strncmp(element->GetTypeName(),"auto_ptr<",strlen("auto_ptr<"))==0) {
3921 TMakeProject::AddInclude( fp, "memory", kTRUE, inclist);
3922 } else {
3923 TString incName( include, strlen(include)-1 );
3924 incName = TMakeProject::GetHeaderName(incName,extrainfos);
3925 TMakeProject::AddInclude( fp, incName.Data(), greater, inclist);
3926 }
3927
3928 if (strchr(element->GetTypeName(),'<')) {
3929 // This is a template, we need to check the template parameter.
3930 ninc += TMakeProject::GenerateIncludeForTemplate(fp, element->GetTypeName(), inclist, kFALSE, extrainfos);
3931 }
3932 }
3933 return ninc;
3934}
3935
3936////////////////////////////////////////////////////////////////////////////////
3937/// Generate header file for the class described by this TStreamerInfo
3938/// the function is called by TFile::MakeProject for each class in the file
3939
3940Int_t TStreamerInfo::GenerateHeaderFile(const char *dirname, const TList *subClasses, const TList *extrainfos)
3941{
3942 // if (fClassVersion == -4) return 0;
3943 if ((fClass && fClass->GetCollectionType()) || TClassEdit::IsSTLCont(GetName())) return 0;
3944 if (TClassEdit::IsStdPair(GetName())) return 0;
3945 if (strncmp(GetName(),"auto_ptr<",strlen("auto_ptr<"))==0) return 0;
3946
3948 if (cl) {
3949 if (cl->HasInterpreterInfo()) return 0; // skip known classes
3950 }
3951 Bool_t isTemplate = kFALSE;
3952 if (strchr(GetName(),':')) {
3953 UInt_t len = strlen(GetName());
3954 UInt_t nest = 0;
3955 UInt_t scope = 0;
3956 for(UInt_t i=len; i>0; --i) {
3957 switch(GetName()[i]) {
3958 case '>': ++nest; if (scope==0) { isTemplate = kTRUE; } break;
3959 case '<': --nest; break;
3960 case ':':
3961 if (nest==0 && GetName()[i-1]==':') {
3962 // We have a scope
3963 TString nsname(GetName(), i-1);
3964 cl = gROOT->GetClass(nsname);
3965 if (cl && (cl->Size()!=0 || (cl->Size()==0 && !cl->HasInterpreterInfo() /*empty 'base' class on file*/))) {
3966 // This class is actually nested.
3967 return 0;
3968 } else if (cl == 0 && extrainfos != 0) {
3969 TStreamerInfo *clinfo = (TStreamerInfo*)extrainfos->FindObject(nsname);
3970 if (clinfo && clinfo->GetClassVersion() == -5) {
3971 // This class is actually nested.
3972 return 0;
3973 }
3974 }
3975 ++scope;
3976 }
3977 break;
3978 }
3979 }
3980 }
3981 Bool_t needGenericTemplate = isTemplate && (fElements==0 || fElements->IsEmpty());
3982
3983 if (gDebug) printf("generating code for class %s\n",GetName());
3984
3985 // Open the file
3986
3987 TString headername( TMakeProject::GetHeaderName( GetName(), extrainfos ) );
3988 TString filename;
3989 filename.Form("%s/%s.h",dirname,headername.Data());
3990
3991 FILE *fp = fopen(filename.Data(),"w");
3992 if (!fp) {
3993 Error("MakeProject","Cannot open output file:%s\n",filename.Data());
3994 return 0;
3995 }
3996
3997 filename.Form("%s/%sProjectHeaders.h",dirname,gSystem->BaseName(dirname));
3998 FILE *allfp = fopen(filename.Data(),"a");
3999 if (!allfp) {
4000 Error("MakeProject","Cannot open output file:%s\n",filename.Data());
4001 fclose(fp);
4002 return 0;
4003 }
4004 fprintf(allfp,"#include \"%s.h\"\n", headername.Data());
4005 fclose(allfp);
4006
4007 char *inclist = new char[50000];
4008 inclist[0] = 0;
4009
4010 // Generate class header.
4011 TDatime td;
4012 fprintf(fp,"//////////////////////////////////////////////////////////\n");
4013 fprintf(fp,"// This class has been generated by TFile::MakeProject\n");
4014 fprintf(fp,"// (%s by ROOT version %s)\n",td.AsString(),gROOT->GetVersion());
4015 fprintf(fp,"// from the StreamerInfo in file %s\n",gDirectory->GetFile()->GetName());
4016 fprintf(fp,"//////////////////////////////////////////////////////////\n");
4017 fprintf(fp,"\n");
4018 fprintf(fp,"\n");
4019 fprintf(fp,"#ifndef %s_h\n",headername.Data());
4020 fprintf(fp,"#define %s_h\n",headername.Data());
4021 TMakeProject::GenerateForwardDeclaration(fp, GetName(), inclist, kFALSE, needGenericTemplate, extrainfos);
4022 fprintf(fp,"\n");
4023
4024 UInt_t ninc = 0;
4025 ninc += GenerateIncludes(fp, inclist, extrainfos);
4026 if (subClasses) {
4027 TIter subnext(subClasses);
4028 TStreamerInfo *subinfo;
4029 while ((subinfo = (TStreamerInfo*)subnext())) {
4030 ninc = subinfo->GenerateIncludes(fp, inclist, extrainfos);
4031 }
4032 }
4033 fprintf(fp,"\n");
4034
4035 TString sourcename; sourcename.Form( "%s/%sProjectSource.cxx", dirname, gSystem->BaseName(dirname) );
4036 FILE *sfp = fopen( sourcename.Data(), "a" );
4037 if (sfp) {
4038 GenerateDeclaration(fp, sfp, subClasses);
4039 } else {
4040 Error("GenerateHeaderFile","Could not open %s for appending",sourcename.Data());
4041 }
4042 TMakeProject::GeneratePostDeclaration(fp, this, inclist);
4043
4044 fprintf(fp,"#endif\n");
4045
4046 delete [] inclist;
4047 fclose(fp);
4048 if (sfp) fclose(sfp);
4049 return 1;
4050}
4051
4052////////////////////////////////////////////////////////////////////////////////
4053/// Compute data member offset.
4054/// Return pointer to the Streamer function if one exists
4055
4057{
4058 TIter nextr(fClass->GetListOfRealData());
4059 char dmbracket[256];
4060 snprintf(dmbracket,255,"%s[",dm->GetName());
4061 Int_t offset = kMissing;
4062 if (!fClass->IsLoaded()) {
4063 // If the 'class' is not loaded, we do not have a TClass bootstrap and thus
4064 // the 'RealData' might not have enough information because of the lack
4065 // of proper ShowMember implementation.
4066 if (! (dm->Property() & kIsStatic) ) {
4067 // Give an offset only to non-static members.
4068 offset = dm->GetOffset();
4069 }
4070 }
4071 TRealData *rdm;
4072 while ((rdm = (TRealData*)nextr())) {
4073 char *rdmc = (char*)rdm->GetName();
4074 //next statement required in case a class and one of its parent class
4075 //have data members with the same name
4076 if (dm->IsaPointer() && rdmc[0] == '*') rdmc++;
4077
4078 if (rdm->GetDataMember() != dm) continue;
4079 if (strcmp(rdmc,dm->GetName()) == 0) {
4080 offset = rdm->GetThisOffset();
4081 streamer = rdm->GetStreamer();
4082 break;
4083 }
4084 if (strcmp(rdm->GetName(),dm->GetName()) == 0) {
4085 if (rdm->IsObject()) {
4086 offset = rdm->GetThisOffset();
4087 streamer = rdm->GetStreamer();
4088 break;
4089 }
4090 }
4091 if (strstr(rdm->GetName(),dmbracket)) {
4092 offset = rdm->GetThisOffset();
4093 streamer = rdm->GetStreamer();
4094 break;
4095 }
4096 }
4097 return offset;
4098}
4099
4100////////////////////////////////////////////////////////////////////////////////
4101/// Return the offset of the data member as indicated by this StreamerInfo.
4102
4103Int_t TStreamerInfo::GetOffset(const char *elementName) const
4104{
4105 if (elementName == 0) return 0;
4106
4107 Int_t offset = 0;
4108 TStreamerElement *elem = (TStreamerElement*)fElements->FindObject(elementName);
4109 if (elem) offset = elem->GetOffset();
4110
4111 return offset;
4112}
4113
4114////////////////////////////////////////////////////////////////////////////////
4115/// Return total size of all persistent elements of the class (with offsets).
4116
4118{
4119 return fSize;
4120}
4121
4122////////////////////////////////////////////////////////////////////////////////
4123/// Return total size of all persistent elements of the class
4124/// use GetSize if you want to get the real size in memory.
4125
4127{
4128 TIter next(fElements);
4129 TStreamerElement *element;
4130 Int_t asize = 0;
4131 while ((element = (TStreamerElement*)next())) {
4132 asize += element->GetSize();
4133 }
4134 return asize;
4135}
4136
4137////////////////////////////////////////////////////////////////////////////////
4138/// Return the StreamerElement of "datamember" inside our
4139/// class or any of its base classes.
4140///
4141/// The offset information
4142/// contained in the StreamerElement is related to its immediately
4143/// containing class, so we return in 'offset' the offset inside
4144/// our class.
4145
4146TStreamerElement* TStreamerInfo::GetStreamerElement(const char* datamember, Int_t& offset) const
4147{
4148 if (!fElements) {
4149 return 0;
4150 }
4151
4152 // Look first at the data members and base classes
4153 // of our class.
4154 TStreamerElement* element = (TStreamerElement*) fElements->FindObject(datamember);
4155 if (element) {
4156 offset = element->GetOffset();
4157 return element;
4158 }
4159
4160 // Not found, so now try the data members and base classes
4161 // of the base classes of our class.
4162 if (fClass->HasDataMemberInfo()) {
4163 // Our class has a dictionary loaded, use it to search the base classes.
4164 TStreamerElement* base_element = 0;
4165 TBaseClass* base = 0;
4166 TClass* base_cl = 0;
4167 Int_t base_offset = 0;
4168 Int_t local_offset = 0;
4169 TIter nextb(fClass->GetListOfBases());
4170 // Iterate on list of base classes.
4171 while ((base = (TBaseClass*) nextb())) {
4172 base_cl = TClass::GetClass(base->GetName());
4173 base_element = (TStreamerElement*) fElements->FindObject(base->GetName());
4174 if (!base_cl || !base_element) {
4175 continue;
4176 }
4177 base_offset = base_element->GetOffset();
4178 element = ((TStreamerInfo*)base_cl->GetStreamerInfo())->GetStreamerElement(datamember, local_offset);
4179 if (element) {
4180 offset = base_offset + local_offset;
4181 return element;
4182 }
4183 }
4184 } else {
4185 // Our class's dictionary is not loaded. Search through the base class streamer elements.
4186 TIter next(fElements);
4187 TStreamerElement* curelem = 0;
4188 while ((curelem = (TStreamerElement*) next())) {
4189 if (curelem->InheritsFrom(TStreamerBase::Class())) {
4190 TClass* baseClass = curelem->GetClassPointer();
4191 if (!baseClass) {
4192 continue;
4193 }
4194 Int_t base_offset = curelem->GetOffset();
4195 Int_t local_offset = 0;
4196 TStreamerInfo *baseInfo;
4197 if (baseClass->Property() & kIsAbstract) {
4198 baseInfo = (TStreamerInfo*)baseClass->GetStreamerInfoAbstractEmulated();
4199 } else {
4200 baseInfo = (TStreamerInfo*)baseClass->GetStreamerInfo();
4201 }
4202 if (baseInfo) element = baseInfo->GetStreamerElement(datamember, local_offset);
4203 if (element) {
4204 offset = base_offset + local_offset;
4205 return element;
4206 }
4207 }
4208 }
4209 }
4210 return 0;
4211}
4212
4213////////////////////////////////////////////////////////////////////////////////
4214/// <b>Obsolete</b>: this routine is obsolete and should not longer be used.
4215///
4216/// TStreamerInfo holds two types of data structures
4217/// - TObjArray* fElements; containing the list of all TStreamerElement
4218/// objects for this class version.
4219/// - ULong_t* fElem; containing the preprocessed information
4220/// by TStreamerInfo::Compile In case consecutive data members
4221/// are of the same type, the Compile function declares the consecutive
4222/// elements as one single element in fElems.
4223///
4224/// Example with the class TAttLine:
4225/// ~~~{.cpp}
4226/// TClass::GetClass("TAttLine")->GetStreamerInfo()->ls(); produces;
4227/// StreamerInfo for class: TAttLine, version=1
4228/// short fLineColor offset= 4 type= 2 line color
4229/// short fLineStyle offset= 6 type= 2 line style
4230/// short fLineWidth offset= 8 type= 2 line width
4231/// i= 0, fLineColor type= 22, offset= 4, len=3, method=0
4232/// ~~~
4233/// For I/O implementations (eg. XML) , one has to know the original name
4234/// of the data member. This function can be used to return a pointer
4235/// to the original TStreamerElement object corresponding to the j-th
4236/// element of a compressed array in fElems.
4237/// Parameters description:
4238/// - i: the serial number in array fElem
4239/// - j: the element number in the array of consecutive types
4240/// In the above example the class TAttLine has 3 consecutive data members
4241/// of the same type "short". Compile makes one single array of 3 elements.
4242/// To access the TStreamerElement for the second element
4243/// of this array, one can call:
4244/// ~~~{.cpp}
4245/// auto el = GetStreamerElementReal(0,1);
4246/// auto membername = el->GetName();
4247/// ~~~
4248/// This function is typically called from TBuffer, TXmlBuffer.
4249
4251{
4252 ::Obsolete("TStreamerInfo::GetStreamerElementReal", "v5-34-20", "v6-00-02");
4253
4254 if (i < 0 || i >= fNdata) return 0;
4255 if (j < 0) return 0;
4256 if (!fElements) return 0;
4257 TStreamerElement *se = (TStreamerElement*)fCompOpt[i]->fElem;
4258 if (!se) return 0;
4259 Int_t nelems = fElements->GetEntriesFast();
4260 for (Int_t ise=0;ise < nelems;ise++) {
4261 if (se != (TStreamerElement*)fElements->UncheckedAt(ise)) continue;
4262 if (ise+j >= nelems) return 0;
4263 return (TStreamerElement*)fElements->UncheckedAt(ise+j);
4264 }
4265 return 0;
4266}
4267
4268////////////////////////////////////////////////////////////////////////////////
4269/// Get the value from inside a collection.
4270
4271template <typename T>
4273{
4274 if (type>=kConv && type<kSTL) {
4275 type -= kConv;
4276 }
4277 switch (type) {
4278 // basic types
4279 case kBool: {Bool_t *val = (Bool_t*)ladd; return T(*val);}
4280 case kChar: {Char_t *val = (Char_t*)ladd; return T(*val);}
4281 case kShort: {Short_t *val = (Short_t*)ladd; return T(*val);}
4282 case kInt: {Int_t *val = (Int_t*)ladd; return T(*val);}
4283 case kLong: {Long_t *val = (Long_t*)ladd; return T(*val);}
4284 case kLong64: {Long64_t *val = (Long64_t*)ladd; return T(*val);}
4285 case kFloat: {Float_t *val = (Float_t*)ladd; return T(*val);}
4286 case kFloat16: {Float_t *val = (Float_t*)ladd; return T(*val);}
4287 case kDouble: {Double_t *val = (Double_t*)ladd; return T(*val);}
4288 case kDouble32: {Double_t *val = (Double_t*)ladd; return T(*val);}
4289 case kUChar: {UChar_t *val = (UChar_t*)ladd; return T(*val);}
4290 case kUShort: {UShort_t *val = (UShort_t*)ladd; return T(*val);}
4291 case kUInt: {UInt_t *val = (UInt_t*)ladd; return T(*val);}
4292 case kULong: {ULong_t *val = (ULong_t*)ladd; return T(*val);}
4293#if defined(_MSC_VER) && (_MSC_VER <= 1200)
4294 case kULong64: {Long64_t *val = (Long64_t*)ladd; return T(*val);}
4295#else
4296 case kULong64: {ULong64_t *val= (ULong64_t*)ladd; return T(*val);}
4297#endif
4298 case kBits: {UInt_t *val = (UInt_t*)ladd; return T(*val);}
4299
4300 // array of basic types array[8]
4301 case kOffsetL + kBool: {Bool_t *val = (Bool_t*)ladd; return T(val[k]);}
4302 case kOffsetL + kChar: {Char_t *val = (Char_t*)ladd; return T(val[k]);}
4303 case kOffsetL + kShort: {Short_t *val = (Short_t*)ladd; return T(val[k]);}
4304 case kOffsetL + kInt: {Int_t *val = (Int_t*)ladd; return T(val[k]);}
4305 case kOffsetL + kLong: {Long_t *val = (Long_t*)ladd; return T(val[k]);}
4306 case kOffsetL + kLong64: {Long64_t *val = (Long64_t*)ladd; return T(val[k]);}
4307 case kOffsetL + kFloat: {Float_t *val = (Float_t*)ladd; return T(val[k]);}
4308 case kOffsetL + kFloat16: {Float_t *val = (Float_t*)ladd; return T(val[k]);}
4309 case kOffsetL + kDouble: {Double_t *val = (Double_t*)ladd; return T(val[k]);}
4310 case kOffsetL + kDouble32:{Double_t *val = (Double_t*)ladd; return T(val[k]);}
4311 case kOffsetL + kUChar: {UChar_t *val = (UChar_t*)ladd; return T(val[k]);}
4312 case kOffsetL + kUShort: {UShort_t *val = (UShort_t*)ladd; return T(val[k]);}
4313 case kOffsetL + kUInt: {UInt_t *val = (UInt_t*)ladd; return T(val[k]);}
4314 case kOffsetL + kULong: {ULong_t *val = (ULong_t*)ladd; return T(val[k]);}
4315#if defined(_MSC_VER) && (_MSC_VER <= 1200)
4316 case kOffsetL + kULong64: {Long64_t *val = (Long64_t*)ladd; return T(val[k]);}
4317#else
4318 case kOffsetL + kULong64:{ULong64_t *val= (ULong64_t*)ladd; return T(val[k]);}
4319#endif
4320
4321#define READ_ARRAY(TYPE_t) \
4322 { \
4323 Int_t sub_instance, index; \
4324 Int_t instance = k; \
4325 if (len) { \
4326 index = instance / len; \
4327 sub_instance = instance % len; \
4328 } else { \
4329 index = instance; \
4330 sub_instance = 0; \
4331 } \
4332 TYPE_t **val =(TYPE_t**)(ladd); \
4333 return T((val[sub_instance])[index]); \
4334 }
4335
4336 // pointer to an array of basic types array[n]
4343 case kOffsetP + kFloat16_t:
4345 case kOffsetP + kDouble32_t:
4351#if defined(_MSC_VER) && (_MSC_VER <= 1200)
4353#else
4355#endif
4356
4357 // array counter //[n]
4358 case kCounter: {Int_t *val = (Int_t*)ladd; return T(*val);}
4359 }
4360 return 0;
4361}
4362
4363
4364
4365template Double_t TStreamerInfo::GetTypedValue(char *pointer, Int_t i, Int_t j, Int_t len) const;
4366template Long64_t TStreamerInfo::GetTypedValue(char *pointer, Int_t i, Int_t j, Int_t len) const;
4367template LongDouble_t TStreamerInfo::GetTypedValue(char *pointer, Int_t i, Int_t j, Int_t len) const;
4368
4369////////////////////////////////////////////////////////////////////////////////
4370/// Return value of element i in object at pointer.
4371/// The function may be called in two ways:
4372/// - method1 len < 0: i is assumed to be the TStreamerElement number i in StreamerInfo
4373/// - method2 len >= 0: i is the type, address of variable is directly pointer.
4374
4375template <typename T>
4376T TStreamerInfo::GetTypedValue(char *pointer, Int_t i, Int_t j, Int_t len) const
4377{
4378 char *ladd;
4379 Int_t atype;
4380 if (len >= 0) {
4381 ladd = pointer;
4382 atype = i;
4383 } else {
4384 if (i < 0) return 0;
4385 ladd = pointer + fCompFull[i]->fOffset;
4386 atype = fCompFull[i]->fNewType;
4387 len = fCompFull[i]->fElem->GetArrayLength();
4388 if (atype == kSTL) {
4389 TClass *newClass = fCompFull[i]->fElem->GetNewClass();
4390 if (newClass == 0) {
4391 newClass = fCompFull[i]->fElem->GetClassPointer();
4392 }
4393 TClass *innerClass = newClass->GetCollectionProxy()->GetValueClass();
4394 if (innerClass) {
4395 return 0; // We don't know which member of the class we would want.
4396 } else {
4397 TVirtualCollectionProxy *proxy = newClass->GetCollectionProxy();
4398 // EDataType is a subset of TStreamerInfo::EReadWrite
4399 atype = (TStreamerInfo::EReadWrite)proxy->GetType();
4400 TVirtualCollectionProxy::TPushPop pop(proxy,ladd);
4401 Int_t nc = proxy->Size();
4402 if (j >= nc) return 0;
4403 char *element_ptr = (char*)proxy->At(j);
4404 return GetTypedValueAux<T>(atype,element_ptr,0,1);
4405 }
4406 }
4407 }
4408 return GetTypedValueAux<T>(atype,ladd,j,len);
4409}
4410
4411////////////////////////////////////////////////////////////////////////////////
4412
4413template Double_t TStreamerInfo::GetTypedValueClones<Double_t>(TClonesArray *clones, Int_t i, Int_t j, int k, Int_t eoffset) const;
4414template Long64_t TStreamerInfo::GetTypedValueClones(TClonesArray *clones, Int_t i, Int_t j, int k, Int_t eoffset) const;
4415template LongDouble_t TStreamerInfo::GetTypedValueClones(TClonesArray *clones, Int_t i, Int_t j, int k, Int_t eoffset) const;
4416
4417template <typename T>
4419{
4420 // return value of element i in object number j in a TClonesArray and eventually
4421 // element k in a sub-array.
4422
4423 Int_t nc = clones->GetEntriesFast();
4424 if (j >= nc) return 0;
4425
4426 char *pointer = (char*)clones->UncheckedAt(j);
4427 char *ladd = pointer + eoffset + fCompFull[i]->fOffset;
4428 return GetTypedValueAux<T>(fCompFull[i]->fType,ladd,k,((TStreamerElement*)fCompFull[i]->fElem)->GetArrayLength());
<