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