Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TBufferText.cxx
Go to the documentation of this file.
1 // $Id$
2 // Author: Sergey Linev 21.12.2017
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2017, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 /**
13 \class TBufferText
14 \ingroup IO
15 
16 Base class for text-based streamers like TBufferJSON or TBufferXML
17 Special actions list will use methods, introduced in this class.
18 
19 Idea to have equivalent methods names in TBufferFile and TBufferText, that
20 actions list for both are the same.
21 */
22 
23 #include "TBufferText.h"
24 
25 #include "TStreamerInfo.h"
26 #include "TStreamerElement.h"
27 #include "TStreamerInfoActions.h"
28 #include "TFile.h"
29 #include "TVirtualMutex.h"
30 #include "TInterpreter.h"
31 #include "TROOT.h"
32 #include "TExMap.h"
33 #include "TError.h"
34 
35 ClassImp(TBufferText);
36 
37 const char *TBufferText::fgFloatFmt = "%e";
38 const char *TBufferText::fgDoubleFmt = "%.14e";
39 
40 ////////////////////////////////////////////////////////////////////////////////
41 /// Default constructor
42 
43 TBufferText::TBufferText() : TBufferIO()
44 {
45 }
46 
47 ////////////////////////////////////////////////////////////////////////////////
48 /// Normal constructor
49 
50 TBufferText::TBufferText(TBuffer::EMode mode, TObject *parent) : TBufferIO(mode)
51 {
52  fBufSize = 1000000000;
53 
54  SetParent(parent);
55  SetBit(kCannotHandleMemberWiseStreaming);
56 }
57 
58 ////////////////////////////////////////////////////////////////////////////////
59 /// destructor
60 
61 TBufferText::~TBufferText()
62 {
63 }
64 
65 ////////////////////////////////////////////////////////////////////////////////
66 /// Read one collection of objects from the buffer using the StreamerInfoLoopAction.
67 /// The collection needs to be a split TClonesArray or a split vector of pointers.
68 
69 Int_t TBufferText::ApplySequence(const TStreamerInfoActions::TActionSequence &sequence, void *obj)
70 {
71  TVirtualStreamerInfo *info = sequence.fStreamerInfo;
72  IncrementLevel(info);
73 
74  if (gDebug) {
75  // loop on all active members
76  TStreamerInfoActions::ActionContainer_t::const_iterator end = sequence.fActions.end();
77  for (TStreamerInfoActions::ActionContainer_t::const_iterator iter = sequence.fActions.begin(); iter != end;
78  ++iter) {
79  // Idea: Try to remove this function call as it is really needed only for JSON streaming.
80  SetStreamerElementNumber((*iter).fConfiguration->fCompInfo->fElem, (*iter).fConfiguration->fCompInfo->fType);
81  (*iter).PrintDebug(*this, obj);
82  (*iter)(*this, obj);
83  }
84  } else {
85  // loop on all active members
86  TStreamerInfoActions::ActionContainer_t::const_iterator end = sequence.fActions.end();
87  for (TStreamerInfoActions::ActionContainer_t::const_iterator iter = sequence.fActions.begin(); iter != end;
88  ++iter) {
89  // Idea: Try to remove this function call as it is really needed only for JSON streaming.
90  SetStreamerElementNumber((*iter).fConfiguration->fCompInfo->fElem, (*iter).fConfiguration->fCompInfo->fType);
91  (*iter)(*this, obj);
92  }
93  }
94  DecrementLevel(info);
95  return 0;
96 }
97 
98 ////////////////////////////////////////////////////////////////////////////////
99 /// Read one collection of objects from the buffer using the StreamerInfoLoopAction.
100 /// The collection needs to be a split TClonesArray or a split vector of pointers.
101 
102 Int_t TBufferText::ApplySequenceVecPtr(const TStreamerInfoActions::TActionSequence &sequence, void *start_collection,
103  void *end_collection)
104 {
105  TVirtualStreamerInfo *info = sequence.fStreamerInfo;
106  IncrementLevel(info);
107 
108  if (gDebug) {
109  // loop on all active members
110  TStreamerInfoActions::ActionContainer_t::const_iterator end = sequence.fActions.end();
111  for (TStreamerInfoActions::ActionContainer_t::const_iterator iter = sequence.fActions.begin(); iter != end;
112  ++iter) {
113  // Idea: Try to remove this function call as it is really needed only for JSON streaming.
114  SetStreamerElementNumber((*iter).fConfiguration->fCompInfo->fElem, (*iter).fConfiguration->fCompInfo->fType);
115  (*iter).PrintDebug(
116  *this, *(char **)start_collection); // Warning: This limits us to TClonesArray and vector of pointers.
117  (*iter)(*this, start_collection, end_collection);
118  }
119  } else {
120  // loop on all active members
121  TStreamerInfoActions::ActionContainer_t::const_iterator end = sequence.fActions.end();
122  for (TStreamerInfoActions::ActionContainer_t::const_iterator iter = sequence.fActions.begin(); iter != end;
123  ++iter) {
124  // Idea: Try to remove this function call as it is really needed only for JSON streaming.
125  SetStreamerElementNumber((*iter).fConfiguration->fCompInfo->fElem, (*iter).fConfiguration->fCompInfo->fType);
126  (*iter)(*this, start_collection, end_collection);
127  }
128  }
129  DecrementLevel(info);
130  return 0;
131 }
132 
133 ////////////////////////////////////////////////////////////////////////////////
134 /// Read one collection of objects from the buffer using the StreamerInfoLoopAction.
135 
136 Int_t TBufferText::ApplySequence(const TStreamerInfoActions::TActionSequence &sequence, void *start_collection,
137  void *end_collection)
138 {
139  TVirtualStreamerInfo *info = sequence.fStreamerInfo;
140  IncrementLevel(info);
141 
142  TStreamerInfoActions::TLoopConfiguration *loopconfig = sequence.fLoopConfig;
143  if (gDebug) {
144 
145  // Get the address of the first item for the PrintDebug.
146  // (Performance is not essential here since we are going to print to
147  // the screen anyway).
148  void *arr0 = loopconfig->GetFirstAddress(start_collection, end_collection);
149  // loop on all active members
150  TStreamerInfoActions::ActionContainer_t::const_iterator end = sequence.fActions.end();
151  for (TStreamerInfoActions::ActionContainer_t::const_iterator iter = sequence.fActions.begin(); iter != end;
152  ++iter) {
153  // Idea: Try to remove this function call as it is really needed only for JSON streaming.
154  SetStreamerElementNumber((*iter).fConfiguration->fCompInfo->fElem, (*iter).fConfiguration->fCompInfo->fType);
155  (*iter).PrintDebug(*this, arr0);
156  (*iter)(*this, start_collection, end_collection, loopconfig);
157  }
158  } else {
159  // loop on all active members
160  TStreamerInfoActions::ActionContainer_t::const_iterator end = sequence.fActions.end();
161  for (TStreamerInfoActions::ActionContainer_t::const_iterator iter = sequence.fActions.begin(); iter != end;
162  ++iter) {
163  // Idea: Try to remove this function call as it is really needed only for JSON streaming.
164  SetStreamerElementNumber((*iter).fConfiguration->fCompInfo->fElem, (*iter).fConfiguration->fCompInfo->fType);
165  (*iter)(*this, start_collection, end_collection, loopconfig);
166  }
167  }
168  DecrementLevel(info);
169  return 0;
170 }
171 
172 ////////////////////////////////////////////////////////////////////////////////
173 /// Function called by the Streamer functions to serialize object at p
174 /// to buffer b. The optional argument info may be specified to give an
175 /// alternative StreamerInfo instead of using the default StreamerInfo
176 /// automatically built from the class definition.
177 /// For more information, see class TStreamerInfo.
178 
179 Int_t TBufferText::WriteClassBuffer(const TClass *cl, void *pointer)
180 {
181  // build the StreamerInfo if first time for the class
182  TStreamerInfo *sinfo = (TStreamerInfo *)const_cast<TClass *>(cl)->GetCurrentStreamerInfo();
183  if (!sinfo) {
184  // Have to be sure between the check and the taking of the lock if the current streamer has changed
185  R__LOCKGUARD(gInterpreterMutex);
186  sinfo = (TStreamerInfo *)const_cast<TClass *>(cl)->GetCurrentStreamerInfo();
187  if (!sinfo) {
188  const_cast<TClass *>(cl)->BuildRealData(pointer);
189  sinfo = new TStreamerInfo(const_cast<TClass *>(cl));
190  const_cast<TClass *>(cl)->SetCurrentStreamerInfo(sinfo);
191  const_cast<TClass *>(cl)->RegisterStreamerInfo(sinfo);
192  if (gDebug > 0)
193  Info("WriteClassBuffer", "Creating StreamerInfo for class: %s, version: %d", cl->GetName(),
194  cl->GetClassVersion());
195  sinfo->Build();
196  }
197  } else if (!sinfo->IsCompiled()) {
198  R__LOCKGUARD(gInterpreterMutex);
199  // Redo the test in case we have been victim of a data race on fIsCompiled.
200  if (!sinfo->IsCompiled()) {
201  const_cast<TClass *>(cl)->BuildRealData(pointer);
202  sinfo->BuildOld();
203  }
204  }
205 
206  // write the class version number and reserve space for the byte count
207  UInt_t R__c = WriteVersion(cl, kTRUE);
208 
209  // NOTE: In the future Philippe wants this to happen via a custom action
210  TagStreamerInfo(sinfo);
211  ApplySequence(*(sinfo->GetWriteTextActions()), (char *)pointer);
212 
213  // write the byte count at the start of the buffer
214  SetByteCount(R__c, kTRUE);
215 
216  if (gDebug > 2)
217  Info("WriteClassBuffer", "class: %s version %d has written %d bytes", cl->GetName(), cl->GetClassVersion(),
218  UInt_t(fBufCur - fBuffer) - R__c - (UInt_t)sizeof(UInt_t));
219  return 0;
220 }
221 
222 ////////////////////////////////////////////////////////////////////////////////
223 /// Deserialize information from a buffer into an object.
224 ///
225 /// Note: This function is called by the xxx::Streamer() functions in
226 /// rootcint-generated dictionaries.
227 /// This function assumes that the class version and the byte count
228 /// information have been read.
229 ///
230 /// \param[in] version The version number of the class
231 /// \param[in] start The starting position in the buffer b
232 /// \param[in] count The number of bytes for this object in the buffer
233 ///
234 
235 Int_t TBufferText::ReadClassBuffer(const TClass *cl, void *pointer, Int_t version, UInt_t start, UInt_t count,
236  const TClass *onFileClass)
237 {
238 
239  //---------------------------------------------------------------------------
240  // The ondisk class has been specified so get foreign streamer info
241  /////////////////////////////////////////////////////////////////////////////
242 
243  TStreamerInfo *sinfo = nullptr;
244  if (onFileClass) {
245  sinfo = (TStreamerInfo *)cl->GetConversionStreamerInfo(onFileClass, version);
246  if (!sinfo) {
247  Error("ReadClassBuffer",
248  "Could not find the right streamer info to convert %s version %d into a %s, object skipped at offset %d",
249  onFileClass->GetName(), version, cl->GetName(), Length());
250  CheckByteCount(start, count, onFileClass);
251  return 0;
252  }
253  }
254  //---------------------------------------------------------------------------
255  // Get local streamer info
256  /////////////////////////////////////////////////////////////////////////////
257  /// The StreamerInfo should exist at this point.
258 
259  else {
260  R__LOCKGUARD(gInterpreterMutex);
261  auto infos = cl->GetStreamerInfos();
262  auto ninfos = infos->GetSize();
263  if (version < -1 || version >= ninfos) {
264  Error("ReadBuffer1", "class: %s, attempting to access a wrong version: %d, object skipped at offset %d",
265  cl->GetName(), version, Length());
266  CheckByteCount(start, count, cl);
267  return 0;
268  }
269  sinfo = (TStreamerInfo *)infos->At(version);
270  if (!sinfo) {
271  // Unless the data is coming via a socket connection from with schema evolution
272  // (tracking) was not enabled. So let's create the StreamerInfo if it is the
273  // one for the current version, otherwise let's complain ...
274  // We could also get here if there old class version was '1' and the new class version is higher than 1
275  // AND the checksum is the same.
276  if (version == cl->GetClassVersion() || version == 1) {
277  const_cast<TClass *>(cl)->BuildRealData(pointer);
278  // This creation is alright since we just checked within the
279  // current 'locked' section.
280  sinfo = new TStreamerInfo(const_cast<TClass *>(cl));
281  const_cast<TClass *>(cl)->RegisterStreamerInfo(sinfo);
282  if (gDebug > 0)
283  Info("ReadClassBuffer", "Creating StreamerInfo for class: %s, version: %d", cl->GetName(), version);
284  sinfo->Build();
285  } else if (version == 0) {
286  // When the object was written the class was version zero, so
287  // there is no StreamerInfo to be found.
288  // Check that the buffer position corresponds to the byte count.
289  CheckByteCount(start, count, cl);
290  return 0;
291  } else {
292  Error("ReadClassBuffer",
293  "Could not find the StreamerInfo for version %d of the class %s, object skipped at offset %d",
294  version, cl->GetName(), Length());
295  CheckByteCount(start, count, cl);
296  return 0;
297  }
298  } else if (!sinfo->IsCompiled()) { // Note this read is protected by the above lock.
299  // Streamer info has not been compiled, but exists.
300  // Therefore it was read in from a file and we have to do schema evolution.
301  const_cast<TClass *>(cl)->BuildRealData(pointer);
302  sinfo->BuildOld();
303  }
304  }
305 
306  // Deserialize the object.
307  ApplySequence(*(sinfo->GetReadTextActions()), (char *)pointer);
308  if (sinfo->IsRecovered())
309  count = 0;
310 
311  // Check that the buffer position corresponds to the byte count.
312  CheckByteCount(start, count, cl);
313  return 0;
314 }
315 
316 ////////////////////////////////////////////////////////////////////////////////
317 /// Deserialize information from a buffer into an object.
318 ///
319 /// Note: This function is called by the xxx::Streamer()
320 /// functions in rootcint-generated dictionaries.
321 ///
322 
323 Int_t TBufferText::ReadClassBuffer(const TClass *cl, void *pointer, const TClass *onFileClass)
324 {
325  // Read the class version from the buffer.
326  UInt_t R__s = 0; // Start of object.
327  UInt_t R__c = 0; // Count of bytes.
328  Version_t version;
329 
330  if (onFileClass)
331  version = ReadVersion(&R__s, &R__c, onFileClass);
332  else
333  version = ReadVersion(&R__s, &R__c, cl);
334 
335  Bool_t v2file = kFALSE;
336  TFile *file = (TFile *)GetParent();
337  if (file && file->GetVersion() < 30000) {
338  version = -1; // This is old file
339  v2file = kTRUE;
340  }
341 
342  //---------------------------------------------------------------------------
343  // The ondisk class has been specified so get foreign streamer info
344  /////////////////////////////////////////////////////////////////////////////
345 
346  TStreamerInfo *sinfo = nullptr;
347  if (onFileClass) {
348  sinfo = (TStreamerInfo *)cl->GetConversionStreamerInfo(onFileClass, version);
349  if (!sinfo) {
350  Error("ReadClassBuffer",
351  "Could not find the right streamer info to convert %s version %d into a %s, object skipped at offset %d",
352  onFileClass->GetName(), version, cl->GetName(), Length());
353  CheckByteCount(R__s, R__c, onFileClass);
354  return 0;
355  }
356  }
357  //---------------------------------------------------------------------------
358  // Get local streamer info
359  /////////////////////////////////////////////////////////////////////////////
360  /// The StreamerInfo should exist at this point.
361 
362  else {
363  TStreamerInfo *guess = (TStreamerInfo *)cl->GetLastReadInfo();
364  if (guess && guess->GetClassVersion() == version) {
365  sinfo = guess;
366  } else {
367  // The last one is not the one we are looking for.
368  {
369  R__LOCKGUARD(gInterpreterMutex);
370 
371  const TObjArray *infos = cl->GetStreamerInfos();
372  Int_t infocapacity = infos->Capacity();
373  if (infocapacity) {
374  if (version < -1 || version >= infocapacity) {
375  Error("ReadClassBuffer",
376  "class: %s, attempting to access a wrong version: %d, object skipped at offset %d",
377  cl->GetName(), version, Length());
378  CheckByteCount(R__s, R__c, cl);
379  return 0;
380  }
381  sinfo = (TStreamerInfo *)infos->UncheckedAt(version);
382  if (sinfo) {
383  if (!sinfo->IsCompiled()) {
384  // Streamer info has not been compiled, but exists.
385  // Therefore it was read in from a file and we have to do schema evolution?
386  R__LOCKGUARD(gInterpreterMutex);
387  const_cast<TClass *>(cl)->BuildRealData(pointer);
388  sinfo->BuildOld();
389  }
390  // If the compilation succeeded, remember this StreamerInfo.
391  // const_cast okay because of the lock on gInterpreterMutex.
392  if (sinfo->IsCompiled())
393  const_cast<TClass *>(cl)->SetLastReadInfo(sinfo);
394  }
395  }
396  }
397 
398  if (!sinfo) {
399  // Unless the data is coming via a socket connection from with schema evolution
400  // (tracking) was not enabled. So let's create the StreamerInfo if it is the
401  // one for the current version, otherwise let's complain ...
402  // We could also get here when reading a file prior to the introduction of StreamerInfo.
403  // We could also get here if there old class version was '1' and the new class version is higher than 1
404  // AND the checksum is the same.
405  if (v2file || version == cl->GetClassVersion() || version == 1) {
406  R__LOCKGUARD(gInterpreterMutex);
407 
408  // We need to check if another thread did not get here first
409  // and did the StreamerInfo creation already.
410  auto infos = cl->GetStreamerInfos();
411  auto ninfos = infos->GetSize();
412  if (!(version < -1 || version >= ninfos)) {
413  sinfo = (TStreamerInfo *)infos->At(version);
414  }
415  if (!sinfo) {
416  const_cast<TClass *>(cl)->BuildRealData(pointer);
417  sinfo = new TStreamerInfo(const_cast<TClass *>(cl));
418  sinfo->SetClassVersion(version);
419  const_cast<TClass *>(cl)->RegisterStreamerInfo(sinfo);
420  if (gDebug > 0)
421  Info("ReadClassBuffer", "Creating StreamerInfo for class: %s, version: %d", cl->GetName(),
422  version);
423  if (v2file) {
424  sinfo->Build(); // Get the elements.
425  sinfo->Clear("build"); // Undo compilation.
426  sinfo->BuildEmulated(file); // Fix the types and redo compilation.
427  } else {
428  sinfo->Build();
429  }
430  }
431  } else if (version == 0) {
432  // When the object was written the class was version zero, so
433  // there is no StreamerInfo to be found.
434  // Check that the buffer position corresponds to the byte count.
435  CheckByteCount(R__s, R__c, cl);
436  return 0;
437  } else {
438  Error("ReadClassBuffer",
439  "Could not find the StreamerInfo for version %d of the class %s, object skipped at offset %d",
440  version, cl->GetName(), Length());
441  CheckByteCount(R__s, R__c, cl);
442  return 0;
443  }
444  }
445  }
446  }
447 
448  // deserialize the object
449  ApplySequence(*(sinfo->GetReadTextActions()), (char *)pointer);
450  if (sinfo->TStreamerInfo::IsRecovered())
451  R__c = 0; // 'TStreamerInfo::' avoids going via a virtual function.
452 
453  // Check that the buffer position corresponds to the byte count.
454  CheckByteCount(R__s, R__c, cl);
455 
456  if (gDebug > 2)
457  Info("ReadClassBuffer", "for class: %s has read %d bytes", cl->GetName(), R__c);
458 
459  return 0;
460 }
461 
462 ////////////////////////////////////////////////////////////////////////////////
463 /// stream object to/from buffer
464 
465 void TBufferText::StreamObject(void *obj, const std::type_info &typeinfo, const TClass * /* onFileClass */)
466 {
467  StreamObject(obj, TClass::GetClass(typeinfo));
468 }
469 
470 ////////////////////////////////////////////////////////////////////////////////
471 /// stream object to/from buffer
472 
473 void TBufferText::StreamObject(void *obj, const char *className, const TClass * /* onFileClass */)
474 {
475  StreamObject(obj, TClass::GetClass(className));
476 }
477 
478 void TBufferText::StreamObject(TObject *obj)
479 {
480  // stream object to/from buffer
481 
482  StreamObject(obj, obj ? obj->IsA() : TObject::Class());
483 }
484 
485 ////////////////////////////////////////////////////////////////////////////////
486 /// read a Float16_t from the buffer
487 
488 void TBufferText::ReadFloat16(Float_t *f, TStreamerElement * /*ele*/)
489 {
490  ReadFloat(*f);
491 }
492 
493 ////////////////////////////////////////////////////////////////////////////////
494 /// read a Double32_t from the buffer
495 
496 void TBufferText::ReadDouble32(Double_t *d, TStreamerElement * /*ele*/)
497 {
498  ReadDouble(*d);
499 }
500 
501 ////////////////////////////////////////////////////////////////////////////////
502 /// Read a Double32_t from the buffer when the factor and minimun value have
503 /// been specified
504 /// see comments about Double32_t encoding at TBufferFile::WriteDouble32().
505 /// Currently TBufferText does not optimize space in this case.
506 
507 void TBufferText::ReadWithFactor(Float_t *f, Double_t /* factor */, Double_t /* minvalue */)
508 {
509  ReadFloat(*f);
510 }
511 
512 ////////////////////////////////////////////////////////////////////////////////
513 /// Read a Float16_t from the buffer when the number of bits is specified
514 /// (explicitly or not)
515 /// see comments about Float16_t encoding at TBufferFile::WriteFloat16().
516 /// Currently TBufferText does not optimize space in this case.
517 
518 void TBufferText::ReadWithNbits(Float_t *f, Int_t /* nbits */)
519 {
520  ReadFloat(*f);
521 }
522 
523 ////////////////////////////////////////////////////////////////////////////////
524 /// Read a Double32_t from the buffer when the factor and minimun value have
525 /// been specified
526 /// see comments about Double32_t encoding at TBufferFile::WriteDouble32().
527 /// Currently TBufferText does not optimize space in this case.
528 
529 void TBufferText::ReadWithFactor(Double_t *d, Double_t /* factor */, Double_t /* minvalue */)
530 {
531  ReadDouble(*d);
532 }
533 
534 ////////////////////////////////////////////////////////////////////////////////
535 /// Read a Double32_t from the buffer when the number of bits is specified
536 /// (explicitly or not)
537 /// see comments about Double32_t encoding at TBufferFile::WriteDouble32().
538 /// Currently TBufferText does not optimize space in this case.
539 
540 void TBufferText::ReadWithNbits(Double_t *d, Int_t /* nbits */)
541 {
542  ReadDouble(*d);
543 }
544 
545 ////////////////////////////////////////////////////////////////////////////////
546 /// write a Float16_t to the buffer
547 
548 void TBufferText::WriteFloat16(Float_t *f, TStreamerElement * /*ele*/)
549 {
550  WriteFloat(*f);
551 }
552 
553 ////////////////////////////////////////////////////////////////////////////////
554 /// write a Double32_t to the buffer
555 
556 void TBufferText::WriteDouble32(Double_t *d, TStreamerElement * /*ele*/)
557 {
558  WriteDouble(*d);
559 }
560 
561 ////////////////////////////////////////////////////////////////////////////////
562 /// Read array of Float16_t from buffer
563 
564 Int_t TBufferText::ReadArrayFloat16(Float_t *&f, TStreamerElement * /*ele*/)
565 {
566  return ReadArray(f);
567 }
568 
569 ////////////////////////////////////////////////////////////////////////////////
570 /// Read array of Double32_t from buffer
571 
572 Int_t TBufferText::ReadArrayDouble32(Double_t *&d, TStreamerElement * /*ele*/)
573 {
574  return ReadArray(d);
575 }
576 
577 ////////////////////////////////////////////////////////////////////////////////
578 /// Read array of Float16_t from buffer
579 
580 Int_t TBufferText::ReadStaticArrayFloat16(Float_t *f, TStreamerElement * /*ele*/)
581 {
582  return ReadStaticArray(f);
583 }
584 
585 ////////////////////////////////////////////////////////////////////////////////
586 /// Read array of Double32_t from buffer
587 
588 Int_t TBufferText::ReadStaticArrayDouble32(Double_t *d, TStreamerElement * /*ele*/)
589 {
590  return ReadStaticArray(d);
591 }
592 
593 ////////////////////////////////////////////////////////////////////////////////
594 /// read array of Float16_t from buffer
595 
596 void TBufferText::ReadFastArrayFloat16(Float_t *f, Int_t n, TStreamerElement * /*ele*/)
597 {
598  ReadFastArray(f, n);
599 }
600 
601 ////////////////////////////////////////////////////////////////////////////////
602 /// read array of Float16_t from buffer
603 
604 void TBufferText::ReadFastArrayWithFactor(Float_t *f, Int_t n, Double_t /* factor */, Double_t /* minvalue */)
605 {
606  ReadFastArray(f, n);
607 }
608 
609 ////////////////////////////////////////////////////////////////////////////////
610 /// read array of Float16_t from buffer
611 
612 void TBufferText::ReadFastArrayWithNbits(Float_t *f, Int_t n, Int_t /*nbits*/)
613 {
614  ReadFastArray(f, n);
615 }
616 
617 ////////////////////////////////////////////////////////////////////////////////
618 /// read array of Double32_t from buffer
619 
620 void TBufferText::ReadFastArrayDouble32(Double_t *d, Int_t n, TStreamerElement * /*ele*/)
621 {
622  ReadFastArray(d, n);
623 }
624 
625 ////////////////////////////////////////////////////////////////////////////////
626 /// read array of Double32_t from buffer
627 
628 void TBufferText::ReadFastArrayWithFactor(Double_t *d, Int_t n, Double_t /* factor */, Double_t /* minvalue */)
629 {
630  ReadFastArray(d, n);
631 }
632 
633 ////////////////////////////////////////////////////////////////////////////////
634 /// read array of Double32_t from buffer
635 
636 void TBufferText::ReadFastArrayWithNbits(Double_t *d, Int_t n, Int_t /*nbits*/)
637 {
638  ReadFastArray(d, n);
639 }
640 ////////////////////////////////////////////////////////////////////////////////
641 /// Write array of Float16_t to buffer
642 
643 void TBufferText::WriteArrayFloat16(const Float_t *f, Int_t n, TStreamerElement * /*ele*/)
644 {
645  WriteArray(f, n);
646 }
647 
648 ////////////////////////////////////////////////////////////////////////////////
649 /// Write array of Double32_t to buffer
650 
651 void TBufferText::WriteArrayDouble32(const Double_t *d, Int_t n, TStreamerElement * /*ele*/)
652 {
653  WriteArray(d, n);
654 }
655 
656 ////////////////////////////////////////////////////////////////////////////////
657 /// Write array of Float16_t to buffer
658 
659 void TBufferText::WriteFastArrayFloat16(const Float_t *f, Int_t n, TStreamerElement * /*ele*/)
660 {
661  WriteFastArray(f, n);
662 }
663 
664 ////////////////////////////////////////////////////////////////////////////////
665 /// Write array of Double32_t to buffer
666 
667 void TBufferText::WriteFastArrayDouble32(const Double_t *d, Int_t n, TStreamerElement * /*ele*/)
668 {
669  WriteFastArray(d, n);
670 }
671 
672 ////////////////////////////////////////////////////////////////////////////////
673 /// Skip class version from I/O buffer.
674 
675 void TBufferText::SkipVersion(const TClass *cl)
676 {
677  ReadVersion(nullptr, nullptr, cl);
678 }
679 
680 ////////////////////////////////////////////////////////////////////////////////
681 /// Write data of base class.
682 
683 void TBufferText::WriteBaseClass(void *start, TStreamerBase *elem)
684 {
685  elem->WriteBuffer(*this, (char *)start);
686 }
687 
688 ////////////////////////////////////////////////////////////////////////////////
689 /// Read data of base class.
690 
691 void TBufferText::ReadBaseClass(void *start, TStreamerBase *elem)
692 {
693  elem->ReadBuffer(*this, (char *)start);
694 }
695 
696 ////////////////////////////////////////////////////////////////////////////////
697 /// method compress float string, excluding exp and/or move float point
698 /// - 1.000000e-01 -> 0.1
699 /// - 3.750000e+00 -> 3.75
700 /// - 3.750000e-03 -> 0.00375
701 /// - 3.750000e-04 -> 3.75e-4
702 /// - 1.100000e-10 -> 1.1e-10
703 
704 void TBufferText::CompactFloatString(char *sbuf, unsigned len)
705 {
706  char *pnt = nullptr, *exp = nullptr, *lastdecimal = nullptr, *s = sbuf;
707  bool negative_exp = false;
708  int power = 0;
709  while (*s && --len) {
710  switch (*s) {
711  case '.': pnt = s; break;
712  case 'E':
713  case 'e': exp = s; break;
714  case '-':
715  if (exp)
716  negative_exp = true;
717  break;
718  case '+': break;
719  default: // should be digits from '0' to '9'
720  if ((*s < '0') || (*s > '9'))
721  return;
722  if (exp)
723  power = power * 10 + (*s - '0');
724  else if (pnt && *s != '0')
725  lastdecimal = s;
726  break;
727  }
728  ++s;
729  }
730  if (*s)
731  return; // if end-of-string was not found
732 
733  if (!exp) {
734  // value without exponent like 123.4569000
735  if (pnt) {
736  if (lastdecimal)
737  *(lastdecimal + 1) = 0;
738  else
739  *pnt = 0;
740  }
741  } else if (power == 0) {
742  if (lastdecimal)
743  *(lastdecimal + 1) = 0;
744  else if (pnt)
745  *pnt = 0;
746  } else if (!negative_exp && pnt && exp && (exp - pnt > power)) {
747  // this is case of value 1.23000e+02
748  // we can move point and exclude exponent easily
749  for (int cnt = 0; cnt < power; ++cnt) {
750  char tmp = *pnt;
751  *pnt = *(pnt + 1);
752  *(++pnt) = tmp;
753  }
754  if (lastdecimal && (pnt < lastdecimal))
755  *(lastdecimal + 1) = 0;
756  else
757  *pnt = 0;
758  } else if (negative_exp && pnt && exp && (power < (s - exp))) {
759  // this is small negative exponent like 1.2300e-02
760  if (!lastdecimal)
761  lastdecimal = pnt;
762  *(lastdecimal + 1) = 0;
763  // copy most significant digit on the point place
764  *pnt = *(pnt - 1);
765 
766  for (char *pos = lastdecimal + 1; pos >= pnt; --pos)
767  *(pos + power) = *pos;
768  *(pnt - 1) = '0';
769  *pnt = '.';
770  for (int cnt = 1; cnt < power; ++cnt)
771  *(pnt + cnt) = '0';
772  } else if (pnt && exp) {
773  // keep exponent, but non-significant zeros
774  if (lastdecimal)
775  pnt = lastdecimal + 1;
776  // copy exponent sign
777  *pnt++ = *exp++;
778  if (*exp == '+')
779  ++exp;
780  else if (*exp == '-')
781  *pnt++ = *exp++;
782  // exclude zeros in the begin of exponent
783  while (*exp == '0')
784  ++exp;
785  while (*exp)
786  *pnt++ = *exp++;
787  *pnt = 0;
788  }
789 }
790 
791 ////////////////////////////////////////////////////////////////////////////////
792 /// set printf format for float/double members, default "%e"
793 /// to change format only for doubles, use SetDoubleFormat
794 
795 void TBufferText::SetFloatFormat(const char *fmt)
796 {
797  if (!fmt)
798  fmt = "%e";
799  fgFloatFmt = fmt;
800  fgDoubleFmt = fmt;
801 }
802 
803 ////////////////////////////////////////////////////////////////////////////////
804 /// return current printf format for float members, default "%e"
805 
806 const char *TBufferText::GetFloatFormat()
807 {
808  return fgFloatFmt;
809 }
810 
811 ////////////////////////////////////////////////////////////////////////////////
812 /// set printf format for double members, default "%.14e"
813 /// use it after SetFloatFormat, which also overwrites format for doubles
814 
815 void TBufferText::SetDoubleFormat(const char *fmt)
816 {
817  if (!fmt)
818  fmt = "%.14e";
819  fgDoubleFmt = fmt;
820 }
821 
822 ////////////////////////////////////////////////////////////////////////////////
823 /// return current printf format for double members, default "%.14e"
824 
825 const char *TBufferText::GetDoubleFormat()
826 {
827  return fgDoubleFmt;
828 }
829 
830 ////////////////////////////////////////////////////////////////////////////////
831 /// convert float to string with configured format
832 
833 const char *TBufferText::ConvertFloat(Float_t value, char *buf, unsigned len, Bool_t not_optimize)
834 {
835  if (not_optimize) {
836  snprintf(buf, len, fgFloatFmt, value);
837  } else if ((value == std::nearbyint(value)) && (std::abs(value) < 1e15)) {
838  snprintf(buf, len, "%1.0f", value);
839  } else {
840  snprintf(buf, len, fgFloatFmt, value);
841  CompactFloatString(buf, len);
842  }
843  return buf;
844 }
845 
846 ////////////////////////////////////////////////////////////////////////////////
847 /// convert float to string with configured format
848 
849 const char *TBufferText::ConvertDouble(Double_t value, char *buf, unsigned len, Bool_t not_optimize)
850 {
851  if (not_optimize) {
852  snprintf(buf, len, fgFloatFmt, value);
853  } else if ((value == std::nearbyint(value)) && (std::abs(value) < 1e25)) {
854  snprintf(buf, len, "%1.0f", value);
855  } else {
856  snprintf(buf, len, fgDoubleFmt, value);
857  CompactFloatString(buf, len);
858  }
859  return buf;
860 }