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