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 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 while(!done) {
3038 local.Clear();
3039 other.Clear();
3040 el = (TStreamerElement*)next();
3041 while (el && (el->IsBase() || el->IsA() == TStreamerArtificial::Class())) {
3042 el = (TStreamerElement*)next();
3043 }
3044 if (el) {
3045 local.SetName( el->GetName() );
3046 local.SetClassName( el->GetTypeName() );
3047 local.SetComment( el->GetTitle() );
3048 local.SetDataType( el->GetType() );
3049 }
3050 if (cl) {
3051 TDataMember *tdm = (TDataMember*)membernext();
3052 while(tdm && ( (!tdm->IsPersistent()) || (tdm->Property()&kIsStatic) || (el && local.fName != tdm->GetName()) )) {
3053 tdm = (TDataMember*)membernext();
3054 }
3055 if (tdm) {
3056 other.SetName( tdm->GetName() );
3057 other.SetClassName( tdm->GetTrueTypeName() );
3058 other.SetComment( tdm->GetTitle() );
3059 if (tdm->GetDataType()) {
3060 // Need to update the type for arrays.
3061 if (tdm->IsaPointer()) {
3062 if (tdm->GetDataType()->GetType() == TVirtualStreamerInfo::kChar && !tdm->GetArrayDim() && tdm->GetArrayIndex()[0]==0) {
3063 other.SetDataType( TVirtualStreamerInfo::kCharStar );
3064 } else {
3065 other.SetDataType( tdm->GetDataType()->GetType() + TVirtualStreamerInfo::kOffsetP);
3066 }
3067 } else {
3068 if (tdm->GetArrayDim()) {
3069 other.SetDataType( tdm->GetDataType()->GetType() + TVirtualStreamerInfo::kOffsetL);
3070 } else {
3071 other.SetDataType( tdm->GetDataType()->GetType() );
3072 }
3073 }
3074 }
3075 } else if (el==0) {
3076 done = kTRUE;
3077 break;
3078 }
3079 } else {
3080 infoel = (TStreamerElement*)infonext();
3081 while (infoel && (infoel->IsBase() || infoel->IsA() == TStreamerArtificial::Class())) {
3082 infoel = (TStreamerElement*)infonext();
3083 }
3084 if (infoel) {
3085 other.SetName( infoel->GetName() );
3086 other.SetClassName( infoel->GetTypeName() );
3087 other.SetComment( infoel->GetTitle() );
3088 other.SetDataType( infoel->GetType() );
3089 } else if (el==0) {
3090 done = kTRUE;
3091 break;
3092 }
3093 }
3094 if (local!=other) {
3095 if (warn) {
3096 if (!el) {
3097 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"
3098 " %s %s; //%s"
3100 ,other.fClassName.Data(),other.fName.Data(),other.fComment.Data());
3101
3102 } else if (other.fName.Length()==0) {
3103 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"
3104 " %s %s; //%s"
3106 ,local.fClassName.Data(),local.fName.Data(),local.fComment.Data());
3107 } else {
3108 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"
3109 " %s %s; //%s\n"
3110 "vs\n"
3111 " %s %s; //%s"
3113 ,local.fClassName.Data(),local.fName.Data(),local.fComment.Data()
3114 ,other.fClassName.Data(),other.fName.Data(),other.fComment.Data());
3115 }
3116 }
3117 result = result && kFALSE;
3118 if (!complete) return result;
3119 }
3120 }
3121 return result;
3122}
3123
3124
3125////////////////////////////////////////////////////////////////////////////////
3126/// Compute total size of all persistent elements of the class
3127
3129{
3130 if (this == fClass->GetCurrentStreamerInfo()) {
3133 return;
3134 }
3135 }
3136
3138 //faster and more precise to use last element offset +size
3139 //on 64 bit machines, offset may be forced to be a multiple of 8 bytes
3140 fSize = element ? element->GetOffset() + element->GetSize() : 0;
3141 if (fNVirtualInfoLoc > 0 && (fVirtualInfoLoc[0]+sizeof(TStreamerInfo*)) >= (ULong_t)fSize) {
3142 fSize = fVirtualInfoLoc[0] + sizeof(TStreamerInfo*);
3143 }
3144
3145 // On some platform and in some case of layout non-basic data types needs
3146 // to be aligned. So let's be on the safe side and align on the size of
3147 // the pointers. (Question: is that the right thing on x32 ABI ?)
3148 constexpr size_t kSizeOfPtr = sizeof(void*);
3149 if ((fSize % kSizeOfPtr) != 0 && !fClass->IsSyntheticPair()) {
3150 fSize = fSize - (fSize % kSizeOfPtr) + kSizeOfPtr;
3151 }
3152}
3153
3154////////////////////////////////////////////////////////////////////////////////
3155/// Recursively mark streamer infos for writing to a file.
3156///
3157/// Will force this TStreamerInfo to the file and also
3158/// all the dependencies.
3159/// If argument force > 0 the loop on class dependencies is forced.
3160/// This function is called when streaming a class that contains
3161/// a null pointer. In this case, the TStreamerInfo for the class
3162/// with the null pointer must be written to the file and also all
3163/// the TStreamerInfo of all the classes referenced by the class.
3164/// We must be given a file to write to.
3165
3167{
3168 if (!file || fNumber < 0) {
3169 return;
3170 }
3171 // Get the given file's list of streamer infos marked for writing.
3172 TArrayC* cindex = file->GetClassIndex();
3173 //the test below testing fArray[fNumber]>1 is to avoid a recursivity
3174 //problem in some cases like:
3175 // class aProblemChild: public TNamed {
3176 // aProblemChild *canBeNull;
3177 // };
3178 if ( // -- Done if already marked, and we are not forcing, or forcing is blocked.
3179 (cindex->fArray[fNumber] && !force) || // Streamer info is already marked, and not forcing, or
3180 (cindex->fArray[fNumber] > 1) // == 2 means ignore forcing to prevent infinite recursion.
3181 ) {
3182 return;
3183 }
3184
3185 auto recurseIntoContent = [file, force](TClass *contentClass)
3186 {
3187 TVirtualStreamerInfo* si = 0;
3188 if (contentClass->Property() & kIsAbstract) {
3189 // If the class of the element is abstract, register the
3190 // TStreamerInfo only if it has already been built.
3191 // Otherwise call cl->GetStreamerInfo() would generate an
3192 // incorrect StreamerInfo.
3193 si = contentClass->GetCurrentStreamerInfo();
3194 } else {
3195 si = contentClass->GetStreamerInfo();
3196 }
3197 if (si) {
3198 si->ForceWriteInfo(file, force);
3199 }
3200 };
3201
3202 // We do not want to write streamer info to the file
3203 // for std::string.
3204 static TClassRef string_classref("string");
3205 if (fClass == string_classref) { // We are std::string.
3206 return;
3207 }
3208 // We do not want to write streamer info to the file
3209 // for STL containers.
3210 if (fClass==0) {
3211 // Build or BuildCheck has not been called yet.
3212 // Let's use another means of checking.
3213 if (fElements && fElements->GetEntriesFast()==1 && strcmp("This",fElements->UncheckedAt(0)->GetName())==0) {
3214 // We are an STL collection.
3215 return;
3216 }
3217 } else if (fClass->GetCollectionProxy()) { // We are an STL collection.
3218 TClass *valueClass = fClass->GetCollectionProxy()->GetValueClass();
3219 if (valueClass)
3220 recurseIntoContent(valueClass);
3221 return;
3222 }
3223 // Mark ourselves for output, and block
3224 // forcing to prevent infinite recursion.
3225 cindex->fArray[fNumber] = 2;
3226 // Signal the file that the marked streamer info list has changed.
3227 cindex->fArray[0] = 1;
3228 // Recursively mark the streamer infos for
3229 // all of our elements.
3230 TIter next(fElements);
3231 TStreamerElement* element = (TStreamerElement*) next();
3232 for (; element; element = (TStreamerElement*) next()) {
3233 if (element->IsTransient()) continue;
3234 TClass* cl = element->GetClassPointer();
3235 if (cl)
3236 recurseIntoContent(cl);
3237 }
3238
3239}
3240
3241////////////////////////////////////////////////////////////////////////////////
3242/// Assuming that obj points to (the part of) an object that is of the
3243/// type described by this streamerInfo, return the actual type of the
3244/// object (i.e. the type described by this streamerInfo is a base class
3245/// of the actual type of the object.
3246/// This routine should only be called if the class described by this
3247/// StreamerInfo is 'emulated'.
3248
3250{
3252
3253 if (fNVirtualInfoLoc != 0) {
3254 TStreamerInfo *allocator = *(TStreamerInfo**)( (const char*)obj + fVirtualInfoLoc[0] );
3255 if (allocator) return allocator->GetClass();
3256 }
3257 return (TClass*)fClass;
3258}
3259
3260////////////////////////////////////////////////////////////////////////////////
3261/// Return true if the checksum passed as argument is one of the checksum
3262/// value produced by the older checksum calculation algorithm.
3263
3265{
3266 for(UInt_t i = 1; i < TClass::kLatestCheckSum; ++i) {
3267 if ( checksum == GetCheckSum( (TClass::ECheckSum) i) ) return kTRUE;
3268 }
3269 return kFALSE;
3270}
3271
3272////////////////////////////////////////////////////////////////////////////////
3273/// Recalculate the checksum of this TStreamerInfo based on its code.
3274///
3275/// The class ckecksum is used by the automatic schema evolution algorithm
3276/// to uniquely identify a class version.
3277/// The check sum is built from the names/types of base classes and
3278/// data members.
3279/// The valid range of code is determined by ECheckSum.
3280/// - kNoEnum: data members of type enum are not counted in the checksum
3281/// - kNoRange: return the checksum of data members and base classes, not including the ranges and array size found in comments.
3282/// - kWithTypeDef: use the sugared type name in the calculation.
3283///
3284/// This is needed for backward compatibility.
3285/// ### WARNING
3286/// This function must be kept in sync with TClass::GetCheckSum.
3287/// They are both used to handle backward compatibility and should both return the same values.
3288/// TStreamerInfo uses the information in TStreamerElement while TClass uses the information
3289/// from TClass::GetListOfBases and TClass::GetListOfDataMembers.
3290/// Original algorithm from Victor Perevovchikov (perev@bnl.gov).
3291
3293{
3294 // kCurrentCheckSum (0) should be kept for backward compatibility, to be
3295 // able to use the inequality checks, we need to set the code to the largest
3296 // value.
3298
3299 UInt_t id = 0;
3300
3301 int il;
3302 TString name = GetName();
3303 TString type;
3304 il = name.Length();
3305 for (int i=0; i<il; i++) id = id*3+name[i];
3306
3307 TIter next(GetElements());
3308 TStreamerElement *el;
3309 // Here we skip he base classes in case this is a pair or STL collection,
3310 // otherwise, on some STL implementations, it can happen that pair has
3311 // base classes which are an internal implementation detail.
3313 while ( (el=(TStreamerElement*)next())) { // loop over bases
3314 if (el->IsBase()) {
3315 name = el->GetName();
3316 il = name.Length();
3317 for (int i=0; i<il; i++) id = id*3+name[i];
3318 if (code > TClass::kNoBaseCheckSum && el->IsA() == TStreamerBase::Class()) {
3319 TStreamerBase *base = (TStreamerBase*)el;
3320 id = id*3 + base->GetBaseCheckSum();
3321 }
3322 }
3323 } /* End of Base Loop */
3324 }
3325
3326 next.Reset();
3327 while ( (el=(TStreamerElement*)next()) ) {
3328 if (el->IsBase()) continue;
3329
3330 // humm can we tell if a TStreamerElement is an enum?
3331 // Maybe something like:
3332 Bool_t isenum = kFALSE;
3333 if ( el->GetType()==3 && gROOT->GetType(el->GetTypeName())==0) {
3334 // If the type is not an enum but a typedef to int then
3335 // el->GetTypeName() should be return 'int'
3336 isenum = kTRUE;
3337 }
3338 if ( (code > TClass::kNoEnum) && isenum) id = id*3 + 1;
3339
3340 name = el->GetName(); il = name.Length();
3341
3342 int i;
3343 for (i=0; i<il; i++) id = id*3+name[i];
3344
3345 if (code == TClass::kReflex || code == TClass::kReflexNoComment) {
3346 // With TClass::kReflexV5 we do not want the Long64 in the name
3347 // nor any typedef.
3349
3350 } else if (code <= TClass::kWithTypeDef) {
3351 // humm ... In the streamerInfo we only have the desugared/normalized
3352 // names, so we are unable to calculate the name with typedefs ...
3353 // except for the case of the ROOT typedef (Int_t, etc.) which are
3354 // kept by TClassEdit::ResolveTypedef(typeName) but not by TCling's
3355 // normalization ...
3356 //
3357 type = el->GetTypeName();
3358 } else {
3360 }
3363 }
3364 if (code == TClass::kReflex || code == TClass::kReflexNoComment) {
3365 type.ReplaceAll("ULong64_t","unsigned long long");
3366 type.ReplaceAll("Long64_t","long long");
3367 type.ReplaceAll("signed char","char");
3368 type.ReplaceAll("<signed char","<char");
3369 type.ReplaceAll(",signed char",",char");
3370 if (type=="signed char") type = "char";
3371 }
3372
3373 il = type.Length();
3374 for (i=0; i<il; i++) id = id*3+type[i];
3375
3376 int dim = el->GetArrayDim();
3377 if (dim) {
3378 for (i=0;i<dim;i++) id = id*3+el->GetMaxIndex(i);
3379 }
3380
3381
3382 if (code > TClass::kNoRange) {
3383 const char *left;
3384 if (code > TClass::kNoRangeCheck)
3386 else
3387 left = strstr(el->GetTitle(),"[");
3388 if (left) {
3389 const char *right = strstr(left,"]");
3390 if (right) {
3391 ++left;
3392 while (left != right) {
3393 id = id*3 + *left;
3394 ++left;
3395 }
3396 }
3397 }
3398 }
3399 }
3400 return id;
3401}
3402
3403////////////////////////////////////////////////////////////////////////////////
3404
3405static void R__WriteConstructorBody(FILE *file, TIter &next)
3406{
3407 TStreamerElement *element = 0;
3408 next.Reset();
3409 while ((element = (TStreamerElement*)next())) {
3414 if(element->GetArrayLength() <= 1) {
3415 fprintf(file," %s = 0;\n",element->GetName());
3416 } else {
3417 fprintf(file," memset(%s,0,%d);\n",element->GetName(),element->GetSize());
3418 }
3419 }
3420 if (TVirtualStreamerInfo::kOffsetP <= element->GetType() && element->GetType() < TVirtualStreamerInfo::kObject ) {
3421 fprintf(file," %s = 0;\n",element->GetName());
3422 }
3423 }
3424}
3425
3426static constexpr int str_length(const char* str)
3427{
3428 return *str ? 1 + str_length(str + 1) : 0;
3429}
3430
3431////////////////////////////////////////////////////////////////////////////////
3432/// Return true if the element is auto_ptr or unique_ptr
3433
3434static bool R__IsUniquePtr(TStreamerElement *element) {
3435
3436 constexpr auto auto_ptr_len = str_length("auto_ptr<");
3437 constexpr auto unique_ptr_len = str_length("unique_ptr<");
3438
3439 const char *name = element->GetTypeNameBasic();
3440
3441 return ((strncmp(name, "auto_ptr<", auto_ptr_len) == 0)
3442 || (strncmp(name, "unique_ptr<", unique_ptr_len) == 0));
3443}
3444
3445////////////////////////////////////////////////////////////////////////////////
3446/// Write down the pointers and arrays part of the body of the 'move' constructor.
3447
3448static void R__WriteMoveBodyPointersArrays(FILE *file, const TString &protoname, TIter &next)
3449{
3450 TStreamerElement *element = 0;
3451 next.Reset();
3452 Bool_t defMod = kFALSE;
3453 while ((element = (TStreamerElement*)next())) {
3457 {
3458 if (!defMod) { fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE; };
3459 const char *ename = element->GetName();
3460 const char *colon2 = strstr(ename,"::");
3461 if (colon2) ename = colon2+2;
3462 if(element->GetArrayLength() <= 1) {
3463 fprintf(file," modrhs.%s = 0;\n",ename);
3464 } else {
3465 fprintf(file," memset(modrhs.%s,0,%d);\n",ename,element->GetSize());
3466 }
3467 } else {
3468 const char *ename = element->GetName();
3469 if (element->GetType() == kCharStar) {
3470 if (!defMod) {
3471 fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE;
3472 };
3473 fprintf(file," modrhs.%s = 0;\n",ename);
3474 } else if (TVirtualStreamerInfo::kOffsetP <= element->GetType() && element->GetType() < TVirtualStreamerInfo::kObject ) {
3475 if (!defMod) {
3476 fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE;
3477 };
3478 fprintf(file," modrhs.%s = 0;\n",ename);
3479 } else if (element->GetArrayLength() > 1) {
3480 // FIXME: Need to add support for variable length array.
3481 if (element->GetArrayDim() == 1) {
3482 fprintf(file," for (Int_t i=0;i<%d;i++) %s[i] = rhs.%s[i];\n",element->GetArrayLength(),ename,ename);
3483 } else if (element->GetArrayDim() >= 2) {
3484 fprintf(file," for (Int_t i=0;i<%d;i++) reinterpret_cast<%s *>(%s", element->GetArrayLength(), element->GetTypeName(), ename);
3485 fprintf(file,")[i] = reinterpret_cast<%s const *>(rhs.%s)[i];\n", element->GetTypeName(), ename);
3486 }
3487 } else if (element->GetType() == TVirtualStreamerInfo::kSTLp) {
3488 if (!defMod) { fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE; };
3489 fprintf(file," modrhs.%s = 0;\n",ename);
3490 } else if (element->GetType() == TVirtualStreamerInfo::kSTL) {
3491 if (!defMod) {
3492 fprintf(file," %s &modrhs = const_cast<%s &>( rhs );\n",protoname.Data(),protoname.Data()); defMod = kTRUE;
3493 }
3494 TClass *cle = element->GetClassPointer();
3495 TVirtualCollectionProxy *proxy = cle ? element->GetClassPointer()->GetCollectionProxy() : 0;
3496 std::string method_name = "clear";
3497 if (!element->TestBit(TStreamerElement::kDoNotDelete) && proxy && (((TStreamerSTL*)element)->GetSTLtype() == ROOT::kSTLbitset)) {
3498 method_name = "reset";
3499 }
3500 if (element->IsBase()) {
3501 fprintf(file," modrhs.%s();\n", method_name.c_str());
3502 } else {
3503 fprintf(file," modrhs.%s.%s();\n",ename, method_name.c_str());
3504 }
3505 }
3506 }
3507 }
3508}
3509
3510////////////////////////////////////////////////////////////////////////////////
3511/// Write down the body of the 'move' constructor.
3512
3513static void R__WriteMoveConstructorBody(FILE *file, const TString &protoname, TIter &next)
3514{
3515 TStreamerElement *element = 0;
3516 next.Reset();
3517 Bool_t atstart = kTRUE;
3518 while ((element = (TStreamerElement*)next())) {
3519 if (element->IsBase()) {
3520 if (atstart) { fprintf(file," : "); atstart = kFALSE; }
3521 else fprintf(file," , ");
3522 fprintf(file, "%s(const_cast<%s &>( rhs ))\n", element->GetName(),protoname.Data());
3523 } else {
3524 if (element->GetArrayLength() <= 1) {
3525 if (atstart) { fprintf(file," : "); atstart = kFALSE; }
3526 else fprintf(file," , ");
3527 if (R__IsUniquePtr(element)) {
3528 fprintf(file, "%s(const_cast<%s &>( rhs ).%s.release() )\n",element->GetName(),protoname.Data(),element->GetName());
3529 } else {
3530 fprintf(file, "%s(const_cast<%s &>( rhs ).%s)\n",element->GetName(),protoname.Data(),element->GetName());
3531 }
3532 }
3533 }
3534 }
3535 fprintf(file,"{\n");
3536 fprintf(file," // This is NOT a copy constructor. This is actually a move constructor (for stl container's sake).\n");
3537 fprintf(file," // Use at your own risk!\n");
3538 fprintf(file," (void)rhs; // avoid warning about unused parameter\n");
3539
3540 R__WriteMoveBodyPointersArrays(file, protoname, next);
3541}
3542
3543////////////////////////////////////////////////////////////////////////////////
3544/// Write down the body of the 'move' constructor.
3545
3546static void R__WriteOddOperatorEqualBody(FILE *file, const TString &protoname, TIter &next)
3547{
3548 fprintf(file,"{\n");
3549 fprintf(file," // This is NOT a copy operator=. This is actually a move operator= (for stl container's sake).\n");
3550 fprintf(file," // Use at your own risk!\n");
3551 fprintf(file," (void)rhs; // avoid warning about unused parameter\n");
3552
3553 TStreamerElement *element = 0;
3554 next.Reset();
3555 while ((element = (TStreamerElement*)next())) {
3556 if (element->IsBase()) {
3557 fprintf(file, " %s::operator=(const_cast<%s &>( rhs ));\n", element->GetName(),protoname.Data());
3558 } else {
3559 if (element->GetArrayLength() <= 1) {
3560 if (R__IsUniquePtr(element)) {
3561 fprintf(file, " %s = std::move((const_cast<%s &>( rhs ).%s));\n",element->GetName(),protoname.Data(),element->GetName());
3562 } else {
3563 fprintf(file, " %s = (const_cast<%s &>( rhs ).%s);\n",element->GetName(),protoname.Data(),element->GetName());
3564 }
3565 }
3566 }
3567 }
3568
3569 R__WriteMoveBodyPointersArrays(file, protoname, next);
3570
3571 fprintf(file, " return *this;\n");
3572}
3573
3574////////////////////////////////////////////////////////////////////////////////
3575
3576static void R__WriteDestructorBody(FILE *file, TIter &next)
3577{
3578 TStreamerElement *element = 0;
3579 next.Reset();
3580 while ((element = (TStreamerElement*)next())) {
3584 {
3585 const char *ename = element->GetName();
3586 const char *colon2 = strstr(ename,"::");
3587 if (colon2) ename = colon2+2;
3589 if(element->GetArrayLength() <= 1) {
3590 fprintf(file," %s = 0;\n",ename);
3591 } else {
3592 fprintf(file," memset(%s,0,%d);\n",ename,element->GetSize());
3593 }
3594 } else {
3595 if(element->GetArrayLength() <= 1) {
3596 fprintf(file," delete %s; %s = 0;\n",ename,ename);
3597 } else {
3598 fprintf(file," for (Int_t i=0;i<%d;i++) delete %s[i]; memset(%s,0,%d);\n",element->GetArrayLength(),ename,ename,element->GetSize());
3599 }
3600 }
3601 }
3602 if (element->GetType() == TVirtualStreamerInfo::kCharStar) {
3603 const char *ename = element->GetName();
3605 fprintf(file," %s = 0;\n",ename);
3606 } else {
3607 fprintf(file," delete [] %s; %s = 0;\n",ename,ename);
3608 }
3609 }
3610 if (TVirtualStreamerInfo::kOffsetP <= element->GetType() && element->GetType() < TVirtualStreamerInfo::kObject ) {
3611 const char *ename = element->GetName();
3613 fprintf(file," %s = 0;\n",ename);
3614 } else if (element->HasCounter()) {
3615 fprintf(file," delete %s; %s = 0;\n",ename,ename);
3616 } else {
3617 fprintf(file," delete [] %s; %s = 0;\n",ename,ename);
3618 }
3619 }
3620 if (element->GetType() == TVirtualStreamerInfo::kSTL || element->GetType() == TVirtualStreamerInfo::kSTLp) {
3621 const char *ename = element->GetName();
3622 const char *prefix = "";
3623 if ( element->GetType() == TVirtualStreamerInfo::kSTLp ) {
3624 prefix = "*";
3625 } else if ( element->IsBase() ) {
3626 ename = "this";
3627 }
3628 TClass *cle = element->GetClassPointer();
3629 TVirtualCollectionProxy *proxy = cle ? element->GetClassPointer()->GetCollectionProxy() : 0;
3630 if (!element->TestBit(TStreamerElement::kDoNotDelete) && proxy) {
3631 Int_t stltype = ((TStreamerSTL*)element)->GetSTLtype();
3632
3633 if (proxy->HasPointers()) {
3634 fprintf(file," std::for_each( (%s %s).rbegin(), (%s %s).rend(), DeleteObjectFunctor() );\n",prefix,ename,prefix,ename);
3635 //fprintf(file," %s::iterator iter;\n");
3636 //fprintf(file," %s::iterator begin = (%s %s).begin();\n");
3637 //fprintf(file," %s::iterator end (%s %s).end();\n");
3638 //fprintf(file," for( iter = begin; iter != end; ++iter) { delete *iter; }\n");
3639 } else {
3640 if (stltype == ROOT::kSTLmap || stltype == ROOT::kSTLmultimap) {
3642 std::vector<std::string> inside;
3643 int nestedLoc;
3644 TClassEdit::GetSplit(enamebasic, inside, nestedLoc, TClassEdit::kLong64);
3645 if ((!inside[1].empty() && inside[1][inside[1].size()-1]=='*')
3646 || (!inside[2].empty() && inside[2][inside[2].size()-1]=='*')) {
3647 fprintf(file," std::for_each( (%s %s).rbegin(), (%s %s).rend(), DeleteObjectFunctor() );\n",prefix,ename,prefix,ename);
3648 }
3649 }
3650 }
3651 }
3652 if ( prefix[0] ) {
3653 fprintf(file," delete %s; %s = 0;\n",ename,ename);
3654 }
3655 }
3656 }
3657}
3658
3659////////////////////////////////////////////////////////////////////////////////
3660/// Write the Declaration of class.
3661
3662void TStreamerInfo::GenerateDeclaration(FILE *fp, FILE *sfp, const TList *subClasses, Bool_t top)
3663{
3664 if (fClassVersion == -3) {
3665 return;
3666 }
3667
3668 Bool_t needGenericTemplate = fElements==0 || fElements->IsEmpty();
3669 Bool_t isTemplate = kFALSE;
3670 const char *clname = GetName();
3671 TString template_protoname;
3672 if (strchr(clname, ':')) {
3673 // We might have a namespace in front of the classname.
3674 Int_t len = strlen(clname);
3675 const char *name = clname;
3676 UInt_t nest = 0;
3677 UInt_t pr_pos = 0;
3678 for (Int_t cur = 0; cur < len; ++cur) {
3679 switch (clname[cur]) {
3680 case '<':
3681 ++nest;
3682 pr_pos = cur;
3683 isTemplate = kTRUE;
3684 break;
3685 case '>':
3686 if (nest == 0) { cur = len; continue; } // the name is not well formed, give up.
3687 --nest;
3688 break;
3689 case ':': {
3690 if (nest == 0 && clname[cur+1] == ':') {
3691 // We have a scope
3692 isTemplate = kFALSE;
3693 name = clname + cur + 2;
3694 }
3695 break;
3696 }
3697 }
3698 }
3699 if (isTemplate) {
3700 template_protoname.Append(clname,pr_pos);
3701 }
3702 clname = name;
3703 } else {
3704 const char *where = strstr(clname, "<");
3705 isTemplate = where != 0;
3706 if (isTemplate) {
3707 template_protoname.Append(clname,where-clname);
3708 }
3709 }
3710
3711 if (needGenericTemplate && isTemplate) {
3712 TString templateName(TMakeProject::GetHeaderName("template "+template_protoname,0));
3713 fprintf(fp, "#ifndef %s_h\n", templateName.Data());
3714 fprintf(fp, "#define %s_h\n", templateName.Data());
3715 }
3716
3717 TString protoname;
3718 UInt_t numberOfNamespaces = TMakeProject::GenerateClassPrefix(fp, GetName(), top, protoname, 0, kFALSE, needGenericTemplate);
3719
3720 // Generate class statement with base classes.
3721 TStreamerElement *element;
3722 TIter next(fElements);
3723 Int_t nbase = 0;
3724 while ((element = (TStreamerElement*)next())) {
3725 if (!element->IsBase()) continue;
3726 nbase++;
3727 const char *ename = element->GetName();
3728 if (nbase == 1) fprintf(fp," : public %s",ename);
3729 else fprintf(fp," , public %s",ename);
3730 }
3731 fprintf(fp," {\n");
3732
3733 // Generate forward declaration nested classes.
3734 if (subClasses && subClasses->GetEntries()) {
3735 Bool_t needheader = true;
3736
3737 TIter subnext(subClasses);
3738 TStreamerInfo *subinfo;
3739 Int_t len = strlen(GetName());
3740 while ((subinfo = (TStreamerInfo*)subnext())) {
3741 if (strncmp(GetName(),subinfo->GetName(),len)==0 && (subinfo->GetName()[len]==':') ) {
3742 if (subinfo->GetName()[len+1]==':' && strstr(subinfo->GetName()+len+2,":")==0) {
3743 if (needheader) {
3744 fprintf(fp,"\npublic:\n");
3745 fprintf(fp,"// Nested classes forward declaration.\n");
3746 needheader = false;
3747 }
3748 TString sub_protoname;
3749 UInt_t sub_numberOfClasses = 0;
3750 UInt_t sub_numberOfNamespaces;
3751 if (subinfo->GetClassVersion() == -3) {
3752 sub_numberOfNamespaces = TMakeProject::GenerateClassPrefix(fp, subinfo->GetName() + len+2, kFALSE, sub_protoname, &sub_numberOfClasses, 3);
3753 } else {
3754 sub_numberOfNamespaces = TMakeProject::GenerateClassPrefix(fp, subinfo->GetName() + len+2, kFALSE, sub_protoname, &sub_numberOfClasses, kFALSE);
3755 fprintf(fp, ";\n");
3756 }
3757
3758 for (UInt_t i = 0;i < sub_numberOfClasses;++i) {
3759 fprintf(fp, "}; // end of class.\n");
3760 }
3761 if (sub_numberOfNamespaces > 0) {
3762 Error("GenerateDeclaration","Nested classes %s thought to be inside a namespace inside the class %s",subinfo->GetName(),GetName());
3763 }
3764 }
3765 }
3766 }
3767 }
3768
3769 fprintf(fp,"\npublic:\n");
3770 fprintf(fp,"// Nested classes declaration.\n");
3771
3772 // Generate nested classes.
3773 if (subClasses && subClasses->GetEntries()) {
3774 TIter subnext(subClasses,kIterBackward);
3775 TStreamerInfo *subinfo;
3776 Int_t len = strlen(GetName());
3777 while ((subinfo = (TStreamerInfo*)subnext())) {
3778 if (strncmp(GetName(),subinfo->GetName(),len)==0 && (subinfo->GetName()[len]==':')) {
3779 if (subinfo->GetName()[len+1]==':' && strstr(subinfo->GetName()+len+2,":")==0) {
3780 subinfo->GenerateDeclaration(fp, sfp, subClasses, kFALSE);
3781 }
3782 }
3783 }
3784 }
3785
3786 fprintf(fp,"\npublic:\n");
3787 fprintf(fp,"// Data Members.\n");
3788
3789 {
3790 // Generate data members.
3791 TString name(128);
3792 Int_t ltype = 12;
3793 Int_t ldata = 10;
3794 Int_t lt,ld,is;
3795 TString line;
3796 line.Resize(kMaxLen);
3797 next.Reset();
3798 while ((element = (TStreamerElement*)next())) {
3799
3800 if (element->IsBase()) continue;
3801 const char *ename = element->GetName();
3802
3803 name = ename;
3804 for (Int_t i=0;i < element->GetArrayDim();i++) {
3805 name += TString::Format("[%d]",element->GetMaxIndex(i));
3806 }
3807 name += ";";
3808 ld = name.Length();
3809
3810 TString enamebasic = element->GetTypeNameBasic();
3811 if (element->IsA() == TStreamerSTL::Class()) {
3812 // If we have a map, multimap, set or multiset,
3813 // and the key is a class, we need to replace the
3814 // container by a vector since we don't have the
3815 // comparator function.
3816 Int_t stltype = ((TStreamerSTL*)element)->GetSTLtype();
3817 switch (stltype) {
3818 case ROOT::kSTLmap:
3819 case ROOT::kSTLmultimap:
3820 case ROOT::kSTLset:
3821 case ROOT::kSTLmultiset:
3824 {
3825 enamebasic = TMakeProject::UpdateAssociativeToVector(enamebasic);
3826 }
3827 default:
3828 // nothing to do.
3829 break;
3830 }
3831 } else if (strncmp(enamebasic.Data(), "auto_ptr<", strlen("auto_ptr<")) == 0) {
3832 enamebasic = TMakeProject::UpdateAssociativeToVector(enamebasic);
3833 }
3834
3835 lt = enamebasic.Length();
3836
3837 line = " ";
3838 line += enamebasic;
3839 if (lt>=ltype) ltype = lt+1;
3840
3841 for (is = 3+lt; is < (3+ltype); ++is) line += ' ';
3842
3843 line += name;
3844 if (element->IsaPointer() && !strchr(line,'*')) line[2+ltype] = '*';
3845
3846 if (ld>=ldata) ldata = ld+1;
3847 for (is = 3+ltype+ld; is < (3+ltype+ldata); ++is) line += ' ';
3848
3849 line += " //";
3850 line += element->GetTitle();
3851 fprintf(fp,"%s\n",line.Data());
3852 }
3853 }
3854 if (needGenericTemplate && isTemplate) {
3855 // Generate default functions, ClassDef and trailer.
3856 fprintf(fp,"\n %s() {\n",protoname.Data());
3857 R__WriteConstructorBody(fp,next);
3858 fprintf(fp," }\n");
3859 fprintf(fp," %s(%s && ) = default;\n",protoname.Data(),protoname.Data());
3860 fprintf(fp," %s &operator=(const %s & rhs)\n ",protoname.Data(),protoname.Data());
3861 R__WriteOddOperatorEqualBody(fp,protoname,next);
3862 fprintf(fp," }\n");
3863 fprintf(fp," %s(const %s & rhs )\n ",protoname.Data(),protoname.Data());
3864 R__WriteMoveConstructorBody(fp,protoname,next);
3865 fprintf(fp," }\n");
3866 fprintf(fp," virtual ~%s() {\n ",protoname.Data());
3867 R__WriteDestructorBody(fp,next);
3868 fprintf(fp," }\n\n");
3869
3870 } else {
3871 // Generate default functions, ClassDef and trailer.
3872 fprintf(fp,"\n %s();\n",protoname.Data());
3873 fprintf(fp," %s(%s && ) = default;\n",protoname.Data(),protoname.Data());
3874 fprintf(fp," %s &operator=(const %s & );\n",protoname.Data(),protoname.Data());
3875 fprintf(fp," %s(const %s & );\n",protoname.Data(),protoname.Data());
3876 fprintf(fp," virtual ~%s();\n\n",protoname.Data());
3877
3878 // Add the implementations to the source.cxx file.
3880 fprintf(sfp,"#ifndef %s_cxx\n",guard.Data());
3881 fprintf(sfp,"#define %s_cxx\n",guard.Data());
3882 fprintf(sfp,"%s::%s() {\n",GetName(),protoname.Data());
3883 R__WriteConstructorBody(sfp,next);
3884 fprintf(sfp,"}\n");
3885
3886 fprintf(sfp,"%s &%s::operator=(const %s & rhs)\n",GetName(),GetName(),protoname.Data());
3887 R__WriteOddOperatorEqualBody(sfp,protoname,next);
3888 fprintf(sfp,"}\n");
3889
3890 fprintf(sfp,"%s::%s(const %s & rhs)\n",GetName(),protoname.Data(),protoname.Data());
3891 R__WriteMoveConstructorBody(sfp,protoname,next);
3892 fprintf(sfp,"}\n");
3893
3894 fprintf(sfp,"%s::~%s() {\n",GetName(),protoname.Data());
3895 R__WriteDestructorBody(sfp,next);
3896 fprintf(sfp,"}\n");
3897 fprintf(sfp,"#endif // %s_cxx\n\n",guard.Data());
3898 }
3899
3900 TClass *cl = gROOT->GetClass(GetName());
3901 if (fClassVersion > 1 || (cl && cl->IsTObject()) ) {
3902 // add 1 to class version in case we didn't manage reproduce the class layout to 100%.
3903 if (fClassVersion == 0) {
3904 // If the class was declared 'transient', keep it that way.
3905 fprintf(fp," ClassDef(%s,%d); // Generated by MakeProject.\n",protoname.Data(),0);
3906 } else {
3907 fprintf(fp," ClassDef(%s,%d); // Generated by MakeProject.\n",protoname.Data(),fClassVersion + 1);
3908 }
3909 }
3910 fprintf(fp,"};\n");
3911
3912 for(UInt_t i=0;i<numberOfNamespaces;++i) {
3913 fprintf(fp,"} // namespace\n");
3914 }
3915
3916 if (needGenericTemplate && isTemplate) {
3917 fprintf(fp,"#endif // generic template declaration\n");
3918 }
3919}
3920
3921////////////////////////////////////////////////////////////////////////////////
3922/// Add to the header file, the \#include need for this class.
3923
3924UInt_t TStreamerInfo::GenerateIncludes(FILE *fp, char *inclist, const TList *extrainfos)
3925{
3926 if (inclist[0]==0) {
3927 // Always have this include for ClassDef.
3928 TMakeProject::AddInclude( fp, "Rtypes.h", kFALSE, inclist);
3929 }
3930 UInt_t ninc = 0;
3931
3932 const char *clname = GetName();
3933 if (strchr(clname,'<')) {
3934 // This is a template, we need to check the template parameter.
3935 ninc += TMakeProject::GenerateIncludeForTemplate(fp, clname, inclist, kFALSE, extrainfos);
3936 }
3937
3938 TString name(1024);
3939 Int_t ltype = 10;
3940 Int_t ldata = 10;
3941 Int_t lt;
3942 Int_t ld;
3943 TIter next(fElements);
3944 TStreamerElement *element;
3945 Bool_t incRiostream = kFALSE;
3946 while ((element = (TStreamerElement*)next())) {
3947 //if (element->IsA() == TStreamerBase::Class()) continue;
3948 const char *ename = element->GetName();
3949 const char *colon2 = strstr(ename,"::");
3950 if (colon2) ename = colon2+2;
3951 name = ename;
3952 for (Int_t i=0;i < element->GetArrayDim();i++) {
3953 name += TString::Format("[%d]",element->GetMaxIndex(i));
3954 }
3955 ld = name.Length();
3956 lt = strlen(element->GetTypeName());
3957 if (ltype < lt) ltype = lt;
3958 if (ldata < ld) ldata = ld;
3959
3960 //must include Riostream.h in case of an STL container
3961 if (!incRiostream && element->InheritsFrom(TStreamerSTL::Class())) {
3962 incRiostream = kTRUE;
3963 TMakeProject::AddInclude( fp, "Riostream.h", kFALSE, inclist);
3964 }
3965
3966 //get include file name if any
3967 const char *include = element->GetInclude();
3968 if (!include[0]) continue;
3969
3970 Bool_t greater = (include[0]=='<');
3971 include++;
3972
3973 if (strncmp(include,"include/",8)==0) {
3974 include += 8;
3975 }
3976 if (strncmp(include,"include\\",9)==0) {
3977 include += 9;
3978 }
3979 if (TClassEdit::IsStdPair(element->GetTypeName())) {
3980 TMakeProject::AddInclude( fp, "utility", kTRUE, inclist);
3981 } else if (strncmp(element->GetTypeName(),"auto_ptr<",strlen("auto_ptr<"))==0) {
3982 TMakeProject::AddInclude( fp, "memory", kTRUE, inclist);
3983 } else {
3984 TString incName( include, strlen(include)-1 );
3985 incName = TMakeProject::GetHeaderName(incName,extrainfos);
3986 TMakeProject::AddInclude( fp, incName.Data(), greater, inclist);
3987 }
3988
3989 if (strchr(element->GetTypeName(),'<')) {
3990 // This is a template, we need to check the template parameter.
3991 ninc += TMakeProject::GenerateIncludeForTemplate(fp, element->GetTypeName(), inclist, kFALSE, extrainfos);
3992 }
3993 }
3994 return ninc;
3995}
3996
3997////////////////////////////////////////////////////////////////////////////////
3998/// Generate header file for the class described by this TStreamerInfo
3999/// the function is called by TFile::MakeProject for each class in the file
4000
4001Int_t TStreamerInfo::GenerateHeaderFile(const char *dirname, const TList *subClasses, const TList *extrainfos)
4002{
4003 // if (fClassVersion == -4) return 0;
4004 if ((fClass && fClass->GetCollectionType()) || TClassEdit::IsSTLCont(GetName())) return 0;
4005 if (TClassEdit::IsStdPair(GetName())) return 0;
4006 if (strncmp(GetName(),"auto_ptr<",strlen("auto_ptr<"))==0) return 0;
4007
4009 if (cl) {
4010 if (cl->HasInterpreterInfo()) return 0; // skip known classes
4011 }
4012 Bool_t isTemplate = kFALSE;
4013 if (strchr(GetName(),':')) {
4014 UInt_t len = strlen(GetName());
4015 UInt_t nest = 0;
4016 UInt_t scope = 0;
4017 for(UInt_t i=len; i>0; --i) {
4018 switch(GetName()[i]) {
4019 case '>': ++nest; if (scope==0) { isTemplate = kTRUE; } break;
4020 case '<': --nest; break;
4021 case ':':
4022 if (nest==0 && GetName()[i-1]==':') {
4023 // We have a scope
4024 TString nsname(GetName(), i-1);
4025 cl = gROOT->GetClass(nsname);
4026 if (cl && (cl->Size()!=0 || (cl->Size()==0 && !cl->HasInterpreterInfo() /*empty 'base' class on file*/))) {
4027 // This class is actually nested.
4028 return 0;
4029 } else if (cl == 0 && extrainfos != 0) {
4030 TStreamerInfo *clinfo = (TStreamerInfo*)extrainfos->FindObject(nsname);
4031 if (clinfo && clinfo->GetClassVersion() == -5) {
4032 // This class is actually nested.
4033 return 0;
4034 }
4035 }
4036 ++scope;
4037 }
4038 break;
4039 }
4040 }
4041 }
4042 Bool_t needGenericTemplate = isTemplate && (fElements==0 || fElements->IsEmpty());
4043
4044 if (gDebug) printf("generating code for class %s\n",GetName());
4045
4046 // Open the file
4047
4048 TString headername( TMakeProject::GetHeaderName( GetName(), extrainfos ) );
4050 filename.Form("%s/%s.h",dirname,headername.Data());
4051
4052 FILE *fp = fopen(filename.Data(),"w");
4053 if (!fp) {
4054 Error("MakeProject","Cannot open output file:%s\n",filename.Data());
4055 return 0;
4056 }
4057
4058 filename.Form("%s/%sProjectHeaders.h",dirname,gSystem->BaseName(dirname));
4059 FILE *allfp = fopen(filename.Data(),"a");
4060 if (!allfp) {
4061 Error("MakeProject","Cannot open output file:%s\n",filename.Data());
4062 fclose(fp);
4063 return 0;
4064 }
4065 fprintf(allfp,"#include \"%s.h\"\n", headername.Data());
4066 fclose(allfp);
4067
4068 char *inclist = new char[50000];
4069 inclist[0] = 0;
4070
4071 // Generate class header.
4072 TDatime td;
4073 fprintf(fp,"//////////////////////////////////////////////////////////\n");
4074 fprintf(fp,"// This class has been generated by TFile::MakeProject\n");
4075 fprintf(fp,"// (%s by ROOT version %s)\n",td.AsString(),gROOT->GetVersion());
4076 fprintf(fp,"// from the StreamerInfo in file %s\n",gDirectory->GetFile()->GetName());
4077 fprintf(fp,"//////////////////////////////////////////////////////////\n");
4078 fprintf(fp,"\n");
4079 fprintf(fp,"\n");
4080 fprintf(fp,"#ifndef %s_h\n",headername.Data());
4081 fprintf(fp,"#define %s_h\n",headername.Data());
4082 TMakeProject::GenerateForwardDeclaration(fp, GetName(), inclist, kFALSE, needGenericTemplate, extrainfos);
4083 fprintf(fp,"\n");
4084
4085 GenerateIncludes(fp, inclist, extrainfos);
4086 if (subClasses) {
4087 TIter subnext(subClasses);
4088 TStreamerInfo *subinfo;
4089 while ((subinfo = (TStreamerInfo*)subnext())) {
4090 subinfo->GenerateIncludes(fp, inclist, extrainfos);
4091 }
4092 }
4093 fprintf(fp,"\n");
4094
4095 TString sourcename; sourcename.Form( "%s/%sProjectSource.cxx", dirname, gSystem->BaseName(dirname) );
4096 FILE *sfp = fopen( sourcename.Data(), "a" );
4097 if (sfp) {
4098 GenerateDeclaration(fp, sfp, subClasses);
4099 } else {
4100 Error("GenerateHeaderFile","Could not open %s for appending",sourcename.Data());
4101 }
4102 TMakeProject::GeneratePostDeclaration(fp, this, inclist);
4103
4104 fprintf(fp,"#endif\n");
4105
4106 delete [] inclist;
4107 fclose(fp);
4108 if (sfp) fclose(sfp);
4109 return 1;
4110}
4111
4112////////////////////////////////////////////////////////////////////////////////
4113/// Compute data member offset.
4114/// Return pointer to the Streamer function if one exists
4115
4117{
4118 TIter nextr(fClass->GetListOfRealData());
4119 char dmbracket[256];
4120 snprintf(dmbracket,255,"%s[",dm->GetName());
4122 if (!fClass->IsLoaded()) {
4123 // If the 'class' is not loaded, we do not have a TClass bootstrap and thus
4124 // the 'RealData' might not have enough information because of the lack
4125 // of proper ShowMember implementation.
4126 if (! (dm->Property() & kIsStatic) ) {
4127 // Give an offset only to non-static members.
4128 offset = dm->GetOffset();
4129 }
4130 }
4131 TRealData *rdm;
4132 while ((rdm = (TRealData*)nextr())) {
4133 char *rdmc = (char*)rdm->GetName();
4134 //next statement required in case a class and one of its parent class
4135 //have data members with the same name
4136 if (dm->IsaPointer() && rdmc[0] == '*') rdmc++;
4137
4138 if (rdm->GetDataMember() != dm) continue;
4139 if (strcmp(rdmc,dm->GetName()) == 0) {
4140 offset = rdm->GetThisOffset();
4141 streamer = rdm->GetStreamer();
4142 break;
4143 }
4144 if (strcmp(rdm->GetName(),dm->GetName()) == 0) {
4145 if (rdm->IsObject()) {
4146 offset = rdm->GetThisOffset();
4147 streamer = rdm->GetStreamer();
4148 break;
4149 }
4150 }
4151 if (strstr(rdm->GetName(),dmbracket)) {
4152 offset = rdm->GetThisOffset();
4153 streamer = rdm->GetStreamer();
4154 break;
4155 }
4156 }
4157 return offset;
4158}
4159
4160////////////////////////////////////////////////////////////////////////////////
4161/// Return the offset of the data member as indicated by this StreamerInfo.
4162
4163Int_t TStreamerInfo::GetOffset(const char *elementName) const
4164{
4165 if (elementName == 0) return 0;
4166
4167 Int_t offset = 0;
4168 TStreamerElement *elem = (TStreamerElement*)fElements->FindObject(elementName);
4169 if (elem) offset = elem->GetOffset();
4170
4171 return offset;
4172}
4173
4174////////////////////////////////////////////////////////////////////////////////
4175/// Return total size of all persistent elements of the class (with offsets).
4176
4178{
4179 return fSize;
4180}
4181
4182////////////////////////////////////////////////////////////////////////////////
4183/// Return total size of all persistent elements of the class
4184/// use GetSize if you want to get the real size in memory.
4185
4187{
4188 TIter next(fElements);
4189 TStreamerElement *element;
4190 Int_t asize = 0;
4191 while ((element = (TStreamerElement*)next())) {
4192 asize += element->GetSize();
4193 }
4194 return asize;
4195}
4196
4197////////////////////////////////////////////////////////////////////////////////
4198/// Return the StreamerElement of "datamember" inside our
4199/// class or any of its base classes.
4200///
4201/// The offset information
4202/// contained in the StreamerElement is related to its immediately
4203/// containing class, so we return in 'offset' the offset inside
4204/// our class.
4205
4207{
4208 if (!fElements) {
4209 return 0;
4210 }
4211
4212 // Look first at the data members and base classes
4213 // of our class.
4214 TStreamerElement* element = (TStreamerElement*) fElements->FindObject(datamember);
4215 if (element) {
4216 offset = element->GetOffset();
4217 return element;
4218 }
4219
4220 // Not found, so now try the data members and base classes
4221 // of the base classes of our class.
4222 if (fClass->HasDataMemberInfo()) {
4223 // Our class has a dictionary loaded, use it to search the base classes.
4224 TStreamerElement* base_element = 0;
4225 TBaseClass* base = 0;
4226 TClass* base_cl = 0;
4227 Int_t base_offset = 0;
4228 Int_t local_offset = 0;
4229 TIter nextb(fClass->GetListOfBases());
4230 // Iterate on list of base classes.
4231 while ((base = (TBaseClass*) nextb())) {
4232 base_cl = TClass::GetClass(base->GetName());
4233 base_element = (TStreamerElement*) fElements->FindObject(base->GetName());
4234 if (!base_cl || !base_element) {
4235 continue;
4236 }
4237 base_offset = base_element->GetOffset();
4238 element = ((TStreamerInfo*)base_cl->GetStreamerInfo())->GetStreamerElement(datamember, local_offset);
4239 if (element) {
4240 offset = base_offset + local_offset;
4241 return element;
4242 }
4243 }
4244 } else {
4245 // Our class's dictionary is not loaded. Search through the base class streamer elements.
4246 TIter next(fElements);
4247 TStreamerElement* curelem = 0;
4248 while ((curelem = (TStreamerElement*) next())) {
4249 if (curelem->InheritsFrom(TStreamerBase::Class())) {
4250 TClass* baseClass = curelem->GetClassPointer();
4251 if (!baseClass) {
4252 continue;
4253 }
4254 Int_t base_offset = curelem->GetOffset();
4255 Int_t local_offset = 0;
4256 TStreamerInfo *baseInfo;
4257 if (baseClass->Property() & kIsAbstract) {
4258 baseInfo = (TStreamerInfo*)baseClass->GetStreamerInfoAbstractEmulated();
4259 } else {
4260 baseInfo = (TStreamerInfo*)baseClass->GetStreamerInfo();
4261 }
4262 if (baseInfo) element = baseInfo->GetStreamerElement(datamember, local_offset);
4263 if (element) {
4264 offset = base_offset + local_offset;
4265 return element;
4266 }
4267 }
4268 }
4269 }
4270 return 0;
4271}
4272
4273////////////////////////////////////////////////////////////////////////////////
4274/// <b>Obsolete</b>: this routine is obsolete and should not longer be used.
4275///
4276/// TStreamerInfo holds two types of data structures
4277/// - TObjArray* fElements; containing the list of all TStreamerElement
4278/// objects for this class version.
4279/// - ULong_t* fElem; containing the preprocessed information
4280/// by TStreamerInfo::Compile In case consecutive data members
4281/// are of the same type, the Compile function declares the consecutive
4282/// elements as one single element in fElems.
4283///
4284/// Example with the class TAttLine:
4285/// ~~~{.cpp}
4286/// TClass::GetClass("TAttLine")->GetStreamerInfo()->ls(); produces;
4287/// StreamerInfo for class: TAttLine, version=1
4288/// short fLineColor offset= 4 type= 2 line color
4289/// short fLineStyle offset= 6 type= 2 line style
4290/// short fLineWidth offset= 8 type= 2 line width
4291/// i= 0, fLineColor type= 22, offset= 4, len=3, method=0
4292/// ~~~
4293/// For I/O implementations (eg. XML) , one has to know the original name
4294/// of the data member. This function can be used to return a pointer
4295/// to the original TStreamerElement object corresponding to the j-th
4296/// element of a compressed array in fElems.
4297/// Parameters description:
4298/// - i: the serial number in array fElem
4299/// - j: the element number in the array of consecutive types
4300/// In the above example the class TAttLine has 3 consecutive data members
4301/// of the same type "short". Compile makes one single array of 3 elements.
4302/// To access the TStreamerElement for the second element
4303/// of this array, one can call:
4304/// ~~~{.cpp}
4305/// auto el = GetStreamerElementReal(0,1);
4306/// auto membername = el->GetName();
4307/// ~~~
4308/// This function is typically called from TBuffer, TXmlBuffer.
4309
4311{
4312 ::Obsolete("TStreamerInfo::GetStreamerElementReal", "v5-34-20", "v6-00-02");
4313
4314 if (i < 0 || i >= fNdata) return 0;
4315 if (j < 0) return 0;
4316 if (!fElements) return 0;
4317 TStreamerElement *se = (TStreamerElement*)fCompOpt[i]->fElem;
4318 if (!se) return 0;
4319 Int_t nelems = fElements->GetEntriesFast();
4320 for (Int_t ise=0;ise < nelems;ise++) {
4321 if (se != (TStreamerElement*)fElements->UncheckedAt(ise)) continue;
4322 if (ise+j >= nelems) return 0;
4323 return (TStreamerElement*)fElements->UncheckedAt(ise+j);
4324 }
4325 return 0;
4326}
4327
4328////////////////////////////////////////////////////////////////////////////////
4329/// Get the value from inside a collection.
4330
4331template <typename T>
4333{
4334 if (type>=kConv && type<kSTL) {
4335 type -= kConv;
4336 }
4337 switch (type) {
4338 // basic types
4339 case kBool: {Bool_t *val = (Bool_t*)ladd; return T(*val);}
4340 case kChar: {Char_t *val = (Char_t*)ladd; return T(*val);}
4341 case kShort: {Short_t *val = (Short_t*)ladd; return T(*val);}
4342 case kInt: {Int_t *val = (Int_t*)ladd; return T(*val);}
4343 case kLong: {Long_t *val = (Long_t*)ladd; return T(*val);}
4344 case kLong64: {Long64_t *val = (Long64_t*)ladd; return T(*val);}
4345 case kFloat: {Float_t *val = (Float_t*)ladd; return T(*val);}
4346 case kFloat16: {Float_t *val = (Float_t*)ladd; return T(*val);}
4347 case kDouble: {Double_t *val = (Double_t*)ladd; return T(*val);}
4348 case kDouble32: {Double_t *val = (Double_t*)ladd; return T(*val);}
4349 case kUChar: {UChar_t *val = (UChar_t*)ladd; return T(*val);}
4350 case kUShort: {UShort_t *val = (UShort_t*)ladd; return T(*val);}
4351 case kUInt: {UInt_t *val = (UInt_t*)ladd; return T(*val);}
4352 case kULong: {ULong_t *val = (ULong_t*)ladd; return T(*val);}
4353#if defined(_MSC_VER) && (_MSC_VER <= 1200)
4354 case kULong64: {Long64_t *val = (Long64_t*)ladd; return T(*val);}
4355#else
4356 case kULong64: {ULong64_t *val= (ULong64_t*)ladd; return T(*val);}
4357#endif
4358 case kBits: {UInt_t *val = (UInt_t*)ladd; return T(*val);}
4359
4360 // array of basic types array[8]
4361 case kOffsetL + kBool: {Bool_t *val = (Bool_t*)ladd; return T(val[k]);}
4362 case kOffsetL + kChar: {Char_t *val = (Char_t*)ladd; return T(val[k]);}
4363 case kOffsetL + kShort: {Short_t *val = (Short_t*)ladd; return T(val[k]);}
4364 case kOffsetL + kInt: {Int_t *val = (Int_t*)ladd; return T(val[k]);}
4365 case kOffsetL + kLong: {Long_t *val = (Long_t*)ladd; return T(val[k]);}
4366 case kOffsetL + kLong64: {Long64_t *val = (Long64_t*)ladd; return T(val[k]);}
4367 case kOffsetL + kFloat: {Float_t *val = (Float_t*)ladd; return T(val[k]);}
4368 case kOffsetL + kFloat16: {Float_t *val = (Float_t*)ladd; return T(val[k]);}
4369 case kOffsetL + kDouble: {Double_t *val = (Double_t*)ladd; return T(val[k]);}
4370 case kOffsetL + kDouble32:{Double_t *val = (Double_t*)ladd; return T(val[k]);}
4371 case kOffsetL + kUChar: {UChar_t *val = (UChar_t*)ladd; return T(val[k]);}
4372 case kOffsetL + kUShort: {UShort_t *val = (UShort_t*)ladd; return T(val[k]);}
4373 case kOffsetL + kUInt: {UInt_t *val = (UInt_t*)ladd; return T(val[k]);}
4374 case kOffsetL + kULong: {ULong_t *val = (ULong_t*)ladd; return T(val[k]);}
4375#if defined(_MSC_VER) && (_MSC_VER <= 1200)
4376 case kOffsetL + kULong64: {Long64_t *val = (Long64_t*)ladd; return T(val[k]);}
4377#else
4378 case kOffsetL + kULong64:{ULong64_t *val= (ULong64_t*)ladd; return T(val[k]);}
4379#endif
4380
4381#define READ_ARRAY(TYPE_t) \
4382 { \
4383 Int_t sub_instance, index; \
4384 Int_t instance = k; \
4385 if (len) { \
4386 index = instance / len; \
4387 sub_instance = instance % len; \
4388 } else { \
4389 index = instance; \
4390 sub_instance = 0; \
4391 } \
4392 TYPE_t **val =(TYPE_t**)(ladd); \
4393 return T((val[sub_instance])[index]); \
4394 }
4395
4396 // pointer to an array of basic types array[n]
4403 case kOffsetP + kFloat16_t:
4405 case kOffsetP + kDouble32_t:
4411#if defined(_MSC_VER) && (_MSC_VER <= 1200)
4413#else
4414 case kOffsetP +