Logo ROOT   6.30.04
Reference Guide
 All Namespaces Files Pages
TBufferJSON.cxx
Go to the documentation of this file.
1 //
2 // Author: Sergey Linev 4.03.2014
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2019, 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 TBufferJSON
14 \ingroup IO
15 
16 Class for serializing object to and from JavaScript Object Notation (JSON) format.
17 It creates such object representation, which can be directly
18 used in JavaScript ROOT (JSROOT) for drawing.
19 
20 TBufferJSON implements TBuffer interface, therefore most of
21 ROOT and user classes can be converted into JSON.
22 There are certain limitations for classes with custom streamers,
23 which should be equipped specially for this purposes (see TCanvas::Streamer()
24 as example).
25 
26 To perform conversion into JSON, one should use TBufferJSON::ToJSON method:
27 ~~~{.cpp}
28  TH1 *h1 = new TH1I("h1", "title", 100, 0, 10);
29  h1->FillRandom("gaus",10000);
30  TString json = TBufferJSON::ToJSON(h1);
31 ~~~
32 
33 To reconstruct object from the JSON string, one should do:
34 ~~~{.cpp}
35  TH1 *hnew = nullptr;
36  TBufferJSON::FromJSON(hnew, json);
37  if (hnew) hnew->Draw("hist");
38 ~~~
39 JSON does not include stored class version, therefore schema evolution
40 (reading of older class versions) is not supported. JSON should not be used as
41 persistent storage for object data - only for live applications.
42 
43 All STL containers by default converted into JSON Array. Vector of integers:
44 ~~~{.cpp}
45  std::vector<int> vect = {1,4,7};
46  auto json = TBufferJSON::ToJSON(&vect);
47 ~~~
48 Will produce JSON code "[1, 4, 7]".
49 
50 There are special handling for map classes like `map` and `multimap`.
51 They will create Array of pair objects with "first" and "second" as data members. Code:
52 ~~~{.cpp}
53  std::map<int,string> m;
54  m[1] = "number 1";
55  m[2] = "number 2";
56  auto json = TBufferJSON::ToJSON(&m);
57 ~~~
58 Will generate json string:
59 ~~~{.json}
60 [
61  {"$pair" : "pair<int,string>", "first" : 1, "second" : "number 1"},
62  {"$pair" : "pair<int,string>", "first" : 2, "second" : "number 2"}
63 ]
64 ~~~
65 In special cases map container can be converted into JSON object. For that key parameter
66 must be `std::string` and compact parameter should be 5.
67 Like in example:
68 ~~~{.cpp}
69 std::map<std::string,int> data;
70 data["name1"] = 11;
71 data["name2"] = 22;
72 
73 auto json = TBufferJSON::ToJSON(&data, TBufferJSON::kMapAsObject);
74 ~~~
75 Will produce JSON output:
76 ~~~
77 {
78  "_typename": "map<string,int>",
79  "name1": 11,
80  "name2": 22
81 }
82 ~~~
83 Another possibility to enforce such conversion - add "JSON_object" into comment line of correspondent
84 data member like:
85 ~~~{.cpp}
86 class Container {
87  std::map<std::string,int> data; ///< JSON_object
88 };
89 ~~~
90 
91 */
92 
93 #include "TBufferJSON.h"
94 
95 #include <typeinfo>
96 #include <string>
97 #include <string.h>
98 #include <locale.h>
99 #include <cmath>
100 #include <memory>
101 #include <cstdlib>
102 
103 #include <ROOT/RMakeUnique.hxx>
104 
105 #include "Compression.h"
106 
107 #include "TArrayI.h"
108 #include "TObjArray.h"
109 #include "TError.h"
110 #include "TBase64.h"
111 #include "TROOT.h"
112 #include "TClass.h"
113 #include "TClassTable.h"
114 #include "TClassEdit.h"
115 #include "TDataType.h"
116 #include "TRealData.h"
117 #include "TDataMember.h"
118 #include "TMap.h"
119 #include "TStreamerInfo.h"
120 #include "TStreamerElement.h"
121 #include "TFile.h"
122 #include "TMemberStreamer.h"
123 #include "TStreamer.h"
124 #include "Riostream.h"
125 #include "RZip.h"
126 #include "TClonesArray.h"
127 #include "TVirtualMutex.h"
128 #include "TInterpreter.h"
130 
131 #include "json.hpp"
132 
133 ClassImp(TBufferJSON);
134 
135 enum { json_TArray = 100, json_TCollection = -130, json_TString = 110, json_stdstring = 120 };
136 
137 ///////////////////////////////////////////////////////////////
138 // TArrayIndexProducer is used to correctly create
139 /// JSON array separators for multi-dimensional JSON arrays
140 /// It fully reproduces array dimensions as in original ROOT classes
141 /// Contrary to binary I/O, which always writes flat arrays
142 
143 class TArrayIndexProducer {
144 protected:
145  Int_t fTotalLen{0};
146  Int_t fCnt{-1};
147  const char *fSepar{nullptr};
148  TArrayI fIndicies;
149  TArrayI fMaxIndex;
150  TString fRes;
151  Bool_t fIsArray{kFALSE};
152 
153 public:
154  TArrayIndexProducer(TStreamerElement *elem, Int_t arraylen, const char *separ) : fSepar(separ)
155  {
156  Bool_t usearrayindx = elem && (elem->GetArrayDim() > 0);
157  Bool_t isloop = elem && ((elem->GetType() == TStreamerInfo::kStreamLoop) ||
158  (elem->GetType() == TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop));
159  Bool_t usearraylen = (arraylen > (isloop ? 0 : 1));
160 
161  if (usearrayindx && (arraylen > 0)) {
162  if (isloop) {
163  usearrayindx = kFALSE;
164  usearraylen = kTRUE;
165  } else if (arraylen != elem->GetArrayLength()) {
166  ::Error("TArrayIndexProducer", "Problem with JSON coding of element %s type %d", elem->GetName(),
167  elem->GetType());
168  }
169  }
170 
171  if (usearrayindx) {
172  fTotalLen = elem->GetArrayLength();
173  fMaxIndex.Set(elem->GetArrayDim());
174  for (int dim = 0; dim < elem->GetArrayDim(); dim++)
175  fMaxIndex[dim] = elem->GetMaxIndex(dim);
176  fIsArray = fTotalLen > 1;
177  } else if (usearraylen) {
178  fTotalLen = arraylen;
179  fMaxIndex.Set(1);
180  fMaxIndex[0] = arraylen;
181  fIsArray = kTRUE;
182  }
183 
184  if (fMaxIndex.GetSize() > 0) {
185  fIndicies.Set(fMaxIndex.GetSize());
186  fIndicies.Reset(0);
187  }
188  }
189 
190  TArrayIndexProducer(TDataMember *member, Int_t extradim, const char *separ) : fSepar(separ)
191  {
192  Int_t ndim = member->GetArrayDim();
193  if (extradim > 0)
194  ndim++;
195 
196  if (ndim > 0) {
197  fIndicies.Set(ndim);
198  fIndicies.Reset(0);
199  fMaxIndex.Set(ndim);
200  fTotalLen = 1;
201  for (int dim = 0; dim < member->GetArrayDim(); dim++) {
202  fMaxIndex[dim] = member->GetMaxIndex(dim);
203  fTotalLen *= member->GetMaxIndex(dim);
204  }
205 
206  if (extradim > 0) {
207  fMaxIndex[ndim - 1] = extradim;
208  fTotalLen *= extradim;
209  }
210  }
211  fIsArray = fTotalLen > 1;
212  }
213 
214  /// returns number of array dimensions
215  Int_t NumDimensions() const { return fIndicies.GetSize(); }
216 
217  /// return array with current index
218  TArrayI &GetIndices() { return fIndicies; };
219 
220  /// returns total number of elements in array
221  Int_t TotalLength() const { return fTotalLen; }
222 
223  Int_t ReduceDimension()
224  {
225  // reduce one dimension of the array
226  // return size of reduced dimension
227  if (fMaxIndex.GetSize() == 0)
228  return 0;
229  Int_t ndim = fMaxIndex.GetSize() - 1;
230  Int_t len = fMaxIndex[ndim];
231  fMaxIndex.Set(ndim);
232  fIndicies.Set(ndim);
233  fTotalLen = fTotalLen / len;
234  fIsArray = fTotalLen > 1;
235  return len;
236  }
237 
238  Bool_t IsArray() const { return fIsArray; }
239 
240  Bool_t IsDone() const
241  {
242  // return true when iteration over all arrays indexes are done
243  return !IsArray() || (fCnt >= fTotalLen);
244  }
245 
246  const char *GetBegin()
247  {
248  ++fCnt;
249  // return starting separator
250  fRes.Clear();
251  for (Int_t n = 0; n < fIndicies.GetSize(); ++n)
252  fRes.Append("[");
253  return fRes.Data();
254  }
255 
256  const char *GetEnd()
257  {
258  // return ending separator
259  fRes.Clear();
260  for (Int_t n = 0; n < fIndicies.GetSize(); ++n)
261  fRes.Append("]");
262  return fRes.Data();
263  }
264 
265  /// increment indexes and returns intermediate or last separator
266  const char *NextSeparator()
267  {
268  if (++fCnt >= fTotalLen)
269  return GetEnd();
270 
271  Int_t cnt = fIndicies.GetSize() - 1;
272  fIndicies[cnt]++;
273 
274  fRes.Clear();
275 
276  while ((cnt >= 0) && (cnt < fIndicies.GetSize())) {
277  if (fIndicies[cnt] >= fMaxIndex[cnt]) {
278  fRes.Append("]");
279  fIndicies[cnt--] = 0;
280  if (cnt >= 0)
281  fIndicies[cnt]++;
282  continue;
283  }
284  fRes.Append(fIndicies[cnt] == 0 ? "[" : fSepar);
285  cnt++;
286  }
287  return fRes.Data();
288  }
289 
290  nlohmann::json *ExtractNode(nlohmann::json *topnode, bool next = true)
291  {
292  if (!IsArray())
293  return topnode;
294  nlohmann::json *subnode = &((*((nlohmann::json *)topnode))[fIndicies[0]]);
295  for (int k = 1; k < fIndicies.GetSize(); ++k)
296  subnode = &((*subnode)[fIndicies[k]]);
297  if (next)
298  NextSeparator();
299  return subnode;
300  }
301 };
302 
303 // TJSONStackObj is used to keep stack of object hierarchy,
304 // stored in TBuffer. For instance, data for parent class(es)
305 // stored in subnodes, but initial object node will be kept.
306 
307 class TJSONStackObj : public TObject {
308  struct StlRead {
309  Int_t fIndx{0}; //! index of object in STL container
310  Int_t fMap{0}; //! special iterator over STL map::key members
311  Bool_t fFirst{kTRUE}; //! is first or second element is used in the pair
312  nlohmann::json::iterator fIter; //! iterator for std::map stored as JSON object
313  const char *fTypeTag{nullptr}; //! type tag used for std::map stored as JSON object
314  nlohmann::json fValue; //! temporary value reading std::map as JSON
315  nlohmann::json *GetStlNode(nlohmann::json *prnt)
316  {
317  if (fMap <= 0)
318  return &(prnt->at(fIndx++));
319 
320  if (fMap == 1) {
321  nlohmann::json *json = &(prnt->at(fIndx));
322  if (!fFirst) fIndx++;
323  json = &(json->at(fFirst ? "first" : "second"));
324  fFirst = !fFirst;
325  return json;
326  }
327 
328  if (fIndx == 0) {
329  // skip _typename if appears
330  if (fTypeTag && (fIter.key().compare(fTypeTag) == 0))
331  ++fIter;
332  fValue = fIter.key();
333  fIndx++;
334  } else {
335  fValue = fIter.value();
336  ++fIter;
337  fIndx = 0;
338  }
339  return &fValue;
340  }
341  };
342 
343 public:
344  TStreamerInfo *fInfo{nullptr}; //!
345  TStreamerElement *fElem{nullptr}; //! element in streamer info
346  Bool_t fIsStreamerInfo{kFALSE}; //!
347  Bool_t fIsElemOwner{kFALSE}; //!
348  Bool_t fIsPostProcessed{kFALSE}; //! indicate that value is written
349  Bool_t fIsObjStarted{kFALSE}; //! indicate that object writing started, should be closed in postprocess
350  Bool_t fAccObjects{kFALSE}; //! if true, accumulate whole objects in values
351  Bool_t fBase64{kFALSE}; //! enable base64 coding when writing array
352  std::vector<std::string> fValues; //! raw values
353  int fMemberCnt{1}; //! count number of object members, normally _typename is first member
354  int *fMemberPtr{nullptr}; //! pointer on members counter, can be inherit from parent stack objects
355  Int_t fLevel{0}; //! indent level
356  std::unique_ptr<TArrayIndexProducer> fIndx; //! producer of ndim indexes
357  nlohmann::json *fNode{nullptr}; //! JSON node, used for reading
358  std::unique_ptr<StlRead> fStlRead; //! custom structure for stl container reading
359  Version_t fClVersion{0}; //! keep actual class version, workaround for ReadVersion in custom streamer
360 
361  TJSONStackObj() = default;
362 
363  ~TJSONStackObj()
364  {
365  if (fIsElemOwner)
366  delete fElem;
367  }
368 
369  Bool_t IsStreamerInfo() const { return fIsStreamerInfo; }
370 
371  Bool_t IsStreamerElement() const { return !fIsStreamerInfo && fElem; }
372 
373  void PushValue(TString &v)
374  {
375  fValues.emplace_back(v.Data());
376  v.Clear();
377  }
378 
379  void PushIntValue(Int_t v) { fValues.emplace_back(std::to_string(v)); }
380 
381  ////////////////////////////////////////////////////////////////////////
382  /// returns separator for data members
383  const char *NextMemberSeparator()
384  {
385  return (!fMemberPtr || ((*fMemberPtr)++ > 0)) ? "," : "";
386  }
387 
388  Bool_t IsJsonString() { return fNode && fNode->is_string(); }
389 
390  ////////////////////////////////////////////////////////////////////////
391  /// checks if specified JSON node is array (compressed or not compressed)
392  /// returns length of array (or -1 if failure)
393  Int_t IsJsonArray(nlohmann::json *json = nullptr, const char *map_convert_type = nullptr)
394  {
395  if (!json)
396  json = fNode;
397 
398  if (map_convert_type) {
399  if (!json->is_object()) return -1;
400  int sz = 0;
401  // count size of object, excluding _typename tag
402  for (auto it = json->begin(); it != json->end(); ++it) {
403  if ((strlen(map_convert_type)==0) || (it.key().compare(map_convert_type) != 0)) sz++;
404  }
405  return sz;
406  }
407 
408  // normal uncompressed array
409  if (json->is_array())
410  return json->size();
411 
412  // compressed array, full array length in "len" attribute, only ReadFastArray
413  if (json->is_object() && (json->count("$arr") == 1))
414  return json->at("len").get<int>();
415 
416  return -1;
417  }
418 
419  Int_t PopIntValue()
420  {
421  auto res = std::stoi(fValues.back());
422  fValues.pop_back();
423  return res;
424  }
425 
426  std::unique_ptr<TArrayIndexProducer> MakeReadIndexes()
427  {
428  if (!fElem || (fElem->GetType() <= TStreamerInfo::kOffsetL) ||
429  (fElem->GetType() >= TStreamerInfo::kOffsetL + 20) || (fElem->GetArrayDim() < 2))
430  return nullptr;
431 
432  auto indx = std::make_unique<TArrayIndexProducer>(fElem, -1, "");
433 
434  // no need for single dimension - it can be handled directly
435  if (!indx->IsArray() || (indx->NumDimensions() < 2))
436  return nullptr;
437 
438  return indx;
439  }
440 
441  Bool_t IsStl() const { return fStlRead.get() != nullptr; }
442 
443  Bool_t AssignStl(TClass *cl, Int_t map_convert, const char *typename_tag)
444  {
445  fStlRead = std::make_unique<StlRead>();
446  fStlRead->fMap = map_convert;
447  if (map_convert == 2) {
448  if (!fNode->is_object()) {
449  ::Error("TJSONStackObj::AssignStl", "when reading %s expecting JSON object", cl->GetName());
450  return kFALSE;
451  }
452  fStlRead->fIter = fNode->begin();
453  fStlRead->fTypeTag = typename_tag && (strlen(typename_tag) > 0) ? typename_tag : nullptr;
454  } else {
455  if (!fNode->is_array() && !(fNode->is_object() && (fNode->count("$arr") == 1))) {
456  ::Error("TJSONStackObj::AssignStl", "when reading %s expecting JSON array", cl->GetName());
457  return kFALSE;
458  }
459  }
460  return kTRUE;
461  }
462 
463  nlohmann::json *GetStlNode()
464  {
465  return fStlRead ? fStlRead->GetStlNode(fNode) : fNode;
466  }
467 
468  void ClearStl()
469  {
470  fStlRead.reset(nullptr);
471  }
472 };
473 
474 ////////////////////////////////////////////////////////////////////////////////
475 /// Creates buffer object to serialize data into json.
476 
477 TBufferJSON::TBufferJSON(TBuffer::EMode mode)
478  : TBufferText(mode), fOutBuffer(), fOutput(nullptr), fValue(), fStack(), fSemicolon(" : "), fArraySepar(", "),
479  fNumericLocale(), fTypeNameTag("_typename")
480 {
481  fOutBuffer.Capacity(10000);
482  fValue.Capacity(1000);
483  fOutput = &fOutBuffer;
484 
485  // checks if setlocale(LC_NUMERIC) returns others than "C"
486  // in this case locale will be changed and restored at the end of object conversion
487 
488  char *loc = setlocale(LC_NUMERIC, nullptr);
489  if (loc && (strcmp(loc, "C") != 0)) {
490  fNumericLocale = loc;
491  setlocale(LC_NUMERIC, "C");
492  }
493 }
494 
495 ////////////////////////////////////////////////////////////////////////////////
496 /// destroy buffer
497 
498 TBufferJSON::~TBufferJSON()
499 {
500  while (fStack.size() > 0)
501  PopStack();
502 
503  if (fNumericLocale.Length() > 0)
504  setlocale(LC_NUMERIC, fNumericLocale.Data());
505 }
506 
507 ////////////////////////////////////////////////////////////////////////////////
508 /// Converts object, inherited from TObject class, to JSON string
509 /// Lower digit of compact parameter define formatting rules
510 /// - 0 - no any compression, human-readable form
511 /// - 1 - exclude spaces in the begin
512 /// - 2 - remove newlines
513 /// - 3 - exclude spaces as much as possible
514 ///
515 /// Second digit of compact parameter defines algorithm for arrays compression
516 /// - 0 - no compression, standard JSON array
517 /// - 1 - exclude leading and trailing zeros
518 /// - 2 - check values repetition and empty gaps
519 ///
520 /// Maximal compression achieved when compact parameter equal to 23
521 /// When member_name specified, converts only this data member
522 
523 TString TBufferJSON::ConvertToJSON(const TObject *obj, Int_t compact, const char *member_name)
524 {
525  TClass *clActual = nullptr;
526  void *ptr = (void *)obj;
527 
528  if (obj) {
529  clActual = TObject::Class()->GetActualClass(obj);
530  if (!clActual)
531  clActual = TObject::Class();
532  else if (clActual != TObject::Class())
533  ptr = (void *)((Long_t)obj - clActual->GetBaseClassOffset(TObject::Class()));
534  }
535 
536  return ConvertToJSON(ptr, clActual, compact, member_name);
537 }
538 
539 ////////////////////////////////////////////////////////////////////////////////
540 /// Set level of space/newline/array compression
541 /// Lower digit of compact parameter define formatting rules
542 /// - kNoCompress = 0 - no any compression, human-readable form
543 /// - kNoIndent = 1 - remove indentation spaces in the begin of each line
544 /// - kNoNewLine = 2 - remove also newlines
545 /// - kNoSpaces = 3 - exclude all spaces and new lines
546 ///
547 /// Second digit of compact parameter defines algorithm for arrays compression
548 /// - 0 - no compression, standard JSON array
549 /// - kZeroSuppression = 10 - exclude leading and trailing zeros
550 /// - kSameSuppression = 20 - check values repetition and empty gaps
551 ///
552 /// Third digit defines usage of typeinfo
553 /// - kSkipTypeInfo = 100 - "_typename" field will be skipped, reading by ROOT or JSROOT may be impossible
554 
555 void TBufferJSON::SetCompact(int level)
556 {
557  if (level < 0)
558  level = 0;
559  fCompact = level % 10;
560  if (fCompact >= kMapAsObject) {
561  fMapAsObject = kTRUE;
562  fCompact = fCompact % kMapAsObject;
563  }
564  fSemicolon = (fCompact >= kNoSpaces) ? ":" : " : ";
565  fArraySepar = (fCompact >= kNoSpaces) ? "," : ", ";
566  fArrayCompact = ((level / 10) % 10) * 10;
567  if ((((level / 100) % 10) * 100) == kSkipTypeInfo)
568  fTypeNameTag.Clear();
569  else if (fTypeNameTag.Length() == 0)
570  fTypeNameTag = "_typename";
571 }
572 
573 ////////////////////////////////////////////////////////////////////////////////
574 /// Configures _typename tag in JSON structures
575 /// By default "_typename" field in JSON structures used to store class information
576 /// One can specify alternative tag like "$typename" or "xy", but such JSON can not be correctly used in JSROOT
577 /// If empty string is provided, class information will not be stored
578 
579 void TBufferJSON::SetTypenameTag(const char *tag)
580 {
581  if (!tag)
582  fTypeNameTag.Clear();
583  else
584  fTypeNameTag = tag;
585 }
586 
587 ////////////////////////////////////////////////////////////////////////////////
588 /// Configures _typeversion tag in JSON
589 /// One can specify name of the JSON tag like "_typeversion" or "$tv" which will be used to store class version
590 /// Such tag can be used to correctly recover objects from JSON
591 /// If empty string is provided (default), class version will not be stored
592 
593 void TBufferJSON::SetTypeversionTag(const char *tag)
594 {
595  if (!tag)
596  fTypeVersionTag.Clear();
597  else
598  fTypeVersionTag = tag;
599 }
600 
601 ////////////////////////////////////////////////////////////////////////////////
602 /// Specify class which typename will not be stored in JSON
603 /// Several classes can be configured
604 /// To exclude typeinfo for all classes, call TBufferJSON::SetTypenameTag("")
605 
606 void TBufferJSON::SetSkipClassInfo(const TClass *cl)
607 {
608  if (cl && (std::find(fSkipClasses.begin(), fSkipClasses.end(), cl) == fSkipClasses.end()))
609  fSkipClasses.emplace_back(cl);
610 }
611 
612 ////////////////////////////////////////////////////////////////////////////////
613 /// Returns true if class info will be skipped from JSON
614 
615 Bool_t TBufferJSON::IsSkipClassInfo(const TClass *cl) const
616 {
617  return cl && (std::find(fSkipClasses.begin(), fSkipClasses.end(), cl) != fSkipClasses.end());
618 }
619 
620 ////////////////////////////////////////////////////////////////////////////////
621 /// Converts any type of object to JSON string
622 /// One should provide pointer on object and its class name
623 /// Lower digit of compact parameter define formatting rules
624 /// - TBufferJSON::kNoCompress (0) - no any compression, human-readable form
625 /// - TBufferJSON::kNoIndent (1) - exclude spaces in the begin
626 /// - TBufferJSON::kNoNewLine (2) - no indent and no newlines
627 /// - TBufferJSON::kNoSpaces (3) - exclude spaces as much as possible
628 /// Second digit of compact parameter defines algorithm for arrays compression
629 /// - 0 - no compression, standard JSON array
630 /// - TBufferJSON::kZeroSuppression (10) - exclude leading and trailing zeros
631 /// - TBufferJSON::kSameSuppression (20) - check values repetition and empty gaps
632 /// - TBufferJSON::kBase64 (30) - arrays will be coded with base64 coding
633 /// Third digit of compact parameter defines typeinfo storage:
634 /// - TBufferJSON::kSkipTypeInfo (100) - "_typename" will be skipped, not always can be read back
635 /// Maximal none-destructive compression can be achieved when
636 /// compact parameter equal to TBufferJSON::kNoSpaces + TBufferJSON::kSameSuppression
637 /// When member_name specified, converts only this data member
638 
639 TString TBufferJSON::ConvertToJSON(const void *obj, const TClass *cl, Int_t compact, const char *member_name)
640 {
641  TClass *clActual = obj ? cl->GetActualClass(obj) : nullptr;
642  const void *actualStart = obj;
643  if (clActual && (clActual != cl)) {
644  actualStart = (char *)obj - clActual->GetBaseClassOffset(cl);
645  } else {
646  // We could not determine the real type of this object,
647  // let's assume it is the one given by the caller.
648  clActual = const_cast<TClass *>(cl);
649  }
650 
651  if (member_name && actualStart) {
652  TRealData *rdata = clActual->GetRealData(member_name);
653  TDataMember *member = rdata ? rdata->GetDataMember() : nullptr;
654  if (!member) {
655  TIter iter(clActual->GetListOfRealData());
656  while ((rdata = dynamic_cast<TRealData *>(iter())) != nullptr) {
657  member = rdata->GetDataMember();
658  if (member && strcmp(member->GetName(), member_name) == 0)
659  break;
660  }
661  }
662  if (!member)
663  return TString();
664 
665  Int_t arraylen = -1;
666  if (member->GetArrayIndex() != 0) {
667  TRealData *idata = clActual->GetRealData(member->GetArrayIndex());
668  TDataMember *imember = idata ? idata->GetDataMember() : nullptr;
669  if (imember && (strcmp(imember->GetTrueTypeName(), "int") == 0)) {
670  arraylen = *((int *)((char *)actualStart + idata->GetThisOffset()));
671  }
672  }
673 
674  void *ptr = (char *)actualStart + rdata->GetThisOffset();
675  if (member->IsaPointer())
676  ptr = *((char **)ptr);
677 
678  return TBufferJSON::ConvertToJSON(ptr, member, compact, arraylen);
679  }
680 
681  TBufferJSON buf;
682 
683  buf.SetCompact(compact);
684 
685  return buf.StoreObject(actualStart, clActual);
686 }
687 
688 ////////////////////////////////////////////////////////////////////////////////
689 /// Store provided object as JSON structure
690 /// Allows to configure different TBufferJSON properties before converting object into JSON
691 /// Actual object class must be specified here
692 /// Method can be safely called once - after that TBufferJSON instance must be destroyed
693 /// Code should look like:
694 ///
695 /// auto obj = new UserClass();
696 /// TBufferJSON buf;
697 /// buf.SetCompact(TBufferJSON::kNoSpaces); // change any other settings in TBufferJSON
698 /// auto json = buf.StoreObject(obj, TClass::GetClass<UserClass>());
699 ///
700 
701 TString TBufferJSON::StoreObject(const void *obj, const TClass *cl)
702 {
703  if (IsWriting()) {
704 
705  InitMap();
706 
707  PushStack(); // dummy stack entry to avoid extra checks in the beginning
708 
709  JsonWriteObject(obj, cl);
710 
711  PopStack();
712  } else {
713  Error("StoreObject", "Can not store object into TBuffer for reading");
714  }
715 
716  return fOutBuffer.Length() ? fOutBuffer : fValue;
717 }
718 
719 ////////////////////////////////////////////////////////////////////////////////
720 /// Converts selected data member into json
721 /// Parameter ptr specifies address in memory, where data member is located
722 /// compact parameter defines compactness of produced JSON (from 0 to 3)
723 /// arraylen (when specified) is array length for this data member, //[fN] case
724 
725 TString TBufferJSON::ConvertToJSON(const void *ptr, TDataMember *member, Int_t compact, Int_t arraylen)
726 {
727  if (!ptr || !member)
728  return TString("null");
729 
730  Bool_t stlstring = !strcmp(member->GetTrueTypeName(), "string");
731 
732  Int_t isstl = member->IsSTLContainer();
733 
734  TClass *mcl = member->IsBasic() ? nullptr : gROOT->GetClass(member->GetTypeName());
735 
736  if (mcl && (mcl != TString::Class()) && !stlstring && !isstl && (mcl->GetBaseClassOffset(TArray::Class()) != 0) &&
737  (arraylen <= 0) && (member->GetArrayDim() == 0))
738  return TBufferJSON::ConvertToJSON(ptr, mcl, compact);
739 
740  TBufferJSON buf;
741 
742  buf.SetCompact(compact);
743 
744  return buf.JsonWriteMember(ptr, member, mcl, arraylen);
745 }
746 
747 ////////////////////////////////////////////////////////////////////////////////
748 /// Convert object into JSON and store in text file
749 /// Returns size of the produce file
750 /// Used in TObject::SaveAs()
751 
752 Int_t TBufferJSON::ExportToFile(const char *filename, const TObject *obj, const char *option)
753 {
754  if (!obj || !filename || (*filename == 0))
755  return 0;
756 
757  Int_t compact = strstr(filename, ".json.gz") ? 3 : 0;
758  if (option && (*option >= '0') && (*option <= '3'))
759  compact = TString(option).Atoi();
760 
761  TString json = TBufferJSON::ConvertToJSON(obj, compact);
762 
763  std::ofstream ofs(filename);
764 
765  if (strstr(filename, ".json.gz")) {
766  const char *objbuf = json.Data();
767  Long_t objlen = json.Length();
768 
769  unsigned long objcrc = R__crc32(0, NULL, 0);
770  objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
771 
772  // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
773  Int_t buflen = 10 + objlen + 8;
774  if (buflen < 512)
775  buflen = 512;
776 
777  char *buffer = (char *)malloc(buflen);
778  if (!buffer)
779  return 0; // failure
780 
781  char *bufcur = buffer;
782 
783  *bufcur++ = 0x1f; // first byte of ZIP identifier
784  *bufcur++ = 0x8b; // second byte of ZIP identifier
785  *bufcur++ = 0x08; // compression method
786  *bufcur++ = 0x00; // FLAG - empty, no any file names
787  *bufcur++ = 0; // empty timestamp
788  *bufcur++ = 0; //
789  *bufcur++ = 0; //
790  *bufcur++ = 0; //
791  *bufcur++ = 0; // XFL (eXtra FLags)
792  *bufcur++ = 3; // OS 3 means Unix
793  // strcpy(bufcur, "item.json");
794  // bufcur += strlen("item.json")+1;
795 
796  char dummy[8];
797  memcpy(dummy, bufcur - 6, 6);
798 
799  // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
800  unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, (char *)objbuf, objlen);
801 
802  memcpy(bufcur - 6, dummy, 6);
803 
804  bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
805 
806  *bufcur++ = objcrc & 0xff; // CRC32
807  *bufcur++ = (objcrc >> 8) & 0xff;
808  *bufcur++ = (objcrc >> 16) & 0xff;
809  *bufcur++ = (objcrc >> 24) & 0xff;
810 
811  *bufcur++ = objlen & 0xff; // original data length
812  *bufcur++ = (objlen >> 8) & 0xff; // original data length
813  *bufcur++ = (objlen >> 16) & 0xff; // original data length
814  *bufcur++ = (objlen >> 24) & 0xff; // original data length
815 
816  ofs.write(buffer, bufcur - buffer);
817 
818  free(buffer);
819  } else {
820  ofs << json.Data();
821  }
822 
823  ofs.close();
824 
825  return json.Length();
826 }
827 
828 ////////////////////////////////////////////////////////////////////////////////
829 /// Convert object into JSON and store in text file
830 /// Returns size of the produce file
831 
832 Int_t TBufferJSON::ExportToFile(const char *filename, const void *obj, const TClass *cl, const char *option)
833 {
834  if (!obj || !cl || !filename || (*filename == 0))
835  return 0;
836 
837  Int_t compact = strstr(filename, ".json.gz") ? 3 : 0;
838  if (option && (*option >= '0') && (*option <= '3'))
839  compact = TString(option).Atoi();
840 
841  TString json = TBufferJSON::ConvertToJSON(obj, cl, compact);
842 
843  std::ofstream ofs(filename);
844 
845  if (strstr(filename, ".json.gz")) {
846  const char *objbuf = json.Data();
847  Long_t objlen = json.Length();
848 
849  unsigned long objcrc = R__crc32(0, NULL, 0);
850  objcrc = R__crc32(objcrc, (const unsigned char *)objbuf, objlen);
851 
852  // 10 bytes (ZIP header), compressed data, 8 bytes (CRC and original length)
853  Int_t buflen = 10 + objlen + 8;
854  if (buflen < 512)
855  buflen = 512;
856 
857  char *buffer = (char *)malloc(buflen);
858  if (!buffer)
859  return 0; // failure
860 
861  char *bufcur = buffer;
862 
863  *bufcur++ = 0x1f; // first byte of ZIP identifier
864  *bufcur++ = 0x8b; // second byte of ZIP identifier
865  *bufcur++ = 0x08; // compression method
866  *bufcur++ = 0x00; // FLAG - empty, no any file names
867  *bufcur++ = 0; // empty timestamp
868  *bufcur++ = 0; //
869  *bufcur++ = 0; //
870  *bufcur++ = 0; //
871  *bufcur++ = 0; // XFL (eXtra FLags)
872  *bufcur++ = 3; // OS 3 means Unix
873  // strcpy(bufcur, "item.json");
874  // bufcur += strlen("item.json")+1;
875 
876  char dummy[8];
877  memcpy(dummy, bufcur - 6, 6);
878 
879  // R__memcompress fills first 6 bytes with own header, therefore just overwrite them
880  unsigned long ziplen = R__memcompress(bufcur - 6, objlen + 6, (char *)objbuf, objlen);
881 
882  memcpy(bufcur - 6, dummy, 6);
883 
884  bufcur += (ziplen - 6); // jump over compressed data (6 byte is extra ROOT header)
885 
886  *bufcur++ = objcrc & 0xff; // CRC32
887  *bufcur++ = (objcrc >> 8) & 0xff;
888  *bufcur++ = (objcrc >> 16) & 0xff;
889  *bufcur++ = (objcrc >> 24) & 0xff;
890 
891  *bufcur++ = objlen & 0xff; // original data length
892  *bufcur++ = (objlen >> 8) & 0xff; // original data length
893  *bufcur++ = (objlen >> 16) & 0xff; // original data length
894  *bufcur++ = (objlen >> 24) & 0xff; // original data length
895 
896  ofs.write(buffer, bufcur - buffer);
897 
898  free(buffer);
899  } else {
900  ofs << json.Data();
901  }
902 
903  ofs.close();
904 
905  return json.Length();
906 }
907 
908 ////////////////////////////////////////////////////////////////////////////////
909 /// Read TObject-based class from JSON, produced by ConvertToJSON() method.
910 /// If object does not inherit from TObject class, return 0.
911 
912 TObject *TBufferJSON::ConvertFromJSON(const char *str)
913 {
914  TClass *cl = nullptr;
915  void *obj = ConvertFromJSONAny(str, &cl);
916 
917  if (!cl || !obj)
918  return nullptr;
919 
920  Int_t delta = cl->GetBaseClassOffset(TObject::Class());
921 
922  if (delta < 0) {
923  cl->Destructor(obj);
924  return nullptr;
925  }
926 
927  return (TObject *)(((char *)obj) + delta);
928 }
929 
930 ////////////////////////////////////////////////////////////////////////////////
931 /// Read object from JSON
932 /// In class pointer (if specified) read class is returned
933 /// One must specify expected object class, if it is TArray or STL container
934 
935 void *TBufferJSON::ConvertFromJSONAny(const char *str, TClass **cl)
936 {
937  TBufferJSON buf(TBuffer::kRead);
938 
939  return buf.RestoreObject(str, cl);
940 }
941 
942 ////////////////////////////////////////////////////////////////////////////////
943 /// Read object from JSON
944 /// In class pointer (if specified) read class is returned
945 /// One must specify expected object class, if it is TArray or STL container
946 
947 void *TBufferJSON::RestoreObject(const char *json_str, TClass **cl)
948 {
949  if (!IsReading())
950  return nullptr;
951 
952  nlohmann::json docu = nlohmann::json::parse(json_str);
953 
954  if (docu.is_null() || (!docu.is_object() && !docu.is_array()))
955  return nullptr;
956 
957  TClass *objClass = nullptr;
958 
959  if (cl) {
960  objClass = *cl; // this is class which suppose to created when reading JSON
961  *cl = nullptr;
962  }
963 
964  InitMap();
965 
966  PushStack(0, &docu);
967 
968  void *obj = JsonReadObject(nullptr, objClass, cl);
969 
970  PopStack();
971 
972  return obj;
973 }
974 
975 ////////////////////////////////////////////////////////////////////////////////
976 /// Read objects from JSON, one can reuse existing object
977 
978 void *TBufferJSON::ConvertFromJSONChecked(const char *str, const TClass *expectedClass)
979 {
980  if (!expectedClass)
981  return nullptr;
982 
983  TClass *resClass = const_cast<TClass *>(expectedClass);
984 
985  void *res = ConvertFromJSONAny(str, &resClass);
986 
987  if (!res || !resClass)
988  return nullptr;
989 
990  if (resClass == expectedClass)
991  return res;
992 
993  Int_t offset = resClass->GetBaseClassOffset(expectedClass);
994  if (offset < 0) {
995  ::Error("TBufferJSON::ConvertFromJSONChecked", "expected class %s is not base for read class %s",
996  expectedClass->GetName(), resClass->GetName());
997  resClass->Destructor(res);
998  return nullptr;
999  }
1000 
1001  return (char *)res - offset;
1002 }
1003 
1004 ////////////////////////////////////////////////////////////////////////////////
1005 /// Convert single data member to JSON structures
1006 /// Returns string with converted member
1007 
1008 TString TBufferJSON::JsonWriteMember(const void *ptr, TDataMember *member, TClass *memberClass, Int_t arraylen)
1009 {
1010  if (!member)
1011  return "null";
1012 
1013  if (gDebug > 2)
1014  Info("JsonWriteMember", "Write member %s type %s ndim %d", member->GetName(), member->GetTrueTypeName(),
1015  member->GetArrayDim());
1016 
1017  Int_t tid = member->GetDataType() ? member->GetDataType()->GetType() : kNoType_t;
1018  if (strcmp(member->GetTrueTypeName(), "const char*") == 0)
1019  tid = kCharStar;
1020  else if (!member->IsBasic() || (tid == kOther_t) || (tid == kVoid_t))
1021  tid = kNoType_t;
1022 
1023  if (!ptr)
1024  return (tid == kCharStar) ? "\"\"" : "null";
1025 
1026  PushStack(0);
1027  fValue.Clear();
1028 
1029  if (tid != kNoType_t) {
1030 
1031  TArrayIndexProducer indx(member, arraylen, fArraySepar.Data());
1032 
1033  Int_t shift = 1;
1034 
1035  if (indx.IsArray() && (tid == kChar_t))
1036  shift = indx.ReduceDimension();
1037 
1038  char *ppp = (char *)ptr;
1039 
1040  if (indx.IsArray())
1041  fOutBuffer.Append(indx.GetBegin());
1042 
1043  do {
1044  fValue.Clear();
1045 
1046  switch (tid) {
1047  case kChar_t:
1048  if (shift > 1)
1049  JsonWriteConstChar((Char_t *)ppp, shift);
1050  else
1051  JsonWriteBasic(*((Char_t *)ppp));
1052  break;
1053  case kShort_t: JsonWriteBasic(*((Short_t *)ppp)); break;
1054  case kInt_t: JsonWriteBasic(*((Int_t *)ppp)); break;
1055  case kLong_t: JsonWriteBasic(*((Long_t *)ppp)); break;
1056  case kFloat_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1057  case kCounter: JsonWriteBasic(*((Int_t *)ppp)); break;
1058  case kCharStar: JsonWriteConstChar((Char_t *)ppp); break;
1059  case kDouble_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1060  case kDouble32_t: JsonWriteBasic(*((Double_t *)ppp)); break;
1061  case kchar: JsonWriteBasic(*((char *)ppp)); break;
1062  case kUChar_t: JsonWriteBasic(*((UChar_t *)ppp)); break;
1063  case kUShort_t: JsonWriteBasic(*((UShort_t *)ppp)); break;
1064  case kUInt_t: JsonWriteBasic(*((UInt_t *)ppp)); break;
1065  case kULong_t: JsonWriteBasic(*((ULong_t *)ppp)); break;
1066  case kBits: JsonWriteBasic(*((UInt_t *)ppp)); break;
1067  case kLong64_t: JsonWriteBasic(*((Long64_t *)ppp)); break;
1068  case kULong64_t: JsonWriteBasic(*((ULong64_t *)ppp)); break;
1069  case kBool_t: JsonWriteBasic(*((Bool_t *)ppp)); break;
1070  case kFloat16_t: JsonWriteBasic(*((Float_t *)ppp)); break;
1071  case kOther_t:
1072  case kVoid_t: break;
1073  }
1074 
1075  fOutBuffer.Append(fValue);
1076  if (indx.IsArray())
1077  fOutBuffer.Append(indx.NextSeparator());
1078 
1079  ppp += shift * member->GetUnitSize();
1080 
1081  } while (!indx.IsDone());
1082 
1083  fValue = fOutBuffer;
1084 
1085  } else if (memberClass == TString::Class()) {
1086  TString *str = (TString *)ptr;
1087  JsonWriteConstChar(str ? str->Data() : nullptr);
1088  } else if ((member->IsSTLContainer() == ROOT::kSTLvector) || (member->IsSTLContainer() == ROOT::kSTLlist) ||
1089  (member->IsSTLContainer() == ROOT::kSTLforwardlist)) {
1090 
1091  if (memberClass)
1092  memberClass->Streamer((void *)ptr, *this);
1093  else
1094  fValue = "[]";
1095 
1096  if (fValue == "0")
1097  fValue = "[]";
1098 
1099  } else if (memberClass && memberClass->GetBaseClassOffset(TArray::Class()) == 0) {
1100  TArray *arr = (TArray *)ptr;
1101  if (arr && (arr->GetSize() > 0)) {
1102  arr->Streamer(*this);
1103  // WriteFastArray(arr->GetArray(), arr->GetSize());
1104  if (Stack()->fValues.size() > 1) {
1105  Warning("TBufferJSON", "When streaming TArray, more than 1 object in the stack, use second item");
1106  fValue = Stack()->fValues[1].c_str();
1107  }
1108  } else
1109  fValue = "[]";
1110  } else if (memberClass && !strcmp(memberClass->GetName(), "string")) {
1111  // here value contains quotes, stack can be ignored
1112  memberClass->Streamer((void *)ptr, *this);
1113  }
1114  PopStack();
1115 
1116  if (fValue.Length())
1117  return fValue;
1118 
1119  if (!memberClass || (member->GetArrayDim() > 0) || (arraylen > 0))
1120  return "<not supported>";
1121 
1122  return TBufferJSON::ConvertToJSON(ptr, memberClass);
1123 }
1124 
1125 ////////////////////////////////////////////////////////////////////////////////
1126 /// add new level to the structures stack
1127 
1128 TJSONStackObj *TBufferJSON::PushStack(Int_t inclevel, void *readnode)
1129 {
1130  auto next = new TJSONStackObj();
1131  next->fLevel = inclevel;
1132  if (IsReading()) {
1133  next->fNode = (nlohmann::json *)readnode;
1134  } else if (fStack.size() > 0) {
1135  auto prev = Stack();
1136  next->fLevel += prev->fLevel;
1137  next->fMemberPtr = prev->fMemberPtr;
1138  }
1139  fStack.emplace_back(next);
1140  return next;
1141 }
1142 
1143 ////////////////////////////////////////////////////////////////////////////////
1144 /// remove one level from stack
1145 
1146 TJSONStackObj *TBufferJSON::PopStack()
1147 {
1148  if (fStack.size() > 0)
1149  fStack.pop_back();
1150 
1151  return fStack.size() > 0 ? fStack.back().get() : nullptr;
1152 }
1153 
1154 ////////////////////////////////////////////////////////////////////////////////
1155 /// Append two string to the output JSON, normally separate by line break
1156 
1157 void TBufferJSON::AppendOutput(const char *line0, const char *line1)
1158 {
1159  if (line0)
1160  fOutput->Append(line0);
1161 
1162  if (line1) {
1163  if (fCompact < 2)
1164  fOutput->Append("\n");
1165 
1166  if (strlen(line1) > 0) {
1167  if (fCompact < 1) {
1168  if (Stack()->fLevel > 0)
1169  fOutput->Append(' ', Stack()->fLevel);
1170  }
1171  fOutput->Append(line1);
1172  }
1173  }
1174 }
1175 
1176 ////////////////////////////////////////////////////////////////////////////////
1177 /// Start object element with typeinfo
1178 
1179 TJSONStackObj *TBufferJSON::JsonStartObjectWrite(const TClass *obj_class, TStreamerInfo *info)
1180 {
1181  auto stack = PushStack(2);
1182 
1183  // new object started - assign own member counter
1184  stack->fMemberPtr = &stack->fMemberCnt;
1185 
1186  if ((fTypeNameTag.Length() > 0) && !IsSkipClassInfo(obj_class)) {
1187  // stack->fMemberCnt = 1; // default value, comment out here
1188  AppendOutput("{", "\"");
1189  AppendOutput(fTypeNameTag.Data());
1190  AppendOutput("\"");
1191  AppendOutput(fSemicolon.Data());
1192  AppendOutput("\"");
1193  AppendOutput(obj_class->GetName());
1194  AppendOutput("\"");
1195  if (fTypeVersionTag.Length() > 0) {
1196  AppendOutput(stack->NextMemberSeparator(), "\"");
1197  AppendOutput(fTypeVersionTag.Data());
1198  AppendOutput("\"");
1199  AppendOutput(fSemicolon.Data());
1200  AppendOutput(Form("%d", (int)(info ? info->GetClassVersion() : obj_class->GetClassVersion())));
1201  }
1202  } else {
1203  stack->fMemberCnt = 0; // exclude typename
1204  AppendOutput("{");
1205  }
1206 
1207  return stack;
1208 }
1209 
1210 ////////////////////////////////////////////////////////////////////////////////
1211 /// Start new class member in JSON structures
1212 
1213 void TBufferJSON::JsonStartElement(const TStreamerElement *elem, const TClass *base_class)
1214 {
1215  const char *elem_name = nullptr;
1216  Int_t special_kind = JsonSpecialClass(base_class);
1217 
1218  switch (special_kind) {
1219  case 0:
1220  if (!base_class)
1221  elem_name = elem->GetName();
1222  break;
1223  case TClassEdit::kVector: elem_name = "fVector"; break;
1224  case TClassEdit::kList: elem_name = "fList"; break;
1225  case TClassEdit::kForwardlist: elem_name = "fForwardlist"; break;
1226  case TClassEdit::kDeque: elem_name = "fDeque"; break;
1227  case TClassEdit::kMap: elem_name = "fMap"; break;
1228  case TClassEdit::kMultiMap: elem_name = "fMultiMap"; break;
1229  case TClassEdit::kSet: elem_name = "fSet"; break;
1230  case TClassEdit::kMultiSet: elem_name = "fMultiSet"; break;
1231  case TClassEdit::kUnorderedSet: elem_name = "fUnorderedSet"; break;
1232  case TClassEdit::kUnorderedMultiSet: elem_name = "fUnorderedMultiSet"; break;
1233  case TClassEdit::kUnorderedMap: elem_name = "fUnorderedMap"; break;
1234  case TClassEdit::kUnorderedMultiMap: elem_name = "fUnorderedMultiMap"; break;
1235  case TClassEdit::kBitSet: elem_name = "fBitSet"; break;
1236  case json_TArray: elem_name = "fArray"; break;
1237  case json_TString:
1238  case json_stdstring: elem_name = "fString"; break;
1239  }
1240 
1241  if (!elem_name)
1242  return;
1243 
1244  if (IsReading()) {
1245  nlohmann::json *json = Stack()->fNode;
1246 
1247  if (json->count(elem_name) != 1) {
1248  Error("JsonStartElement", "Missing JSON structure for element %s", elem_name);
1249  } else {
1250  Stack()->fNode = &((*json)[elem_name]);
1251  if (special_kind == json_TArray) {
1252  Int_t len = Stack()->IsJsonArray();
1253  Stack()->PushIntValue(len > 0 ? len : 0);
1254  if (len < 0)
1255  Error("JsonStartElement", "Missing array when reading TArray class for element %s", elem->GetName());
1256  }
1257  if ((gDebug > 1) && base_class)
1258  Info("JsonStartElement", "Reading baseclass %s from element %s", base_class->GetName(), elem_name);
1259  }
1260 
1261  } else {
1262  AppendOutput(Stack()->NextMemberSeparator(), "\"");
1263  AppendOutput(elem_name);
1264  AppendOutput("\"");
1265  AppendOutput(fSemicolon.Data());
1266  }
1267 }
1268 
1269 ////////////////////////////////////////////////////////////////////////////////
1270 /// disable post-processing of the code
1271 void TBufferJSON::JsonDisablePostprocessing()
1272 {
1273  Stack()->fIsPostProcessed = kTRUE;
1274 }
1275 
1276 ////////////////////////////////////////////////////////////////////////////////
1277 /// return non-zero value when class has special handling in JSON
1278 /// it is TCollection (-130), TArray (100), TString (110), std::string (120) and STL containers (1..6)
1279 
1280 Int_t TBufferJSON::JsonSpecialClass(const TClass *cl) const
1281 {
1282  if (!cl)
1283  return 0;
1284 
1285  Bool_t isarray = strncmp("TArray", cl->GetName(), 6) == 0;
1286  if (isarray)
1287  isarray = (const_cast<TClass *>(cl))->GetBaseClassOffset(TArray::Class()) == 0;
1288  if (isarray)
1289  return json_TArray;
1290 
1291  // negative value used to indicate that collection stored as object
1292  if ((const_cast<TClass *>(cl))->GetBaseClassOffset(TCollection::Class()) == 0)
1293  return json_TCollection;
1294 
1295  // special case for TString - it is saved as string in JSON
1296  if (cl == TString::Class())
1297  return json_TString;
1298 
1299  bool isstd = TClassEdit::IsStdClass(cl->GetName());
1300  int isstlcont(ROOT::kNotSTL);
1301  if (isstd)
1302  isstlcont = cl->GetCollectionType();
1303  if (isstlcont > 0)
1304  return isstlcont;
1305 
1306  // also special handling for STL string, which handled similar to TString
1307  if (isstd && !strcmp(cl->GetName(), "string"))
1308  return json_stdstring;
1309 
1310  return 0;
1311 }
1312 
1313 ////////////////////////////////////////////////////////////////////////////////
1314 /// Write object to buffer
1315 /// If object was written before, only pointer will be stored
1316 /// If check_map==kFALSE, object will be stored in any case and pointer will not be registered in the map
1317 
1318 void TBufferJSON::JsonWriteObject(const void *obj, const TClass *cl, Bool_t check_map)
1319 {
1320  if (!cl)
1321  obj = nullptr;
1322 
1323  if (gDebug > 0)
1324  Info("JsonWriteObject", "Object %p class %s check_map %s", obj, cl ? cl->GetName() : "null",
1325  check_map ? "true" : "false");
1326 
1327  Int_t special_kind = JsonSpecialClass(cl), map_convert{0};
1328 
1329  TString fObjectOutput, *fPrevOutput{nullptr};
1330 
1331  TJSONStackObj *stack = Stack();
1332 
1333  if (stack && stack->fAccObjects && ((fValue.Length() > 0) || (stack->fValues.size() > 0))) {
1334  // accumulate data of super-object in stack
1335 
1336  if (fValue.Length() > 0)
1337  stack->PushValue(fValue);
1338 
1339  // redirect output to local buffer, use it later as value
1340  fPrevOutput = fOutput;
1341  fOutput = &fObjectOutput;
1342  } else if ((special_kind <= 0) || (special_kind > json_TArray)) {
1343  // FIXME: later post processing should be active for all special classes, while they all keep output in the value
1344  JsonDisablePostprocessing();
1345  } else if ((special_kind == TClassEdit::kMap) || (special_kind == TClassEdit::kMultiMap) ||
1346  (special_kind == TClassEdit::kUnorderedMap) || (special_kind == TClassEdit::kUnorderedMultiMap)) {
1347 
1348  if ((fMapAsObject && (fStack.size()==1)) || (stack && stack->fElem && strstr(stack->fElem->GetTitle(), "JSON_object")))
1349  map_convert = 2; // mapped into normal object
1350  else
1351  map_convert = 1;
1352  }
1353 
1354  if (!obj) {
1355  AppendOutput("null");
1356  goto post_process;
1357  }
1358 
1359  if (special_kind <= 0) {
1360  // add element name which should correspond to the object
1361  if (check_map) {
1362  Long64_t refid = GetObjectTag(obj);
1363  if (refid > 0) {
1364  // old-style refs, coded into string like "$ref12"
1365  // AppendOutput(Form("\"$ref:%u\"", iter->second));
1366  // new-style refs, coded into extra object {"$ref":12}, auto-detected by JSROOT 4.8 and higher
1367  AppendOutput(Form("{\"$ref\":%u}", (unsigned)(refid - 1)));
1368  goto post_process;
1369  }
1370  MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1371  }
1372 
1373  fJsonrCnt++; // object counts required in dereferencing part
1374 
1375  stack = JsonStartObjectWrite(cl);
1376 
1377  } else if (map_convert == 2) {
1378  // special handling of map - it is object, but stored in the fValue
1379 
1380  if (check_map) {
1381  Long64_t refid = GetObjectTag(obj);
1382  if (refid > 0) {
1383  fValue.Form("{\"$ref\":%u}", (unsigned)(refid - 1));
1384  goto post_process;
1385  }
1386  MapObject(obj, cl, fJsonrCnt + 1); // +1 used
1387  }
1388 
1389  fJsonrCnt++; // object counts required in dereferencing part
1390  stack = PushStack(0);
1391 
1392  } else {
1393 
1394  bool base64 = ((special_kind == TClassEdit::kVector) && stack && stack->fElem && strstr(stack->fElem->GetTitle(), "JSON_base64"));
1395 
1396  // for array, string and STL collections different handling -
1397  // they not recognized at the end as objects in JSON
1398  stack = PushStack(0);
1399 
1400  stack->fBase64 = base64;
1401  }
1402 
1403  if (gDebug > 3)
1404  Info("JsonWriteObject", "Starting object %p write for class: %s", obj, cl->GetName());
1405 
1406  stack->fAccObjects = special_kind < ROOT::kSTLend;
1407 
1408  if (special_kind == json_TCollection)
1409  JsonWriteCollection((TCollection *)obj, cl);
1410  else
1411  (const_cast<TClass *>(cl))->Streamer((void *)obj, *this);
1412 
1413  if (gDebug > 3)
1414  Info("JsonWriteObject", "Done object %p write for class: %s", obj, cl->GetName());
1415 
1416  if (special_kind == json_TArray) {
1417  if (stack->fValues.size() != 1)
1418  Error("JsonWriteObject", "Problem when writing array");
1419  stack->fValues.clear();
1420  } else if ((special_kind == json_TString) || (special_kind == json_stdstring)) {
1421  if (stack->fValues.size() > 2)
1422  Error("JsonWriteObject", "Problem when writing TString or std::string");
1423  stack->fValues.clear();
1424  AppendOutput(fValue.Data());
1425  fValue.Clear();
1426  } else if ((special_kind > 0) && (special_kind < ROOT::kSTLend)) {
1427  // here make STL container processing
1428 
1429  if (map_convert == 2) {
1430  // converting map into object
1431 
1432  if (!stack->fValues.empty() && (fValue.Length() > 0))
1433  stack->PushValue(fValue);
1434 
1435  const char *separ = (fCompact < 2) ? ", " : ",";
1436  const char *semi = (fCompact < 2) ? ": " : ":";
1437  bool first = true;
1438 
1439  fValue = "{";
1440  if (fTypeNameTag.Length() > 0) {
1441  fValue.Append("\"");
1442  fValue.Append(fTypeNameTag);
1443  fValue.Append("\"");
1444  fValue.Append(semi);
1445  fValue.Append("\"");
1446  fValue.Append(cl->GetName());
1447  fValue.Append("\"");
1448  first = false;
1449  }
1450  for (Int_t k = 1; k < (int)stack->fValues.size() - 1; k += 2) {
1451  if (!first)
1452  fValue.Append(separ);
1453  first = false;
1454  fValue.Append(stack->fValues[k].c_str());
1455  fValue.Append(semi);
1456  fValue.Append(stack->fValues[k + 1].c_str());
1457  }
1458  fValue.Append("}");
1459  stack->fValues.clear();
1460  } else if (stack->fValues.empty()) {
1461  // empty container
1462  if (fValue != "0")
1463  Error("JsonWriteObject", "With empty stack fValue!=0");
1464  fValue = "[]";
1465  } else {
1466 
1467  auto size = std::stoi(stack->fValues[0]);
1468 
1469  bool trivial_format = false;
1470 
1471  if ((stack->fValues.size() == 1) && ((size > 1) || ((fValue.Length() > 1) && (fValue[0]=='[')))) {
1472  // prevent case of vector<vector<value_class>>
1473  const auto proxy = cl->GetCollectionProxy();
1474  TClass *value_class = proxy ? proxy->GetValueClass() : nullptr;
1475  if (value_class && TClassEdit::IsStdClass(value_class->GetName()) && (value_class->GetCollectionType() != ROOT::kNotSTL))
1476  trivial_format = false;
1477  else
1478  trivial_format = true;
1479  }
1480 
1481  if (trivial_format) {
1482  // case of simple vector, array already in the value
1483  stack->fValues.clear();
1484  if (fValue.Length() == 0) {
1485  Error("JsonWriteObject", "Empty value when it should contain something");
1486  fValue = "[]";
1487  }
1488 
1489  } else {
1490  const char *separ = "[";
1491 
1492  if (fValue.Length() > 0)
1493  stack->PushValue(fValue);
1494 
1495  if ((size * 2 == (int) stack->fValues.size() - 1) && (map_convert > 0)) {
1496  // special handling for std::map.
1497  // Create entries like { '$pair': 'typename' , 'first' : key, 'second' : value }
1498  TString pairtype = cl->GetName();
1499  if (pairtype.Index("unordered_map<") == 0)
1500  pairtype.Replace(0, 14, "pair<");
1501  else if (pairtype.Index("unordered_multimap<") == 0)
1502  pairtype.Replace(0, 19, "pair<");
1503  else if (pairtype.Index("multimap<") == 0)
1504  pairtype.Replace(0, 9, "pair<");
1505  else if (pairtype.Index("map<") == 0)
1506  pairtype.Replace(0, 4, "pair<");
1507  else
1508  pairtype = "TPair";
1509  if (fTypeNameTag.Length() == 0)
1510  pairtype = "1";
1511  else
1512  pairtype = TString("\"") + pairtype + TString("\"");
1513  for (Int_t k = 1; k < (int) stack->fValues.size() - 1; k += 2) {
1514  fValue.Append(separ);
1515  separ = fArraySepar.Data();
1516  // fJsonrCnt++; // do not add entry in the map, can conflict with objects inside values
1517  fValue.Append("{");
1518  fValue.Append("\"$pair\"");
1519  fValue.Append(fSemicolon);
1520  fValue.Append(pairtype.Data());
1521  fValue.Append(fArraySepar);
1522  fValue.Append("\"first\"");
1523  fValue.Append(fSemicolon);
1524  fValue.Append(stack->fValues[k].c_str());
1525  fValue.Append(fArraySepar);
1526  fValue.Append("\"second\"");
1527  fValue.Append(fSemicolon);
1528  fValue.Append(stack->fValues[k + 1].c_str());
1529  fValue.Append("}");
1530  }
1531  } else {
1532  // for most stl containers write just like blob, but skipping first element with size
1533  for (Int_t k = 1; k < (int) stack->fValues.size(); k++) {
1534  fValue.Append(separ);
1535  separ = fArraySepar.Data();
1536  fValue.Append(stack->fValues[k].c_str());
1537  }
1538  }
1539 
1540  fValue.Append("]");
1541  stack->fValues.clear();
1542  }
1543  }
1544  }
1545 
1546  // reuse post-processing code for TObject or TRef
1547  PerformPostProcessing(stack, cl);
1548 
1549  if ((special_kind == 0) && (!stack->fValues.empty() || (fValue.Length() > 0))) {
1550  if (gDebug > 0)
1551  Info("JsonWriteObject", "Create blob value for class %s", cl->GetName());
1552 
1553  AppendOutput(fArraySepar.Data(), "\"_blob\"");
1554  AppendOutput(fSemicolon.Data());
1555 
1556  const char *separ = "[";
1557 
1558  for (auto &elem: stack->fValues) {
1559  AppendOutput(separ);
1560  separ = fArraySepar.Data();
1561  AppendOutput(elem.c_str());
1562  }
1563 
1564  if (fValue.Length() > 0) {
1565  AppendOutput(separ);
1566  AppendOutput(fValue.Data());
1567  }
1568 
1569  AppendOutput("]");
1570 
1571  fValue.Clear();
1572  stack->fValues.clear();
1573  }
1574 
1575  PopStack();
1576 
1577  if ((special_kind <= 0))
1578  AppendOutput(nullptr, "}");
1579 
1580 post_process:
1581 
1582  if (fPrevOutput) {
1583  fOutput = fPrevOutput;
1584  // for STL containers and TArray object in fValue itself
1585  if ((special_kind <= 0) || (special_kind > json_TArray))
1586  fValue = fObjectOutput;
1587  else if (fObjectOutput.Length() != 0)
1588  Error("JsonWriteObject", "Non-empty object output for special class %s", cl->GetName());
1589  }
1590 }
1591 
1592 ////////////////////////////////////////////////////////////////////////////////
1593 /// store content of ROOT collection
1594 
1595 void TBufferJSON::JsonWriteCollection(TCollection *col, const TClass *)
1596 {
1597  AppendOutput(Stack()->NextMemberSeparator(), "\"name\"");
1598  AppendOutput(fSemicolon.Data());
1599  AppendOutput("\"");
1600  AppendOutput(col->GetName());
1601  AppendOutput("\"");
1602  AppendOutput(Stack()->NextMemberSeparator(), "\"arr\"");
1603  AppendOutput(fSemicolon.Data());
1604 
1605  // collection treated as JS Array
1606  AppendOutput("[");
1607 
1608  bool islist = col->InheritsFrom(TList::Class());
1609  TMap *map = nullptr;
1610  if (col->InheritsFrom(TMap::Class()))
1611  map = dynamic_cast<TMap *>(col);
1612 
1613  TString sopt;
1614  if (islist) {
1615  sopt.Capacity(500);
1616  sopt = "[";
1617  }
1618 
1619  TIter iter(col);
1620  TObject *obj;
1621  Bool_t first = kTRUE;
1622  while ((obj = iter()) != nullptr) {
1623  if (!first)
1624  AppendOutput(fArraySepar.Data());
1625 
1626  if (map) {
1627  // fJsonrCnt++; // do not account map pair as JSON object
1628  AppendOutput("{", "\"$pair\"");
1629  AppendOutput(fSemicolon.Data());
1630  AppendOutput("\"TPair\"");
1631  AppendOutput(fArraySepar.Data(), "\"first\"");
1632  AppendOutput(fSemicolon.Data());
1633  }
1634 
1635  WriteObjectAny(obj, TObject::Class());
1636 
1637  if (map) {
1638  AppendOutput(fArraySepar.Data(), "\"second\"");
1639  AppendOutput(fSemicolon.Data());
1640  WriteObjectAny(map->GetValue(obj), TObject::Class());
1641  AppendOutput("", "}");
1642  }
1643 
1644  if (islist) {
1645  if (!first)
1646  sopt.Append(fArraySepar.Data());
1647  sopt.Append("\"");
1648  sopt.Append(iter.GetOption());
1649  sopt.Append("\"");
1650  }
1651 
1652  first = kFALSE;
1653  }
1654 
1655  AppendOutput("]");
1656 
1657  if (islist) {
1658  sopt.Append("]");
1659  AppendOutput(Stack()->NextMemberSeparator(), "\"opt\"");
1660  AppendOutput(fSemicolon.Data());
1661  AppendOutput(sopt.Data());
1662  }
1663  fValue.Clear();
1664 }
1665 
1666 ////////////////////////////////////////////////////////////////////////////////
1667 /// read content of ROOT collection
1668 
1669 void TBufferJSON::JsonReadCollection(TCollection *col, const TClass *)
1670 {
1671  if (!col)
1672  return;
1673 
1674  TList *lst = nullptr;
1675  TMap *map = nullptr;
1676  TClonesArray *clones = nullptr;
1677  if (col->InheritsFrom(TList::Class()))
1678  lst = dynamic_cast<TList *>(col);
1679  else if (col->InheritsFrom(TMap::Class()))
1680  map = dynamic_cast<TMap *>(col);
1681  else if (col->InheritsFrom(TClonesArray::Class()))
1682  clones = dynamic_cast<TClonesArray *>(col);
1683 
1684  nlohmann::json *json = Stack()->fNode;
1685 
1686  std::string name = json->at("name");
1687  col->SetName(name.c_str());
1688 
1689  nlohmann::json &arr = json->at("arr");
1690  int size = arr.size();
1691 
1692  for (int n = 0; n < size; ++n) {
1693  nlohmann::json *subelem = &arr.at(n);
1694 
1695  if (map)
1696  subelem = &subelem->at("first");
1697 
1698  PushStack(0, subelem);
1699 
1700  TClass *readClass = nullptr, *objClass = nullptr;
1701  void *subobj = nullptr;
1702 
1703  if (clones) {
1704  if (n == 0) {
1705  if (!clones->GetClass() || (clones->GetSize() == 0)) {
1706  if (fTypeNameTag.Length() > 0) {
1707  clones->SetClass(subelem->at(fTypeNameTag.Data()).get<std::string>().c_str(), size);
1708  } else {
1709  Error("JsonReadCollection",
1710  "Cannot detect class name for TClonesArray - typename tag not configured");
1711  return;
1712  }
1713  } else if (size > clones->GetSize()) {
1714  Error("JsonReadCollection", "TClonesArray size %d smaller than required %d", clones->GetSize(), size);
1715  return;
1716  }
1717  }
1718  objClass = clones->GetClass();
1719  subobj = clones->ConstructedAt(n);
1720  }
1721 
1722  subobj = JsonReadObject(subobj, objClass, &readClass);
1723 
1724  PopStack();
1725 
1726  if (clones)
1727  continue;
1728 
1729  if (!subobj || !readClass) {
1730  subobj = nullptr;
1731  } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1732  Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1733  subobj = nullptr;
1734  }
1735 
1736  TObject *tobj = static_cast<TObject *>(subobj);
1737 
1738  if (map) {
1739  PushStack(0, &arr.at(n).at("second"));
1740 
1741  readClass = nullptr;
1742  void *subobj2 = JsonReadObject(nullptr, nullptr, &readClass);
1743 
1744  PopStack();
1745 
1746  if (!subobj2 || !readClass) {
1747  subobj2 = nullptr;
1748  } else if (readClass->GetBaseClassOffset(TObject::Class()) != 0) {
1749  Error("JsonReadCollection", "Try to add object %s not derived from TObject", readClass->GetName());
1750  subobj2 = nullptr;
1751  }
1752 
1753  map->Add(tobj, static_cast<TObject *>(subobj2));
1754  } else if (lst) {
1755  std::string opt = json->at("opt").at(n).get<std::string>();
1756  lst->Add(tobj, opt.c_str());
1757  } else {
1758  // generic method, all kinds of TCollection should work
1759  col->Add(tobj);
1760  }
1761  }
1762 }
1763 
1764 ////////////////////////////////////////////////////////////////////////////////
1765 /// Read object from current JSON node
1766 
1767 void *TBufferJSON::JsonReadObject(void *obj, const TClass *objClass, TClass **readClass)
1768 {
1769  if (readClass)
1770  *readClass = nullptr;
1771 
1772  TJSONStackObj *stack = Stack();
1773 
1774  Bool_t process_stl = stack->IsStl();
1775  nlohmann::json *json = stack->GetStlNode();
1776 
1777  // check if null pointer
1778  if (json->is_null())
1779  return nullptr;
1780 
1781  Int_t special_kind = JsonSpecialClass(objClass);
1782 
1783  // Extract pointer
1784  if (json->is_object() && (json->size() == 1) && (json->find("$ref") != json->end())) {
1785  unsigned refid = json->at("$ref").get<unsigned>();
1786 
1787  void *ref_obj = nullptr;
1788  TClass *ref_cl = nullptr;
1789 
1790  GetMappedObject(refid + 1, ref_obj, ref_cl);
1791 
1792  if (!ref_obj || !ref_cl) {
1793  Error("JsonReadObject", "Fail to find object for reference %u", refid);
1794  return nullptr;
1795  }
1796 
1797  if (readClass)
1798  *readClass = ref_cl;
1799 
1800  if (gDebug > 2)
1801  Info("JsonReadObject", "Extract object reference %u %p cl:%s expects:%s", refid, ref_obj, ref_cl->GetName(),
1802  (objClass ? objClass->GetName() : "---"));
1803 
1804  return ref_obj;
1805  }
1806 
1807  // special case of strings - they do not create JSON object, but just string
1808  if ((special_kind == json_stdstring) || (special_kind == json_TString)) {
1809  if (!obj)
1810  obj = objClass->New();
1811 
1812  if (gDebug > 2)
1813  Info("JsonReadObject", "Read string from %s", json->dump().c_str());
1814 
1815  if (special_kind == json_stdstring)
1816  *((std::string *)obj) = json->get<std::string>();
1817  else
1818  *((TString *)obj) = json->get<std::string>().c_str();
1819 
1820  if (readClass)
1821  *readClass = const_cast<TClass *>(objClass);
1822 
1823  return obj;
1824  }
1825 
1826  Bool_t isBase = (stack->fElem && objClass) ? stack->fElem->IsBase() : kFALSE; // base class
1827 
1828  if (isBase && (!obj || !objClass)) {
1829  Error("JsonReadObject", "No object when reading base class");
1830  return obj;
1831  }
1832 
1833  Int_t map_convert = 0;
1834  if ((special_kind == TClassEdit::kMap) || (special_kind == TClassEdit::kMultiMap) ||
1835  (special_kind == TClassEdit::kUnorderedMap) || (special_kind == TClassEdit::kUnorderedMultiMap)) {
1836  map_convert = json->is_object() ? 2 : 1; // check if map was written as array or as object
1837  }
1838 
1839  // from now all operations performed with sub-element,
1840  // stack should be repaired at the end
1841  if (process_stl)
1842  stack = PushStack(0, json);
1843 
1844  TClass *jsonClass = nullptr;
1845  Int_t jsonClassVersion = 0;
1846 
1847  if ((special_kind == json_TArray) || ((special_kind > 0) && (special_kind < ROOT::kSTLend))) {
1848 
1849  jsonClass = const_cast<TClass *>(objClass);
1850 
1851  if (!obj)
1852  obj = jsonClass->New();
1853 
1854  Int_t len = stack->IsJsonArray(json, map_convert == 2 ? fTypeNameTag.Data() : nullptr);
1855 
1856  stack->PushIntValue(len > 0 ? len : 0);
1857 
1858  if (len < 0) // should never happens
1859  Error("JsonReadObject", "Not array when expecting such %s", json->dump().c_str());
1860 
1861  if (gDebug > 1)
1862  Info("JsonReadObject", "Reading special kind %d %s ptr %p", special_kind, objClass->GetName(), obj);
1863 
1864  } else if (isBase) {
1865  // base class has special handling - no additional level and no extra refid
1866 
1867  jsonClass = const_cast<TClass *>(objClass);
1868 
1869  if (gDebug > 1)
1870  Info("JsonReadObject", "Reading baseclass %s ptr %p", objClass->GetName(), obj);
1871  } else {
1872 
1873  if ((fTypeNameTag.Length() > 0) && (json->count(fTypeNameTag.Data()) > 0)) {
1874  std::string clname = json->at(fTypeNameTag.Data()).get<std::string>();
1875  jsonClass = TClass::GetClass(clname.c_str());
1876  if (!jsonClass)
1877  Error("JsonReadObject", "Cannot find class %s", clname.c_str());
1878  } else {
1879  // try to use class which is assigned by streamers - better than nothing
1880  jsonClass = const_cast<TClass *>(objClass);
1881  }
1882 
1883  if (!jsonClass) {
1884  if (process_stl)
1885  PopStack();
1886  return obj;
1887  }
1888 
1889  if ((fTypeVersionTag.Length() > 0) && (json->count(fTypeVersionTag.Data()) > 0))
1890  jsonClassVersion = json->at(fTypeVersionTag.Data()).get<int>();
1891 
1892  if (objClass && (jsonClass != objClass)) {
1893  Error("JsonReadObject", "Class mismatch between provided %s and in JSON %s", objClass->GetName(),
1894  jsonClass->GetName());
1895  }
1896 
1897  if (!obj)
1898  obj = jsonClass->New();
1899 
1900  if (gDebug > 1)
1901  Info("JsonReadObject", "Reading object of class %s refid %u ptr %p", jsonClass->GetName(), fJsonrCnt, obj);
1902 
1903  if (!special_kind)
1904  special_kind = JsonSpecialClass(jsonClass);
1905 
1906  // add new element to the reading map
1907  MapObject(obj, jsonClass, ++fJsonrCnt);
1908  }
1909 
1910  // there are two ways to handle custom streamers
1911  // either prepare data before streamer and tweak basic function which are reading values like UInt32_t
1912  // or try re-implement custom streamer here
1913 
1914  if ((jsonClass == TObject::Class()) || (jsonClass == TRef::Class())) {
1915  // for TObject we re-implement custom streamer - it is much easier
1916 
1917  JsonReadTObjectMembers((TObject *)obj, json);
1918 
1919  } else if (special_kind == json_TCollection) {
1920 
1921  JsonReadCollection((TCollection *)obj, jsonClass);
1922 
1923  } else {
1924 
1925  Bool_t do_read = kTRUE;
1926 
1927  // special handling of STL which coded into arrays
1928  if ((special_kind > 0) && (special_kind < ROOT::kSTLend))
1929  do_read = stack->AssignStl(jsonClass, map_convert, fTypeNameTag.Data());
1930 
1931  // if provided - use class version from JSON
1932  stack->fClVersion = jsonClassVersion ? jsonClassVersion : jsonClass->GetClassVersion();
1933 
1934  if (gDebug > 3)
1935  Info("JsonReadObject", "Calling streamer of class %s", jsonClass->GetName());
1936 
1937  if (isBase && (special_kind == 0))
1938  Error("JsonReadObject", "Should not be used for reading of base class %s", jsonClass->GetName());
1939 
1940  if (do_read)
1941  jsonClass->Streamer((void *)obj, *this);
1942 
1943  stack->fClVersion = 0;
1944 
1945  stack->ClearStl(); // reset STL index for itself to prevent looping
1946  }
1947 
1948  // return back stack position
1949  if (process_stl)
1950  PopStack();
1951 
1952  if (gDebug > 1)
1953  Info("JsonReadObject", "Reading object of class %s done", jsonClass->GetName());
1954 
1955  if (readClass)
1956  *readClass = jsonClass;
1957 
1958  return obj;
1959 }
1960 
1961 ////////////////////////////////////////////////////////////////////////////////
1962 /// Read TObject data members from JSON.
1963 /// Do not call TObject::Streamer() to avoid special tweaking of TBufferJSON interface
1964 
1965 void TBufferJSON::JsonReadTObjectMembers(TObject *tobj, void *node)
1966 {
1967  nlohmann::json *json = node ? (nlohmann::json *)node : Stack()->fNode;
1968 
1969  UInt_t uid = json->at("fUniqueID").get<unsigned>();
1970  UInt_t bits = json->at("fBits").get<unsigned>();
1971  // UInt32_t pid = json->at("fPID").get<unsigned>(); // ignore PID for the moment
1972 
1973  tobj->SetUniqueID(uid);
1974 
1975  static auto tobj_fbits_offset = TObject::Class()->GetDataMemberOffset("fBits");
1976 
1977  // there is no method to set all bits directly - do it differently
1978  if (tobj_fbits_offset > 0) {
1979  UInt_t *fbits = (UInt_t *) ((char* ) tobj + tobj_fbits_offset);
1980  *fbits = (*fbits & (TObject::kIsOnHeap | TObject::kNotDeleted)) | bits;
1981  }
1982 }
1983 
1984 ////////////////////////////////////////////////////////////////////////////////
1985 /// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
1986 /// and indent new level in json structure.
1987 /// This call indicates, that TStreamerInfo functions starts streaming
1988 /// object data of correspondent class
1989 
1990 void TBufferJSON::IncrementLevel(TVirtualStreamerInfo *info)
1991 {
1992  if (gDebug > 2)
1993  Info("IncrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
1994 
1995  WorkWithClass((TStreamerInfo *)info);
1996 }
1997 
1998 ////////////////////////////////////////////////////////////////////////////////
1999 /// Prepares buffer to stream data of specified class
2000 
2001 void TBufferJSON::WorkWithClass(TStreamerInfo *sinfo, const TClass *cl)
2002 {
2003  if (sinfo)
2004  cl = sinfo->GetClass();
2005 
2006  if (!cl)
2007  return;
2008 
2009  if (gDebug > 3)
2010  Info("WorkWithClass", "Class: %s", cl->GetName());
2011 
2012  TJSONStackObj *stack = Stack();
2013 
2014  if (IsReading()) {
2015  stack = PushStack(0, stack->fNode);
2016  } else if (stack && stack->IsStreamerElement() && !stack->fIsObjStarted &&
2017  ((stack->fElem->GetType() == TStreamerInfo::kObject) ||
2018  (stack->fElem->GetType() == TStreamerInfo::kAny))) {
2019 
2020  stack->fIsObjStarted = kTRUE;
2021 
2022  fJsonrCnt++; // count object, but do not keep reference
2023 
2024  stack = JsonStartObjectWrite(cl, sinfo);
2025  } else {
2026  stack = PushStack(0);
2027  }
2028 
2029  stack->fInfo = sinfo;
2030  stack->fIsStreamerInfo = kTRUE;
2031 }
2032 
2033 ////////////////////////////////////////////////////////////////////////////////
2034 /// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2035 /// and decrease level in json structure.
2036 
2037 void TBufferJSON::DecrementLevel(TVirtualStreamerInfo *info)
2038 {
2039  if (gDebug > 2)
2040  Info("DecrementLevel", "Class: %s", (info ? info->GetClass()->GetName() : "custom"));
2041 
2042  TJSONStackObj *stack = Stack();
2043 
2044  if (stack->IsStreamerElement()) {
2045 
2046  if (IsWriting()) {
2047  if (gDebug > 3)
2048  Info("DecrementLevel", " Perform post-processing elem: %s", stack->fElem->GetName());
2049 
2050  PerformPostProcessing(stack);
2051  }
2052 
2053  stack = PopStack(); // remove stack of last element
2054  }
2055 
2056  if (stack->fInfo != (TStreamerInfo *)info)
2057  Error("DecrementLevel", " Mismatch of streamer info");
2058 
2059  PopStack(); // back from data of stack info
2060 
2061  if (gDebug > 3)
2062  Info("DecrementLevel", "Class: %s done", (info ? info->GetClass()->GetName() : "custom"));
2063 }
2064 
2065 ////////////////////////////////////////////////////////////////////////////////
2066 /// Return current streamer info element
2067 
2068 TVirtualStreamerInfo *TBufferJSON::GetInfo()
2069 {
2070  return Stack()->fInfo;
2071 }
2072 
2073 ////////////////////////////////////////////////////////////////////////////////
2074 /// Function is called from TStreamerInfo WriteBuffer and ReadBuffer functions
2075 /// and add/verify next element of json structure
2076 /// This calls allows separate data, correspondent to one class member, from another
2077 
2078 void TBufferJSON::SetStreamerElementNumber(TStreamerElement *elem, Int_t comp_type)
2079 {
2080  if (gDebug > 3)
2081  Info("SetStreamerElementNumber", "Element name %s", elem->GetName());
2082 
2083  WorkWithElement(elem, comp_type);
2084 }
2085 
2086 ////////////////////////////////////////////////////////////////////////////////
2087 /// This is call-back from streamer which indicates
2088 /// that class member will be streamed
2089 /// Name of element used in JSON
2090 
2091 void TBufferJSON::WorkWithElement(TStreamerElement *elem, Int_t)
2092 {
2093  TJSONStackObj *stack = Stack();
2094  if (!stack) {
2095  Error("WorkWithElement", "stack is empty");
2096  return;
2097  }
2098 
2099  if (gDebug > 0)
2100  Info("WorkWithElement", " Start element %s type %d typename %s", elem ? elem->GetName() : "---",
2101  elem ? elem->GetType() : -1, elem ? elem->GetTypeName() : "---");
2102 
2103  if (stack->IsStreamerElement()) {
2104  // this is post processing
2105 
2106  if (IsWriting()) {
2107  if (gDebug > 3)
2108  Info("WorkWithElement", " Perform post-processing elem: %s", stack->fElem->GetName());
2109  PerformPostProcessing(stack);
2110  }
2111 
2112  stack = PopStack(); // go level back
2113  }
2114 
2115  fValue.Clear();
2116 
2117  if (!stack) {
2118  Error("WorkWithElement", "Lost of stack");
2119  return;
2120  }
2121 
2122  TStreamerInfo *info = stack->fInfo;
2123  if (!stack->IsStreamerInfo()) {
2124  Error("WorkWithElement", "Problem in Inc/Dec level");
2125  return;
2126  }
2127 
2128  Int_t number = info ? info->GetElements()->IndexOf(elem) : -1;
2129 
2130  if (!elem) {
2131  Error("WorkWithElement", "streamer info returns elem = nullptr");
2132  return;
2133  }
2134 
2135  TClass *base_class = elem->IsBase() ? elem->GetClassPointer() : nullptr;
2136 
2137  stack = PushStack(0, stack->fNode);
2138  stack->fElem = elem;
2139  stack->fIsElemOwner = (number < 0);
2140 
2141  JsonStartElement(elem, base_class);
2142 
2143  if (base_class && IsReading())
2144  stack->fClVersion = base_class->GetClassVersion();
2145 
2146  if ((elem->GetType() == TStreamerInfo::kOffsetL + TStreamerInfo::kStreamLoop) && (elem->GetArrayDim() > 0)) {
2147  // array of array, start handling here
2148  stack->fIndx = std::make_unique<TArrayIndexProducer>(elem, -1, fArraySepar.Data());
2149  if (IsWriting())
2150  AppendOutput(stack->fIndx->GetBegin());
2151  }
2152 
2153  if (IsReading() && (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20)) {
2154  // reading of such array begins with reading of single Char_t value
2155  // it indicates if array should be read or not
2156  stack->PushIntValue(stack->IsJsonString() || (stack->IsJsonArray() > 0) ? 1 : 0);
2157  }
2158 }
2159 
2160 ////////////////////////////////////////////////////////////////////////////////
2161 /// Should be called in the beginning of custom class streamer.
2162 /// Informs buffer data about class which will be streamed now.
2163 ///
2164 /// ClassBegin(), ClassEnd() and ClassMember() should be used in
2165 /// custom class streamers to specify which kind of data are
2166 /// now streamed. Such information is used to correctly
2167 /// convert class data to JSON. Without that functions calls
2168 /// classes with custom streamers cannot be used with TBufferJSON
2169 
2170 void TBufferJSON::ClassBegin(const TClass *cl, Version_t)
2171 {
2172  WorkWithClass(nullptr, cl);
2173 }
2174 
2175 ////////////////////////////////////////////////////////////////////////////////
2176 /// Should be called at the end of custom streamer
2177 /// See TBufferJSON::ClassBegin for more details
2178 
2179 void TBufferJSON::ClassEnd(const TClass *)
2180 {
2181  DecrementLevel(0);
2182 }
2183 
2184 ////////////////////////////////////////////////////////////////////////////////
2185 /// Method indicates name and typename of class member,
2186 /// which should be now streamed in custom streamer
2187 /// Following combinations are supported:
2188 /// 1. name = "ClassName", typeName = 0 or typename==ClassName
2189 /// This is a case, when data of parent class "ClassName" should be streamed.
2190 /// For instance, if class directly inherited from TObject, custom
2191 /// streamer should include following code:
2192 /// ~~~{.cpp}
2193 /// b.ClassMember("TObject");
2194 /// TObject::Streamer(b);
2195 /// ~~~
2196 /// 2. Basic data type
2197 /// ~~~{.cpp}
2198 /// b.ClassMember("fInt","Int_t");
2199 /// b >> fInt;
2200 /// ~~~
2201 /// 3. Array of basic data types
2202 /// ~~~{.cpp}
2203 /// b.ClassMember("fArr","Int_t", 5);
2204 /// b.ReadFastArray(fArr, 5);
2205 /// ~~~
2206 /// 4. Object as data member
2207 /// ~~~{.cpp}
2208 /// b.ClassMember("fName","TString");
2209 /// fName.Streamer(b);
2210 /// ~~~
2211 /// 5. Pointer on object as data member
2212 /// ~~~{.cpp}
2213 /// b.ClassMember("fObj","TObject*");
2214 /// b.StreamObject(fObj);
2215 /// ~~~
2216 ///
2217 /// arrsize1 and arrsize2 arguments (when specified) indicate first and
2218 /// second dimension of array. Can be used for array of basic types.
2219 /// See ClassBegin() method for more details.
2220 
2221 void TBufferJSON::ClassMember(const char *name, const char *typeName, Int_t arrsize1, Int_t arrsize2)
2222 {
2223  if (!typeName)
2224  typeName = name;
2225 
2226  if (!name || (strlen(name) == 0)) {
2227  Error("ClassMember", "Invalid member name");
2228  return;
2229  }
2230 
2231  TString tname = typeName;
2232 
2233  Int_t typ_id = -1;
2234 
2235  if (strcmp(typeName, "raw:data") == 0)
2236  typ_id = TStreamerInfo::kMissing;
2237 
2238  if (typ_id < 0) {
2239  TDataType *dt = gROOT->GetType(typeName);
2240  if (dt && (dt->GetType() > 0) && (dt->GetType() < 20))
2241  typ_id = dt->GetType();
2242  }
2243 
2244  if (typ_id < 0)
2245  if (strcmp(name, typeName) == 0) {
2246  TClass *cl = TClass::GetClass(tname.Data());
2247  if (cl)
2248  typ_id = TStreamerInfo::kBase;
2249  }
2250 
2251  if (typ_id < 0) {
2252  Bool_t isptr = kFALSE;
2253  if (tname[tname.Length() - 1] == '*') {
2254  tname.Resize(tname.Length() - 1);
2255  isptr = kTRUE;
2256  }
2257  TClass *cl = TClass::GetClass(tname.Data());
2258  if (!cl) {
2259  Error("ClassMember", "Invalid class specifier %s", typeName);
2260  return;
2261  }
2262 
2263  if (cl->IsTObject())
2264  typ_id = isptr ? TStreamerInfo::kObjectp : TStreamerInfo::kObject;
2265  else
2266  typ_id = isptr ? TStreamerInfo::kAnyp : TStreamerInfo::kAny;
2267 
2268  if ((cl == TString::Class()) && !isptr)
2269  typ_id = TStreamerInfo::kTString;
2270  }
2271 
2272  TStreamerElement *elem = nullptr;
2273 
2274  if (typ_id == TStreamerInfo::kMissing) {
2275  elem = new TStreamerElement(name, "title", 0, typ_id, "raw:data");
2276  } else if (typ_id == TStreamerInfo::kBase) {
2277  TClass *cl = TClass::GetClass(tname.Data());
2278  if (cl) {
2279  TStreamerBase *b = new TStreamerBase(tname.Data(), "title", 0);
2280  b->SetBaseVersion(cl->GetClassVersion());
2281  elem = b;
2282  }
2283  } else if ((typ_id > 0) && (typ_id < 20)) {
2284  elem = new TStreamerBasicType(name, "title", 0, typ_id, typeName);
2285  } else if ((typ_id == TStreamerInfo::kObject) || (typ_id == TStreamerInfo::kTObject) ||
2286  (typ_id == TStreamerInfo::kTNamed)) {
2287  elem = new TStreamerObject(name, "title", 0, tname.Data());
2288  } else if (typ_id == TStreamerInfo::kObjectp) {
2289  elem = new TStreamerObjectPointer(name, "title", 0, tname.Data());
2290  } else if (typ_id == TStreamerInfo::kAny) {
2291  elem = new TStreamerObjectAny(name, "title", 0, tname.Data());
2292  } else if (typ_id == TStreamerInfo::kAnyp) {
2293  elem = new TStreamerObjectAnyPointer(name, "title", 0, tname.Data());
2294  } else if (typ_id == TStreamerInfo::kTString) {
2295  elem = new TStreamerString(name, "title", 0);
2296  }
2297 
2298  if (!elem) {
2299  Error("ClassMember", "Invalid combination name = %s type = %s", name, typeName);
2300  return;
2301  }
2302 
2303  if (arrsize1 > 0) {
2304  elem->SetArrayDim(arrsize2 > 0 ? 2 : 1);
2305  elem->SetMaxIndex(0, arrsize1);
2306  if (arrsize2 > 0)
2307  elem->SetMaxIndex(1, arrsize2);
2308  }
2309 
2310  // we indicate that there is no streamerinfo
2311  WorkWithElement(elem, -1);
2312 }
2313 
2314 ////////////////////////////////////////////////////////////////////////////////
2315 /// Function is converts TObject and TString structures to more compact representation
2316 
2317 void TBufferJSON::PerformPostProcessing(TJSONStackObj *stack, const TClass *obj_cl)
2318 {
2319  if (stack->fIsPostProcessed)
2320  return;
2321 
2322  const TStreamerElement *elem = stack->fElem;
2323 
2324  if (!elem && !obj_cl)
2325  return;
2326 
2327  stack->fIsPostProcessed = kTRUE;
2328 
2329  // when element was written as separate object, close only braces and exit
2330  if (stack->fIsObjStarted) {
2331  AppendOutput("", "}");
2332  return;
2333  }
2334 
2335  Bool_t isTObject(kFALSE), isTRef(kFALSE), isTString(kFALSE), isSTLstring(kFALSE), isOffsetPArray(kFALSE),
2336  isTArray(kFALSE);
2337 
2338  if (obj_cl) {
2339  if (obj_cl == TObject::Class())
2340  isTObject = kTRUE;
2341  else if (obj_cl == TRef::Class())
2342  isTRef = kTRUE;
2343  else
2344  return;
2345  } else {
2346  const char *typname = elem->IsBase() ? elem->GetName() : elem->GetTypeName();
2347  isTObject = (elem->GetType() == TStreamerInfo::kTObject) || (strcmp("TObject", typname) == 0);
2348  isTString = elem->GetType() == TStreamerInfo::kTString;
2349  isSTLstring = elem->GetType() == TStreamerInfo::kSTLstring;
2350  isOffsetPArray = (elem->GetType() > TStreamerInfo::kOffsetP) && (elem->GetType() < TStreamerInfo::kOffsetP + 20);
2351  isTArray = (strncmp("TArray", typname, 6) == 0);
2352  }
2353 
2354  if (isTString || isSTLstring) {
2355  // just remove all kind of string length information
2356 
2357  if (gDebug > 3)
2358  Info("PerformPostProcessing", "reformat string value = '%s'", fValue.Data());
2359 
2360  stack->fValues.clear();
2361  } else if (isOffsetPArray) {
2362  // basic array with [fN] comment
2363 
2364  if (stack->fValues.empty() && (fValue == "0")) {
2365  fValue = "[]";
2366  } else if ((stack->fValues.size() == 1) && (stack->fValues[0] == "1")) {
2367  stack->fValues.clear();
2368  } else {
2369  Error("PerformPostProcessing", "Wrong values for kOffsetP element %s", (elem ? elem->GetName() : "---"));
2370  stack->fValues.clear();
2371  fValue = "[]";
2372  }
2373  } else if (isTObject || isTRef) {
2374  // complex workaround for TObject/TRef streamer
2375  // would be nice if other solution can be found
2376  // Here is not supported TRef on TRef (double reference)
2377 
2378  Int_t cnt = stack->fValues.size();
2379  if (fValue.Length() > 0)
2380  cnt++;
2381 
2382  if (cnt < 2 || cnt > 3) {
2383  if (gDebug > 0)
2384  Error("PerformPostProcessing", "When storing TObject/TRef, strange number of items %d", cnt);
2385  AppendOutput(stack->NextMemberSeparator(), "\"dummy\"");
2386  AppendOutput(fSemicolon.Data());
2387  } else {
2388  AppendOutput(stack->NextMemberSeparator(), "\"fUniqueID\"");
2389  AppendOutput(fSemicolon.Data());
2390  AppendOutput(stack->fValues[0].c_str());
2391  AppendOutput(stack->NextMemberSeparator(), "\"fBits\"");
2392  AppendOutput(fSemicolon.Data());
2393  auto tbits = std::atol((stack->fValues.size() > 1) ? stack->fValues[1].c_str() : fValue.Data());
2394  AppendOutput(std::to_string(tbits & ~TObject::kNotDeleted & ~TObject::kIsOnHeap).c_str());
2395  if (cnt == 3) {
2396  AppendOutput(stack->NextMemberSeparator(), "\"fPID\"");
2397  AppendOutput(fSemicolon.Data());
2398  AppendOutput((stack->fValues.size() > 2) ? stack->fValues[2].c_str() : fValue.Data());
2399  }
2400 
2401  stack->fValues.clear();
2402  fValue.Clear();
2403  return;
2404  }
2405 
2406  } else if (isTArray) {
2407  // for TArray one deletes complete stack
2408  stack->fValues.clear();
2409  }
2410 
2411  if (elem && elem->IsBase() && (fValue.Length() == 0)) {
2412  // here base class data already completely stored
2413  return;
2414  }
2415 
2416  if (!stack->fValues.empty()) {
2417  // append element blob data just as abstract array, user is responsible to decode it
2418  AppendOutput("[");
2419  for (auto &blob: stack->fValues) {
2420  AppendOutput(blob.c_str());
2421  AppendOutput(fArraySepar.Data());
2422  }
2423  }
2424 
2425  if (fValue.Length() == 0) {
2426  AppendOutput("null");
2427  } else {
2428  AppendOutput(fValue.Data());
2429  fValue.Clear();
2430  }
2431 
2432  if (!stack->fValues.empty())
2433  AppendOutput("]");
2434 }
2435 
2436 ////////////////////////////////////////////////////////////////////////////////
2437 /// suppressed function of TBuffer
2438 
2439 TClass *TBufferJSON::ReadClass(const TClass *, UInt_t *)
2440 {
2441  return nullptr;
2442 }
2443 
2444 ////////////////////////////////////////////////////////////////////////////////
2445 /// suppressed function of TBuffer
2446 
2447 void TBufferJSON::WriteClass(const TClass *) {}
2448 
2449 ////////////////////////////////////////////////////////////////////////////////
2450 /// read version value from buffer
2451 
2452 Version_t TBufferJSON::ReadVersion(UInt_t *start, UInt_t *bcnt, const TClass *cl)
2453 {
2454  Version_t res = cl ? cl->GetClassVersion() : 0;
2455 
2456  if (start)
2457  *start = 0;
2458  if (bcnt)
2459  *bcnt = 0;
2460 
2461  if (!cl && Stack()->fClVersion) {
2462  res = Stack()->fClVersion;
2463  Stack()->fClVersion = 0;
2464  }
2465 
2466  if (gDebug > 3)
2467  Info("ReadVersion", "Result: %d Class: %s", res, (cl ? cl->GetName() : "---"));
2468 
2469  return res;
2470 }
2471 
2472 ////////////////////////////////////////////////////////////////////////////////
2473 /// Ignored in TBufferJSON
2474 
2475 UInt_t TBufferJSON::WriteVersion(const TClass * /*cl*/, Bool_t /* useBcnt */)
2476 {
2477  return 0;
2478 }
2479 
2480 ////////////////////////////////////////////////////////////////////////////////
2481 /// Read object from buffer. Only used from TBuffer
2482 
2483 void *TBufferJSON::ReadObjectAny(const TClass *expectedClass)
2484 {
2485  if (gDebug > 2)
2486  Info("ReadObjectAny", "From current JSON node");
2487  void *res = JsonReadObject(nullptr, expectedClass);
2488  return res;
2489 }
2490 
2491 ////////////////////////////////////////////////////////////////////////////////
2492 /// Skip any kind of object from buffer
2493 
2494 void TBufferJSON::SkipObjectAny() {}
2495 
2496 ////////////////////////////////////////////////////////////////////////////////
2497 /// Write object to buffer. Only used from TBuffer
2498 
2499 void TBufferJSON::WriteObjectClass(const void *actualObjStart, const TClass *actualClass, Bool_t cacheReuse)
2500 {
2501  if (gDebug > 3)
2502  Info("WriteObjectClass", "Class %s", (actualClass ? actualClass->GetName() : " null"));
2503 
2504  JsonWriteObject(actualObjStart, actualClass, cacheReuse);
2505 }
2506 
2507 ////////////////////////////////////////////////////////////////////////////////
2508 /// If value exists, push in the current stack for post-processing
2509 
2510 void TBufferJSON::JsonPushValue()
2511 {
2512  if (fValue.Length() > 0)
2513  Stack()->PushValue(fValue);
2514 }
2515 
2516 ////////////////////////////////////////////////////////////////////////////////
2517 /// Read array of Bool_t from buffer
2518 
2519 Int_t TBufferJSON::ReadArray(Bool_t *&b)
2520 {
2521  return JsonReadArray(b);
2522 }
2523 
2524 ////////////////////////////////////////////////////////////////////////////////
2525 /// Read array of Char_t from buffer
2526 
2527 Int_t TBufferJSON::ReadArray(Char_t *&c)
2528 {
2529  return JsonReadArray(c);
2530 }
2531 
2532 ////////////////////////////////////////////////////////////////////////////////
2533 /// Read array of UChar_t from buffer
2534 
2535 Int_t TBufferJSON::ReadArray(UChar_t *&c)
2536 {
2537  return JsonReadArray(c);
2538 }
2539 
2540 ////////////////////////////////////////////////////////////////////////////////
2541 /// Read array of Short_t from buffer
2542 
2543 Int_t TBufferJSON::ReadArray(Short_t *&h)
2544 {
2545  return JsonReadArray(h);
2546 }
2547 
2548 ////////////////////////////////////////////////////////////////////////////////
2549 /// Read array of UShort_t from buffer
2550 
2551 Int_t TBufferJSON::ReadArray(UShort_t *&h)
2552 {
2553  return JsonReadArray(h);
2554 }
2555 
2556 ////////////////////////////////////////////////////////////////////////////////
2557 /// Read array of Int_t from buffer
2558 
2559 Int_t TBufferJSON::ReadArray(Int_t *&i)
2560 {
2561  return JsonReadArray(i);
2562 }
2563 
2564 ////////////////////////////////////////////////////////////////////////////////
2565 /// Read array of UInt_t from buffer
2566 
2567 Int_t TBufferJSON::ReadArray(UInt_t *&i)
2568 {
2569  return JsonReadArray(i);
2570 }
2571 
2572 ////////////////////////////////////////////////////////////////////////////////
2573 /// Read array of Long_t from buffer
2574 
2575 Int_t TBufferJSON::ReadArray(Long_t *&l)
2576 {
2577  return JsonReadArray(l);
2578 }
2579 
2580 ////////////////////////////////////////////////////////////////////////////////
2581 /// Read array of ULong_t from buffer
2582 
2583 Int_t TBufferJSON::ReadArray(ULong_t *&l)
2584 {
2585  return JsonReadArray(l);
2586 }
2587 
2588 ////////////////////////////////////////////////////////////////////////////////
2589 /// Read array of Long64_t from buffer
2590 
2591 Int_t TBufferJSON::ReadArray(Long64_t *&l)
2592 {
2593  return JsonReadArray(l);
2594 }
2595 
2596 ////////////////////////////////////////////////////////////////////////////////
2597 /// Read array of ULong64_t from buffer
2598 
2599 Int_t TBufferJSON::ReadArray(ULong64_t *&l)
2600 {
2601  return JsonReadArray(l);
2602 }
2603 
2604 ////////////////////////////////////////////////////////////////////////////////
2605 /// Read array of Float_t from buffer
2606 
2607 Int_t TBufferJSON::ReadArray(Float_t *&f)
2608 {
2609  return JsonReadArray(f);
2610 }
2611 
2612 ////////////////////////////////////////////////////////////////////////////////
2613 /// Read array of Double_t from buffer
2614 
2615 Int_t TBufferJSON::ReadArray(Double_t *&d)
2616 {
2617  return JsonReadArray(d);
2618 }
2619 
2620 ////////////////////////////////////////////////////////////////////////////////
2621 /// Read static array from JSON - not used
2622 
2623 template <typename T>
2624 R__ALWAYS_INLINE Int_t TBufferJSON::JsonReadArray(T *value)
2625 {
2626  Info("ReadArray", "Not implemented");
2627  return value ? 1 : 0;
2628 }
2629 
2630 ////////////////////////////////////////////////////////////////////////////////
2631 /// Read array of Bool_t from buffer
2632 
2633 Int_t TBufferJSON::ReadStaticArray(Bool_t *b)
2634 {
2635  return JsonReadArray(b);
2636 }
2637 
2638 ////////////////////////////////////////////////////////////////////////////////
2639 /// Read array of Char_t from buffer
2640 
2641 Int_t TBufferJSON::ReadStaticArray(Char_t *c)
2642 {
2643  return JsonReadArray(c);
2644 }
2645 
2646 ////////////////////////////////////////////////////////////////////////////////
2647 /// Read array of UChar_t from buffer
2648 
2649 Int_t TBufferJSON::ReadStaticArray(UChar_t *c)
2650 {
2651  return JsonReadArray(c);
2652 }
2653 
2654 ////////////////////////////////////////////////////////////////////////////////
2655 /// Read array of Short_t from buffer
2656 
2657 Int_t TBufferJSON::ReadStaticArray(Short_t *h)
2658 {
2659  return JsonReadArray(h);
2660 }
2661 
2662 ////////////////////////////////////////////////////////////////////////////////
2663 /// Read array of UShort_t from buffer
2664 
2665 Int_t TBufferJSON::ReadStaticArray(UShort_t *h)
2666 {
2667  return JsonReadArray(h);
2668 }
2669 
2670 ////////////////////////////////////////////////////////////////////////////////
2671 /// Read array of Int_t from buffer
2672 
2673 Int_t TBufferJSON::ReadStaticArray(Int_t *i)
2674 {
2675  return JsonReadArray(i);
2676 }
2677 
2678 ////////////////////////////////////////////////////////////////////////////////
2679 /// Read array of UInt_t from buffer
2680 
2681 Int_t TBufferJSON::ReadStaticArray(UInt_t *i)
2682 {
2683  return JsonReadArray(i);
2684 }
2685 
2686 ////////////////////////////////////////////////////////////////////////////////
2687 /// Read array of Long_t from buffer
2688 
2689 Int_t TBufferJSON::ReadStaticArray(Long_t *l)
2690 {
2691  return JsonReadArray(l);
2692 }
2693 
2694 ////////////////////////////////////////////////////////////////////////////////
2695 /// Read array of ULong_t from buffer
2696 
2697 Int_t TBufferJSON::ReadStaticArray(ULong_t *l)
2698 {
2699  return JsonReadArray(l);
2700 }
2701 
2702 ////////////////////////////////////////////////////////////////////////////////
2703 /// Read array of Long64_t from buffer
2704 
2705 Int_t TBufferJSON::ReadStaticArray(Long64_t *l)
2706 {
2707  return JsonReadArray(l);
2708 }
2709 
2710 ////////////////////////////////////////////////////////////////////////////////
2711 /// Read array of ULong64_t from buffer
2712 
2713 Int_t TBufferJSON::ReadStaticArray(ULong64_t *l)
2714 {
2715  return JsonReadArray(l);
2716 }
2717 
2718 ////////////////////////////////////////////////////////////////////////////////
2719 /// Read array of Float_t from buffer
2720 
2721 Int_t TBufferJSON::ReadStaticArray(Float_t *f)
2722 {
2723  return JsonReadArray(f);
2724 }
2725 
2726 ////////////////////////////////////////////////////////////////////////////////
2727 /// Read array of Double_t from buffer
2728 
2729 Int_t TBufferJSON::ReadStaticArray(Double_t *d)
2730 {
2731  return JsonReadArray(d);
2732 }
2733 
2734 ////////////////////////////////////////////////////////////////////////////////
2735 /// Template method to read array from the JSON
2736 
2737 template <typename T>
2738 R__ALWAYS_INLINE void TBufferJSON::JsonReadFastArray(T *arr, Int_t arrsize, bool asstring)
2739 {
2740  if (!arr || (arrsize <= 0))
2741  return;
2742  nlohmann::json *json = Stack()->fNode;
2743  if (gDebug > 2)
2744  Info("ReadFastArray", "Reading array sz %d from JSON %s", arrsize, json->dump().substr(0, 30).c_str());
2745  auto indexes = Stack()->MakeReadIndexes();
2746  if (indexes) { /* at least two dims */
2747  TArrayI &indx = indexes->GetIndices();
2748  Int_t lastdim = indx.GetSize() - 1;
2749  if (indexes->TotalLength() != arrsize)
2750  Error("ReadFastArray", "Mismatch %d-dim array sizes %d %d", lastdim + 1, arrsize, (int)indexes->TotalLength());
2751  for (int cnt = 0; cnt < arrsize; ++cnt) {
2752  nlohmann::json *elem = &(json->at(indx[0]));
2753  for (int k = 1; k < lastdim; ++k)
2754  elem = &((*elem)[indx[k]]);
2755  arr[cnt] = asstring ? elem->get<std::string>()[indx[lastdim]] : (*elem)[indx[lastdim]].get<T>();
2756  indexes->NextSeparator();
2757  }
2758  } else if (asstring) {
2759  std::string str = json->get<std::string>();
2760  for (int cnt = 0; cnt < arrsize; ++cnt)
2761  arr[cnt] = (cnt < (int)str.length()) ? str[cnt] : 0;
2762  } else if (json->is_object() && (json->count("$arr") == 1)) {
2763  if (json->at("len").get<int>() != arrsize)
2764  Error("ReadFastArray", "Mismatch compressed array size %d %d", arrsize, json->at("len").get<int>());
2765 
2766  for (int cnt = 0; cnt < arrsize; ++cnt)
2767  arr[cnt] = 0;
2768 
2769  if (json->count("b") == 1) {
2770  auto base64 = json->at("b").get<std::string>();
2771 
2772  int offset = (json->count("o") == 1) ? json->at("o").get<int>() : 0;
2773 
2774  // TODO: provide TBase64::Decode with direct write into target buffer
2775  auto decode = TBase64::Decode(base64.c_str());
2776 
2777  if (arrsize * (long) sizeof(T) < (offset + decode.Length())) {
2778  Error("ReadFastArray", "Base64 data %ld larger than target array size %ld", (long) decode.Length() + offset, (long) (arrsize*sizeof(T)));
2779  } else if ((sizeof(T) > 1) && (decode.Length() % sizeof(T) != 0)) {
2780  Error("ReadFastArray", "Base64 data size %ld not matches with element size %ld", (long) decode.Length(), (long) sizeof(T));
2781  } else {
2782  memcpy((char *) arr + offset, decode.Data(), decode.Length());
2783  }
2784  return;
2785  }
2786 
2787  int p = 0, id = 0;
2788  std::string idname = "", pname, vname, nname;
2789  while (p < arrsize) {
2790  pname = std::string("p") + idname;
2791  if (json->count(pname) == 1)
2792  p = json->at(pname).get<int>();
2793  vname = std::string("v") + idname;
2794  if (json->count(vname) != 1)
2795  break;
2796  nlohmann::json &v = json->at(vname);
2797  if (v.is_array()) {
2798  for (unsigned sub = 0; sub < v.size(); ++sub)
2799  arr[p++] = v[sub].get<T>();
2800  } else {
2801  nname = std::string("n") + idname;
2802  unsigned ncopy = (json->count(nname) == 1) ? json->at(nname).get<unsigned>() : 1;
2803  for (unsigned sub = 0; sub < ncopy; ++sub)
2804  arr[p++] = v.get<T>();
2805  }
2806  idname = std::to_string(++id);
2807  }
2808  } else {
2809  if ((int)json->size() != arrsize)
2810  Error("ReadFastArray", "Mismatch array sizes %d %d", arrsize, (int)json->size());
2811  for (int cnt = 0; cnt < arrsize; ++cnt)
2812  arr[cnt] = json->at(cnt).get<T>();
2813  }
2814 }
2815 
2816 ////////////////////////////////////////////////////////////////////////////////
2817 /// read array of Bool_t from buffer
2818 
2819 void TBufferJSON::ReadFastArray(Bool_t *b, Int_t n)
2820 {
2821  JsonReadFastArray(b, n);
2822 }
2823 
2824 ////////////////////////////////////////////////////////////////////////////////
2825 /// read array of Char_t from buffer
2826 
2827 void TBufferJSON::ReadFastArray(Char_t *c, Int_t n)
2828 {
2829  JsonReadFastArray(c, n, true);
2830 }
2831 
2832 ////////////////////////////////////////////////////////////////////////////////
2833 /// read array of Char_t from buffer
2834 
2835 void TBufferJSON::ReadFastArrayString(Char_t *c, Int_t n)
2836 {
2837  JsonReadFastArray(c, n, true);
2838 }
2839 
2840 ////////////////////////////////////////////////////////////////////////////////
2841 /// read array of UChar_t from buffer
2842 
2843 void TBufferJSON::ReadFastArray(UChar_t *c, Int_t n)
2844 {
2845  JsonReadFastArray(c, n);
2846 }
2847 
2848 ////////////////////////////////////////////////////////////////////////////////
2849 /// read array of Short_t from buffer
2850 
2851 void TBufferJSON::ReadFastArray(Short_t *h, Int_t n)
2852 {
2853  JsonReadFastArray(h, n);
2854 }
2855 
2856 ////////////////////////////////////////////////////////////////////////////////
2857 /// read array of UShort_t from buffer
2858 
2859 void TBufferJSON::ReadFastArray(UShort_t *h, Int_t n)
2860 {
2861  JsonReadFastArray(h, n);
2862 }
2863 
2864 ////////////////////////////////////////////////////////////////////////////////
2865 /// read array of Int_t from buffer
2866 
2867 void TBufferJSON::ReadFastArray(Int_t *i, Int_t n)
2868 {
2869  JsonReadFastArray(i, n);
2870 }
2871 
2872 ////////////////////////////////////////////////////////////////////////////////
2873 /// read array of UInt_t from buffer
2874 
2875 void TBufferJSON::ReadFastArray(UInt_t *i, Int_t n)
2876 {
2877  JsonReadFastArray(i, n);
2878 }
2879 
2880 ////////////////////////////////////////////////////////////////////////////////
2881 /// read array of Long_t from buffer
2882 
2883 void TBufferJSON::ReadFastArray(Long_t *l, Int_t n)
2884 {
2885  JsonReadFastArray(l, n);
2886 }
2887 
2888 ////////////////////////////////////////////////////////////////////////////////
2889 /// read array of ULong_t from buffer
2890 
2891 void TBufferJSON::ReadFastArray(ULong_t *l, Int_t n)
2892 {
2893  JsonReadFastArray(l, n);
2894 }
2895 
2896 ////////////////////////////////////////////////////////////////////////////////
2897 /// read array of Long64_t from buffer
2898 
2899 void TBufferJSON::ReadFastArray(Long64_t *l, Int_t n)
2900 {
2901  JsonReadFastArray(l, n);
2902 }
2903 
2904 ////////////////////////////////////////////////////////////////////////////////
2905 /// read array of ULong64_t from buffer
2906 
2907 void TBufferJSON::ReadFastArray(ULong64_t *l, Int_t n)
2908 {
2909  JsonReadFastArray(l, n);
2910 }
2911 
2912 ////////////////////////////////////////////////////////////////////////////////
2913 /// read array of Float_t from buffer
2914 
2915 void TBufferJSON::ReadFastArray(Float_t *f, Int_t n)
2916 {
2917  JsonReadFastArray(f, n);
2918 }
2919 
2920 ////////////////////////////////////////////////////////////////////////////////
2921 /// read array of Double_t from buffer
2922 
2923 void TBufferJSON::ReadFastArray(Double_t *d, Int_t n)
2924 {
2925  JsonReadFastArray(d, n);
2926 }
2927 
2928 ////////////////////////////////////////////////////////////////////////////////
2929 /// Read an array of 'n' objects from the I/O buffer.
2930 /// Stores the objects read starting at the address 'start'.
2931 /// The objects in the array are assume to be of class 'cl'.
2932 /// Copied code from TBufferFile
2933 
2934 void TBufferJSON::ReadFastArray(void *start, const TClass *cl, Int_t n, TMemberStreamer * /* streamer */,
2935  const TClass * /* onFileClass */)
2936 {
2937  if (gDebug > 1)
2938  Info("ReadFastArray", "void* n:%d cl:%s", n, cl->GetName());
2939 
2940  // if (streamer) {
2941  // Info("ReadFastArray", "(void*) Calling streamer - not handled correctly");
2942  // streamer->SetOnFileClass(onFileClass);
2943  // (*streamer)(*this, start, 0);
2944  // return;
2945  // }
2946 
2947  int objectSize = cl->Size();
2948  char *obj = (char *)start;
2949 
2950  TJSONStackObj *stack = Stack();
2951  nlohmann::json *topnode = stack->fNode, *subnode = topnode;
2952  if (stack->fIndx)
2953  subnode = stack->fIndx->ExtractNode(topnode);
2954 
2955  TArrayIndexProducer indexes(stack->fElem, n, "");
2956 
2957  if (gDebug > 1)
2958  Info("ReadFastArray", "Indexes ndim:%d totallen:%d", indexes.NumDimensions(), indexes.TotalLength());
2959 
2960  for (Int_t j = 0; j < n; j++, obj += objectSize) {
2961 
2962  stack->fNode = indexes.ExtractNode(subnode);
2963 
2964  JsonReadObject(obj, cl);
2965  }
2966 
2967  // restore top node - show we use stack here?
2968  stack->fNode = topnode;
2969 }
2970 
2971 ////////////////////////////////////////////////////////////////////////////////
2972 /// redefined here to avoid warning message from gcc
2973 
2974 void TBufferJSON::ReadFastArray(void **start, const TClass *cl, Int_t n, Bool_t isPreAlloc,
2975  TMemberStreamer * /* streamer */, const TClass * /* onFileClass */)
2976 {
2977  if (gDebug > 1)
2978  Info("ReadFastArray", "void** n:%d cl:%s prealloc:%s", n, cl->GetName(), (isPreAlloc ? "true" : "false"));
2979 
2980  // if (streamer) {
2981  // Info("ReadFastArray", "(void**) Calling streamer - not handled correctly");
2982  // if (isPreAlloc) {
2983  // for (Int_t j = 0; j < n; j++) {
2984  // if (!start[j])
2985  // start[j] = cl->New();
2986  // }
2987  // }
2988  // streamer->SetOnFileClass(onFileClass);
2989  // (*streamer)(*this, (void *)start, 0);
2990  // return;
2991  // }
2992 
2993  TJSONStackObj *stack = Stack();
2994  nlohmann::json *topnode = stack->fNode, *subnode = topnode;
2995  if (stack->fIndx)
2996  subnode = stack->fIndx->ExtractNode(topnode);
2997 
2998  TArrayIndexProducer indexes(stack->fElem, n, "");
2999 
3000  for (Int_t j = 0; j < n; j++) {
3001 
3002  stack->fNode = indexes.ExtractNode(subnode);
3003 
3004  if (!isPreAlloc) {
3005  void *old = start[j];
3006  start[j] = JsonReadObject(nullptr, cl);
3007  if (old && old != start[j] && TStreamerInfo::CanDelete())
3008  (const_cast<TClass *>(cl))->Destructor(old, kFALSE); // call delete and destruct
3009  } else {
3010  if (!start[j])
3011  start[j] = (const_cast<TClass *>(cl))->New();
3012  JsonReadObject(start[j], cl);
3013  }
3014  }
3015 
3016  stack->fNode = topnode;
3017 }
3018 
3019 template <typename T>
3020 R__ALWAYS_INLINE void TBufferJSON::JsonWriteArrayCompress(const T *vname, Int_t arrsize, const char *typname)
3021 {
3022  bool is_base64 = Stack()->fBase64 || (fArrayCompact == kBase64);
3023 
3024  if (!is_base64 && ((fArrayCompact == 0) || (arrsize < 6))) {
3025  fValue.Append("[");
3026  for (Int_t indx = 0; indx < arrsize; indx++) {
3027  if (indx > 0)
3028  fValue.Append(fArraySepar.Data());
3029  JsonWriteBasic(vname[indx]);
3030  }
3031  fValue.Append("]");
3032  } else if (is_base64 && !arrsize) {
3033  fValue.Append("[]");
3034  } else {
3035  fValue.Append("{");
3036  fValue.Append(TString::Format("\"$arr\":\"%s\"%s\"len\":%d", typname, fArraySepar.Data(), arrsize));
3037  Int_t aindx(0), bindx(arrsize);
3038  while ((aindx < arrsize) && (vname[aindx] == 0))
3039  aindx++;
3040  while ((aindx < bindx) && (vname[bindx - 1] == 0))
3041  bindx--;
3042 
3043  if (is_base64) {
3044  // small initial offset makes no sense - JSON code is large then size gain
3045  if ((aindx * sizeof(T) < 5) && (aindx < bindx))
3046  aindx = 0;
3047 
3048  if ((aindx > 0) && (aindx < bindx))
3049  fValue.Append(TString::Format("%s\"o\":%ld", fArraySepar.Data(), (long) (aindx * (int) sizeof(T))));
3050 
3051  fValue.Append(fArraySepar);
3052  fValue.Append("\"b\":\"");
3053 
3054  if (aindx < bindx)
3055  fValue.Append(TBase64::Encode((const char *) (vname + aindx), (bindx - aindx) * sizeof(T)));
3056 
3057  fValue.Append("\"");
3058  } else if (aindx < bindx) {
3059  TString suffix("");
3060  Int_t p(aindx), suffixcnt(-1), lastp(0);
3061  while (p < bindx) {
3062  if (vname[p] == 0) {
3063  p++;
3064  continue;
3065  }
3066  Int_t p0(p++), pp(0), nsame(1);
3067  if (fArrayCompact != kSameSuppression) {
3068  pp = bindx;
3069  p = bindx + 1;
3070  nsame = 0;
3071  }
3072  for (; p <= bindx; ++p) {
3073  if ((p < bindx) && (vname[p] == vname[p - 1])) {
3074  nsame++;
3075  continue;
3076  }
3077  if (vname[p - 1] == 0) {
3078  if (nsame > 9) {
3079  nsame = 0;
3080  break;
3081  }
3082  } else if (nsame > 5) {
3083  if (pp) {
3084  p = pp;
3085  nsame = 0;
3086  } else
3087  pp = p;
3088  break;
3089  }
3090  pp = p;
3091  nsame = 1;
3092  }
3093  if (pp <= p0)
3094  continue;
3095  if (++suffixcnt > 0)
3096  suffix.Form("%d", suffixcnt);
3097  if (p0 != lastp)
3098  fValue.Append(TString::Format("%s\"p%s\":%d", fArraySepar.Data(), suffix.Data(), p0));
3099  lastp = pp; /* remember cursor, it may be the same */
3100  fValue.Append(TString::Format("%s\"v%s\":", fArraySepar.Data(), suffix.Data()));
3101  if ((nsame > 1) || (pp - p0 == 1)) {
3102  JsonWriteBasic(vname[p0]);
3103  if (nsame > 1)
3104  fValue.Append(TString::Format("%s\"n%s\":%d", fArraySepar.Data(), suffix.Data(), nsame));
3105  } else {
3106  fValue.Append("[");
3107  for (Int_t indx = p0; indx < pp; indx++) {
3108  if (indx > p0)
3109  fValue.Append(fArraySepar.Data());
3110  JsonWriteBasic(vname[indx]);
3111  }
3112  fValue.Append("]");
3113  }
3114  }
3115  }
3116  fValue.Append("}");
3117  }
3118 }
3119 
3120 ////////////////////////////////////////////////////////////////////////////////
3121 /// Write array of Bool_t to buffer
3122 
3123 void TBufferJSON::WriteArray(const Bool_t *b, Int_t n)
3124 {
3125  JsonPushValue();
3126  JsonWriteArrayCompress(b, n, "Bool");
3127 }
3128 
3129 ////////////////////////////////////////////////////////////////////////////////
3130 /// Write array of Char_t to buffer
3131 
3132 void TBufferJSON::WriteArray(const Char_t *c, Int_t n)
3133 {
3134  JsonPushValue();
3135  JsonWriteArrayCompress(c, n, "Int8");
3136 }
3137 
3138 ////////////////////////////////////////////////////////////////////////////////
3139 /// Write array of UChar_t to buffer
3140 
3141 void TBufferJSON::WriteArray(const UChar_t *c, Int_t n)
3142 {
3143  JsonPushValue();
3144  JsonWriteArrayCompress(c, n, "Uint8");
3145 }
3146 
3147 ////////////////////////////////////////////////////////////////////////////////
3148 /// Write array of Short_t to buffer
3149 
3150 void TBufferJSON::WriteArray(const Short_t *h, Int_t n)
3151 {
3152  JsonPushValue();
3153  JsonWriteArrayCompress(h, n, "Int16");
3154 }
3155 
3156 ////////////////////////////////////////////////////////////////////////////////
3157 /// Write array of UShort_t to buffer
3158 
3159 void TBufferJSON::WriteArray(const UShort_t *h, Int_t n)
3160 {
3161  JsonPushValue();
3162  JsonWriteArrayCompress(h, n, "Uint16");
3163 }
3164 
3165 ////////////////////////////////////////////////////////////////////////////////
3166 /// Write array of Int_ to buffer
3167 
3168 void TBufferJSON::WriteArray(const Int_t *i, Int_t n)
3169 {
3170  JsonPushValue();
3171  JsonWriteArrayCompress(i, n, "Int32");
3172 }
3173 
3174 ////////////////////////////////////////////////////////////////////////////////
3175 /// Write array of UInt_t to buffer
3176 
3177 void TBufferJSON::WriteArray(const UInt_t *i, Int_t n)
3178 {
3179  JsonPushValue();
3180  JsonWriteArrayCompress(i, n, "Uint32");
3181 }
3182 
3183 ////////////////////////////////////////////////////////////////////////////////
3184 /// Write array of Long_t to buffer
3185 
3186 void TBufferJSON::WriteArray(const Long_t *l, Int_t n)
3187 {
3188  JsonPushValue();
3189  JsonWriteArrayCompress(l, n, "Int64");
3190 }
3191 
3192 ////////////////////////////////////////////////////////////////////////////////
3193 /// Write array of ULong_t to buffer
3194 
3195 void TBufferJSON::WriteArray(const ULong_t *l, Int_t n)
3196 {
3197  JsonPushValue();
3198  JsonWriteArrayCompress(l, n, "Uint64");
3199 }
3200 
3201 ////////////////////////////////////////////////////////////////////////////////
3202 /// Write array of Long64_t to buffer
3203 
3204 void TBufferJSON::WriteArray(const Long64_t *l, Int_t n)
3205 {
3206  JsonPushValue();
3207  JsonWriteArrayCompress(l, n, "Int64");
3208 }
3209 
3210 ////////////////////////////////////////////////////////////////////////////////
3211 /// Write array of ULong64_t to buffer
3212 
3213 void TBufferJSON::WriteArray(const ULong64_t *l, Int_t n)
3214 {
3215  JsonPushValue();
3216  JsonWriteArrayCompress(l, n, "Uint64");
3217 }
3218 
3219 ////////////////////////////////////////////////////////////////////////////////
3220 /// Write array of Float_t to buffer
3221 
3222 void TBufferJSON::WriteArray(const Float_t *f, Int_t n)
3223 {
3224  JsonPushValue();
3225  JsonWriteArrayCompress(f, n, "Float32");
3226 }
3227 
3228 ////////////////////////////////////////////////////////////////////////////////
3229 /// Write array of Double_t to buffer
3230 
3231 void TBufferJSON::WriteArray(const Double_t *d, Int_t n)
3232 {
3233  JsonPushValue();
3234  JsonWriteArrayCompress(d, n, "Float64");
3235 }
3236 
3237 ////////////////////////////////////////////////////////////////////////////////
3238 /// Template method to write array of arbitrary dimensions
3239 /// Different methods can be used for store last array dimension -
3240 /// either JsonWriteArrayCompress<T>() or JsonWriteConstChar()
3241 
3242 template <typename T>
3243 R__ALWAYS_INLINE void TBufferJSON::JsonWriteFastArray(const T *arr, Int_t arrsize, const char *typname,
3244  void (TBufferJSON::*method)(const T *, Int_t, const char *))
3245 {
3246  JsonPushValue();
3247  if (arrsize <= 0) { /*fJsonrCnt++;*/
3248  fValue.Append("[]");
3249  return;
3250  }
3251 
3252  TStreamerElement *elem = Stack()->fElem;
3253  if (elem && (elem->GetArrayDim() > 1) && (elem->GetArrayLength() == arrsize)) {
3254  TArrayI indexes(elem->GetArrayDim() - 1);
3255  indexes.Reset(0);
3256  Int_t cnt = 0, shift = 0, len = elem->GetMaxIndex(indexes.GetSize());
3257  while (cnt >= 0) {
3258  if (indexes[cnt] >= elem->GetMaxIndex(cnt)) {
3259  fValue.Append("]");
3260  indexes[cnt--] = 0;
3261  if (cnt >= 0)
3262  indexes[cnt]++;
3263  continue;
3264  }
3265  fValue.Append(indexes[cnt] == 0 ? "[" : fArraySepar.Data());
3266  if (++cnt == indexes.GetSize()) {
3267  (*this.*method)((arr + shift), len, typname);
3268  indexes[--cnt]++;
3269  shift += len;
3270  }
3271  }
3272  } else {
3273  (*this.*method)(arr, arrsize, typname);
3274  }
3275 }
3276 
3277 ////////////////////////////////////////////////////////////////////////////////
3278 /// Write array of Bool_t to buffer
3279 
3280 void TBufferJSON::WriteFastArray(const Bool_t *b, Int_t n)
3281 {
3282  JsonWriteFastArray(b, n, "Bool", &TBufferJSON::JsonWriteArrayCompress<Bool_t>);
3283 }
3284 
3285 ////////////////////////////////////////////////////////////////////////////////
3286 /// Write array of Char_t to buffer
3287 
3288 void TBufferJSON::WriteFastArray(const Char_t *c, Int_t n)
3289 {
3290  JsonWriteFastArray(c, n, "Int8", &TBufferJSON::JsonWriteConstChar);
3291 }
3292 
3293 ////////////////////////////////////////////////////////////////////////////////
3294 /// Write array of Char_t to buffer
3295 
3296 void TBufferJSON::WriteFastArrayString(const Char_t *c, Int_t n)
3297 {
3298  JsonWriteFastArray(c, n, "Int8", &TBufferJSON::JsonWriteConstChar);
3299 }
3300 
3301 ////////////////////////////////////////////////////////////////////////////////
3302 /// Write array of UChar_t to buffer
3303 
3304 void TBufferJSON::WriteFastArray(const UChar_t *c, Int_t n)
3305 {
3306  JsonWriteFastArray(c, n, "Uint8", &TBufferJSON::JsonWriteArrayCompress<UChar_t>);
3307 }
3308 
3309 ////////////////////////////////////////////////////////////////////////////////
3310 /// Write array of Short_t to buffer
3311 
3312 void TBufferJSON::WriteFastArray(const Short_t *h, Int_t n)
3313 {
3314  JsonWriteFastArray(h, n, "Int16", &TBufferJSON::JsonWriteArrayCompress<Short_t>);
3315 }
3316 
3317 ////////////////////////////////////////////////////////////////////////////////
3318 /// Write array of UShort_t to buffer
3319 
3320 void TBufferJSON::WriteFastArray(const UShort_t *h, Int_t n)
3321 {
3322  JsonWriteFastArray(h, n, "Uint16", &TBufferJSON::JsonWriteArrayCompress<UShort_t>);
3323 }
3324 
3325 ////////////////////////////////////////////////////////////////////////////////
3326 /// Write array of Int_t to buffer
3327 
3328 void TBufferJSON::WriteFastArray(const Int_t *i, Int_t n)
3329 {
3330  JsonWriteFastArray(i, n, "Int32", &TBufferJSON::JsonWriteArrayCompress<Int_t>);
3331 }
3332 
3333 ////////////////////////////////////////////////////////////////////////////////
3334 /// Write array of UInt_t to buffer
3335 
3336 void TBufferJSON::WriteFastArray(const UInt_t *i, Int_t n)
3337 {
3338  JsonWriteFastArray(i, n, "Uint32", &TBufferJSON::JsonWriteArrayCompress<UInt_t>);
3339 }
3340 
3341 ////////////////////////////////////////////////////////////////////////////////
3342 /// Write array of Long_t to buffer
3343 
3344 void TBufferJSON::WriteFastArray(const Long_t *l, Int_t n)
3345 {
3346  JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long_t>);
3347 }
3348 
3349 ////////////////////////////////////////////////////////////////////////////////
3350 /// Write array of ULong_t to buffer
3351 
3352 void TBufferJSON::WriteFastArray(const ULong_t *l, Int_t n)
3353 {
3354  JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong_t>);
3355 }
3356 
3357 ////////////////////////////////////////////////////////////////////////////////
3358 /// Write array of Long64_t to buffer
3359 
3360 void TBufferJSON::WriteFastArray(const Long64_t *l, Int_t n)
3361 {
3362  JsonWriteFastArray(l, n, "Int64", &TBufferJSON::JsonWriteArrayCompress<Long64_t>);
3363 }
3364 
3365 ////////////////////////////////////////////////////////////////////////////////
3366 /// Write array of ULong64_t to buffer
3367 
3368 void TBufferJSON::WriteFastArray(const ULong64_t *l, Int_t n)
3369 {
3370  JsonWriteFastArray(l, n, "Uint64", &TBufferJSON::JsonWriteArrayCompress<ULong64_t>);
3371 }
3372 
3373 ////////////////////////////////////////////////////////////////////////////////
3374 /// Write array of Float_t to buffer
3375 
3376 void TBufferJSON::WriteFastArray(const Float_t *f, Int_t n)
3377 {
3378  JsonWriteFastArray(f, n, "Float32", &TBufferJSON::JsonWriteArrayCompress<Float_t>);
3379 }
3380 
3381 ////////////////////////////////////////////////////////////////////////////////
3382 /// Write array of Double_t to buffer
3383 
3384 void TBufferJSON::WriteFastArray(const Double_t *d, Int_t n)
3385 {
3386  JsonWriteFastArray(d, n, "Float64", &TBufferJSON::JsonWriteArrayCompress<Double_t>);
3387 }
3388 
3389 ////////////////////////////////////////////////////////////////////////////////
3390 /// Recall TBuffer function to avoid gcc warning message
3391 
3392 void TBufferJSON::WriteFastArray(void *start, const TClass *cl, Int_t n, TMemberStreamer * /* streamer */)
3393 {
3394  if (gDebug > 2)
3395  Info("WriteFastArray", "void *start cl:%s n:%d", cl ? cl->GetName() : "---", n);
3396 
3397  // if (streamer) {
3398  // JsonDisablePostprocessing();
3399  // (*streamer)(*this, start, 0);
3400  // return;
3401  // }
3402 
3403  if (n < 0) {
3404  // special handling of empty StreamLoop
3405  AppendOutput("null");
3406  JsonDisablePostprocessing();
3407  } else {
3408 
3409  char *obj = (char *)start;
3410  if (!n)
3411  n = 1;
3412  int size = cl->Size();
3413 
3414  TArrayIndexProducer indexes(Stack()->fElem, n, fArraySepar.Data());
3415 
3416  if (indexes.IsArray()) {
3417  JsonDisablePostprocessing();
3418  AppendOutput(indexes.GetBegin());
3419  }
3420 
3421  for (Int_t j = 0; j < n; j++, obj += size) {
3422 
3423  if (j > 0)
3424  AppendOutput(indexes.NextSeparator());
3425 
3426  JsonWriteObject(obj, cl, kFALSE);
3427 
3428  if (indexes.IsArray() && (fValue.Length() > 0)) {
3429  AppendOutput(fValue.Data());
3430  fValue.Clear();
3431  }
3432  }
3433 
3434  if (indexes.IsArray())
3435  AppendOutput(indexes.GetEnd());
3436  }
3437 
3438  if (Stack()->fIndx)
3439  AppendOutput(Stack()->fIndx->NextSeparator());
3440 }
3441 
3442 ////////////////////////////////////////////////////////////////////////////////
3443 /// Recall TBuffer function to avoid gcc warning message
3444 
3445 Int_t TBufferJSON::WriteFastArray(void **start, const TClass *cl, Int_t n, Bool_t isPreAlloc,
3446  TMemberStreamer * /* streamer */)
3447 {
3448  if (gDebug > 2)
3449  Info("WriteFastArray", "void **startp cl:%s n:%d", cl->GetName(), n);
3450 
3451  // if (streamer) {
3452  // JsonDisablePostprocessing();
3453  // (*streamer)(*this, (void *)start, 0);
3454  // return 0;
3455  // }
3456 
3457  if (n <= 0)
3458  return 0;
3459 
3460  Int_t res = 0;
3461 
3462  TArrayIndexProducer indexes(Stack()->fElem, n, fArraySepar.Data());
3463 
3464  if (indexes.IsArray()) {
3465  JsonDisablePostprocessing();
3466  AppendOutput(indexes.GetBegin());
3467  }
3468 
3469  for (Int_t j = 0; j < n; j++) {
3470 
3471  if (j > 0)
3472  AppendOutput(indexes.NextSeparator());
3473 
3474  if (!isPreAlloc) {
3475  res |= WriteObjectAny(start[j], cl);
3476  } else {
3477  if (!start[j])
3478  start[j] = (const_cast<TClass *>(cl))->New();
3479  // ((TClass*)cl)->Streamer(start[j],*this);
3480  JsonWriteObject(start[j], cl, kFALSE);
3481  }
3482 
3483  if (indexes.IsArray() && (fValue.Length() > 0)) {
3484  AppendOutput(fValue.Data());
3485  fValue.Clear();
3486  }
3487  }
3488 
3489  if (indexes.IsArray())
3490  AppendOutput(indexes.GetEnd());
3491 
3492  if (Stack()->fIndx)
3493  AppendOutput(Stack()->fIndx->NextSeparator());
3494 
3495  return res;
3496 }
3497 
3498 ////////////////////////////////////////////////////////////////////////////////
3499 /// stream object to/from buffer
3500 
3501 void TBufferJSON::StreamObject(void *obj, const TClass *cl, const TClass * /* onfileClass */)
3502 {
3503  if (gDebug > 3)
3504  Info("StreamObject", "Class: %s", (cl ? cl->GetName() : "none"));
3505 
3506  if (IsWriting())
3507  JsonWriteObject(obj, cl);
3508  else
3509  JsonReadObject(obj, cl);
3510 }
3511 
3512 ////////////////////////////////////////////////////////////////////////////////
3513 /// Template function to read basic value from JSON
3514 
3515 template <typename T>
3516 R__ALWAYS_INLINE void TBufferJSON::JsonReadBasic(T &value)
3517 {
3518  value = Stack()->GetStlNode()->get<T>();
3519 }
3520 
3521 ////////////////////////////////////////////////////////////////////////////////
3522 /// Reads Bool_t value from buffer
3523 
3524 void TBufferJSON::ReadBool(Bool_t &val)
3525 {
3526  JsonReadBasic(val);
3527 }
3528 
3529 ////////////////////////////////////////////////////////////////////////////////
3530 /// Reads Char_t value from buffer
3531 
3532 void TBufferJSON::ReadChar(Char_t &val)
3533 {
3534  if (!Stack()->fValues.empty())
3535  val = (Char_t)Stack()->PopIntValue();
3536  else
3537  val = Stack()->GetStlNode()->get<Char_t>();
3538 }
3539 
3540 ////////////////////////////////////////////////////////////////////////////////
3541 /// Reads UChar_t value from buffer
3542 
3543 void TBufferJSON::ReadUChar(UChar_t &val)
3544 {
3545  JsonReadBasic(val);
3546 }
3547 
3548 ////////////////////////////////////////////////////////////////////////////////
3549 /// Reads Short_t value from buffer
3550 
3551 void TBufferJSON::ReadShort(Short_t &val)
3552 {
3553  JsonReadBasic(val);
3554 }
3555 
3556 ////////////////////////////////////////////////////////////////////////////////
3557 /// Reads UShort_t value from buffer
3558 
3559 void TBufferJSON::ReadUShort(UShort_t &val)
3560 {
3561  JsonReadBasic(val);
3562 }
3563 
3564 ////////////////////////////////////////////////////////////////////////////////
3565 /// Reads Int_t value from buffer
3566 
3567 void TBufferJSON::ReadInt(Int_t &val)
3568 {
3569  if (!Stack()->fValues.empty())
3570  val = Stack()->PopIntValue();
3571  else
3572  JsonReadBasic(val);
3573 }
3574 
3575 ////////////////////////////////////////////////////////////////////////////////
3576 /// Reads UInt_t value from buffer
3577 
3578 void TBufferJSON::ReadUInt(UInt_t &val)
3579 {
3580  JsonReadBasic(val);
3581 }
3582 
3583 ////////////////////////////////////////////////////////////////////////////////
3584 /// Reads Long_t value from buffer
3585 
3586 void TBufferJSON::ReadLong(Long_t &val)
3587 {
3588  JsonReadBasic(val);
3589 }
3590 
3591 ////////////////////////////////////////////////////////////////////////////////
3592 /// Reads ULong_t value from buffer
3593 
3594 void TBufferJSON::ReadULong(ULong_t &val)
3595 {
3596  JsonReadBasic(val);
3597 }
3598 
3599 ////////////////////////////////////////////////////////////////////////////////
3600 /// Reads Long64_t value from buffer
3601 
3602 void TBufferJSON::ReadLong64(Long64_t &val)
3603 {
3604  JsonReadBasic(val);
3605 }
3606 
3607 ////////////////////////////////////////////////////////////////////////////////
3608 /// Reads ULong64_t value from buffer
3609 
3610 void TBufferJSON::ReadULong64(ULong64_t &val)
3611 {
3612  JsonReadBasic(val);
3613 }
3614 
3615 ////////////////////////////////////////////////////////////////////////////////
3616 /// Reads Float_t value from buffer
3617 
3618 void TBufferJSON::ReadFloat(Float_t &val)
3619 {
3620  nlohmann::json *json = Stack()->GetStlNode();
3621  if (json->is_null())
3622  val = std::numeric_limits<Float_t>::quiet_NaN();
3623  else
3624  val = json->get<Float_t>();
3625 }
3626 
3627 ////////////////////////////////////////////////////////////////////////////////
3628 /// Reads Double_t value from buffer
3629 
3630 void TBufferJSON::ReadDouble(Double_t &val)
3631 {
3632  nlohmann::json *json = Stack()->GetStlNode();
3633  if (json->is_null())
3634  val = std::numeric_limits<Double_t>::quiet_NaN();
3635  else
3636  val = json->get<Double_t>();
3637 }
3638 
3639 ////////////////////////////////////////////////////////////////////////////////
3640 /// Reads array of characters from buffer
3641 
3642 void TBufferJSON::ReadCharP(Char_t *)
3643 {
3644  Error("ReadCharP", "Not implemented");
3645 }
3646 
3647 ////////////////////////////////////////////////////////////////////////////////
3648 /// Reads a TString
3649 
3650 void TBufferJSON::ReadTString(TString &val)
3651 {
3652  std::string str;
3653  JsonReadBasic(str);
3654  val = str.c_str();
3655 }
3656 
3657 ////////////////////////////////////////////////////////////////////////////////
3658 /// Reads a std::string
3659 
3660 void TBufferJSON::ReadStdString(std::string *val)
3661 {
3662  JsonReadBasic(*val);
3663 }
3664 
3665 ////////////////////////////////////////////////////////////////////////////////
3666 /// Reads a char* string
3667 
3668 void TBufferJSON::ReadCharStar(char *&s)
3669 {
3670  std::string str;
3671  JsonReadBasic(str);
3672 
3673  if (s) {
3674  delete[] s;
3675  s = nullptr;
3676  }
3677 
3678  std::size_t nch = str.length();
3679  if (nch > 0) {
3680  s = new char[nch + 1];
3681  memcpy(s, str.c_str(), nch);
3682  s[nch] = 0;
3683  }
3684 }
3685 
3686 ////////////////////////////////////////////////////////////////////////////////
3687 /// Writes Bool_t value to buffer
3688 
3689 void TBufferJSON::WriteBool(Bool_t b)
3690 {
3691  JsonPushValue();
3692  JsonWriteBasic(b);
3693 }
3694 
3695 ////////////////////////////////////////////////////////////////////////////////
3696 /// Writes Char_t value to buffer
3697 
3698 void TBufferJSON::WriteChar(Char_t c)
3699 {
3700  JsonPushValue();
3701  JsonWriteBasic(c);
3702 }
3703 
3704 ////////////////////////////////////////////////////////////////////////////////
3705 /// Writes UChar_t value to buffer
3706 
3707 void TBufferJSON::WriteUChar(UChar_t c)
3708 {
3709  JsonPushValue();
3710  JsonWriteBasic(c);
3711 }
3712 
3713 ////////////////////////////////////////////////////////////////////////////////
3714 /// Writes Short_t value to buffer
3715 
3716 void TBufferJSON::WriteShort(Short_t h)
3717 {
3718  JsonPushValue();
3719  JsonWriteBasic(h);
3720 }
3721 
3722 ////////////////////////////////////////////////////////////////////////////////
3723 /// Writes UShort_t value to buffer
3724 
3725 void TBufferJSON::WriteUShort(UShort_t h)
3726 {
3727  JsonPushValue();
3728  JsonWriteBasic(h);
3729 }
3730 
3731 ////////////////////////////////////////////////////////////////////////////////
3732 /// Writes Int_t value to buffer
3733 
3734 void TBufferJSON::WriteInt(Int_t i)
3735 {
3736  JsonPushValue();
3737  JsonWriteBasic(i);
3738 }
3739 
3740 ////////////////////////////////////////////////////////////////////////////////
3741 /// Writes UInt_t value to buffer
3742 
3743 void TBufferJSON::WriteUInt(UInt_t i)
3744 {
3745  JsonPushValue();
3746  JsonWriteBasic(i);
3747 }
3748 
3749 ////////////////////////////////////////////////////////////////////////////////
3750 /// Writes Long_t value to buffer
3751 
3752 void TBufferJSON::WriteLong(Long_t l)
3753 {
3754  JsonPushValue();
3755  JsonWriteBasic(l);
3756 }
3757 
3758 ////////////////////////////////////////////////////////////////////////////////
3759 /// Writes ULong_t value to buffer
3760 
3761 void TBufferJSON::WriteULong(ULong_t l)
3762 {
3763  JsonPushValue();
3764  JsonWriteBasic(l);
3765 }
3766 
3767 ////////////////////////////////////////////////////////////////////////////////
3768 /// Writes Long64_t value to buffer
3769 
3770 void TBufferJSON::WriteLong64(Long64_t l)
3771 {
3772  JsonPushValue();
3773  JsonWriteBasic(l);
3774 }
3775 
3776 ////////////////////////////////////////////////////////////////////////////////
3777 /// Writes ULong64_t value to buffer
3778 
3779 void TBufferJSON::WriteULong64(ULong64_t l)
3780 {
3781  JsonPushValue();
3782  JsonWriteBasic(l);
3783 }
3784 
3785 ////////////////////////////////////////////////////////////////////////////////
3786 /// Writes Float_t value to buffer
3787 
3788 void TBufferJSON::WriteFloat(Float_t f)
3789 {
3790  JsonPushValue();
3791  JsonWriteBasic(f);
3792 }
3793 
3794 ////////////////////////////////////////////////////////////////////////////////
3795 /// Writes Double_t value to buffer
3796 
3797 void TBufferJSON::WriteDouble(Double_t d)
3798 {
3799  JsonPushValue();
3800  JsonWriteBasic(d);
3801 }
3802 
3803 ////////////////////////////////////////////////////////////////////////////////
3804 /// Writes array of characters to buffer
3805 
3806 void TBufferJSON::WriteCharP(const Char_t *c)
3807 {
3808  JsonPushValue();
3809 
3810  JsonWriteConstChar(c);
3811 }
3812 
3813 ////////////////////////////////////////////////////////////////////////////////
3814 /// Writes a TString
3815 
3816 void TBufferJSON::WriteTString(const TString &s)
3817 {
3818  JsonPushValue();
3819 
3820  JsonWriteConstChar(s.Data(), s.Length());
3821 }
3822 
3823 ////////////////////////////////////////////////////////////////////////////////
3824 /// Writes a std::string
3825 
3826 void TBufferJSON::WriteStdString(const std::string *s)
3827 {
3828  JsonPushValue();
3829 
3830  if (s)
3831  JsonWriteConstChar(s->c_str(), s->length());
3832  else
3833  JsonWriteConstChar("", 0);
3834 }
3835 
3836 ////////////////////////////////////////////////////////////////////////////////
3837 /// Writes a char*
3838 
3839 void TBufferJSON::WriteCharStar(char *s)
3840 {
3841  JsonPushValue();
3842 
3843  JsonWriteConstChar(s);
3844 }
3845 
3846 ////////////////////////////////////////////////////////////////////////////////
3847 /// converts Char_t to string and add to json value buffer
3848 
3849 void TBufferJSON::JsonWriteBasic(Char_t value)
3850 {
3851  char buf[50];
3852  snprintf(buf, sizeof(buf), "%d", value);
3853  fValue.Append(buf);
3854 }
3855 
3856 ////////////////////////////////////////////////////////////////////////////////
3857 /// converts Short_t to string and add to json value buffer
3858 
3859 void TBufferJSON::JsonWriteBasic(Short_t value)
3860 {
3861  char buf[50];
3862  snprintf(buf, sizeof(buf), "%hd", value);
3863  fValue.Append(buf);
3864 }
3865 
3866 ////////////////////////////////////////////////////////////////////////////////
3867 /// converts Int_t to string and add to json value buffer
3868 
3869 void TBufferJSON::JsonWriteBasic(Int_t value)
3870 {
3871  char buf[50];
3872  snprintf(buf, sizeof(buf), "%d", value);
3873  fValue.Append(buf);
3874 }
3875 
3876 ////////////////////////////////////////////////////////////////////////////////
3877 /// converts Long_t to string and add to json value buffer
3878 
3879 void TBufferJSON::JsonWriteBasic(Long_t value)
3880 {
3881  char buf[50];
3882  snprintf(buf, sizeof(buf), "%ld", value);
3883  fValue.Append(buf);
3884 }
3885 
3886 ////////////////////////////////////////////////////////////////////////////////
3887 /// converts Long64_t to string and add to json value buffer
3888 
3889 void TBufferJSON::JsonWriteBasic(Long64_t value)
3890 {
3891  fValue.Append(std::to_string(value).c_str());
3892 }
3893 
3894 ////////////////////////////////////////////////////////////////////////////////
3895 /// converts Float_t to string and add to json value buffer
3896 
3897 void TBufferJSON::JsonWriteBasic(Float_t value)
3898 {
3899  if (std::isinf(value)) {
3900  fValue.Append((value < 0.) ? "-2e308" : "2e308"); // Number.MAX_VALUE is approx 1.79e308
3901  } else if (std::isnan(value)) {
3902  fValue.Append("null");
3903  } else {
3904  char buf[200];
3905  ConvertFloat(value, buf, sizeof(buf));
3906  fValue.Append(buf);
3907  }
3908 }
3909 
3910 ////////////////////////////////////////////////////////////////////////////////
3911 /// converts Double_t to string and add to json value buffer
3912 
3913 void TBufferJSON::JsonWriteBasic(Double_t value)
3914 {
3915  if (std::isinf(value)) {
3916  fValue.Append((value < 0.) ? "-2e308" : "2e308"); // Number.MAX_VALUE is approx 1.79e308
3917  } else if (std::isnan(value)) {
3918  fValue.Append("null");
3919  } else {
3920  char buf[200];
3921  ConvertDouble(value, buf, sizeof(buf));
3922  fValue.Append(buf);
3923  }
3924 }
3925 
3926 ////////////////////////////////////////////////////////////////////////////////
3927 /// converts Bool_t to string and add to json value buffer
3928 
3929 void TBufferJSON::JsonWriteBasic(Bool_t value)
3930 {
3931  fValue.Append(value ? "true" : "false");
3932 }
3933 
3934 ////////////////////////////////////////////////////////////////////////////////
3935 /// converts UChar_t to string and add to json value buffer
3936 
3937 void TBufferJSON::JsonWriteBasic(UChar_t value)
3938 {
3939  char buf[50];
3940  snprintf(buf, sizeof(buf), "%u", value);
3941  fValue.Append(buf);
3942 }
3943 
3944 ////////////////////////////////////////////////////////////////////////////////
3945 /// converts UShort_t to string and add to json value buffer
3946 
3947 void TBufferJSON::JsonWriteBasic(UShort_t value)
3948 {
3949  char buf[50];
3950  snprintf(buf, sizeof(buf), "%hu", value);
3951  fValue.Append(buf);
3952 }
3953 
3954 ////////////////////////////////////////////////////////////////////////////////
3955 /// converts UInt_t to string and add to json value buffer
3956 
3957 void TBufferJSON::JsonWriteBasic(UInt_t value)
3958 {
3959  char buf[50];
3960  snprintf(buf, sizeof(buf), "%u", value);
3961  fValue.Append(buf);
3962 }
3963 
3964 ////////////////////////////////////////////////////////////////////////////////
3965 /// converts ULong_t to string and add to json value buffer
3966 
3967 void TBufferJSON::JsonWriteBasic(ULong_t value)
3968 {
3969  char buf[50];
3970  snprintf(buf, sizeof(buf), "%lu", value);
3971  fValue.Append(buf);
3972 }
3973 
3974 ////////////////////////////////////////////////////////////////////////////////
3975 /// converts ULong64_t to string and add to json value buffer
3976 
3977 void TBufferJSON::JsonWriteBasic(ULong64_t value)
3978 {
3979  fValue.Append(std::to_string(value).c_str());
3980 }
3981 
3982 ////////////////////////////////////////////////////////////////////////////////
3983 /// writes string value, processing all kind of special characters
3984 
3985 void TBufferJSON::JsonWriteConstChar(const char *value, Int_t len, const char * /* typname */)
3986 {
3987  if (!value) {
3988 
3989  fValue.Append("\"\"");
3990 
3991  } else {
3992 
3993  fValue.Append("\"");
3994 
3995  if (len < 0)
3996  len = strlen(value);
3997 
3998  for (Int_t n = 0; n < len; n++) {
3999  char c = value[n];
4000  if (c == 0)
4001  break;
4002  switch (c) {
4003  case '\n': fValue.Append("\\n"); break;
4004  case '\t': fValue.Append("\\t"); break;
4005  case '\"': fValue.Append("\\\""); break;
4006  case '\\': fValue.Append("\\\\"); break;
4007  case '\b': fValue.Append("\\b"); break;
4008  case '\f': fValue.Append("\\f"); break;
4009  case '\r': fValue.Append("\\r"); break;
4010  case '/': fValue.Append("\\/"); break;
4011  default:
4012  if ((c > 31) && (c < 127))
4013  fValue.Append(c);
4014  else
4015  fValue.Append(TString::Format("\\u%04x", (unsigned)c));
4016  }
4017  }
4018 
4019  fValue.Append("\"");
4020  }
4021 }
4022 
4023 ////////////////////////////////////////////////////////////////////////////////
4024 /// Read data of base class.
4025 
4026 void TBufferJSON::ReadBaseClass(void *start, TStreamerBase *elem)
4027 {
4028  if (elem->GetClassPointer() == TObject::Class()) {
4029  JsonReadTObjectMembers((TObject *)start);
4030  } else {
4031  TBufferText::ReadBaseClass(start, elem);
4032  }
4033 }