labplot  2.8.2
About: LabPlot is an application for plotting and analysis of 2D and 3D functions and data. It is a complete rewrite of LabPlot1 and lacks in the first release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior.
  Fossies Dox: labplot-2.8.2.tar.gz  ("unofficial" and yet experimental doxygen-generated source code documentation)  

ROOTFilter.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 File : ROOTFilter.cpp
3 Project : LabPlot
4 Description : ROOT(CERN) I/O-filter
5 --------------------------------------------------------------------
6 Copyright : (C) 2018 by Christoph Roick (chrisito@gmx.de)
7 Copyright : (C) 2018 by Stefan Gerlach (stefan.gerlach@uni.kn)
8 ***************************************************************************/
9 
10 /***************************************************************************
11 * *
12 * This program is free software; you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation; either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 * This program is distributed in the hope that it will be useful, *
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
20 * GNU General Public License for more details. *
21 * *
22 * You should have received a copy of the GNU General Public License *
23 * along with this program; if not, write to the Free Software *
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, *
25 * Boston, MA 02110-1301 USA *
26 * *
27 ***************************************************************************/
28 
33 
34 #include <KLocalizedString>
35 
36 #include <QDebug>
37 #include <QFileInfo>
38 #include <QSet>
39 #include <QStack>
40 
41 #ifdef HAVE_ZIP
42 #include <lz4.h>
43 #include <zlib.h>
44 #endif
45 
46 #include <cmath>
47 #include <fstream>
48 #include <limits>
49 #include <map>
50 #include <string>
51 #include <vector>
52 
54 
55 ROOTFilter::~ROOTFilter() = default;
56 
57 void ROOTFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource,
58  AbstractFileFilter::ImportMode importMode) {
59  d->readDataFromFile(fileName, dataSource, importMode);
60 }
61 
62 void ROOTFilter::write(const QString& fileName, AbstractDataSource* dataSource) {
63  d->write(fileName, dataSource);
64 }
65 
66 void ROOTFilter::loadFilterSettings(const QString& filterName) {
67  Q_UNUSED(filterName);
68 }
69 
70 void ROOTFilter::saveFilterSettings(const QString& filterName) const {
71  Q_UNUSED(filterName);
72 }
73 
74 void ROOTFilter::setCurrentObject(const QString& object) {
75  d->currentObject = object;
76 }
77 
78 const QString ROOTFilter::currentObject() const {
79  return d->currentObject;
80 }
81 
82 ROOTFilter::Directory ROOTFilter::listHistograms(const QString& fileName) const {
83  return d->listHistograms(fileName);
84 }
85 
86 ROOTFilter::Directory ROOTFilter::listTrees(const QString& fileName) const {
87  return d->listTrees(fileName);
88 }
89 
90 QVector<QStringList> ROOTFilter::listLeaves(const QString& fileName, qint64 pos) const {
91  return d->listLeaves(fileName, pos);
92 }
93 
94 QVector<QStringList> ROOTFilter::previewCurrentObject(const QString& fileName, int first, int last) const {
95  return d->previewCurrentObject(fileName, first, last);
96 }
97 
98 int ROOTFilter::rowsInCurrentObject(const QString& fileName) const {
99  return d->rowsInCurrentObject(fileName);
100 }
101 
102 void ROOTFilter::setStartRow(const int s) {
103  d->startRow = s;
104 }
105 
106 int ROOTFilter::startRow() const {
107  return d->startRow;
108 }
109 
110 void ROOTFilter::setEndRow(const int e) {
111  d->endRow = e;
112 }
113 
114 int ROOTFilter::endRow() const {
115  return d->endRow;
116 }
117 
119  d->columns = columns;
120 }
121 
123  return d->columns;
124 }
125 
126 void ROOTFilter::save(QXmlStreamWriter* writer) const {
127  writer->writeStartElement("rootFilter");
128  writer->writeAttribute("object", d->currentObject);
129  writer->writeAttribute("startRow", QString::number(d->startRow));
130  writer->writeAttribute("endRow", QString::number(d->endRow));
131  for (const auto & c : d->columns) {
132  writer->writeStartElement("column");
133  for (const auto & s : c)
134  writer->writeTextElement("id", s);
135  writer->writeEndElement();
136  }
137  writer->writeEndElement();
138 }
139 
141  QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used");
142  QXmlStreamAttributes attribs = reader->attributes();
143 
144  // read attributes
145  d->currentObject = attribs.value("object").toString();
146  if (d->currentObject.isEmpty())
147  reader->raiseWarning(attributeWarning.arg("object"));
148 
149  QString str = attribs.value("startRow").toString();
150  if (str.isEmpty())
151  reader->raiseWarning(attributeWarning.arg("startRow"));
152  else
153  d->startRow = str.toInt();
154 
155  str = attribs.value("endRow").toString();
156  if (str.isEmpty())
157  reader->raiseWarning(attributeWarning.arg("endRow"));
158  else
159  d->endRow = str.toInt();
160 
161  d->columns.clear();
162  while (reader->readNextStartElement()) {
163  if (reader->name() == "column") {
164  QStringList c;
165  while (reader->readNextStartElement()) {
166  if (reader->name() == "id")
167  c << reader->readElementText();
168  else
169  reader->skipCurrentElement();
170  }
171  if (!c.empty())
172  d->columns << c;
173  } else
174  reader->skipCurrentElement();
175  }
176  if (d->columns.empty())
177  reader->raiseWarning(i18n("No column available"));
178 
179  return true;
180 }
181 
182 /**************** ROOTFilterPrivate implementation *******************/
183 
185 
187 {
188  QStringList typeobject = currentObject.split(':');
189  if (typeobject.size() < 2)
190  return FileType::Invalid;
191 
192  FileType type;
193  if (typeobject.first() == QStringLiteral("Hist"))
195  else if (typeobject.first() == QStringLiteral("Tree"))
197  else
198  return FileType::Invalid;
199 
200  typeobject.removeFirst();
201  QStringList path = typeobject.join(':').split('/');
202  ROOTFilter::Directory dir = type == FileType::Hist ? listHistograms(fileName) : listTrees(fileName);
203  const ROOTFilter::Directory* node = &dir;
204  while (path.size() > 1) {
205  bool next = false;
206  for (const auto& child : node->children) {
207  if (child.name == path.first()) {
208  node = &child;
209  path.pop_front();
210  next = true;
211  break;
212  }
213  }
214  if (!next)
215  return FileType::Invalid;
216  }
217  for (const auto& child : node->content) {
218  if (child.first == path.first()) {
219  pos = child.second;
220  break;
221  }
222  }
223  return type;
224 }
225 
226 void ROOTFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource,
227  AbstractFileFilter::ImportMode importMode) {
228  DEBUG("ROOTFilterPrivate::readDataFromFile()");
229 
230  long int pos = 0;
231  auto type = currentObjectPosition(fileName, pos);
232  if (pos == 0)
233  return;
234 
235  if (type == FileType::Hist) {
236  auto bins = readHistogram(pos);
237  const int nbins = static_cast<int>(bins.size());
238 
239  // skip underflow and overflow bins by default
240  int first = qMax(qAbs(startRow), 0);
241  int last = endRow < 0 ? nbins - 1 : qMax(first - 1, qMin(endRow, nbins - 1));
242 
243  QStringList headers;
244  for (const auto& l : columns) {
245  headers << l.last();
246  }
247 
248  std::vector<void*> dataContainer;
249  const int columnOffset = dataSource->prepareImport(dataContainer, importMode, last - first + 1, columns.size(),
251 
252  // read data
253  DEBUG(" reading " << first - last + 1 << " lines");
254 
255  int c = 0;
256  Spreadsheet* spreadsheet = dynamic_cast<Spreadsheet*>(dataSource);
257 
258  for (const auto& l : columns) {
259  QVector<double>& container = *static_cast<QVector<double>*>(dataContainer[c]);
260  if (l.first() == QStringLiteral("center")) {
261  if (spreadsheet)
262  spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::X);
263  for (int i = first; i <= last; ++i)
264  container[i - first] = (i > 0 && i < nbins - 1) ? 0.5 * (bins[i].lowedge + bins[i + 1].lowedge)
265  : i == 0 ? bins.front().lowedge // -infinity
266  : -bins.front().lowedge; // +infinity
267  } else if (l.first() == QStringLiteral("low")) {
268  if (spreadsheet)
269  spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::X);
270  for (int i = first; i <= last; ++i)
271  container[i - first] = bins[i].lowedge;
272  } else if (l.first() == QStringLiteral("content")) {
273  if (spreadsheet)
274  spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::Y);
275  for (int i = first; i <= last; ++i)
276  container[i - first] = bins[i].content;
277  } else if (l.first() == QStringLiteral("error")) {
278  if (spreadsheet)
279  spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::YError);
280  for (int i = first; i <= last; ++i)
281  container[i - first] = std::sqrt(bins[i].sumw2);
282  }
283  ++c;
284  }
285 
286  dataSource->finalizeImport(columnOffset, 0, columns.size() - 1, QString(), importMode);
287  } else if (type == FileType::Tree) {
288  const int nentries = static_cast<int>(currentROOTData->treeEntries(pos));
289 
290  int first = qMax(qAbs(startRow), 0);
291  int last = qMax(first - 1, qMin(endRow, nentries - 1));
292 
293  QStringList headers;
294  for (const auto& l : columns) {
295  QString lastelement = l.back();
296  bool isArray = false;
297  if (lastelement.at(0) == '[' && lastelement.at(lastelement.size() - 1) == ']') {
298  lastelement.midRef(1, lastelement.length() - 2).toUInt(&isArray);
299  }
300  if (!isArray || l.count() == 2)
301  headers << l.join(isArray ? QString() : QString(':'));
302  else
303  headers << l.first() + QChar(':') + l.at(1) + l.back();
304  }
305 
306  std::vector<void*> dataContainer;
307  const int columnOffset = dataSource->prepareImport(dataContainer, importMode, last - first + 1, columns.size(),
309 
310  int c = 0;
311  for (const auto& l : columns) {
312  unsigned int element = 0;
313  QString lastelement = l.back(), leaf = l.front();
314  bool isArray = false;
315  if (lastelement.at(0) == '[' && lastelement.at(lastelement.size() - 1) == ']') {
316  element = lastelement.midRef(1, lastelement.length() - 2).toUInt(&isArray);
317  if (!isArray)
318  element = 0;
319  if (l.count() > 2)
320  leaf = l.at(1);
321  } else if (l.count() > 1)
322  leaf = l.at(1);
323 
324  QVector<double>& container = *static_cast<QVector<double>*>(dataContainer[c++]);
325  auto data = readTree(pos, l.first(), leaf, (int)element, last);
326  for (int i = first; i <= last; ++i)
327  container[i - first] = data[i];
328  }
329 
330  dataSource->finalizeImport(columnOffset, 0, columns.size() - 1, QString(), importMode);
331  }
332 }
333 
334 void ROOTFilterPrivate::write(const QString& fileName, AbstractDataSource* dataSource) {
335  Q_UNUSED(fileName);
336  Q_UNUSED(dataSource);
337 }
338 
339 ROOTFilter::Directory ROOTFilterPrivate::listContent(const std::map<long int, ROOTData::Directory>& dataContent, std::string (ROOTData::*nameFunc)(long int))
340 {
342  QHash<const std::remove_reference<decltype(dataContent)>::type::value_type*, ROOTFilter::Directory*> filledDirs;
343  for (const auto& path : dataContent) {
344  if (!path.second.content.empty()) {
345  QStack<decltype(filledDirs)::key_type> addpath;
346  auto pos = &path;
347  ROOTFilter::Directory* currentdir = &dirs;
348  while (true) {
349  auto it = filledDirs.find(pos);
350  if (it != filledDirs.end()) {
351  currentdir = it.value();
352  break;
353  }
354 
355  auto jt = dataContent.find(pos->second.parent);
356  if (jt != dataContent.end()) {
357  addpath.push(pos);
358  pos = &(*jt);
359  } else
360  break;
361  }
362  while (!addpath.empty()) {
363  auto pos = addpath.pop();
365  dir.name = QString::fromStdString(pos->second.name);
366  currentdir->children << dir;
367  currentdir = &currentdir->children.last();
368  filledDirs[pos] = currentdir;
369  }
370  for (auto hist : path.second.content) {
371  auto name = ((*currentROOTData).*nameFunc)(hist);
372  if (!name.empty())
373  currentdir->content << qMakePair(QString::fromStdString(name), hist);
374  }
375  }
376  }
377 
378  return dirs;
379 }
380 
382  if (setFile(fileName))
383  return listContent(currentROOTData->listHistograms(), &ROOTData::histogramName);
384  else
385  return ROOTFilter::Directory{};
386 }
387 
389  if (setFile(fileName))
390  return listContent(currentROOTData->listTrees(), &ROOTData::treeName);
391  else
392  return ROOTFilter::Directory{};
393 }
394 
395 QVector<QStringList> ROOTFilterPrivate::listLeaves(const QString& fileName, quint64 pos) {
396  QVector<QStringList> leafList;
397 
398  if (setFile(fileName)) {
399  for (const auto& leaf : currentROOTData->listLeaves(pos)) {
400  leafList << QStringList(QString::fromStdString(leaf.branch));
401  if (leaf.branch != leaf.leaf)
402  leafList.last() << QString::fromStdString(leaf.leaf);
403  if (leaf.elements > 1)
404  leafList.last() << QString("[%1]").arg(leaf.elements);
405  }
406  }
407 
408  return leafList;
409 }
410 
411 QVector<QStringList> ROOTFilterPrivate::previewCurrentObject(const QString& fileName, int first, int last) {
412  DEBUG("ROOTFilterPrivate::previewCurrentObject()");
413 
414  long int pos = 0;
415  auto type = currentObjectPosition(fileName, pos);
416  if (pos == 0)
417  return QVector<QStringList>(1, QStringList());
418 
419  if (type == FileType::Hist) {
420  auto bins = readHistogram(pos);
421  const int nbins = static_cast<int>(bins.size());
422 
423  last = qMin(nbins - 1, last);
424 
425  QVector<QStringList> preview(qMax(last - first + 2, 1));
426  DEBUG(" reading " << preview.size() - 1 << " lines");
427 
428  // set headers
429  for (const auto& l : columns) {
430  preview.last() << l.last();
431  }
432 
433  // read data
434  for (const auto& l : columns) {
435  if (l.first() == QStringLiteral("center")) {
436  for (int i = first; i <= last; ++i)
437  preview[i - first] << QString::number(
438  (i > 0 && i < nbins - 1) ? 0.5 * (bins[i].lowedge + bins[i + 1].lowedge)
439  : i == 0 ? bins.front().lowedge // -infinity
440  : -bins.front().lowedge); // +infinity
441  } else if (l.first() == QStringLiteral("low")) {
442  for (int i = first; i <= last; ++i)
443  preview[i - first] << QString::number(bins[i].lowedge);
444  } else if (l.first() == QStringLiteral("content")) {
445  for (int i = first; i <= last; ++i)
446  preview[i - first] << QString::number(bins[i].content);
447  } else if (l.first() == QStringLiteral("error")) {
448  for (int i = first; i <= last; ++i)
449  preview[i - first] << QString::number(std::sqrt(bins[i].sumw2));
450  }
451  }
452 
453  return preview;
454  } else if (type == FileType::Tree) {
455  last = qMin(last, currentROOTData->treeEntries(pos) - 1);
456 
457  QVector<QStringList> preview(qMax(last - first + 2, 1));
458  DEBUG(" reading " << preview.size() - 1 << " lines");
459 
460  // read data leaf by leaf and set headers
461  for (const auto& l : columns) {
462  unsigned int element = 0;
463  QString lastelement = l.back(), leaf = l.front();
464  bool isArray = false;
465  if (lastelement.at(0) == '[' && lastelement.at(lastelement.size() - 1) == ']') {
466  element = lastelement.midRef(1, lastelement.length() - 2).toUInt(&isArray);
467  if (!isArray)
468  element = 0;
469  if (l.count() > 2)
470  leaf = l.at(1);
471  } else if (l.count() > 1)
472  leaf = l.at(1);
473 
474  auto data = readTree(pos, l.first(), leaf, (int)element, last);
475  for (int i = first; i <= last; ++i)
476  preview[i - first] << QString::number(data[i]);
477  if (!isArray || l.count() == 2)
478  preview.last() << l.join(isArray ? QString() : QString(':'));
479  else
480  preview.last() << l.first() + QChar(':') + l.at(1) + l.back();
481  }
482 
483  return preview;
484  } else
485  return QVector<QStringList>(1, QStringList());
486 }
487 
488 int ROOTFilterPrivate::rowsInCurrentObject(const QString& fileName) {
489  long int pos = 0;
490  auto type = currentObjectPosition(fileName, pos);
491  if (pos == 0)
492  return 0;
493 
494  switch (type) {
495  case FileType::Hist:
496  return currentROOTData->histogramBins(pos);
497  case FileType::Tree:
498  return currentROOTData->treeEntries(pos);
499  case FileType::Invalid:
500  default:
501  return 0;
502  }
503 }
504 
505 bool ROOTFilterPrivate::setFile(const QString& fileName) {
506  QFileInfo file(fileName);
507  if (!file.exists()) {
508  currentObject.clear();
509  columns.clear();
510  currentROOTData.reset();
511  return false;
512  }
513 
514  QDateTime modified = file.lastModified();
515  qint64 size = file.size();
516  if (!currentROOTData || fileName != currentFile.name
517  || modified != currentFile.modified
518  || size != currentFile.size) {
519  currentFile.name = fileName;
520  currentFile.modified = modified;
521  currentFile.size = size;
522  currentROOTData.reset(new ROOTData(fileName.toStdString()));
523  }
524  return true;
525 }
526 
527 std::vector<ROOTData::BinPars> ROOTFilterPrivate::readHistogram(quint64 pos) {
528  return currentROOTData->readHistogram(pos);
529 }
530 
531 std::vector<double> ROOTFilterPrivate::readTree(quint64 pos, const QString& branchName, const QString& leafName, int element, int last) {
532  return currentROOTData->listEntries<double>(pos, branchName.toStdString(), leafName.toStdString(), element, last + 1);
533 }
534 
535 
536 /******************** ROOTData implementation ************************/
537 
538 namespace ROOTDataHelpers {
539 
540 /// Read value from stream
541 template<class T>
542 T read(std::ifstream& is) {
543  union {
544  T val;
545  char buf[sizeof(T)];
546  } r;
547  for (size_t i = 0; i < sizeof(T); ++i)
548  is.get(r.buf[sizeof(T) - i - 1]);
549 
550  return r.val;
551 }
552 
553 /// Read value from buffer
554 template<class T>
555 T read(char*& s) {
556  union {
557  T val;
558  char buf[sizeof(T)];
559  } r;
560  for (size_t i = 0; i < sizeof(T); ++i)
561  r.buf[sizeof(T) - i - 1] = *(s++);
562 
563  return r.val;
564 }
565 
566 /// Read value from buffer and cast to U
567 template<class T, class U>
568 U readcast(char*& s) {
569  return static_cast<U>(read<T>(s));
570 }
571 
572 /// Get version of ROOT object, obtain number of bytes in object
573 short Version(char*& buffer, size_t& count) {
574  // root/io/io/src/TBufferFile.cxx -> ReadVersion
575  count = read<unsigned int>(buffer);
576  short version = (count & 0x40000000) ? read<short>(buffer) : read<short>(buffer -= 4);
577  count = (count & 0x40000000) ? (count & ~0x40000000) - 2 : 2;
578  return version;
579 }
580 
581 /// Get version of ROOT object
582 short Version(char*& buffer) {
583  size_t c;
584  return Version(buffer, c);
585 }
586 
587 /// Skip ROOT object
588 void Skip(char*& buffer, size_t n) {
589  for (size_t i = 0; i < n; ++i) {
590  size_t count;
591  Version(buffer, count);
592  buffer += count;
593  }
594 }
595 
596 /// Skip TObject header
597 void SkipObject(char*& buffer) {
598  Version(buffer);
599  buffer += 8;
600 }
601 
602 /// Get TString
603 std::string String(char*& buffer) {
604  // root/io/io/src/TBufferFile.cxx -> ReadTString
605  size_t s = *(buffer++);
606  if (s == 0)
607  return std::string();
608  else {
609  if (s == 0xFF)
610  s = read<int>(buffer);
611  buffer += s;
612  return std::string(buffer - s, buffer);
613  }
614 }
615 
616 /// Get the header of an object in TObjArray
617 std::string readObject(char*& buf, char* const buf0, std::map<size_t, std::string>& tags) {
618  // root/io/io/src/TBufferFile.cxx -> ReadObjectAny
619  std::string clname;
620  unsigned int tag = read<unsigned int>(buf);
621  if (tag & 0x40000000) {
622  tag = read<unsigned int>(buf);
623  if (tag == 0xFFFFFFFF) {
624  tags[buf - buf0 - 2] = clname = buf;
625  buf += clname.size() + 1;
626  } else {
627  clname = tags[tag & ~0x80000000];
628  }
629  }
630 
631  return clname;
632 }
633 
634 }
635 
636 using namespace ROOTDataHelpers;
637 
638 ROOTData::ROOTData(const std::string& filename) : filename(filename) {
639  // The file structure is described in root/io/io/src/TFile.cxx
640  std::ifstream is(filename, std::ifstream::binary);
641  std::string root(4, 0);
642  is.read(const_cast<char*>(root.data()), 4);
643  if (root != "root")
644  return;
645 
646  int fileVersion = read<int>(is);
647  long int pos = read<int>(is);
648  histdirs.emplace(pos, Directory{});
649  treedirs.emplace(pos, Directory{});
650  long int endpos = fileVersion < 1000000 ? read<int>(is) : read<long int>(is);
651 
652  is.seekg(33);
653  int compression = read<int>(is);
654  compression = compression > 0 ? compression : 0;
655 
656  while (is.good() && pos < endpos) {
657  is.seekg(pos);
658  int lcdata = read<int>(is);
659  if (lcdata == 0) {
660  break;
661  }
662  if (lcdata < 0) {
663  pos -= lcdata;
664  continue;
665  }
666  short version = read<short>(is);
667  size_t ldata = read<unsigned int>(is);
668  is.seekg(4, is.cur); // skip the date
669  size_t lkey = read<unsigned short int>(is);
670  short cycle = read<short>(is);
671  long int pseek;
672  if (version > 1000) {
673  is.seekg(8, is.cur);
674  pseek = read<long int>(is);
675  } else {
676  is.seekg(4, is.cur);
677  pseek = read<int>(is);
678  }
679  std::string cname(read<unsigned char>(is), 0);
680  is.read(&cname[0], cname.size());
681  std::string name(read<unsigned char>(is), 0);
682  is.read(&name[0], name.size());
683  std::string title(read<unsigned char>(is), 0);
684  is.read(&title[0], title.size());
685 
687  if (cname.size() == 4 && cname.substr(0, 3) == "TH1") {
688  type = histType(cname[3]);
689  } else if (cname == "TTree")
691  else if (cname.substr(0, 7) == "TNtuple")
693  else if (cname == "TBasket")
695  else if (cname == "TList" && name == "StreamerInfo")
697  else if (cname == "TDirectory") {
698  auto it = histdirs.find(pseek);
699  if (it == histdirs.end())
700  it = histdirs.begin();
701  histdirs.emplace(pos, Directory{name, it->first});
702  it = treedirs.find(pseek);
703  if (it == treedirs.end())
704  it = treedirs.begin();
705  treedirs.emplace(pos, Directory{name, it->first});
706  }
707 
708  if (type != ContentType::Invalid) {
709  if (type == ContentType::Basket)
710  is.seekg(19, std::ifstream::cur); // TODO read info instead?
711  KeyBuffer buffer;
712  buffer.type = ContentType::Invalid;
713  // see root/io/io/src/TKey.cxx for reference
714  int complib = 0;
715  if (compression) {
716  // Default: compression level
717  // ZLIB: 100 + compression level
718  // LZ4: 400 + compression level
719  // do not rely on this, but read the header
720  std::string lib(2, 0);
721  is.read(&lib[0], 2);
722  complib = lib == "ZL" ? 1 :
723  lib == "XZ" ? 2 :
724  lib == "CS" ? 3 :
725  lib == "L4" ? 4 : 0;
726  }
727  if (complib > 0) {
728 # ifdef HAVE_ZIP
729  // see root/core/zip/src/RZip.cxx -> R__unzip
730  const int method = is.get();
731  size_t chcdata = is.get();
732  chcdata |= (is.get() << 8);
733  chcdata |= (is.get() << 16);
734  size_t chdata = is.get();
735  chdata |= (is.get() << 8);
736  chdata |= (is.get() << 16);
737 
738  if (chcdata == lcdata - lkey - 9 && chdata == ldata) {
739  if (complib == 1 && method == Z_DEFLATED) {
740  buffer = KeyBuffer{type, name, title, cycle, lkey, KeyBuffer::CompressionType::zlib,
741  pos + lkey + 9, chcdata, chdata, 0};
742  } else if (complib == 4 && method == LZ4_versionNumber() / 10000) {
743  buffer = KeyBuffer{type, name, title, cycle, lkey, KeyBuffer::CompressionType::lz4,
744  pos + lkey + 9 + 8, chcdata - 8, chdata, 0};
745  }
746  }
747 # endif
748  } else {
749  buffer = KeyBuffer{type, name, title, cycle, lkey, KeyBuffer::CompressionType::none,
750  pos + lkey, ldata, ldata, 0};
751  }
752  switch (buffer.type) {
753  case ContentType::Basket:
754  basketkeys.emplace(pos, buffer);
755  break;
756  case ContentType::Tree:
757  case ContentType::NTuple: {
758  auto it = treedirs.find(pseek);
759  if (it == treedirs.end())
760  it = treedirs.begin();
761  bool keyreplaced = false;
762  for (auto & tpos : it->second.content) {
763  auto jt = treekeys.find(tpos);
764  if (jt != treekeys.end() && jt->second.name == buffer.name && jt->second.cycle < buffer.cycle) {
765  // override key with lower cylce number
766  tpos = pos;
767  treekeys.erase(jt);
768  keyreplaced = true;
769  break;
770  }
771  }
772  if (!keyreplaced)
773  it->second.content.push_back(pos);
774  treekeys.emplace(pos, buffer);
775  break;
776  } case ContentType::Streamer:
777  readStreamerInfo(buffer);
778  break;
780  auto it = histdirs.find(pseek);
781  if (it == histdirs.end())
782  it = histdirs.begin();
783  it->second.content.push_back(pos);
784  histkeys.emplace(pos, buffer);
785  break;
787  break;
788  }
789  }
790  pos += lcdata;
791  }
792 
793  // Create default object structures if no StreamerInfo was found.
794  // Obtained by running the following in ROOT with a file passed as an argument:
795  //
796  // _file0->GetStreamerInfoList()->Print()
797  //
798  // auto l = (TStreamerInfo*)_file0->GetStreamerInfoList()->At(ENTRYNUMBER);
799  // l->Print();
800  // for (int i = 0; i < l->GetNelement(); ++i) {
801  // auto e = l->GetElement(i);
802  // e->Print();
803  // cout << e->GetFullName() << " " << e->GetTypeName() << " " << e->GetSize() << endl;
804  // }
805 
806  static const StreamerInfo dummyobject{"Object", 0, std::string(), false, false};
807  if (!treekeys.empty()) {
808  if (!streamerInfo.count("TTree")) {
809  streamerInfo["TTree"] = {dummyobject, dummyobject, dummyobject, dummyobject,
810  StreamerInfo{"fEntries", 8, std::string(), false, false},
811  StreamerInfo{std::string(), 5 * 8 + 4 * 4, std::string(), false, false},
812  StreamerInfo{"fNClusterRange", 4, std::string(), true, false},
813  StreamerInfo{std::string(), 6 * 8, std::string(), false, false},
814  StreamerInfo{"fNClusterRangeEnd", 8, "fNClusterRange", false, true},
815  StreamerInfo{"fNClusterSize", 8, "fNClusterRange", false, true},
816  StreamerInfo{"fBranches", 0, std::string(), false, false}
817  };
818  }
819  if (!streamerInfo.count("TBranch")) {
820  streamerInfo["TBranch"] = {StreamerInfo{"TNamed", 0, std::string(), false, false}, dummyobject,
821  StreamerInfo{std::string(), 3 * 4, std::string(), false, false},
822  StreamerInfo{"fWriteBasket", 4, std::string(), false, false},
823  StreamerInfo{std::string(), 8 + 4, std::string(), false, false},
824  StreamerInfo{"fMaxBaskets", 4, std::string(), true, false},
825  StreamerInfo{std::string(), 4 + 4 * 8, std::string(), false, false},
826  StreamerInfo{"fBranches", 0, std::string(), false, false},
827  StreamerInfo{"fLeaves", 0, std::string(), false, false},
828  StreamerInfo{"fBaskets", 0, std::string(), false, false},
829  StreamerInfo{"fBasketBytes", 4, "fMaxBaskets", false, true},
830  StreamerInfo{"fBasketEntry", 8, "fMaxBaskets", false, true},
831  StreamerInfo{"fBasketSeek", 8, "fMaxBaskets", false, true}
832  };
833  }
834  }
835  if (!histkeys.empty()) {
836  if (!streamerInfo.count("TH1")) {
837  streamerInfo["TH1"] = {dummyobject, dummyobject, dummyobject, dummyobject,
838  StreamerInfo{"fNcells", 4, std::string(), false, false},
839  StreamerInfo{"fXaxis", 0, std::string(), false, false},
840  StreamerInfo{"fYaxis", 0, std::string(), false, false},
841  StreamerInfo{"fZaxis", 0, std::string(), false, false},
842  StreamerInfo{std::string(), 2 * 2 + 8 * 8, std::string(), false, false},
843  dummyobject,
844  StreamerInfo{"fSumw2", 0, std::string(), false, false}};
845  }
846  if (!streamerInfo.count("TAxis")) {
847  streamerInfo["TAxis"] = {dummyobject, dummyobject,
848  StreamerInfo{"fNbins", 4, std::string(), false, false},
849  StreamerInfo{"fXmin", 8, std::string(), false, false},
850  StreamerInfo{"fXmax", 8, std::string(), false, false},
851  StreamerInfo{"fXbins", 0, std::string(), false, false}};
852  }
853  }
854 
855  for (auto& tree : treekeys)
856  readNEntries(tree.second);
857  for (auto& hist : histkeys)
858  readNBins(hist.second);
859 }
860 
862  std::string buffer = data(kbuffer);
863  if (!buffer.empty()) {
864  char* buf = &buffer[0];
865  std::map<std::string, size_t> counts;
866  Version(buf); // TH1(D/F/I/S/C)
867  Version(buf); // TH1
868  advanceTo(buf, streamerInfo.find("TH1")->second, std::string(), "fNcells", counts);
869  kbuffer.nrows = read<int>(buf); // fNcells
870  }
871 }
872 
873 std::string ROOTData::histogramName(long int pos)
874 {
875  auto it = histkeys.find(pos);
876  if (it != histkeys.end())
877  return it->second.name + ';' + std::to_string(it->second.cycle);
878  return std::string();
879 }
880 
881 int ROOTData::histogramBins(long int pos)
882 {
883  auto it = histkeys.find(pos);
884  if (it != histkeys.end())
885  return it->second.nrows;
886  return 0;
887 }
888 
889 std::vector<ROOTData::BinPars> ROOTData::readHistogram(long int pos) {
890  auto it = histkeys.find(pos);
891  if (it == histkeys.end())
892  return std::vector<ROOTData::BinPars>();
893 
894  std::string buffer = data(it->second);
895  if (!buffer.empty()) {
896  char* buf = &buffer[0];
897  std::map<std::string, size_t> counts;
898  auto& streamerTH1 = streamerInfo.find("TH1")->second;
899  auto& streamerTAxis = streamerInfo.find("TAxis")->second;
900 
901  size_t count;
902  Version(buf); // TH1(D/F/I/S/C)
903  Version(buf, count); // TH1
904  char* const dbuf = buf + count;
905  advanceTo(buf, streamerTH1, std::string(), "fNcells", counts);
906 
907  std::vector<BinPars> r(read<int>(buf)); // fNcells
908  if (r.size() < 3)
909  return std::vector<BinPars>();
910 
911  r.front().lowedge = -std::numeric_limits<double>::infinity();
912 
913  advanceTo(buf, streamerTH1, "fNcells", "fXaxis", counts);
914  // x-Axis
915  Version(buf, count); // TAxis
916  char* const nbuf = buf + count;
917  advanceTo(buf, streamerTAxis, std::string(), "fNbins", counts);
918  const int nbins = read<int>(buf);
919  advanceTo(buf, streamerTAxis, "fNbins", "fXmin", counts);
920  const double xmin = read<double>(buf);
921  advanceTo(buf, streamerTAxis, "fXmin", "fXmax", counts);
922  const double xmax = read<double>(buf);
923  advanceTo(buf, streamerTAxis, "fXmax", "fXbins", counts);
924  const size_t nborders = read<int>(buf); // TArrayD
925  // root/core/cont/src/TArrayD.cxx -> Streamer
926  if (nborders == r.size() - 1) {
927  for (size_t i = 0; i < nborders; ++i) {
928  r[i + 1].lowedge = read<double>(buf);
929  }
930  } else {
931  //UNUSED: buf += sizeof(double) * nbins;
932  const double scale = (xmax - xmin) / static_cast<double>(nbins);
933  for (size_t i = 0; i < r.size() - 1; ++i) {
934  r[i + 1].lowedge = static_cast<double>(i) * scale + xmin;
935  }
936  }
937  buf = nbuf; // go beyond x-Axis
938 
939  advanceTo(buf, streamerTH1, "fXaxis", "fSumw2", counts);
940  if (static_cast<size_t>(read<int>(buf)) == r.size()) { // TArrayD
941  for (auto& b : r)
942  b.sumw2 = read<double>(buf); // always double
943  }
944  buf = dbuf; // skip to contents of TH1(D/F/I/S/C)
945 
946  if (static_cast<size_t>(read<int>(buf)) == r.size()) {
947  auto readf = readType<double>(it->second.type);
948  for (auto& b : r)
949  b.content = readf(buf);
950  }
951 
952  return r;
953  } else
954  return std::vector<BinPars>();
955 }
956 
958  std::string buffer = data(kbuffer);
959  if (!buffer.empty()) {
960  char* buf = &buffer[0];
961  std::map<std::string, size_t> counts;
962  if (kbuffer.type == ContentType::NTuple)
963  Version(buf); // TNtuple(D)
964  Version(buf); // TTree
965  advanceTo(buf, streamerInfo.find("TTree")->second, std::string(), "fEntries", counts);
966  kbuffer.nrows = read<long int>(buf); // fEntries
967  }
968 }
969 
970 std::string ROOTData::treeName(long int pos)
971 {
972  auto it = treekeys.find(pos);
973  if (it != treekeys.end())
974  return it->second.name;
975  return std::string();
976 }
977 
978 int ROOTData::treeEntries(long int pos)
979 {
980  auto it = treekeys.find(pos);
981  if (it != treekeys.end())
982  return it->second.nrows;
983  else
984  return 0;
985 }
986 
987 std::vector<ROOTData::LeafInfo> ROOTData::listLeaves(long int pos) const {
988  std::vector<LeafInfo> leaves;
989 
990  auto it = treekeys.find(pos);
991  if (it == treekeys.end())
992  return leaves;
993 
994  std::ifstream is(filename, std::ifstream::binary);
995  std::string datastring = data(it->second, is);
996  if (datastring.empty())
997  return leaves;
998 
999  char* buf = &datastring[0];
1000  char* const buf0 = buf - it->second.keylength;
1001  std::map<std::string, size_t> counts;
1002  auto& streamerTBranch = streamerInfo.find("TBranch")->second;
1003 
1004  if (it->second.type == ContentType::NTuple)
1005  Version(buf); // TNtuple(D)
1006  Version(buf); // TTree
1007  advanceTo(buf, streamerInfo.find("TTree")->second, std::string(), "fBranches", counts);
1008 
1009  // read the list of branches
1010  Version(buf); // TObjArray
1011  SkipObject(buf);
1012  String(buf);
1013  const size_t nbranches = read<int>(buf);
1014  const size_t lowb = read<int>(buf);
1015  std::map<size_t, std::string> tags;
1016  for (size_t i = 0; i < nbranches; ++i) {
1017  std::string clname = readObject(buf, buf0, tags);
1018  size_t count;
1019  Version(buf, count); // TBranch or TBranchElement
1020  char* const nbuf = buf + count;
1021  if (i >= lowb) {
1022  if (clname == "TBranchElement") {
1023  Version(buf); // TBranch
1024  }
1025  advanceTo(buf, streamerTBranch, std::string(), "TNamed", counts);
1026  Version(buf); // TNamed
1027  SkipObject(buf);
1028  const std::string branch = String(buf);
1029  String(buf);
1030  // TODO add reading of nested branches (fBranches)
1031  advanceTo(buf, streamerTBranch, "TNamed", "fLeaves", counts);
1032 
1033  // fLeaves
1034  Version(buf); // TObjArray
1035  SkipObject(buf);
1036  String(buf);
1037  const size_t nleaves = read<int>(buf);
1038  const size_t lowb = read<int>(buf);
1039  for (size_t i = 0; i < nleaves; ++i) {
1040  std::string clname = readObject(buf, buf0, tags);
1041  Version(buf, count); // TLeaf(D/F/B/S/I/L/C/O)
1042  char* nbuf = buf + count;
1043  if (i >= lowb && clname.size() == 6 && clname.compare(0, 5, "TLeaf") == 0) {
1044  Version(buf); // TLeaf
1045  Version(buf); // TNamed
1046  SkipObject(buf);
1047  const std::string leafname = String(buf);
1048  String(buf); // title
1049  size_t elements = read<int>(buf);
1050  int bytes = read<int>(buf);
1051  if ((static_cast<int>(leafType(clname.back())) & 0xF) != bytes)
1052  qDebug() << "ROOTData: type " << clname.back() << " does not match its size!";
1053  buf += 5;
1054  leaves.emplace_back(LeafInfo{branch, leafname, leafType(clname.back()), !read<char>(buf), elements});
1055  }
1056 
1057  buf = nbuf;
1058  }
1059  }
1060 
1061  buf = nbuf;
1062  }
1063  return leaves;
1064 }
1065 
1066 template<class T>
1067 std::vector<T> ROOTData::listEntries(long int pos, const std::string& branchname, const std::string& leafname, const size_t element, const size_t nentries) const {
1068  std::vector<T> entries;
1069 
1070  auto it = treekeys.find(pos);
1071  if (it == treekeys.end())
1072  return entries;
1073 
1074  std::ifstream is(filename, std::ifstream::binary);
1075  std::string datastring = data(it->second, is);
1076  if (datastring.empty())
1077  return entries;
1078 
1079  char* buf = &datastring[0];
1080  char* const buf0 = buf - it->second.keylength;
1081  std::map<std::string, size_t> counts;
1082  auto& streamerTTree = streamerInfo.find("TTree")->second;
1083  auto& streamerTBranch = streamerInfo.find("TBranch")->second;
1084 
1085  if (it->second.type == ContentType::NTuple)
1086  Version(buf); // TNtuple(D)
1087  Version(buf); // TTree
1088  advanceTo(buf, streamerTTree, std::string(), "fEntries", counts);
1089  entries.reserve(std::min(static_cast<size_t>(read<long int>(buf)), nentries)); // reserve space (maximum for number of entries)
1090  advanceTo(buf, streamerTTree, "fEntries", "fBranches", counts);
1091 
1092  // read the list of branches
1093  Version(buf); // TObjArray
1094  SkipObject(buf);
1095  String(buf);
1096  const size_t nbranches = read<int>(buf);
1097  const size_t lowb = read<int>(buf);
1098  std::map<size_t, std::string> tags;
1099  for (size_t i = 0; i < nbranches; ++i) {
1100  std::string clname = readObject(buf, buf0, tags);
1101  size_t count;
1102  Version(buf, count); // TBranch or TBranchElement
1103  char* const nbuf = buf + count;
1104  if (i >= lowb) {
1105  if (clname == "TBranchElement") {
1106  Version(buf);
1107  }
1108  Version(buf); // TNamed
1109  SkipObject(buf);
1110  const std::string currentbranch = String(buf);
1111  String(buf);
1112 
1113  advanceTo(buf, streamerTBranch, "TNamed", "fWriteBasket", counts);
1114  int fWriteBasket = read<int>(buf);
1115  // TODO add reading of nested branches (fBranches)
1116  advanceTo(buf, streamerTBranch, "fWriteBasket", "fLeaves", counts);
1117 
1118  // fLeaves
1119  Version(buf); // TObjArray
1120  SkipObject(buf);
1121  String(buf);
1122  const size_t nleaves = read<int>(buf);
1123  const size_t lowb = read<int>(buf);
1124  int leafoffset = 0, leafcount = 0, leafcontent = 0, leafsize = 0;
1125  bool leafsign = false;
1126  ContentType leaftype = ContentType::Invalid;
1127  for (size_t i = 0; i < nleaves; ++i) {
1128  std::string clname = readObject(buf, buf0, tags);
1129  Version(buf, count); // TLeaf(D/F/L/I/S/B/O/C/Element)
1130  char* nbuf = buf + count;
1131  if (currentbranch == branchname) {
1132  if (i >= lowb && clname.size() >= 5 && clname.compare(0, 5, "TLeaf") == 0) {
1133  Version(buf); // TLeaf
1134  Version(buf); // TNamed
1135  SkipObject(buf);
1136  const bool istheleaf = (clname.size() == 6 && leafname == String(buf));
1137  String(buf);
1138  const int len = read<int>(buf);
1139  const int size = read<int>(buf);
1140  if (istheleaf) {
1141  leafoffset = leafcount;
1142  leafsize = size;
1143  leaftype = leafType(clname.back());
1144  }
1145  leafcount += len * size;
1146  if (istheleaf) {
1147  leafcontent = leafcount - leafoffset;
1148  buf += 1;
1149  leafsign = !read<bool>(buf);
1150  }
1151  }
1152  }
1153 
1154  buf = nbuf;
1155  }
1156  if (leafcontent == 0) {
1157  buf = nbuf;
1158  continue;
1159  }
1160 
1161  if (static_cast<int>(element) * leafsize >= leafcontent) {
1162  qDebug() << "ROOTData: " << leafname.c_str() << " only contains " << leafcontent / leafsize << " elements.";
1163  break;
1164  }
1165 
1166  advanceTo(buf, streamerTBranch, "fLeaves", "fBaskets", counts);
1167  // fBaskets (probably empty)
1168  Version(buf, count); // TObjArray
1169  char* const basketsbuf = buf += count + 1; // TODO there is one byte to be skipped in fBaskets, why is that?
1170 
1171  advanceTo(buf, streamerTBranch, "fBaskets", "fBasketEntry", counts);
1172  for (int i = 0; i <= fWriteBasket; ++i) {
1173  if (static_cast<size_t>(read<long int>(buf)) > nentries) {
1174  fWriteBasket = i;
1175  break;
1176  }
1177  }
1178  // rewind to the end of fBaskets and look for the fBasketSeek array
1179  advanceTo(buf = basketsbuf, streamerTBranch, "fBaskets", "fBasketSeek", counts);
1180  auto readf = readType<T>(leaftype, leafsign);
1181  for (int i = 0; i < fWriteBasket; ++i) {
1182  long int pos = read<long int>(buf);
1183  auto it = basketkeys.find(pos);
1184  if (it != basketkeys.end()) {
1185  std::string basketbuffer = data(it->second);
1186  if (!basketbuffer.empty()) {
1187  char* bbuf = &basketbuffer[0];
1188  char* const bufend = bbuf + basketbuffer.size();
1189  while (bbuf + leafcount <= bufend && entries.size() < nentries) {
1190  bbuf += leafoffset + leafsize * element;
1191  entries.emplace_back(readf(bbuf));
1192  bbuf += leafcount - leafsize * (element + 1) - leafoffset;
1193  }
1194  }
1195  } else {
1196  qDebug() << "ROOTData: fBasketSeek(" << i << "): " << pos << " (not available)";
1197  }
1198  }
1199  }
1200 
1201  buf = nbuf;
1202  }
1203 
1204  return entries;
1205 }
1206 
1208  switch (type) {
1209  case 'D':
1210  return ContentType::Double;
1211  case 'F':
1212  return ContentType::Float;
1213  case 'I':
1214  return ContentType::Int;
1215  case 'S':
1216  return ContentType::Short;
1217  case 'C':
1218  return ContentType::Byte;
1219  default:
1220  return ContentType::Invalid;
1221  }
1222 }
1223 
1225  switch (type) {
1226  case 'D':
1227  return ContentType::Double;
1228  case 'F':
1229  return ContentType::Float;
1230  case 'L':
1231  return ContentType::Long;
1232  case 'I':
1233  return ContentType::Int;
1234  case 'S':
1235  return ContentType::Short;
1236  case 'B':
1237  return ContentType::Byte;
1238  case 'O':
1239  return ContentType::Bool;
1240  case 'C':
1241  return ContentType::CString;
1242  default:
1243  return ContentType::Invalid;
1244  }
1245 }
1246 
1247 template<class T>
1248 T (*ROOTData::readType(ROOTData::ContentType type, bool sign) const)(char*&) {
1249  switch (type) {
1250  case ContentType::Double:
1251  return readcast<double, T>;
1252  case ContentType::Float:
1253  return readcast<float, T>;
1254  case ContentType::Long:
1255  return sign ? readcast<long, T> : readcast<unsigned long, T>;
1256  case ContentType::Int:
1257  return sign ? readcast<int, T> : readcast<unsigned int, T>;
1258  case ContentType::Short:
1259  return sign ? readcast<short, T> : readcast<unsigned short, T>;
1260  case ContentType::Byte:
1261  return sign ? readcast<char, T> : readcast<unsigned char, T>;
1262  case ContentType::Bool:
1263  return readcast<bool, T>;
1264  case ContentType::CString:
1265  case ContentType::Tree:
1266  case ContentType::NTuple:
1267  case ContentType::Basket:
1268  case ContentType::Streamer:
1269  case ContentType::Invalid:
1270  break;
1271  }
1272  return readcast<char, T>;
1273 }
1274 
1275 std::string ROOTData::data(const ROOTData::KeyBuffer& buffer) const {
1276  std::ifstream is(filename, std::ifstream::binary);
1277  return data(buffer, is);
1278 }
1279 
1280 std::string ROOTData::data(const ROOTData::KeyBuffer& buffer, std::ifstream& is) const {
1281  std::string data(buffer.count, 0);
1282  is.seekg(buffer.start);
1284  is.read(&data[0], buffer.count);
1285  return data;
1286 #ifdef HAVE_ZIP
1287  } else if (buffer.compression == KeyBuffer::CompressionType::zlib) {
1288  std::string cdata(buffer.compressed_count, 0);
1289  is.read(&cdata[0], buffer.compressed_count);
1290  uLongf luncomp = (uLongf)buffer.count;
1291  if (uncompress((Bytef *)data.data(), &luncomp, (Bytef *)cdata.data(), (uLong)cdata.size()) == Z_OK && data.size() == luncomp)
1292  return data;
1293  } else {
1294  std::string cdata(buffer.compressed_count, 0);
1295  is.read(&cdata[0], buffer.compressed_count);
1296  if (LZ4_decompress_safe(cdata.data(), const_cast<char*>(data.data()), (int)buffer.compressed_count, (int)buffer.count) == static_cast<int>(buffer.count))
1297  return data;
1298 #endif
1299  }
1300 
1301  return std::string();
1302 }
1303 
1305  std::ifstream is(filename, std::ifstream::binary);
1306  std::string datastring = data(buffer, is);
1307  if (!datastring.empty()) {
1308  char* buf = &datastring[0];
1309  char* const buf0 = buf - buffer.keylength;
1310  Version(buf);
1311  SkipObject(buf); // TCollection
1312  String(buf);
1313  const int nobj = read<int>(buf);
1314  std::map<size_t, std::string> tags;
1315  for (int i = 0; i < nobj; ++i) {
1316  std::string clname = readObject(buf, buf0, tags);
1317  size_t count;
1318  Version(buf, count);
1319  char* const nbuf = buf + count;
1320  if (clname == "TStreamerInfo") {
1321  Version(buf);
1322  SkipObject(buf);
1323  std::vector<StreamerInfo>& sinfo = streamerInfo[String(buf)];
1324  String(buf);
1325  buf += 8; // skip check sum and version
1326 
1327  clname = readObject(buf, buf0, tags);
1328  Version(buf, count);
1329  if (clname != "TObjArray") {
1330  buf += count;
1331  continue;
1332  }
1333 
1334  SkipObject(buf); // TObjArray
1335  String(buf);
1336  const int nobj = read<int>(buf);
1337  const int lowb = read<int>(buf);
1338  for (int i = 0; i < nobj; ++i) {
1339  std::string clname = readObject(buf, buf0, tags);
1340  Version(buf, count);
1341  char* const nbuf = buf + count;
1342 
1343  const bool isbasicpointer = clname == "TStreamerBasicPointer";
1344  const bool ispointer = isbasicpointer || clname == "TStreamerObjectPointer";
1345  if (i >= lowb) {
1346  if (ispointer ||
1347  clname == "TStreamerBase" ||
1348  clname == "TStreamerBasicType" ||
1349  clname == "TStreamerObject" ||
1350  clname == "TStreamerObjectAny" ||
1351  clname == "TStreamerString" ||
1352  clname == "TStreamerSTL")
1353  {
1354  Version(buf); // TStreamerXXX
1355  Version(buf); // TStreamerElement
1356  SkipObject(buf);
1357  const std::string name = String(buf);
1358  const std::string title = String(buf);
1359  int type = read<int>(buf);
1360  size_t size = read<int>(buf);
1361 
1362  if (clname.compare(0, 15, "TStreamerObject") == 0)
1363  size = 0;
1364  std::string counter;
1365  bool iscounter = false;
1366  if (ispointer) {
1367  if (!title.empty() && title.front() == '[') {
1368  const size_t endref = title.find(']', 1);
1369  if (endref != title.npos) {
1370  counter = title.substr(1, endref - 1);
1371  }
1372  }
1373  if (isbasicpointer) {
1374  // see root/io/io/inc/TStreamerInfo.h -> TStreamerInfo::EReadWrite
1375  switch (type - 40) {
1376  case 1: // char
1377  case 11: // unsigned char
1378  size = 1;
1379  break;
1380  case 2: // short
1381  case 12: // unsigned short
1382  case 19: // float16
1383  size = 2;
1384  break;
1385  case 3: // int
1386  case 5: // float
1387  case 9: // double32
1388  case 13: // unsigned int
1389  size = 4;
1390  break;
1391  case 4: // long
1392  case 8: // double
1393  case 14: // unsigned long
1394  case 16: // long
1395  case 17: // unsigned long
1396  size = 8;
1397  break;
1398  }
1399  }
1400  } else if (clname == "TStreamerBasicType") {
1401  iscounter = type == 6; // see root/io/io/inc/TStreamerInfo.h -> TStreamerInfo::EReadWrite
1402  }
1403  sinfo.emplace_back(StreamerInfo{name, size, counter, iscounter, ispointer});
1404  }
1405  }
1406  buf = nbuf;
1407  }
1408  } else
1409  buf = nbuf;
1410  buf += 1; // trailing zero of TObjArray*
1411  }
1412  } else
1413  DEBUG("ROOTData: Inflation failed!")
1414 }
1415 
1416 bool ROOTData::advanceTo(char*& buf, const std::vector<ROOTData::StreamerInfo>& objects, const std::string& current, const std::string& target, std::map<std::string, size_t>& counts) {
1417  // The object structure can be retrieved from TFile::GetStreamerInfoList().
1418  // Every ROOT object contains a version number which may include the byte count
1419  // for the object. The latter is currently assumed to be present to skip unused
1420  // objects. No checks are performed. The corresponding ROOT code is quite nested
1421  // but the actual readout is straight forward.
1422  auto it = objects.begin();
1423  if (!current.empty()) {
1424  for (; it != objects.end(); ++it) {
1425  if (it->name == target) {
1426  return false; // target lies before current buffer position
1427  } else if (it->name == current) {
1428  ++it;
1429  break;
1430  }
1431  }
1432  }
1433 
1434  for (; it != objects.end(); ++it) {
1435  if (it->name == target)
1436  return true;
1437 
1438  if (it->size == 0)
1439  Skip(buf, 1);
1440  else if (it->iscounter)
1441  counts[it->name] = read<int>(buf);
1442  else if (it->ispointer) {
1443  if (it->counter.empty())
1444  buf += it->size + 1;
1445  else
1446  buf += it->size * counts[it->counter] + 1;
1447  } else
1448  buf += it->size;
1449  }
1450 
1451  return false;
1452 }
1453 
1454 // needs to be after ROOTDataHelpers namespace declaration
1455 
1456 QString ROOTFilter::fileInfoString(const QString& fileName) {
1457  DEBUG("ROOTFilter::fileInfoString()");
1458  QString info;
1459 
1460  // The file structure is described in root/io/io/src/TFile.cxx
1461  std::ifstream is(fileName.toStdString(), std::ifstream::binary);
1462  std::string root(4, 0);
1463  is.read(const_cast<char*>(root.data()), 4);
1464  if (root != "root") {
1465  DEBUG(" Not a ROOT file. root = " << root);
1466  return i18n("Not a ROOT file");
1467  }
1468 
1469  int version = read<int>(is);
1470 
1471  info += i18n("File format version: %1", QString::number(version));
1472  info += QLatin1String("<br>");
1473 
1474  is.seekg(20);
1475  int freeBytes = read<int>(is);
1476  int freeRecords = read<int>(is);
1477  int namedBytes = read<int>(is);
1478  char pointerBytes = read<char>(is);
1479  info += i18n("FREE data record size: %1 bytes", QString::number(freeBytes));
1480  info += QLatin1String("<br>");
1481  info += i18n("Number of free data records: %1", QString::number(freeRecords));
1482  info += QLatin1String("<br>");
1483  info += i18n("TNamed size: %1 bytes", QString::number(namedBytes));
1484  info += QLatin1String("<br>");
1485  info += i18n("Size of file pointers: %1 bytes", QString::number(pointerBytes));
1486  info += QLatin1String("<br>");
1487 
1488  int compression = read<int>(is);
1489  compression = compression > 0 ? compression : 0;
1490  info += i18n("Compression level and algorithm: %1", QString::number(compression));
1491  info += QLatin1String("<br>");
1492 
1493  is.seekg(41);
1494  int infoBytes = read<int>(is);
1495  info += i18n("Size of TStreamerInfo record: %1 bytes", QString::number(infoBytes));
1496  info += QLatin1String("<br>");
1497 
1498  Q_UNUSED(fileName);
1499 
1500  return info;
1501 }
Interface for the data sources.
virtual int prepareImport(std::vector< void * > &dataContainer, AbstractFileFilter::ImportMode, int actualRows, int actualCols, QStringList colNameList=QStringList(), QVector< AbstractColumn::ColumnMode >=QVector< AbstractColumn::ColumnMode >())=0
virtual void finalizeImport(int columnOffset=0, int startColumn=0, int endColumn=0, const QString &dateTimeFormat=QString(), AbstractFileFilter::ImportMode importMode=AbstractFileFilter::ImportMode::Replace)=0
Interface for the input/output file filters.
void setPlotDesignation(AbstractColumn::PlotDesignation) override
Set the column plot designation.
Definition: Column.cpp:403
Read TH1 histograms and TTrees from ROOT files without depending on ROOT libraries.
std::string histogramName(long int pos)
Get name of the histogram at a position in the file.
Definition: ROOTFilter.cpp:873
static ContentType leafType(const char type)
Get data type from leaf identifier.
ContentType
Identifiers for different data types.
std::map< long int, Directory > histdirs
std::string filename
std::vector< LeafInfo > listLeaves(long int pos) const
List information about data contained in leaves.
Definition: ROOTFilter.cpp:987
std::string treeName(long int pos)
Get name of the tree at a position in the file.
Definition: ROOTFilter.cpp:970
ROOTData(const std::string &filename)
Open ROOT file and save file positions of histograms and trees.
Definition: ROOTFilter.cpp:638
std::string data(const KeyBuffer &buffer) const
Get buffer from file content at histogram position.
std::map< std::string, std::vector< StreamerInfo > > streamerInfo
void readNBins(KeyBuffer &buffer)
Get the number of bins contained in a histogram.
Definition: ROOTFilter.cpp:861
std::vector< BinPars > readHistogram(long int pos)
Read histogram from file.
Definition: ROOTFilter.cpp:889
std::map< long int, KeyBuffer > histkeys
int histogramBins(long int pos)
Get number of bins in histogram.
Definition: ROOTFilter.cpp:881
std::vector< T > listEntries(long int pos, const std::string &branchname, const std::string &leafname, const size_t element=0, const size_t nentries=std::numeric_limits< size_t >::max()) const
Get entries of a leaf.
static bool advanceTo(char *&buf, const std::vector< StreamerInfo > &objects, const std::string &current, const std::string &target, std::map< std::string, size_t > &counts)
Advance to an object inside a class according to streamer information.
void readNEntries(KeyBuffer &buffer)
Get the number of entries contained in a tree.
Definition: ROOTFilter.cpp:957
T(*)(char *&) readType(ContentType type, bool sign=true) const
Get function to read a buffer of the specified type.
std::map< long int, Directory > treedirs
int treeEntries(long int pos)
Get number of entries in tree.
Definition: ROOTFilter.cpp:978
void readStreamerInfo(const KeyBuffer &buffer)
Load streamer information.
static ContentType histType(const char type)
Get data type from histogram identifier.
std::map< long int, KeyBuffer > basketkeys
std::map< long int, KeyBuffer > treekeys
void write(const QString &fileName, AbstractDataSource *)
Currently writing to ROOT files is not supported.
Definition: ROOTFilter.cpp:334
ROOTFilter::Directory listHistograms(const QString &fileName)
List names of histograms contained in ROOT file.
Definition: ROOTFilter.cpp:381
ROOTFilter::Directory listTrees(const QString &fileName)
List names of trees contained in ROOT file.
Definition: ROOTFilter.cpp:388
QVector< QStringList > columns
Columns to read.
std::vector< double > readTree(quint64 pos, const QString &branchName, const QString &leafName, int element, int last)
Calls listEntries from ROOTData.
Definition: ROOTFilter.cpp:531
int rowsInCurrentObject(const QString &fileName)
Get the number of bins in the current histogram.
Definition: ROOTFilter.cpp:488
void readDataFromFile(const QString &fileName, AbstractDataSource *dataSource, AbstractFileFilter::ImportMode importMode)
Read data from the currently selected histogram.
Definition: ROOTFilter.cpp:226
bool setFile(const QString &fileName)
Checks and updates the current ROOT file path.
Definition: ROOTFilter.cpp:505
QVector< QStringList > listLeaves(const QString &fileName, quint64 pos)
List names of leaves contained in ROOT tree.
Definition: ROOTFilter.cpp:395
std::vector< ROOTData::BinPars > readHistogram(quint64 pos)
Calls ReadHistogram from ROOTData.
Definition: ROOTFilter.cpp:527
int startRow
First row to read (can be -1, skips the underflow bin 0)
QVector< QStringList > previewCurrentObject(const QString &fileName, int first, int last)
Get preview data of the currently set histogram.
Definition: ROOTFilter.cpp:411
struct ROOTFilterPrivate::@4 currentFile
Information about currently set ROOT file.
int endRow
Last row to read (can be -1, skips the overflow bin)
ROOTFilter::Directory listContent(const std::map< long int, ROOTData::Directory > &dataContent, std::string(ROOTData::*nameFunc)(long int))
Parse the internal directory structure of the ROOT file and return a human readable version.
Definition: ROOTFilter.cpp:339
QString currentObject
Identifier of the current histogram.
std::unique_ptr< ROOTData > currentROOTData
ROOTData instance kept alive while currentFile does not change.
FileType currentObjectPosition(const QString &fileName, long int &pos)
Parse currentObject to find the corresponding position in the file.
Definition: ROOTFilter.cpp:186
~ROOTFilter() override
void setCurrentObject(const QString &)
Set the current histograms, which is one out of listHistograms.
Definition: ROOTFilter.cpp:74
QVector< QStringList > previewCurrentObject(const QString &fileName, int first, int last) const
Get preview data of the currently set object.
Definition: ROOTFilter.cpp:94
void save(QXmlStreamWriter *) const override
Save bin limitation settings.
Definition: ROOTFilter.cpp:126
void setColumns(const QVector< QStringList > &columns)
Set the columns of the object to be read.
Definition: ROOTFilter.cpp:118
void saveFilterSettings(const QString &) const override
Definition: ROOTFilter.cpp:70
bool load(XmlStreamReader *) override
Load bin limitation settings.
Definition: ROOTFilter.cpp:140
void write(const QString &fileName, AbstractDataSource *) override
Currently writing to ROOT files is not supported.
Definition: ROOTFilter.cpp:62
Directory listTrees(const QString &fileName) const
List names of trees contained in ROOT file.
Definition: ROOTFilter.cpp:86
int startRow() const
Get the index of the first row to be read.
Definition: ROOTFilter.cpp:106
void setStartRow(const int bin)
Set the last bin of the object to be read.
Definition: ROOTFilter.cpp:102
int rowsInCurrentObject(const QString &fileName) const
Get the number of rows in the current object.
Definition: ROOTFilter.cpp:98
const QString currentObject() const
Get the name of the currently set object.
Definition: ROOTFilter.cpp:78
void setEndRow(const int bin)
Set the last row of the object to be read.
Definition: ROOTFilter.cpp:110
void loadFilterSettings(const QString &) override
Definition: ROOTFilter.cpp:66
QVector< QStringList > columns() const
Get the columns to be read.
Definition: ROOTFilter.cpp:122
void readDataFromFile(const QString &fileName, AbstractDataSource *dataSource, AbstractFileFilter::ImportMode importMode) override
Read data from the currently selected histogram.
Definition: ROOTFilter.cpp:57
int endRow() const
Get the index of the last row to be read.
Definition: ROOTFilter.cpp:114
QVector< QStringList > listLeaves(const QString &fileName, qint64 pos) const
List names of leaves contained in ROOT tree.
Definition: ROOTFilter.cpp:90
std::unique_ptr< ROOTFilterPrivate > const d
Definition: ROOTFilter.h:130
Directory listHistograms(const QString &fileName) const
List names of histograms contained in ROOT file.
Definition: ROOTFilter.cpp:82
static QString fileInfoString(const QString &)
Aspect providing a spreadsheet table with column logic.
Definition: Spreadsheet.h:40
Column * column(int index) const
XML stream parser that supports errors as well as warnings. This class also adds line and column numb...
void raiseWarning(const QString &)
Definition: tree.hh:108
#define DEBUG(x)
Definition: macros.h:50
void Skip(char *&buffer, size_t n)
Skip ROOT object.
Definition: ROOTFilter.cpp:588
U readcast(char *&s)
Read value from buffer and cast to U.
Definition: ROOTFilter.cpp:568
T read(std::ifstream &is)
Read value from stream.
Definition: ROOTFilter.cpp:542
void SkipObject(char *&buffer)
Skip TObject header.
Definition: ROOTFilter.cpp:597
short Version(char *&buffer, size_t &count)
Get version of ROOT object, obtain number of bytes in object.
Definition: ROOTFilter.cpp:573
std::string readObject(char *&buf, char *const buf0, std::map< size_t, std::string > &tags)
Get the header of an object in TObjArray.
Definition: ROOTFilter.cpp:617
short Version(char *&buffer)
Get version of ROOT object.
Definition: ROOTFilter.cpp:582
std::string String(char *&buffer)
Get TString.
Definition: ROOTFilter.cpp:603
#define i18n(m)
Definition: nsl_common.h:38
Directory structure in a ROOT file where seek positions to the objects inside the file are stored.
enum ROOTData::KeyBuffer::CompressionType compression
Information about leaf contents.
Internal directory structure in a ROOT file.
Definition: ROOTFilter.h:66
QVector< QPair< QString, quint64 > > content
Definition: ROOTFilter.h:68
QVector< Directory > children
Definition: ROOTFilter.h:69