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