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