File indexing completed on 2024-12-22 03:35:49
0001 /* 0002 File : ROOTFilter.cpp 0003 Project : LabPlot 0004 Description : ROOT(CERN) I/O-filter 0005 -------------------------------------------------------------------- 0006 SPDX-FileCopyrightText: 2018 Christoph Roick <chrisito@gmx.de> 0007 SPDX-FileCopyrightText: 2018-2022 Stefan Gerlach <stefan.gerlach@uni.kn> 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "backend/datasources/filters/ROOTFilter.h" 0012 #include "backend/core/column/Column.h" 0013 #include "backend/datasources/filters/ROOTFilterPrivate.h" 0014 #include "backend/lib/XmlStreamReader.h" 0015 #include "backend/lib/macros.h" 0016 #include "backend/spreadsheet/Spreadsheet.h" 0017 0018 #include <KLocalizedString> 0019 0020 #include <QFileInfo> 0021 #include <QStack> 0022 0023 #include <cmath> 0024 #include <fstream> 0025 0026 #ifdef HAVE_ZIP 0027 #include <lz4.h> 0028 #include <zlib.h> 0029 #endif 0030 0031 ROOTFilter::ROOTFilter() 0032 : AbstractFileFilter(FileType::ROOT) 0033 , d(new ROOTFilterPrivate) { 0034 } 0035 0036 ROOTFilter::~ROOTFilter() = default; 0037 0038 void ROOTFilter::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) { 0039 d->readDataFromFile(fileName, dataSource, importMode); 0040 } 0041 0042 void ROOTFilter::write(const QString& fileName, AbstractDataSource* dataSource) { 0043 d->write(fileName, dataSource); 0044 } 0045 0046 void ROOTFilter::setCurrentObject(const QString& object) { 0047 d->currentObject = object; 0048 } 0049 0050 const QString ROOTFilter::currentObject() const { 0051 return d->currentObject; 0052 } 0053 0054 ROOTFilter::Directory ROOTFilter::listHistograms(const QString& fileName) const { 0055 return d->listHistograms(fileName); 0056 } 0057 0058 ROOTFilter::Directory ROOTFilter::listTrees(const QString& fileName) const { 0059 return d->listTrees(fileName); 0060 } 0061 0062 QVector<QStringList> ROOTFilter::listLeaves(const QString& fileName, qint64 pos) const { 0063 return d->listLeaves(fileName, pos); 0064 } 0065 0066 QVector<QStringList> ROOTFilter::previewCurrentObject(const QString& fileName, int first, int last) const { 0067 return d->previewCurrentObject(fileName, first, last); 0068 } 0069 0070 int ROOTFilter::rowsInCurrentObject(const QString& fileName) const { 0071 return d->rowsInCurrentObject(fileName); 0072 } 0073 0074 void ROOTFilter::setStartRow(const int s) { 0075 d->startRow = s; 0076 } 0077 0078 int ROOTFilter::startRow() const { 0079 return d->startRow; 0080 } 0081 0082 void ROOTFilter::setEndRow(const int e) { 0083 d->endRow = e; 0084 } 0085 0086 int ROOTFilter::endRow() const { 0087 return d->endRow; 0088 } 0089 0090 void ROOTFilter::setColumns(const QVector<QStringList>& columns) { 0091 d->columns = columns; 0092 } 0093 0094 QVector<QStringList> ROOTFilter::columns() const { 0095 return d->columns; 0096 } 0097 0098 void ROOTFilter::save(QXmlStreamWriter* writer) const { 0099 writer->writeStartElement(QStringLiteral("rootFilter")); 0100 writer->writeAttribute(QStringLiteral("object"), d->currentObject); 0101 writer->writeAttribute(QStringLiteral("startRow"), QString::number(d->startRow)); 0102 writer->writeAttribute(QStringLiteral("endRow"), QString::number(d->endRow)); 0103 for (const auto& c : d->columns) { 0104 writer->writeStartElement(QStringLiteral("column")); 0105 for (const auto& s : c) 0106 writer->writeTextElement(QStringLiteral("id"), s); 0107 writer->writeEndElement(); 0108 } 0109 writer->writeEndElement(); 0110 } 0111 0112 bool ROOTFilter::load(XmlStreamReader* reader) { 0113 QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used"); 0114 QXmlStreamAttributes attribs = reader->attributes(); 0115 0116 // read attributes 0117 d->currentObject = attribs.value(QStringLiteral("object")).toString(); 0118 if (d->currentObject.isEmpty()) 0119 reader->raiseWarning(attributeWarning.arg(QStringLiteral("object"))); 0120 0121 QString str = attribs.value(QStringLiteral("startRow")).toString(); 0122 if (str.isEmpty()) 0123 reader->raiseWarning(attributeWarning.arg(QStringLiteral("startRow"))); 0124 else 0125 d->startRow = str.toInt(); 0126 0127 str = attribs.value(QStringLiteral("endRow")).toString(); 0128 if (str.isEmpty()) 0129 reader->raiseWarning(attributeWarning.arg(QStringLiteral("endRow"))); 0130 else 0131 d->endRow = str.toInt(); 0132 0133 d->columns.clear(); 0134 while (reader->readNextStartElement()) { 0135 if (reader->name() == QLatin1String("column")) { 0136 QStringList c; 0137 while (reader->readNextStartElement()) { 0138 if (reader->name() == QLatin1String("id")) 0139 c << reader->readElementText(); 0140 else 0141 reader->skipCurrentElement(); 0142 } 0143 if (!c.empty()) 0144 d->columns << c; 0145 } else 0146 reader->skipCurrentElement(); 0147 } 0148 if (d->columns.empty()) 0149 reader->raiseWarning(i18n("No column available")); 0150 0151 return true; 0152 } 0153 0154 /**************** ROOTFilterPrivate implementation *******************/ 0155 0156 ROOTFilterPrivate::ROOTFilterPrivate() = default; 0157 0158 ROOTFilterPrivate::FileType ROOTFilterPrivate::currentObjectPosition(const QString& fileName, long int& pos) { 0159 QStringList typeobject = currentObject.split(QLatin1Char(':')); 0160 if (typeobject.size() < 2) 0161 return FileType::Invalid; 0162 0163 FileType type; 0164 if (typeobject.first() == QStringLiteral("Hist")) 0165 type = FileType::Hist; 0166 else if (typeobject.first() == QStringLiteral("Tree")) 0167 type = FileType::Tree; 0168 else 0169 return FileType::Invalid; 0170 0171 typeobject.removeFirst(); 0172 QStringList path = typeobject.join(QLatin1Char(':')).split(QLatin1Char('/')); 0173 ROOTFilter::Directory dir = type == FileType::Hist ? listHistograms(fileName) : listTrees(fileName); 0174 const ROOTFilter::Directory* node = &dir; 0175 while (path.size() > 1) { 0176 bool next = false; 0177 for (const auto& child : node->children) { 0178 if (child.name == path.first()) { 0179 node = &child; 0180 path.pop_front(); 0181 next = true; 0182 break; 0183 } 0184 } 0185 if (!next) 0186 return FileType::Invalid; 0187 } 0188 for (const auto& child : node->content) { 0189 if (child.first == path.first()) { 0190 pos = child.second; 0191 break; 0192 } 0193 } 0194 return type; 0195 } 0196 0197 void ROOTFilterPrivate::readDataFromFile(const QString& fileName, AbstractDataSource* dataSource, AbstractFileFilter::ImportMode importMode) { 0198 DEBUG(Q_FUNC_INFO << ", object: " << STDSTRING(currentObject)); 0199 0200 long int pos = 0; 0201 const auto type = currentObjectPosition(fileName, pos); 0202 if (pos == 0) // is not changed??? 0203 return; 0204 0205 DEBUG("start/end row = " << startRow << " " << endRow) 0206 0207 if (type == FileType::Hist) { 0208 auto bins = readHistogram(pos); 0209 const int nbins = static_cast<int>(bins.size()); 0210 0211 // skip underflow and overflow bins by default 0212 int first = std::max(std::abs(startRow), 0); 0213 int last = endRow < 0 ? nbins - 1 : std::max(first - 1, std::min(endRow, nbins - 1)); 0214 0215 DEBUG("first/last = " << first << " " << last) 0216 0217 QStringList headers; 0218 for (const auto& l : columns) { 0219 headers << l.last(); 0220 } 0221 0222 std::vector<void*> dataContainer; 0223 const int columnOffset = dataSource->prepareImport(dataContainer, 0224 importMode, 0225 last - first + 1, 0226 columns.size(), 0227 headers, 0228 QVector<AbstractColumn::ColumnMode>(columns.size(), AbstractColumn::ColumnMode::Double)); 0229 0230 // read data 0231 DEBUG(" reading " << last - first + 1 << " lines"); 0232 0233 int c = 0; 0234 auto* spreadsheet = dynamic_cast<Spreadsheet*>(dataSource); 0235 0236 for (const auto& l : columns) { 0237 QVector<double>& container = *static_cast<QVector<double>*>(dataContainer[c]); 0238 if (l.first() == QStringLiteral("center")) { 0239 if (spreadsheet) 0240 spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::X); 0241 for (int i = first; i <= last; ++i) 0242 container[i - first] = (i > 0 && i < nbins - 1) ? 0.5 * (bins[i].lowedge + bins[i + 1].lowedge) 0243 : i == 0 ? bins.front().lowedge // -infinity 0244 : -bins.front().lowedge; // +infinity 0245 } else if (l.first() == QStringLiteral("low")) { 0246 if (spreadsheet) 0247 spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::X); 0248 for (int i = first; i <= last; ++i) 0249 container[i - first] = bins[i].lowedge; 0250 } else if (l.first() == QStringLiteral("content")) { 0251 if (spreadsheet) 0252 spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::Y); 0253 for (int i = first; i <= last; ++i) 0254 container[i - first] = bins[i].content; 0255 } else if (l.first() == QStringLiteral("error")) { 0256 if (spreadsheet) 0257 spreadsheet->column(columnOffset + c)->setPlotDesignation(AbstractColumn::PlotDesignation::YError); 0258 for (int i = first; i <= last; ++i) 0259 container[i - first] = std::sqrt(bins[i].sumw2); 0260 } 0261 ++c; 0262 } 0263 0264 dataSource->finalizeImport(columnOffset, 0, columns.size() - 1, QString(), importMode); 0265 } else if (type == FileType::Tree) { 0266 const int nentries = static_cast<int>(currentROOTData->treeEntries(pos)); 0267 0268 int first = std::max(std::abs(startRow), 0); 0269 int last = std::max(first - 1, std::min(endRow, nentries - 1)); 0270 0271 DEBUG("first/last = " << first << " " << last << ", nentries = " << nentries) 0272 0273 QStringList headers; 0274 for (const auto& l : columns) { 0275 QString lastelement = l.back(); 0276 bool isArray = false; 0277 if (lastelement.at(0) == QLatin1Char('[') && lastelement.at(lastelement.size() - 1) == QLatin1Char(']')) { 0278 lastelement.mid(1, lastelement.length() - 2).toUInt(&isArray); 0279 } 0280 if (!isArray || l.count() == 2) 0281 headers << l.join(isArray ? QString() : QLatin1String(":")); 0282 else 0283 headers << l.first() + QLatin1Char(':') + l.at(1) + l.back(); 0284 } 0285 0286 std::vector<void*> dataContainer; 0287 const int columnOffset = dataSource->prepareImport(dataContainer, 0288 importMode, 0289 last - first + 1, 0290 columns.size(), 0291 headers, 0292 QVector<AbstractColumn::ColumnMode>(columns.size(), AbstractColumn::ColumnMode::Double)); 0293 0294 int c = 0; 0295 for (const auto& l : columns) { 0296 // DEBUG("column " << c) 0297 unsigned int element = 0; 0298 QString lastelement = l.back(), leaf = l.front(); 0299 bool isArray = false; 0300 if (lastelement.at(0) == QLatin1Char('[') && lastelement.at(lastelement.size() - 1) == QLatin1Char(']')) { 0301 element = lastelement.mid(1, lastelement.length() - 2).toUInt(&isArray); 0302 if (!isArray) 0303 element = 0; 0304 if (l.count() > 2) 0305 leaf = l.at(1); 0306 } else if (l.count() > 1) 0307 leaf = l.at(1); 0308 0309 QVector<double>& container = *static_cast<QVector<double>*>(dataContainer[c++]); 0310 auto data = readTree(pos, l.first(), leaf, (int)element, last); 0311 // QDEBUG("DATA = " << data) 0312 for (int i = first; i <= last; ++i) 0313 container[i - first] = data[i]; 0314 } 0315 0316 dataSource->finalizeImport(columnOffset, 0, columns.size() - 1, QString(), importMode); 0317 } 0318 } 0319 0320 void ROOTFilterPrivate::write(const QString& /*fileName*/, AbstractDataSource* /*dataSource*/) { 0321 } 0322 0323 ROOTFilter::Directory ROOTFilterPrivate::listContent(const std::map<long int, ROOTData::Directory>& dataContent, std::string (ROOTData::*nameFunc)(long int)) { 0324 ROOTFilter::Directory dirs; 0325 QHash<const std::remove_reference<decltype(dataContent)>::type::value_type*, ROOTFilter::Directory*> filledDirs; 0326 for (const auto& path : dataContent) { 0327 if (!path.second.content.empty()) { 0328 QStack<decltype(filledDirs)::key_type> addpath; 0329 auto pos = &path; 0330 ROOTFilter::Directory* currentdir = &dirs; 0331 while (true) { 0332 auto it = filledDirs.find(pos); 0333 if (it != filledDirs.end()) { 0334 currentdir = it.value(); 0335 break; 0336 } 0337 0338 auto jt = dataContent.find(pos->second.parent); 0339 if (jt != dataContent.end()) { 0340 addpath.push(pos); 0341 pos = &(*jt); 0342 } else 0343 break; 0344 } 0345 while (!addpath.empty()) { 0346 auto pos = addpath.pop(); 0347 ROOTFilter::Directory dir; 0348 dir.name = QString::fromStdString(pos->second.name); 0349 currentdir->children << dir; 0350 currentdir = ¤tdir->children.last(); 0351 filledDirs[pos] = currentdir; 0352 } 0353 for (auto hist : path.second.content) { 0354 auto name = ((*currentROOTData).*nameFunc)(hist); 0355 if (!name.empty()) 0356 currentdir->content << qMakePair(QString::fromStdString(name), hist); 0357 } 0358 } 0359 } 0360 0361 return dirs; 0362 } 0363 0364 ROOTFilter::Directory ROOTFilterPrivate::listHistograms(const QString& fileName) { 0365 if (setFile(fileName)) 0366 return listContent(currentROOTData->listHistograms(), &ROOTData::histogramName); 0367 else 0368 return ROOTFilter::Directory{}; 0369 } 0370 0371 ROOTFilter::Directory ROOTFilterPrivate::listTrees(const QString& fileName) { 0372 if (setFile(fileName)) 0373 return listContent(currentROOTData->listTrees(), &ROOTData::treeName); 0374 else 0375 return ROOTFilter::Directory{}; 0376 } 0377 0378 QVector<QStringList> ROOTFilterPrivate::listLeaves(const QString& fileName, quint64 pos) { 0379 QVector<QStringList> leafList; 0380 0381 if (setFile(fileName)) { 0382 for (const auto& leaf : currentROOTData->listLeaves(pos)) { 0383 leafList << QStringList(QString::fromStdString(leaf.branch)); 0384 if (leaf.branch != leaf.leaf) 0385 leafList.last() << QString::fromStdString(leaf.leaf); 0386 if (leaf.elements > 1) 0387 leafList.last() << QStringLiteral("[%1]").arg(leaf.elements); 0388 } 0389 } 0390 0391 return leafList; 0392 } 0393 0394 QVector<QStringList> ROOTFilterPrivate::previewCurrentObject(const QString& fileName, int first, int last) { 0395 DEBUG(Q_FUNC_INFO); 0396 0397 long int pos = 0; 0398 auto type = currentObjectPosition(fileName, pos); 0399 if (pos == 0) 0400 return {1, QStringList()}; 0401 0402 if (type == FileType::Hist) { 0403 auto bins = readHistogram(pos); 0404 const int nbins = static_cast<int>(bins.size()); 0405 0406 last = std::min(nbins - 1, last); 0407 0408 QVector<QStringList> preview(std::max(last - first + 2, 1)); 0409 DEBUG(" reading " << preview.size() - 1 << " lines"); 0410 0411 // set headers 0412 for (const auto& l : columns) { 0413 preview.last() << l.last(); 0414 } 0415 0416 // read data 0417 for (const auto& l : columns) { 0418 if (l.first() == QStringLiteral("center")) { 0419 for (int i = first; i <= last; ++i) 0420 preview[i - first] << QString::number((i > 0 && i < nbins - 1) ? 0.5 * (bins[i].lowedge + bins[i + 1].lowedge) 0421 : i == 0 ? bins.front().lowedge // -infinity 0422 : -bins.front().lowedge); // +infinity 0423 } else if (l.first() == QStringLiteral("low")) { 0424 for (int i = first; i <= last; ++i) 0425 preview[i - first] << QString::number(bins[i].lowedge); 0426 } else if (l.first() == QStringLiteral("content")) { 0427 for (int i = first; i <= last; ++i) 0428 preview[i - first] << QString::number(bins[i].content); 0429 } else if (l.first() == QStringLiteral("error")) { 0430 for (int i = first; i <= last; ++i) 0431 preview[i - first] << QString::number(std::sqrt(bins[i].sumw2)); 0432 } 0433 } 0434 0435 return preview; 0436 } else if (type == FileType::Tree) { 0437 last = std::min(last, currentROOTData->treeEntries(pos) - 1); 0438 0439 QVector<QStringList> preview(std::max(last - first + 2, 1)); 0440 DEBUG(" reading " << preview.size() - 1 << " lines"); 0441 0442 // read data leaf by leaf and set headers 0443 for (const auto& l : columns) { 0444 unsigned int element = 0; 0445 QString lastelement = l.back(), leaf = l.front(); 0446 bool isArray = false; 0447 if (lastelement.at(0) == QLatin1Char('[') && lastelement.at(lastelement.size() - 1) == QLatin1Char(']')) { 0448 element = lastelement.mid(1, lastelement.length() - 2).toUInt(&isArray); 0449 if (!isArray) 0450 element = 0; 0451 if (l.count() > 2) 0452 leaf = l.at(1); 0453 } else if (l.count() > 1) 0454 leaf = l.at(1); 0455 0456 auto data = readTree(pos, l.first(), leaf, (int)element, last); 0457 for (int i = first; i <= last; ++i) 0458 preview[i - first] << QString::number(data[i]); 0459 if (!isArray || l.count() == 2) 0460 preview.last() << l.join(isArray ? QString() : QLatin1String(":")); 0461 else 0462 preview.last() << l.first() + QLatin1Char(':') + l.at(1) + l.back(); 0463 } 0464 0465 return preview; 0466 } 0467 0468 return {1, QStringList()}; 0469 } 0470 0471 int ROOTFilterPrivate::rowsInCurrentObject(const QString& fileName) { 0472 long int pos = 0; 0473 auto type = currentObjectPosition(fileName, pos); 0474 if (pos == 0) 0475 return 0; 0476 0477 switch (type) { 0478 case FileType::Hist: 0479 return currentROOTData->histogramBins(pos); 0480 case FileType::Tree: 0481 return currentROOTData->treeEntries(pos); 0482 case FileType::Invalid: 0483 default: 0484 return 0; 0485 } 0486 } 0487 0488 bool ROOTFilterPrivate::setFile(const QString& fileName) { 0489 QFileInfo file(fileName); 0490 if (!file.exists()) { 0491 currentObject.clear(); 0492 columns.clear(); 0493 currentROOTData.reset(); 0494 return false; 0495 } 0496 0497 QDateTime modified = file.lastModified(); 0498 qint64 size = file.size(); 0499 if (!currentROOTData || fileName != currentFile.name || modified != currentFile.modified || size != currentFile.size) { 0500 currentFile.name = fileName; 0501 currentFile.modified = modified; 0502 currentFile.size = size; 0503 currentROOTData.reset(new ROOTData(fileName.toStdString())); 0504 } 0505 return true; 0506 } 0507 0508 std::vector<ROOTData::BinPars> ROOTFilterPrivate::readHistogram(quint64 pos) { 0509 return currentROOTData->readHistogram(pos); 0510 } 0511 0512 std::vector<double> ROOTFilterPrivate::readTree(quint64 pos, const QString& branchName, const QString& leafName, int element, int last) { 0513 // QDEBUG("branch/leaf name =" << branchName << " " << leafName << ", element/last =" << element << " " << last) 0514 return currentROOTData->listEntries<double>(pos, branchName.toStdString(), leafName.toStdString(), element, last + 1); 0515 } 0516 0517 /******************** ROOTData implementation ************************/ 0518 0519 namespace ROOTDataHelpers { 0520 0521 /// Read value from stream 0522 template<class T> 0523 T read(std::ifstream& is) { 0524 union { 0525 T val; 0526 char buf[sizeof(T)]; 0527 } r; 0528 for (size_t i = 0; i < sizeof(T); ++i) 0529 is.get(r.buf[sizeof(T) - i - 1]); 0530 0531 return r.val; 0532 } 0533 0534 /// Read value from buffer 0535 template<class T> 0536 T read(char*& s) { 0537 union { 0538 T val; 0539 char buf[sizeof(T)]; 0540 } r; 0541 for (size_t i = 0; i < sizeof(T); ++i) 0542 r.buf[sizeof(T) - i - 1] = *(s++); 0543 0544 return r.val; 0545 } 0546 0547 /// Read value from buffer and cast to U 0548 template<class T, class U> 0549 U readcast(char*& s) { 0550 return static_cast<U>(read<T>(s)); 0551 } 0552 0553 /// Get version of ROOT object, obtain number of bytes in object 0554 short Version(char*& buffer, size_t& count) { 0555 // root/io/io/src/TBufferFile.cxx -> ReadVersion 0556 count = read<unsigned int>(buffer); 0557 short version = (count & 0x40000000) ? read<short>(buffer) : read<short>(buffer -= 4); 0558 count = (count & 0x40000000) ? (count & ~0x40000000) - 2 : 2; 0559 return version; 0560 } 0561 0562 /// Get version of ROOT object 0563 short Version(char*& buffer) { 0564 size_t c; 0565 return Version(buffer, c); 0566 } 0567 0568 /// Skip ROOT object 0569 void Skip(char*& buffer, size_t n) { 0570 for (size_t i = 0; i < n; ++i) { 0571 size_t count; 0572 Version(buffer, count); 0573 buffer += count; 0574 } 0575 } 0576 0577 /// Skip TObject header 0578 void SkipObject(char*& buffer) { 0579 Version(buffer); 0580 buffer += 8; 0581 } 0582 0583 /// Get TString 0584 std::string String(char*& buffer) { 0585 // root/io/io/src/TBufferFile.cxx -> ReadTString 0586 size_t s = *(buffer++); 0587 if (s == 0) 0588 return {}; 0589 else { 0590 if (s == 0xFF) 0591 s = read<int>(buffer); 0592 buffer += s; 0593 return {buffer - s, buffer}; 0594 } 0595 } 0596 0597 /// Get the header of an object in TObjArray 0598 std::string readObject(char*& buf, char* const buf0, std::map<size_t, std::string>& tags) { 0599 // root/io/io/src/TBufferFile.cxx -> ReadObjectAny 0600 std::string clname; 0601 unsigned int tag = read<unsigned int>(buf); 0602 if (tag & 0x40000000) { 0603 tag = read<unsigned int>(buf); 0604 if (tag == 0xFFFFFFFF) { 0605 tags[buf - buf0 - 2] = clname = buf; 0606 buf += clname.size() + 1; 0607 } else { 0608 clname = tags[tag & ~0x80000000]; 0609 } 0610 } 0611 0612 return clname; 0613 } 0614 0615 } 0616 0617 using namespace ROOTDataHelpers; 0618 0619 ROOTData::ROOTData(const std::string& filename) 0620 : filename(filename) { 0621 // The file structure is described in root/io/io/src/TFile.cxx 0622 std::ifstream is(filename, std::ifstream::binary); 0623 std::string root(4, 0); 0624 is.read(const_cast<char*>(root.data()), 4); 0625 if (root != "root") 0626 return; 0627 0628 int fileVersion = read<int>(is); 0629 long int pos = read<int>(is); 0630 histdirs.emplace(pos, Directory{}); 0631 treedirs.emplace(pos, Directory{}); 0632 long int endpos = fileVersion < 1000000 ? read<int>(is) : read<long int>(is); 0633 0634 is.seekg(33); 0635 int compression = read<int>(is); 0636 compression = compression > 0 ? compression : 0; 0637 0638 while (is.good() && pos < endpos) { 0639 is.seekg(pos); 0640 int lcdata = read<int>(is); 0641 if (lcdata == 0) { 0642 break; 0643 } 0644 if (lcdata < 0) { 0645 pos -= lcdata; 0646 continue; 0647 } 0648 short version = read<short>(is); 0649 size_t ldata = read<unsigned int>(is); 0650 is.seekg(4, is.cur); // skip the date 0651 size_t lkey = read<unsigned short int>(is); 0652 short cycle = read<short>(is); 0653 long int pseek; 0654 if (version > 1000) { 0655 is.seekg(8, is.cur); 0656 pseek = read<long int>(is); 0657 } else { 0658 is.seekg(4, is.cur); 0659 pseek = read<int>(is); 0660 } 0661 std::string cname(read<unsigned char>(is), 0); 0662 is.read(&cname[0], cname.size()); 0663 std::string name(read<unsigned char>(is), 0); 0664 is.read(&name[0], name.size()); 0665 std::string title(read<unsigned char>(is), 0); 0666 is.read(&title[0], title.size()); 0667 0668 ContentType type = ContentType::Invalid; 0669 if (cname.size() == 4 && cname.substr(0, 3) == "TH1") { 0670 type = histType(cname[3]); 0671 } else if (cname == "TTree") 0672 type = ContentType::Tree; 0673 else if (cname.substr(0, 7) == "TNtuple") 0674 type = ContentType::NTuple; 0675 else if (cname == "TBasket") 0676 type = ContentType::Basket; 0677 else if (cname == "TList" && name == "StreamerInfo") 0678 type = ContentType::Streamer; 0679 else if (cname == "TDirectory") { 0680 auto it = histdirs.find(pseek); 0681 if (it == histdirs.end()) 0682 it = histdirs.begin(); 0683 histdirs.emplace(pos, Directory{name, it->first}); 0684 it = treedirs.find(pseek); 0685 if (it == treedirs.end()) 0686 it = treedirs.begin(); 0687 treedirs.emplace(pos, Directory{name, it->first}); 0688 } 0689 0690 if (type != ContentType::Invalid) { 0691 if (type == ContentType::Basket) 0692 is.seekg(19, std::ifstream::cur); // TODO read info instead? 0693 KeyBuffer buffer; 0694 buffer.type = ContentType::Invalid; 0695 // see root/io/io/src/TKey.cxx for reference 0696 int complib = 0; 0697 if (compression) { 0698 // Default: compression level 0699 // ZLIB: 100 + compression level 0700 // LZ4: 400 + compression level 0701 // do not rely on this, but read the header 0702 std::string lib(2, 0); 0703 is.read(&lib[0], 2); 0704 complib = lib == "ZL" ? 1 : lib == "XZ" ? 2 : lib == "CS" ? 3 : lib == "L4" ? 4 : 0; 0705 } 0706 if (complib > 0) { 0707 #ifdef HAVE_ZIP 0708 // see root/core/zip/src/RZip.cxx -> R__unzip 0709 const int method = is.get(); 0710 size_t chcdata = is.get(); 0711 chcdata |= (is.get() << 8); 0712 chcdata |= (is.get() << 16); 0713 size_t chdata = is.get(); 0714 chdata |= (is.get() << 8); 0715 chdata |= (is.get() << 16); 0716 0717 if (chcdata == lcdata - lkey - 9 && chdata == ldata) { 0718 if (complib == 1 && method == Z_DEFLATED) { 0719 buffer = KeyBuffer{type, name, title, cycle, lkey, KeyBuffer::CompressionType::zlib, pos + lkey + 9, chcdata, chdata, 0}; 0720 } else if (complib == 4 && method == LZ4_versionNumber() / 10000) { 0721 buffer = KeyBuffer{type, name, title, cycle, lkey, KeyBuffer::CompressionType::lz4, pos + lkey + 9 + 8, chcdata - 8, chdata, 0}; 0722 } 0723 } 0724 #endif 0725 } else { 0726 buffer = KeyBuffer{type, name, title, cycle, lkey, KeyBuffer::CompressionType::none, pos + lkey, ldata, ldata, 0}; 0727 } 0728 switch (buffer.type) { 0729 case ContentType::Basket: 0730 basketkeys.emplace(pos, buffer); 0731 break; 0732 case ContentType::Tree: 0733 case ContentType::NTuple: { 0734 auto it = treedirs.find(pseek); 0735 if (it == treedirs.end()) 0736 it = treedirs.begin(); 0737 bool keyreplaced = false; 0738 for (auto& tpos : it->second.content) { 0739 auto jt = treekeys.find(tpos); 0740 if (jt != treekeys.end() && jt->second.name == buffer.name && jt->second.cycle < buffer.cycle) { 0741 // override key with lower cylce number 0742 tpos = pos; 0743 treekeys.erase(jt); 0744 keyreplaced = true; 0745 break; 0746 } 0747 } 0748 if (!keyreplaced) 0749 it->second.content.push_back(pos); 0750 treekeys.emplace(pos, buffer); 0751 break; 0752 } 0753 case ContentType::Streamer: 0754 readStreamerInfo(buffer); 0755 break; 0756 case ContentType::Double: 0757 case ContentType::Float: 0758 case ContentType::Int: 0759 case ContentType::Short: 0760 case ContentType::Byte: { 0761 auto it = histdirs.find(pseek); 0762 if (it == histdirs.end()) 0763 it = histdirs.begin(); 0764 it->second.content.push_back(pos); 0765 histkeys.emplace(pos, buffer); 0766 break; 0767 } 0768 case ContentType::Invalid: 0769 case ContentType::Long: 0770 case ContentType::Bool: 0771 case ContentType::CString: 0772 break; 0773 } 0774 } 0775 pos += lcdata; 0776 } 0777 0778 // Create default object structures if no StreamerInfo was found. 0779 // Obtained by running the following in ROOT with a file passed as an argument: 0780 // 0781 // _file0->GetStreamerInfoList()->Print() 0782 // 0783 // auto l = (TStreamerInfo*)_file0->GetStreamerInfoList()->At(ENTRYNUMBER); 0784 // l->Print(); 0785 // for (int i = 0; i < l->GetNelement(); ++i) { 0786 // auto e = l->GetElement(i); 0787 // e->Print(); 0788 // cout << e->GetFullName() << " " << e->GetTypeName() << " " << e->GetSize() << endl; 0789 // } 0790 0791 static const StreamerInfo dummyobject{"Object", 0, std::string(), false, false}; 0792 if (!treekeys.empty()) { 0793 if (!streamerInfo.count("TTree")) { 0794 streamerInfo["TTree"] = {dummyobject, 0795 dummyobject, 0796 dummyobject, 0797 dummyobject, 0798 StreamerInfo{"fEntries", 8, std::string(), false, false}, 0799 StreamerInfo{std::string(), 5 * 8 + 4 * 4, std::string(), false, false}, 0800 StreamerInfo{"fNClusterRange", 4, std::string(), true, false}, 0801 StreamerInfo{std::string(), 6 * 8, std::string(), false, false}, 0802 StreamerInfo{"fNClusterRangeEnd", 8, "fNClusterRange", false, true}, 0803 StreamerInfo{"fNClusterSize", 8, "fNClusterRange", false, true}, 0804 StreamerInfo{"fBranches", 0, std::string(), false, false}}; 0805 } 0806 if (!streamerInfo.count("TBranch")) { 0807 streamerInfo["TBranch"] = {StreamerInfo{"TNamed", 0, std::string(), false, false}, 0808 dummyobject, 0809 StreamerInfo{std::string(), 3 * 4, std::string(), false, false}, 0810 StreamerInfo{"fWriteBasket", 4, std::string(), false, false}, 0811 StreamerInfo{std::string(), 8 + 4, std::string(), false, false}, 0812 StreamerInfo{"fMaxBaskets", 4, std::string(), true, false}, 0813 StreamerInfo{std::string(), 4 + 4 * 8, std::string(), false, false}, 0814 StreamerInfo{"fBranches", 0, std::string(), false, false}, 0815 StreamerInfo{"fLeaves", 0, std::string(), false, false}, 0816 StreamerInfo{"fBaskets", 0, std::string(), false, false}, 0817 StreamerInfo{"fBasketBytes", 4, "fMaxBaskets", false, true}, 0818 StreamerInfo{"fBasketEntry", 8, "fMaxBaskets", false, true}, 0819 StreamerInfo{"fBasketSeek", 8, "fMaxBaskets", false, true}}; 0820 } 0821 } 0822 if (!histkeys.empty()) { 0823 if (!streamerInfo.count("TH1")) { 0824 streamerInfo["TH1"] = {dummyobject, 0825 dummyobject, 0826 dummyobject, 0827 dummyobject, 0828 StreamerInfo{"fNcells", 4, std::string(), false, false}, 0829 StreamerInfo{"fXaxis", 0, std::string(), false, false}, 0830 StreamerInfo{"fYaxis", 0, std::string(), false, false}, 0831 StreamerInfo{"fZaxis", 0, std::string(), false, false}, 0832 StreamerInfo{std::string(), 2 * 2 + 8 * 8, std::string(), false, false}, 0833 dummyobject, 0834 StreamerInfo{"fSumw2", 0, std::string(), false, false}}; 0835 } 0836 if (!streamerInfo.count("TAxis")) { 0837 streamerInfo["TAxis"] = {dummyobject, 0838 dummyobject, 0839 StreamerInfo{"fNbins", 4, std::string(), false, false}, 0840 StreamerInfo{"fXmin", 8, std::string(), false, false}, 0841 StreamerInfo{"fXmax", 8, std::string(), false, false}, 0842 StreamerInfo{"fXbins", 0, std::string(), false, false}}; 0843 } 0844 } 0845 0846 for (auto& tree : treekeys) 0847 readNEntries(tree.second); 0848 for (auto& hist : histkeys) 0849 readNBins(hist.second); 0850 } 0851 0852 void ROOTData::readNBins(ROOTData::KeyBuffer& kbuffer) { 0853 std::string buffer = data(kbuffer); 0854 if (!buffer.empty()) { 0855 char* buf = &buffer[0]; 0856 std::map<std::string, size_t> counts; 0857 Version(buf); // TH1(D/F/I/S/C) 0858 Version(buf); // TH1 0859 advanceTo(buf, streamerInfo.find("TH1")->second, std::string(), "fNcells", counts); 0860 kbuffer.nrows = read<int>(buf); // fNcells 0861 } 0862 } 0863 0864 std::string ROOTData::histogramName(long int pos) { 0865 auto it = histkeys.find(pos); 0866 if (it != histkeys.end()) 0867 return it->second.name + ';' + std::to_string(it->second.cycle); 0868 return {}; 0869 } 0870 0871 int ROOTData::histogramBins(long int pos) { 0872 auto it = histkeys.find(pos); 0873 if (it != histkeys.end()) 0874 return it->second.nrows; 0875 return 0; 0876 } 0877 0878 std::vector<ROOTData::BinPars> ROOTData::readHistogram(long int pos) { 0879 auto it = histkeys.find(pos); 0880 if (it == histkeys.end()) 0881 return {}; 0882 0883 std::string buffer = data(it->second); 0884 if (!buffer.empty()) { 0885 char* buf = &buffer[0]; 0886 std::map<std::string, size_t> counts; 0887 auto& streamerTH1 = streamerInfo.find("TH1")->second; 0888 auto& streamerTAxis = streamerInfo.find("TAxis")->second; 0889 0890 size_t count; 0891 Version(buf); // TH1(D/F/I/S/C) 0892 Version(buf, count); // TH1 0893 char* const dbuf = buf + count; 0894 advanceTo(buf, streamerTH1, std::string(), "fNcells", counts); 0895 0896 std::vector<BinPars> r(read<int>(buf)); // fNcells 0897 if (r.size() < 3) 0898 return {}; 0899 0900 r.front().lowedge = -std::numeric_limits<double>::infinity(); 0901 0902 advanceTo(buf, streamerTH1, "fNcells", "fXaxis", counts); 0903 // x-Axis 0904 Version(buf, count); // TAxis 0905 char* const nbuf = buf + count; 0906 advanceTo(buf, streamerTAxis, std::string(), "fNbins", counts); 0907 const int nbins = read<int>(buf); 0908 advanceTo(buf, streamerTAxis, "fNbins", "fXmin", counts); 0909 const double xmin = read<double>(buf); 0910 advanceTo(buf, streamerTAxis, "fXmin", "fXmax", counts); 0911 const double xmax = read<double>(buf); 0912 advanceTo(buf, streamerTAxis, "fXmax", "fXbins", counts); 0913 const size_t nborders = read<int>(buf); // TArrayD 0914 // root/core/cont/src/TArrayD.cxx -> Streamer 0915 if (nborders == r.size() - 1) { 0916 for (size_t i = 0; i < nborders; ++i) { 0917 r[i + 1].lowedge = read<double>(buf); 0918 } 0919 } else { 0920 // UNUSED: buf += sizeof(double) * nbins; 0921 const double scale = (xmax - xmin) / static_cast<double>(nbins); 0922 for (size_t i = 0; i < r.size() - 1; ++i) { 0923 r[i + 1].lowedge = static_cast<double>(i) * scale + xmin; 0924 } 0925 } 0926 buf = nbuf; // go beyond x-Axis 0927 0928 advanceTo(buf, streamerTH1, "fXaxis", "fSumw2", counts); 0929 if (static_cast<size_t>(read<int>(buf)) == r.size()) { // TArrayD 0930 for (auto& b : r) 0931 b.sumw2 = read<double>(buf); // always double 0932 } 0933 buf = dbuf; // skip to contents of TH1(D/F/I/S/C) 0934 0935 if (static_cast<size_t>(read<int>(buf)) == r.size()) { 0936 auto readf = readType<double>(it->second.type); 0937 for (auto& b : r) 0938 b.content = readf(buf); 0939 } 0940 0941 return r; 0942 } else 0943 return {}; 0944 } 0945 0946 void ROOTData::readNEntries(ROOTData::KeyBuffer& kbuffer) { 0947 std::string buffer = data(kbuffer); 0948 if (!buffer.empty()) { 0949 char* buf = &buffer[0]; 0950 std::map<std::string, size_t> counts; 0951 if (kbuffer.type == ContentType::NTuple) 0952 Version(buf); // TNtuple(D) 0953 Version(buf); // TTree 0954 advanceTo(buf, streamerInfo.find("TTree")->second, std::string(), "fEntries", counts); 0955 kbuffer.nrows = read<long int>(buf); // fEntries 0956 } 0957 } 0958 0959 std::string ROOTData::treeName(long int pos) { 0960 auto it = treekeys.find(pos); 0961 if (it != treekeys.end()) 0962 return it->second.name; 0963 return {}; 0964 } 0965 0966 int ROOTData::treeEntries(long int pos) { 0967 auto it = treekeys.find(pos); 0968 if (it != treekeys.end()) 0969 return it->second.nrows; 0970 else 0971 return 0; 0972 } 0973 0974 std::vector<ROOTData::LeafInfo> ROOTData::listLeaves(long int pos) const { 0975 std::vector<LeafInfo> leaves; 0976 0977 auto it = treekeys.find(pos); 0978 if (it == treekeys.end()) 0979 return leaves; 0980 0981 std::ifstream is(filename, std::ifstream::binary); 0982 std::string datastring = data(it->second, is); 0983 if (datastring.empty()) 0984 return leaves; 0985 0986 char* buf = &datastring[0]; 0987 char* const buf0 = buf - it->second.keylength; 0988 std::map<std::string, size_t> counts; 0989 auto& streamerTBranch = streamerInfo.find("TBranch")->second; 0990 0991 if (it->second.type == ContentType::NTuple) 0992 Version(buf); // TNtuple(D) 0993 Version(buf); // TTree 0994 advanceTo(buf, streamerInfo.find("TTree")->second, std::string(), "fBranches", counts); 0995 0996 // read the list of branches 0997 Version(buf); // TObjArray 0998 SkipObject(buf); 0999 String(buf); 1000 const size_t nbranches = read<int>(buf); 1001 const size_t lowb = read<int>(buf); 1002 std::map<size_t, std::string> tags; 1003 for (size_t i = 0; i < nbranches; ++i) { 1004 std::string clname = readObject(buf, buf0, tags); 1005 size_t count; 1006 Version(buf, count); // TBranch or TBranchElement 1007 char* const nbuf = buf + count; 1008 if (i >= lowb) { 1009 if (clname == "TBranchElement") { 1010 Version(buf); // TBranch 1011 } 1012 advanceTo(buf, streamerTBranch, std::string(), "TNamed", counts); 1013 Version(buf); // TNamed 1014 SkipObject(buf); 1015 const std::string branch = String(buf); 1016 String(buf); 1017 // TODO add reading of nested branches (fBranches) 1018 advanceTo(buf, streamerTBranch, "TNamed", "fLeaves", counts); 1019 1020 // fLeaves 1021 Version(buf); // TObjArray 1022 SkipObject(buf); 1023 String(buf); 1024 const size_t nleaves = read<int>(buf); 1025 const size_t lowb = read<int>(buf); 1026 for (size_t i = 0; i < nleaves; ++i) { 1027 std::string clname = readObject(buf, buf0, tags); 1028 Version(buf, count); // TLeaf(D/F/B/S/I/L/C/O) 1029 char* nbuf = buf + count; 1030 if (i >= lowb && clname.size() == 6 && clname.compare(0, 5, "TLeaf") == 0) { 1031 Version(buf); // TLeaf 1032 Version(buf); // TNamed 1033 SkipObject(buf); 1034 const std::string leafname = String(buf); 1035 String(buf); // title 1036 size_t elements = read<int>(buf); 1037 int bytes = read<int>(buf); 1038 if ((static_cast<int>(leafType(clname.back())) & 0xF) != bytes) 1039 DEBUG("ROOTData: type " << clname.back() << " does not match its size!") 1040 buf += 5; 1041 leaves.emplace_back(LeafInfo{branch, leafname, leafType(clname.back()), !read<char>(buf), elements}); 1042 } 1043 1044 buf = nbuf; 1045 } 1046 } 1047 1048 buf = nbuf; 1049 } 1050 return leaves; 1051 } 1052 1053 template<class T> 1054 std::vector<T> 1055 ROOTData::listEntries(long int pos, const std::string& branchname, const std::string& leafname, const size_t element, const size_t nentries) const { 1056 std::vector<T> entries; 1057 1058 auto it = treekeys.find(pos); 1059 if (it == treekeys.end()) 1060 return entries; 1061 1062 std::ifstream is(filename, std::ifstream::binary); 1063 std::string datastring = data(it->second, is); 1064 if (datastring.empty()) 1065 return entries; 1066 1067 char* buf = &datastring[0]; 1068 char* const buf0 = buf - it->second.keylength; 1069 std::map<std::string, size_t> counts; 1070 auto& streamerTTree = streamerInfo.find("TTree")->second; 1071 auto& streamerTBranch = streamerInfo.find("TBranch")->second; 1072 1073 if (it->second.type == ContentType::NTuple) 1074 Version(buf); // TNtuple(D) 1075 Version(buf); // TTree 1076 advanceTo(buf, streamerTTree, std::string(), "fEntries", counts); 1077 entries.reserve(std::min(static_cast<size_t>(read<long int>(buf)), nentries)); // reserve space (maximum for number of entries) 1078 advanceTo(buf, streamerTTree, "fEntries", "fBranches", counts); 1079 1080 // read the list of branches 1081 Version(buf); // TObjArray 1082 SkipObject(buf); 1083 String(buf); 1084 const size_t nbranches = read<int>(buf); 1085 const size_t lowb = read<int>(buf); 1086 std::map<size_t, std::string> tags; 1087 for (size_t i = 0; i < nbranches; ++i) { 1088 std::string clname = readObject(buf, buf0, tags); 1089 size_t count; 1090 Version(buf, count); // TBranch or TBranchElement 1091 char* const nbuf = buf + count; 1092 if (i >= lowb) { 1093 if (clname == "TBranchElement") { 1094 Version(buf); 1095 } 1096 Version(buf); // TNamed 1097 SkipObject(buf); 1098 const std::string currentbranch = String(buf); 1099 String(buf); 1100 1101 advanceTo(buf, streamerTBranch, "TNamed", "fWriteBasket", counts); 1102 int fWriteBasket = read<int>(buf); 1103 // TODO add reading of nested branches (fBranches) 1104 advanceTo(buf, streamerTBranch, "fWriteBasket", "fLeaves", counts); 1105 1106 // fLeaves 1107 Version(buf); // TObjArray 1108 SkipObject(buf); 1109 String(buf); 1110 const size_t nleaves = read<int>(buf); 1111 const size_t lowb = read<int>(buf); 1112 int leafoffset = 0, leafcount = 0, leafcontent = 0, leafsize = 0; 1113 bool leafsign = false; 1114 ContentType leaftype = ContentType::Invalid; 1115 for (size_t i = 0; i < nleaves; ++i) { 1116 std::string clname = readObject(buf, buf0, tags); 1117 Version(buf, count); // TLeaf(D/F/L/I/S/B/O/C/Element) 1118 char* nbuf = buf + count; 1119 if (currentbranch == branchname) { 1120 if (i >= lowb && clname.size() >= 5 && clname.compare(0, 5, "TLeaf") == 0) { 1121 Version(buf); // TLeaf 1122 Version(buf); // TNamed 1123 SkipObject(buf); 1124 const bool istheleaf = (clname.size() == 6 && leafname == String(buf)); 1125 String(buf); 1126 const int len = read<int>(buf); 1127 const int size = read<int>(buf); 1128 if (istheleaf) { 1129 leafoffset = leafcount; 1130 leafsize = size; 1131 leaftype = leafType(clname.back()); 1132 } 1133 leafcount += len * size; 1134 if (istheleaf) { 1135 leafcontent = leafcount - leafoffset; 1136 buf += 1; 1137 leafsign = !read<bool>(buf); 1138 } 1139 } 1140 } 1141 1142 buf = nbuf; 1143 } 1144 if (leafcontent == 0) { 1145 buf = nbuf; 1146 continue; 1147 } 1148 1149 if (static_cast<int>(element) * leafsize >= leafcontent) { 1150 DEBUG("ROOTData: " << leafname.c_str() << " only contains " << leafcontent / leafsize << " elements."); 1151 break; 1152 } 1153 1154 advanceTo(buf, streamerTBranch, "fLeaves", "fBaskets", counts); 1155 // fBaskets (probably empty) 1156 Version(buf, count); // TObjArray 1157 char* const basketsbuf = buf += count + 1; // TODO there is one byte to be skipped in fBaskets, why is that? 1158 1159 advanceTo(buf, streamerTBranch, "fBaskets", "fBasketEntry", counts); 1160 for (int i = 0; i <= fWriteBasket; ++i) { 1161 if (static_cast<size_t>(read<long int>(buf)) > nentries) { 1162 fWriteBasket = i; 1163 break; 1164 } 1165 } 1166 // rewind to the end of fBaskets and look for the fBasketSeek array 1167 advanceTo(buf = basketsbuf, streamerTBranch, "fBaskets", "fBasketSeek", counts); 1168 auto readf = readType<T>(leaftype, leafsign); 1169 for (int i = 0; i < fWriteBasket; ++i) { 1170 long int pos = read<long int>(buf); 1171 auto it = basketkeys.find(pos); 1172 if (it != basketkeys.end()) { 1173 std::string basketbuffer = data(it->second); 1174 if (!basketbuffer.empty()) { 1175 char* bbuf = &basketbuffer[0]; 1176 char* const bufend = bbuf + basketbuffer.size(); 1177 while (bbuf + leafcount <= bufend && entries.size() < nentries) { 1178 bbuf += leafoffset + leafsize * element; 1179 entries.emplace_back(readf(bbuf)); 1180 bbuf += leafcount - leafsize * (element + 1) - leafoffset; 1181 } 1182 } 1183 } else { 1184 DEBUG("ROOTData: fBasketSeek(" << i << "): " << pos << " (not available)") 1185 } 1186 } 1187 } 1188 1189 buf = nbuf; 1190 } 1191 1192 return entries; 1193 } 1194 1195 ROOTData::ContentType ROOTData::histType(const char type) { 1196 switch (type) { 1197 case 'D': 1198 return ContentType::Double; 1199 case 'F': 1200 return ContentType::Float; 1201 case 'I': 1202 return ContentType::Int; 1203 case 'S': 1204 return ContentType::Short; 1205 case 'C': 1206 return ContentType::Byte; 1207 default: 1208 return ContentType::Invalid; 1209 } 1210 } 1211 1212 ROOTData::ContentType ROOTData::leafType(const char type) { 1213 switch (type) { 1214 case 'D': 1215 return ContentType::Double; 1216 case 'F': 1217 return ContentType::Float; 1218 case 'L': 1219 return ContentType::Long; 1220 case 'I': 1221 return ContentType::Int; 1222 case 'S': 1223 return ContentType::Short; 1224 case 'B': 1225 return ContentType::Byte; 1226 case 'O': 1227 return ContentType::Bool; 1228 case 'C': 1229 return ContentType::CString; 1230 default: 1231 return ContentType::Invalid; 1232 } 1233 } 1234 1235 template<class T> 1236 T (*ROOTData::readType(ROOTData::ContentType type, bool sign) const) 1237 (char*&) { 1238 switch (type) { 1239 case ContentType::Double: 1240 return readcast<double, T>; 1241 case ContentType::Float: 1242 return readcast<float, T>; 1243 case ContentType::Long: 1244 return sign ? readcast<long, T> : readcast<unsigned long, T>; 1245 case ContentType::Int: 1246 return sign ? readcast<int, T> : readcast<unsigned int, T>; 1247 case ContentType::Short: 1248 return sign ? readcast<short, T> : readcast<unsigned short, T>; 1249 case ContentType::Byte: 1250 return sign ? readcast<char, T> : readcast<unsigned char, T>; 1251 case ContentType::Bool: 1252 return readcast<bool, T>; 1253 case ContentType::CString: 1254 case ContentType::Tree: 1255 case ContentType::NTuple: 1256 case ContentType::Basket: 1257 case ContentType::Streamer: 1258 case ContentType::Invalid: 1259 break; 1260 } 1261 return readcast<char, T>; 1262 } 1263 1264 std::string ROOTData::data(const ROOTData::KeyBuffer& buffer) const { 1265 std::ifstream is(filename, std::ifstream::binary); 1266 return data(buffer, is); 1267 } 1268 1269 std::string ROOTData::data(const ROOTData::KeyBuffer& buffer, std::ifstream& is) const { 1270 std::string data(buffer.count, 0); 1271 is.seekg(buffer.start); 1272 if (buffer.compression == KeyBuffer::CompressionType::none) { 1273 is.read(&data[0], buffer.count); 1274 return data; 1275 #ifdef HAVE_ZIP 1276 } else if (buffer.compression == KeyBuffer::CompressionType::zlib) { 1277 std::string cdata(buffer.compressed_count, 0); 1278 is.read(&cdata[0], buffer.compressed_count); 1279 uLongf luncomp = (uLongf)buffer.count; 1280 if (uncompress((Bytef*)data.data(), &luncomp, (Bytef*)cdata.data(), (uLong)cdata.size()) == Z_OK && data.size() == luncomp) 1281 return data; 1282 } else { 1283 std::string cdata(buffer.compressed_count, 0); 1284 is.read(&cdata[0], buffer.compressed_count); 1285 if (LZ4_decompress_safe(cdata.data(), const_cast<char*>(data.data()), (int)buffer.compressed_count, (int)buffer.count) 1286 == static_cast<int>(buffer.count)) 1287 return data; 1288 #endif 1289 } 1290 1291 return {}; 1292 } 1293 1294 void ROOTData::readStreamerInfo(const ROOTData::KeyBuffer& buffer) { 1295 std::ifstream is(filename, std::ifstream::binary); 1296 std::string datastring = data(buffer, is); 1297 if (!datastring.empty()) { 1298 char* buf = &datastring[0]; 1299 char* const buf0 = buf - buffer.keylength; 1300 Version(buf); 1301 SkipObject(buf); // TCollection 1302 String(buf); 1303 const int nobj = read<int>(buf); 1304 std::map<size_t, std::string> tags; 1305 for (int i = 0; i < nobj; ++i) { 1306 std::string clname = readObject(buf, buf0, tags); 1307 size_t count; 1308 Version(buf, count); 1309 char* const nbuf = buf + count; 1310 if (clname == "TStreamerInfo") { 1311 Version(buf); 1312 SkipObject(buf); 1313 std::vector<StreamerInfo>& sinfo = streamerInfo[String(buf)]; 1314 String(buf); 1315 buf += 8; // skip check sum and version 1316 1317 clname = readObject(buf, buf0, tags); 1318 Version(buf, count); 1319 if (clname != "TObjArray") { 1320 buf += count; 1321 continue; 1322 } 1323 1324 SkipObject(buf); // TObjArray 1325 String(buf); 1326 const int nobj = read<int>(buf); 1327 const int lowb = read<int>(buf); 1328 for (int i = 0; i < nobj; ++i) { 1329 std::string clname = readObject(buf, buf0, tags); 1330 Version(buf, count); 1331 char* const nbuf = buf + count; 1332 1333 const bool isbasicpointer = clname == "TStreamerBasicPointer"; 1334 const bool ispointer = isbasicpointer || clname == "TStreamerObjectPointer"; 1335 if (i >= lowb) { 1336 if (ispointer || clname == "TStreamerBase" || clname == "TStreamerBasicType" || clname == "TStreamerObject" 1337 || clname == "TStreamerObjectAny" || clname == "TStreamerString" || clname == "TStreamerSTL") { 1338 Version(buf); // TStreamerXXX 1339 Version(buf); // TStreamerElement 1340 SkipObject(buf); 1341 const std::string name = String(buf); 1342 const std::string title = String(buf); 1343 int type = read<int>(buf); 1344 size_t size = read<int>(buf); 1345 1346 if (clname.compare(0, 15, "TStreamerObject") == 0) 1347 size = 0; 1348 std::string counter; 1349 bool iscounter = false; 1350 if (ispointer) { 1351 if (!title.empty() && title.front() == '[') { 1352 const size_t endref = title.find(']', 1); 1353 if (endref != title.npos) { 1354 counter = title.substr(1, endref - 1); 1355 } 1356 } 1357 if (isbasicpointer) { 1358 // see root/io/io/inc/TStreamerInfo.h -> TStreamerInfo::EReadWrite 1359 switch (type - 40) { 1360 case 1: // char 1361 case 11: // unsigned char 1362 size = 1; 1363 break; 1364 case 2: // short 1365 case 12: // unsigned short 1366 case 19: // float16 1367 size = 2; 1368 break; 1369 case 3: // int 1370 case 5: // float 1371 case 9: // double32 1372 case 13: // unsigned int 1373 size = 4; 1374 break; 1375 case 4: // long 1376 case 8: // double 1377 case 14: // unsigned long 1378 case 16: // long 1379 case 17: // unsigned long 1380 size = 8; 1381 break; 1382 } 1383 } 1384 } else if (clname == "TStreamerBasicType") { 1385 iscounter = type == 6; // see root/io/io/inc/TStreamerInfo.h -> TStreamerInfo::EReadWrite 1386 } 1387 sinfo.emplace_back(StreamerInfo{name, size, counter, iscounter, ispointer}); 1388 } 1389 } 1390 buf = nbuf; 1391 } 1392 } else 1393 buf = nbuf; 1394 buf += 1; // trailing zero of TObjArray* 1395 } 1396 } else 1397 DEBUG("ROOTData: Inflation failed!") 1398 } 1399 1400 bool ROOTData::advanceTo(char*& buf, 1401 const std::vector<ROOTData::StreamerInfo>& objects, 1402 const std::string& current, 1403 const std::string& target, 1404 std::map<std::string, size_t>& counts) { 1405 // The object structure can be retrieved from TFile::GetStreamerInfoList(). 1406 // Every ROOT object contains a version number which may include the byte count 1407 // for the object. The latter is currently assumed to be present to skip unused 1408 // objects. No checks are performed. The corresponding ROOT code is quite nested 1409 // but the actual readout is straight forward. 1410 auto it = objects.begin(); 1411 if (!current.empty()) { 1412 for (; it != objects.end(); ++it) { 1413 if (it->name == target) { 1414 return false; // target lies before current buffer position 1415 } else if (it->name == current) { 1416 ++it; 1417 break; 1418 } 1419 } 1420 } 1421 1422 for (; it != objects.end(); ++it) { 1423 if (it->name == target) 1424 return true; 1425 1426 if (it->size == 0) 1427 Skip(buf, 1); 1428 else if (it->iscounter) 1429 counts[it->name] = read<int>(buf); 1430 else if (it->ispointer) { 1431 if (it->counter.empty()) 1432 buf += it->size + 1; 1433 else 1434 buf += it->size * counts[it->counter] + 1; 1435 } else 1436 buf += it->size; 1437 } 1438 1439 return false; 1440 } 1441 1442 // needs to be after ROOTDataHelpers namespace declaration 1443 1444 QString ROOTFilter::fileInfoString(const QString& fileName) { 1445 DEBUG("ROOTFilter::fileInfoString()"); 1446 QString info; 1447 1448 // The file structure is described in root/io/io/src/TFile.cxx 1449 std::ifstream is(fileName.toStdString(), std::ifstream::binary); 1450 std::string root(4, 0); 1451 is.read(const_cast<char*>(root.data()), 4); 1452 if (root != "root") { 1453 DEBUG(" Not a ROOT file. root = " << root); 1454 return i18n("Not a ROOT file"); 1455 } 1456 1457 int version = read<int>(is); 1458 1459 info += i18n("File format version: %1", QString::number(version)); 1460 info += QLatin1String("<br>"); 1461 1462 is.seekg(20); 1463 int freeBytes = read<int>(is); 1464 int freeRecords = read<int>(is); 1465 int namedBytes = read<int>(is); 1466 char pointerBytes = read<char>(is); 1467 info += i18n("FREE data record size: %1 bytes", QString::number(freeBytes)); 1468 info += QLatin1String("<br>"); 1469 info += i18n("Number of free data records: %1", QString::number(freeRecords)); 1470 info += QLatin1String("<br>"); 1471 info += i18n("TNamed size: %1 bytes", QString::number(namedBytes)); 1472 info += QLatin1String("<br>"); 1473 info += i18n("Size of file pointers: %1 bytes", QString::number(pointerBytes)); 1474 info += QLatin1String("<br>"); 1475 1476 int compression = read<int>(is); 1477 compression = compression > 0 ? compression : 0; 1478 info += i18n("Compression level and algorithm: %1", QString::number(compression)); 1479 info += QLatin1String("<br>"); 1480 1481 is.seekg(41); 1482 int infoBytes = read<int>(is); 1483 info += i18n("Size of TStreamerInfo record: %1 bytes", QString::number(infoBytes)); 1484 info += QLatin1String("<br>"); 1485 1486 return info; 1487 }