LCOV - code coverage report
Current view: top level - alma/ASDMBinaries - SDMDataObjectStreamReader.cc (source / functions) Hit Total Coverage
Test: casacpp_coverage.info Lines: 324 495 65.5 %
Date: 2024-12-11 20:54:31 Functions: 30 56 53.6 %

          Line data    Source code
       1             : #include <alma/ASDMBinaries/SDMDataObjectStreamReader.h>
       2             : 
       3             : #ifdef WITHOUT_BOOST
       4             : #include <regex>
       5             : #include <alma/ASDM/Misc.h>
       6             : #endif
       7             : 
       8             : using namespace CorrelationModeMod;
       9             : using namespace CorrelatorTypeMod;
      10             : using namespace PrimitiveDataTypeMod;
      11             : using namespace ProcessorTypeMod;
      12             : 
      13             : using namespace std;
      14             : 
      15             : namespace asdmbinaries {
      16             : 
      17           0 :   SDMDataObjectStreamReaderException::SDMDataObjectStreamReaderException():message("SDMDataObjectStreamReaderException:") {;}
      18             : 
      19           0 :   SDMDataObjectStreamReaderException::SDMDataObjectStreamReaderException(const string & message): message("SDMDataObjectStreamReaderException:" + message) {;}   
      20             : 
      21           0 :   const string& SDMDataObjectStreamReaderException::getMessage() { return message; }
      22             : 
      23         848 :   SDMDataObjectStreamReader::SDMDataObjectStreamReader()  {
      24             :   // cout << "SDMDataObjectStreamReader::SDMDataObjectStreamReader() : entering" << endl;
      25         848 :     const char *partNames[] = {"actualDurations", "actualTimes", "autoData", "crossData", "zeroLags", "flags"};
      26         848 :     set<string> dummy(partNames, partNames+6);
      27         848 :     s_partNames = dummy;
      28         848 :     sdmDataSubset = SDMDataSubset(&sdmDataObject);
      29             : 
      30         848 :     boundary_1 = "" ;
      31         848 :     currentState = S_NO_BDF;
      32             :   // cout << "SDMDataObjectStreamReader::SDMDataObjectStreamReader() : exiting" << endl;
      33         848 :   }
      34             : 
      35         848 :   SDMDataObjectStreamReader::~SDMDataObjectStreamReader() {
      36             :     //cout << "SDMDataObjectStreamReader::~SDMDataObjectStreamReader : entering" << endl;
      37         848 :     this->close();
      38             :     //cout << "SDMDataObjectStreamReader::~SDMDataObjectStreamReader : exiting" << endl;
      39         848 :   }
      40             : 
      41        2585 :   void SDMDataObjectStreamReader::open(const string& path) {
      42        2585 :     checkState(T_OPEN, "open");
      43        2585 :     this->path = path;
      44        2585 :     f.open(path.c_str(), ifstream::in);
      45        2585 :     if (f.fail())
      46           0 :       throw SDMDataObjectStreamReaderException("could not open '" + path + "'.");
      47             : 
      48        2585 :     boundary_1 = requireMIMEHeader();
      49             :     // cout << "Boundary = " << boundary_1 << endl;
      50        2585 :     sdmDataObject.valid_ = true;
      51        2585 :     requireSDMDataHeaderMIMEPart();
      52        2585 :     sdmDataObject.owns();
      53        2585 :     integrationIndex = -1; // We have not yet read any integration.
      54             : 
      55        2585 :     currentState = S_AT_BEGINNING;
      56        2585 :   }
      57             : 
      58       41718 :   int64_t SDMDataObjectStreamReader::position() {
      59       41718 :     return (int64_t) f.tellg();
      60             :   }
      61             : 
      62       41718 :   void SDMDataObjectStreamReader::position(int64_t p) {
      63       41718 :     f.seekg(p);
      64       41718 :   }
      65             : 
      66        3828 :   void SDMDataObjectStreamReader::close() {
      67             :     // cout << "SDMDataObjectStreamReader::close -- Entering" << endl;
      68        3828 :     if (f.is_open()) {
      69        2585 :       releaseMemory(sdmDataSubset);
      70        2585 :       for (unsigned int i = 0; i < remainingSubsets.size(); i++)
      71           0 :         releaseMemory(remainingSubsets[i]);
      72       23487 :       for (unsigned int i = 0; i < someSubsets.size(); i++)
      73       20902 :         releaseMemory(someSubsets[i]);
      74        2585 :       f.close();
      75        2585 :       currentState = S_NO_BDF;
      76             :     }
      77             :     // cout << "SDMDataObjectStreamReader::close -- Exiting" << endl;
      78        3828 :   }
      79             : 
      80           0 :   unsigned int SDMDataObjectStreamReader::currentIntegrationIndex() const { checkState(T_QUERY, "currentIntegrationIndex"); return integrationIndex; }
      81           0 :   unsigned long long SDMDataObjectStreamReader::currentIntegrationStartsAt() const { checkState(T_QUERY, "currentIntegrationStartAt"); return integrationStartsAt; }
      82           0 :   string                                SDMDataObjectStreamReader::title() const { checkState(T_QUERY, "title"); return sdmDataObject.title(); }
      83          31 :   const ByteOrder*                      SDMDataObjectStreamReader::byteOrder() const { checkState(T_QUERY, "byteOrder"); return sdmDataObject.byteOrder(); }
      84           0 :   unsigned long long                    SDMDataObjectStreamReader::startTime() const { checkState(T_QUERY, "startTime"); return sdmDataObject.startTime(); }
      85        4702 :   unsigned int                          SDMDataObjectStreamReader::numTime() const { checkState(T_QUERY, "numTime"); return sdmDataObject.numTime(); }
      86           0 :   string                                SDMDataObjectStreamReader::dataOID() const { checkState(T_QUERY, "dataOID"); return sdmDataObject.dataOID(); }
      87           0 :   string                                SDMDataObjectStreamReader::execBlockUID() const { checkState(T_QUERY, "execBlockUID"); return sdmDataObject.execBlockUID(); }
      88           0 :   unsigned int                          SDMDataObjectStreamReader::execBlockNum() const { checkState(T_QUERY, "execBlockNum"); return sdmDataObject.execBlockNum(); }
      89           0 :   unsigned int                          SDMDataObjectStreamReader::scanNum() const { checkState(T_QUERY, "scanNum"); return sdmDataObject.scanNum(); }
      90           0 :   unsigned int                          SDMDataObjectStreamReader::subscanNum() const { checkState(T_QUERY, "subscanNum"); return sdmDataObject.subscanNum(); }
      91        1840 :   unsigned int                          SDMDataObjectStreamReader::numAntenna() const { checkState(T_QUERY, "numAntenna"); return sdmDataObject.numAntenna(); }
      92        2572 :   CorrelationMode                       SDMDataObjectStreamReader::correlationMode() const { checkState(T_QUERY, "correlationMode"); return sdmDataObject.correlationMode(); }
      93           0 :   OptionalSpectralResolutionType        SDMDataObjectStreamReader::spectralResolutionType() const { checkState(T_QUERY, "spectralResolutionType"); return sdmDataObject.spectralResolutionType(); }
      94        1840 :   ProcessorType                         SDMDataObjectStreamReader::processorType() const { checkState(T_QUERY, "processorType"); return sdmDataObject.processorType(); }
      95           0 :   CorrelatorType                        SDMDataObjectStreamReader::correlatorType() const { checkState(T_QUERY, "correlatorType"); return sdmDataObject.correlatorType(); }
      96        1069 :   bool                                  SDMDataObjectStreamReader::hasPackedData() const { checkState(T_QUERY, "isTP"); return sdmDataObject.hasPackedData();} 
      97           0 :   bool                                  SDMDataObjectStreamReader::isTP() const { checkState(T_QUERY, "isTP"); return sdmDataObject.isTP();} 
      98           0 :   bool                                  SDMDataObjectStreamReader::isWVR() const {checkState(T_QUERY, "isCorrelation"); return sdmDataObject.isWVR();} 
      99           0 :   bool                                  SDMDataObjectStreamReader::isCorrelation() const {checkState(T_QUERY, "isCorrelation"); return sdmDataObject.isCorrelation();} 
     100       11605 :   const SDMDataObject::DataStruct&  SDMDataObjectStreamReader::dataStruct() const { checkState(T_QUERY, "dataStruct"); return sdmDataObject.dataStruct();}
     101           0 :   bool                                  SDMDataObjectStreamReader::aborted() const {checkState(T_QUERY, "aborted"); return sdmDataObject.aborted();} 
     102           0 :   unsigned long long                    SDMDataObjectStreamReader::abortTime() const { checkState(T_QUERY, "abortTime"); return sdmDataObject.abortTime(); }
     103           0 :   string                                SDMDataObjectStreamReader::abortReason() const { checkState(T_QUERY, "abortReason"); return sdmDataObject.abortReason(); }
     104           0 :   string                                SDMDataObjectStreamReader::toString() const { checkState(T_QUERY, "toString"); return sdmDataObject.toString(); }
     105             : 
     106       90676 :   void SDMDataObjectStreamReader::checkState(Transitions t, const string& methodName) const {
     107             :     // cout << "Entering checkState with currentState = " << currentState << " and transition = " << t << " for method " << methodName << endl;
     108       90676 :     switch (currentState) {
     109        2585 :     case S_NO_BDF:
     110        2585 :       if ( t == T_OPEN ) {
     111        2585 :         return;
     112             :       }
     113           0 :       break;
     114             :       
     115       34051 :     case S_AT_BEGINNING :
     116       34051 :       switch (t) {
     117       34051 :       case T_QUERY:
     118             :       case T_TEST_END:
     119             :       case T_READ:
     120             :       case T_READ_NEXT:
     121             :       case T_READ_ALL:
     122             :       case T_CLOSE:
     123       34051 :         return;
     124           0 :       default :
     125           0 :         break;
     126             :       }
     127           0 :       break;
     128             :       
     129       54040 :     case S_READING:
     130       54040 :       switch (t) {
     131       54040 :       case T_TEST_END:
     132             :       case T_READ:
     133             :       case T_READ_NEXT:
     134             :       case T_READ_ALL:
     135             :       case T_QUERY:
     136             :       case T_CLOSE:
     137       54040 :         return;
     138           0 :       default :
     139           0 :         break;
     140             :       }
     141           0 :       break;
     142             : 
     143           0 :     case S_AT_END:
     144           0 :       switch(t) {
     145           0 :       case  T_TEST_END:
     146             :       case T_QUERY: 
     147             :       case T_READ_NEXT:
     148             :       case T_READ_ALL:
     149             :       case T_CLOSE:
     150           0 :         return;
     151           0 :       default:
     152           0 :         break;
     153             :       }
     154           0 :       break;
     155             :     } // end switch on currentState
     156             : 
     157             :     // Any other pair (transition, currentState) will throw an exception.
     158           0 :     throw SDMDataObjectStreamReaderException("Invalid call of method '" + methodName + "' in the current context.");
     159             :   }
     160             : 
     161       40899 :   bool SDMDataObjectStreamReader::hasSubset() {
     162       40899 :     checkState(T_TEST_END, "hasSubset");
     163       40899 :     bool atEnd = currentLine.compare("--"+boundary_1+"--") == 0;
     164       40899 :     if (atEnd) currentState = S_AT_END;
     165       40899 :     return !atEnd;
     166             :   }
     167             :  
     168         589 :   const vector<SDMDataSubset>& SDMDataObjectStreamReader::nextSubsets(unsigned int nSubsets) {
     169         589 :     checkState(T_READ_NEXT, "nextSubsets");
     170             :     
     171             :     // Deep empty of the vector nextSubsets
     172             :     // Firstly free all memory dynamically allocated in every element of the vector.
     173       17908 :     for (unsigned int i = 0; i < someSubsets.size(); i++)
     174       17319 :       releaseMemory(someSubsets.at(i));
     175             :     
     176             :     // Then clear the vector.
     177         589 :     someSubsets.clear();
     178             : 
     179             :     // Then populate the vector nextSubsets with as many SDMDataSubsets as possible up to a limit
     180             :     // of nSubsets read from the current position.
     181         589 :     unsigned int nRead = 0;
     182       19363 :     while ((nRead < nSubsets) && hasSubset()) {
     183       18774 :       someSubsets.push_back(SDMDataSubset(&sdmDataObject));
     184       18774 :       integrationIndex++; nRead++;
     185       18774 :       requireSDMDataSubsetMIMEPart(someSubsets.back());
     186       18774 :       string line = nextLine();      
     187       18774 :     }
     188             : 
     189         589 :     return someSubsets;
     190             :   }
     191             : 
     192           0 :   const vector<SDMDataSubset>& SDMDataObjectStreamReader::allRemainingSubsets() {
     193             :     // cout << "SDMDataObjectStreamReader::allRemainingSubsets: entering." << endl;
     194           0 :     checkState(T_READ_ALL, "allRemainingSubsets");
     195             : 
     196             :     // Deep empty of the vector remainingSubsets.
     197             :     // Firstly free all memory dynamically allocated in every element of the vector.
     198           0 :     for (unsigned int i = 0; i < remainingSubsets.size(); i++)
     199           0 :       releaseMemory(remainingSubsets.at(i));
     200             :     
     201             :     // Then clear the vector.
     202           0 :     remainingSubsets.clear();
     203             : 
     204             :     // Then populate the vector with a new collection.
     205           0 :     while (hasSubset()) {
     206           0 :       remainingSubsets.push_back(SDMDataSubset(&sdmDataObject));
     207           0 :       integrationIndex++;
     208           0 :       requireSDMDataSubsetMIMEPart(remainingSubsets.back());
     209           0 :       string line = nextLine();
     210           0 :     }
     211             : 
     212             :     // cout << "SDMDataObjectStreamReader::allRemainingSubsets: exiting." << endl;
     213           0 :     return remainingSubsets;
     214             :   }
     215             : 
     216       22944 :   const SDMDataSubset & SDMDataObjectStreamReader::getSubset() {
     217       22944 :     checkState(T_READ, "getSubset");
     218       22944 :     integrationIndex++;
     219       22944 :     requireSDMDataSubsetMIMEPart(sdmDataSubset);
     220       22944 :     string line = nextLine();
     221             : 
     222       22944 :     currentState = S_READING;
     223       22944 :     return sdmDataSubset;
     224       22944 :   }
     225             : 
     226      995671 :   string SDMDataObjectStreamReader::nextLine() {
     227      995671 :     unsigned long long whereAmI = f.tellg();
     228      995671 :     getline(f, currentLine);
     229      995671 :     if (f.fail()) {
     230           0 :       ostringstream oss ;
     231           0 :       oss << "SDMDataObjectStreamReader::nextLine() : I could not read a line in '" << path <<  "' at position " << whereAmI << ".";
     232           0 :       throw SDMDataObjectStreamReaderException(oss.str());
     233           0 :     }
     234             :     // cout << "nextLine has read '" << currentLine << "'" << endl;
     235      995671 :     return currentLine;
     236             :   }
     237             : 
     238      302360 :   pair<string, string> SDMDataObjectStreamReader::headerField2Pair(const string& hf){
     239      302360 :     string name, value;
     240      302360 :     size_t colonIndex = hf.find(":");
     241      302360 :     if (colonIndex == string::npos)
     242           0 :       throw SDMDataObjectStreamReaderException(" could not detect a well formed MIME header field in '"+hf+"'");
     243             : 
     244      302360 :     if (colonIndex > 0) {
     245      302360 :       name = hf.substr(0, colonIndex);
     246             : #ifndef WITHOUT_BOOST
     247             :       boost::algorithm::trim(name);
     248             : #else
     249      302360 :       asdm::trim(name);
     250             : #endif
     251             :     }
     252             : 
     253      302360 :     if (colonIndex < hf.size()) {
     254      302360 :       value = hf.substr(colonIndex+1);
     255             : #ifndef WITHOUT_BOOST
     256             :       boost::algorithm::trim(value);
     257             : #else
     258      302360 :       asdm::trim(value);
     259             : #endif
     260             :     }
     261             : 
     262      604720 :     return make_pair(name, value);
     263      302360 :   }
     264             : 
     265      299775 :   pair<string, string> SDMDataObjectStreamReader::requireHeaderField(const string & hf) {
     266      299775 :     pair<string, string> hf2pair(headerField2Pair(nextLine()));
     267             :     // cout << hf2pair.first << ", " << hf2pair.second << endl;
     268             : #ifndef WITHOUT_BOOST
     269             :     if (boost::algorithm::to_upper_copy(hf2pair.first) != hf)
     270             :       throw SDMDataObjectStreamReaderException("read '" + currentLine + "'. Was expecting '" + hf + "'...");
     271             : #else
     272      299775 :     if (asdm::str_toupper(hf2pair.first) != hf)
     273           0 :       throw SDMDataObjectStreamReaderException("read '" + currentLine + "'. Was expecting '" + hf + "'...");
     274             : #endif
     275      299775 :     return hf2pair;
     276           0 :   }
     277             : 
     278       44303 :   string unquote(const string& s, string& unquoted) {
     279       44303 :     if (s.size() >= 2) 
     280       44303 :       if (((s.at(0) == '"') && (s.at(s.size()-1) == '"')) || ((s.at(0) == '\'') && (s.at(s.size()-1) == '\''))) {
     281       42630 :         if (s.size() == 2)
     282           0 :           unquoted = "";
     283             :         else
     284       42630 :           unquoted = s.substr(1, s.size() - 2);
     285             :       }
     286             :       else
     287        1673 :         unquoted = s;
     288             :     else
     289           0 :       unquoted = s;
     290       44303 :     return unquoted;
     291             :   }
     292             :   
     293             : 
     294             : 
     295       44303 :   string SDMDataObjectStreamReader::requireBoundaryInCT(const string& ctValue) {
     296       44303 :     vector<string> cvValueItems;
     297             :  
     298             : #ifndef WITHOUT_BOOST
     299             :     boost::algorithm::split (cvValueItems, ctValue, boost::algorithm::is_any_of(";"));
     300             :     vector<string> cvValueItemsNameValue;
     301             :     for ( vector<string>::const_iterator iter = cvValueItems.begin(); iter != cvValueItems.end() ; iter++ ) {
     302             :       cvValueItemsNameValue.clear();
     303             :       boost::algorithm::split(cvValueItemsNameValue, *iter, boost::algorithm::is_any_of("="));
     304             :       string boundary;
     305             :       if ((cvValueItemsNameValue.size() > 1) && (boost::algorithm::to_upper_copy(boost::algorithm::trim_copy(cvValueItemsNameValue[0])) == "BOUNDARY") && (unquote(cvValueItemsNameValue[1], boundary).size() > 0))
     306             :         return boundary;
     307             :     }
     308             : #else
     309       44303 :     asdm::strsplit (ctValue, ';', cvValueItems);
     310       44303 :     vector<string> cvValueItemsNameValue;
     311       88606 :     for ( vector<string>::const_iterator iter = cvValueItems.begin(); iter != cvValueItems.end() ; iter++ ) {
     312       88606 :       cvValueItemsNameValue.clear();
     313       88606 :       asdm::strsplit(*iter,'=',cvValueItemsNameValue);
     314       88606 :       string boundary;
     315       88606 :       if ((cvValueItemsNameValue.size() > 1) && (asdm::str_toupper(asdm::trim_copy(cvValueItemsNameValue[0])) == "BOUNDARY") && (unquote(cvValueItemsNameValue[1], boundary).size() > 0))
     316       88606 :         return boundary;
     317       88606 :     }
     318             : #endif
     319           0 :     throw SDMDataObjectStreamReaderException("could not find a boundary definition in '" + ctValue + "'.");
     320       44303 :   }
     321             : 
     322       41718 :   void SDMDataObjectStreamReader::skipAsLongAsLineStartsWith(const string& start) {
     323       41718 :     int64_t curpos = 0;
     324             :     do {
     325       41718 :       curpos = position();
     326       41718 :       nextLine();
     327             :     }
     328       41718 :     while (currentLine.find(start) == 0);
     329       41718 :     position(curpos);
     330       41718 :     return;
     331             :   }
     332             : 
     333      151180 :   void SDMDataObjectStreamReader::skipUntilEmptyLine(int maxSkips) {
     334             :     // cout << "Entering skipUntilEmptyLine" << endl;
     335      151180 :     int numSkips = 0;
     336      151180 :     string line = nextLine();
     337      247146 :     while ((line.size() != 0) && (numSkips <= maxSkips)) {
     338       95966 :       line = nextLine();
     339       95966 :       numSkips += 1;
     340             :     }
     341             : 
     342      151180 :     if (numSkips > maxSkips) {
     343           0 :       ostringstream oss;
     344           0 :       oss << "could not find an empty line is less than " << maxSkips + 1 << " lines." << endl;
     345           0 :       throw SDMDataObjectStreamReaderException(oss.str());
     346           0 :     } 
     347             :     // cout << "Exiting skipUntilEmptyLine" << endl;
     348      151180 :   }
     349             : 
     350             : 
     351        2585 :   string SDMDataObjectStreamReader::requireMIMEHeader() {
     352             :     // MIME-Version
     353        2585 :     pair<string, string>name_value(headerField2Pair(nextLine()));
     354             :     // cout << name_value.first << "=" << name_value.second << endl;
     355             :     // if (currentLine != "MIME-Version: 1.0") // a work around for the case when the very first character is not the expected "M" (happened with some corrupted data).
     356             : #ifndef WITHOUT_BOOST
     357             :     if (! boost::algorithm::iends_with(currentLine, "IME-Version: 1.0"))
     358             : #else
     359        2585 :     std::string versionEnd = "IME-Version: 1.0";
     360        2585 :     if ((currentLine.size()<=versionEnd.size()) || (currentLine.compare((currentLine.size()-versionEnd.size()),versionEnd.size(),versionEnd)!=0))
     361             : #endif
     362           0 :       throw SDMDataObjectStreamReaderException("'MIME-Version: 1.0' missing at the very beginning of the file '"+path+"'.");
     363             : 
     364             :     // Content-Type
     365        2585 :     boundary_1 = requireBoundaryInCT(requireHeaderField("CONTENT-TYPE").second);
     366             : 
     367             :     // cout << "boundary_1 =" << boundary_1 << endl;
     368             : 
     369             :     // Content-Description
     370        2585 :     name_value = requireHeaderField("CONTENT-DESCRIPTION");
     371             : 
     372             :     // Content-Location
     373        2585 :     name_value = requireHeaderField("CONTENT-LOCATION");
     374             : 
     375             :     // Look for an empty line in the at most 10 subsequent lines.
     376        2585 :     skipUntilEmptyLine(10);
     377             : 
     378        5170 :     return boundary_1;
     379        2585 :   }
     380             : 
     381       44303 :   string SDMDataObjectStreamReader::accumulateUntilBoundary(const string& boundary, int maxLines) {
     382             :     // cout << "Entering accumulateUntilBoundary with maxLines = " << maxLines << endl;
     383       44303 :     int numLines = 0;
     384       44303 :     string line = nextLine();
     385       44303 :     string result;
     386      108230 :     while ((numLines <= maxLines) && (line.find("--"+boundary) == string::npos)) {
     387       63927 :       result += line;
     388       63927 :       numLines++;
     389       63927 :       line = nextLine();
     390             :     }
     391             : 
     392       44303 :     if (numLines > maxLines) {
     393           0 :       ostringstream oss;
     394           0 :       oss << "could not find the boundary string '"<< boundary << "' in less than " << maxLines + 1 << " lines." << endl;
     395           0 :       throw SDMDataObjectStreamReaderException(oss.str());    
     396           0 :     }
     397             : 
     398       88606 :     return result;
     399       44303 :   }
     400             : 
     401       44303 :   void SDMDataObjectStreamReader::requireBoundary(const string& boundary, int maxLines) {
     402             :     // cout << "Entering require boundary with boundary == '" << boundary << "' and maxLines = " << maxLines << endl; 
     403       44303 :     int numLines = 0;
     404       44303 :     string dashdashBoundary = "--"+boundary;
     405       44303 :     string line = nextLine();
     406       45915 :     while ((numLines <= maxLines) && (line.compare(dashdashBoundary) != 0)) {
     407        1612 :       numLines++;
     408        1612 :       line = nextLine();
     409             :     }
     410             : 
     411       44303 :     if (numLines > maxLines) {
     412           0 :       ostringstream oss;
     413           0 :       oss << "could not find the boundary string '"<< boundary << "' in less than " << maxLines + 1 << " lines." << endl;
     414           0 :       throw SDMDataObjectStreamReaderException(oss.str());
     415           0 :     }
     416       44303 :   }
     417             : 
     418           0 :   void SDMDataObjectStreamReader::lookForBinaryPartSize(xmlNode* aNode) {
     419             : #ifndef WITHOUT_BOOST
     420             :     const boost::regex UINT("[0-9]+");
     421             : #else
     422           0 :     const std::regex UINT("[0-9]+");
     423             : #endif
     424           0 :     xmlNode *curNode = NULL;
     425             : 
     426           0 :     for (curNode = aNode; curNode ; curNode = curNode->next) {
     427           0 :       if (curNode->type == XML_ELEMENT_NODE) {
     428           0 :         if (s_partNames.find(string((char *)curNode->name)) != s_partNames.end()){
     429           0 :           if (xmlHasProp(curNode, (const xmlChar*) "size")) {
     430           0 :             xmlChar * value = xmlGetProp(curNode, (const xmlChar *) "size");
     431             : #ifndef WITHOUT_BOOST
     432             :             boost::cmatch what;
     433             :             if (boost::regex_match((char*) value, what, UINT)) {
     434             : #else
     435           0 :             std::cmatch what;
     436           0 :             if (std::regex_match((char*) value, what, UINT)) {
     437             : #endif
     438           0 :               int64_t result = ::atoi(what[0].first);
     439           0 :               xmlFree(value);
     440           0 :               binaryPartSize[string((char *) curNode->name)] = result;
     441             :             }
     442             :             else {
     443           0 :               xmlFree(value);
     444           0 :               throw SDMDataObjectStreamReaderException("In '" + string((const char*) curNode->name) + "' failed to parse the value of '"+string((const char*) value)+"' as an int.");
     445             :             }    
     446           0 :           }
     447             :           else {
     448           0 :             throw SDMDataObjectStreamReaderException("In '" + string((const char*) curNode->name) + "' could not find the attribute 'size'.");
     449             :           }
     450             :         }
     451             :       }
     452           0 :       lookForBinaryPartSize(curNode->children);
     453             :     }
     454           0 :   }
     455             : 
     456           0 :   string SDMDataObjectStreamReader::requireCrossDataType(xmlNode* parent) {
     457           0 :     string result;
     458             : 
     459           0 :     string comparee("crossData");
     460           0 :     xmlNode * child = parent->children;
     461             : 
     462           0 :     while ((child != 0) && (comparee.compare((const char*) child->name) != 0))
     463           0 :       child = child->next;
     464             : 
     465           0 :     if ((child == 0) || (child->type != XML_ELEMENT_NODE)) {
     466           0 :       ostringstream oss;
     467           0 :       oss << "could not find the element 'crossData'." << endl;
     468           0 :       throw SDMDataObjectStreamReaderException(oss.str());
     469           0 :     }
     470             : 
     471           0 :     if (xmlHasProp(child, (const xmlChar*) "type")) {
     472           0 :       xmlChar * value = xmlGetProp(child, (const xmlChar *) "type");
     473           0 :       result = string((const char *) value);
     474           0 :       xmlFree(value);
     475           0 :       return result;
     476             :     }
     477             :     else
     478           0 :       throw SDMDataObjectStreamReaderException("In '" + string((const char*) child->name) + "' could not find the attribute 'type'.");
     479           0 :   }
     480             : 
     481           0 :   void SDMDataObjectStreamReader::printElementNames(xmlNode * a_node) {
     482           0 :     xmlNode *cur_node = NULL;
     483             : 
     484           0 :     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
     485           0 :       if (cur_node->type == XML_ELEMENT_NODE) {
     486           0 :         cout << "node type: Element, name: " << cur_node->name << endl;
     487             :       }
     488           0 :       printElementNames(cur_node->children);
     489             :     }
     490           0 :   }
     491             : 
     492        2585 :   void SDMDataObjectStreamReader::requireSDMDataHeaderMIMEPart() {
     493             :     //
     494             :     // Requires the presense of boundary_1
     495        2585 :     requireBoundary(boundary_1, 0);
     496             : 
     497             :     // Ignore header fields
     498             :     // requireHeaderField("CONTENT-TYPE")
     499             :     // requireHeaderField("CONTENT-TRANSFER-ENCODING")
     500             :     // requireHeaderField("CONTENT-LOCATION")
     501             : 
     502             :     //
     503             :     // Look for an empty line at most distant from 100 lines from here.
     504        2585 :     skipUntilEmptyLine(10);
     505        2585 :     string sdmDataHeader = accumulateUntilBoundary(boundary_1, 100);
     506             :  
     507             :     /*
     508             :     xmlDoc * doc = xmlReadMemory(sdmDataHeader.data(), sdmDataHeader.size(),  "SDMDataHeader.xml", NULL, XML_PARSE_NOBLANKS);
     509             :     xmlChar* xmlBuff;
     510             :     int bufferSize;
     511             :     xmlDocDumpFormatMemory(doc, &xmlBuff, &bufferSize, 1);
     512             :     cout << string((const char*) xmlBuff, bufferSize) << endl;
     513             :     xmlFree(xmlBuff);
     514             :     xmlNode* root_element = xmlDocGetRootElement(doc);
     515             :     if ( root_element == NULL || root_element->type != XML_ELEMENT_NODE )
     516             :       throw SDMDataObjectStreamReaderException("Failed to parse the SDMDataHeader into a DOM structure."); 
     517             :     */
     518             :  
     519        2585 :     parser.parseMemoryHeader(sdmDataHeader, sdmDataObject);
     520             :     unsigned int bps;
     521        2585 :     if ((bps = sdmDataObject.dataStruct().flags().size()))
     522        2443 :       binaryPartSize["flags"] = bps;
     523             : 
     524        2585 :     if ((bps = sdmDataObject.dataStruct().actualTimes().size()))
     525        1994 :       binaryPartSize["actualTimes"] = bps;
     526             : 
     527        2585 :     if ((bps = sdmDataObject.dataStruct().actualDurations().size()))
     528        1994 :       binaryPartSize["actualDurations"] = bps;
     529             : 
     530        2585 :     if ((bps = sdmDataObject.dataStruct().zeroLags().size()))
     531         460 :       binaryPartSize["zeroLags"] = bps;
     532             : 
     533        2585 :     if ((bps = sdmDataObject.dataStruct().autoData().size()))
     534        2511 :       binaryPartSize["autoData"] = bps;
     535             :     
     536        2585 :     if ((bps = sdmDataObject.dataStruct().crossData().size()))
     537         804 :       binaryPartSize["crossData"] = bps;
     538        2585 :   }
     539             : 
     540       41718 :   void SDMDataObjectStreamReader::requireSDMDataSubsetMIMEPart(SDMDataSubset & sdmDataSubset) {
     541       41718 :     integrationStartsAt = f.tellg();
     542             :     
     543       41718 :     skipAsLongAsLineStartsWith("--"+boundary_1);  // <--- this was added to preserve compatiblity with SDMDataObjectReader, which
     544             :                                                   // was less strict on the respect of the BDF specifications. (cf CAS-8151)
     545             :                                                   // Here the problem was caused by two successive occurrences of --MIME_boundary_1 
     546             :                                                   // instead of only one as indicated in the specs at the very beginning of
     547             :                                                   // an SDMDataSubsetHeader. M.Caillat - 4 decembre 2015
     548       83436 :     pair<string, string> name_value = requireHeaderField("CONTENT-TYPE");
     549       41718 :     boundary_2 = requireBoundaryInCT(name_value.second);
     550             :     // cout << "boundary_2 = " << boundary_2 << endl;
     551       41718 :     name_value = requireHeaderField("CONTENT-DESCRIPTION");
     552       41718 :     requireBoundary(boundary_2, 10);
     553       41718 :     skipUntilEmptyLine(10);
     554             : 
     555             :     //
     556             :     // We assume that the subset header can't be longer than 100 lines.
     557             :     //
     558       41718 :     string sdmDataSubsetHeader = accumulateUntilBoundary(boundary_2, 100);
     559             : 
     560             :     //
     561             :     // Empty sdmDataSubset if necessary.
     562             :     //
     563       41718 :     releaseMemory(sdmDataSubset);
     564             : 
     565             :     // We can start to acquire a new SDMDataSubset.
     566       41718 :     if (sdmDataObject.isCorrelation())
     567       41201 :       parser.parseMemoryCorrSubsetHeader(sdmDataSubsetHeader, sdmDataSubset);
     568             :     else
     569         517 :       parser.parseMemoryTPSubsetHeader(sdmDataSubsetHeader, sdmDataSubset);
     570             : 
     571       41718 :     attachmentFlags.reset();
     572             : #ifndef WITHOUT_BOOST
     573             :     boost::regex BINARYPARTLOC("([0-9]+/)+(actualDurations|actualTimes|autoData|crossData|zeroLags|flags)\\.bin");
     574             : #else
     575       41718 :     std::regex BINARYPARTLOC("([0-9]+/)+(actualDurations|actualTimes|autoData|crossData|zeroLags|flags)\\.bin");
     576             : #endif
     577       41718 :     bool done = false;
     578      146010 :     while (!done) {
     579      104292 :       name_value = requireHeaderField("CONTENT-TYPE");
     580      104292 :       name_value = requireHeaderField("CONTENT-LOCATION");
     581             :     
     582             : #ifndef WITHOUT_BOOST
     583             :       boost::smatch what;
     584             :       const string contentLocation = boost::algorithm::trim_copy(name_value.second);
     585             :       if (!boost::regex_search(contentLocation, what, BINARYPARTLOC)) {
     586             :         throw SDMDataObjectStreamReaderException("Invalid field : '" + name_value.first + ":" + name_value.second + "'.");
     587             :       }
     588             : #else
     589      104292 :       std::smatch what;
     590      104292 :       const string contentLocation = asdm::trim_copy(name_value.second);
     591      104292 :       if (!std::regex_search(contentLocation, what, BINARYPARTLOC)) {
     592           0 :         throw SDMDataObjectStreamReaderException("Invalid field : '" + name_value.first + ":" + name_value.second + "'.");
     593             :       }
     594             : #endif
     595             :       // cout << "Binary part name = " << what[2] << "...";
     596      104292 :       string binaryPartName = string(what[2]);
     597      104292 :       if (binaryPartSize.find(binaryPartName) == binaryPartSize.end())
     598           0 :         throw SDMDataObjectStreamReaderException("The size of '"+binaryPartName+"' was not announced in the data header.!");
     599             : 
     600      104292 :       if (binaryPartSize[binaryPartName] == 0)
     601           0 :         throw SDMDataObjectStreamReaderException("The size of '"+binaryPartName+"' was announced as null. I was not expecting a '"+binaryPartName+"' attachment here.");
     602             : 
     603      104292 :       skipUntilEmptyLine(10);
     604      104292 :       int numberOfCharsPerValue = 0;
     605      104292 :       char** binaryPartPtrPtr = 0;
     606      104292 :       if (binaryPartName == "actualDurations") {
     607        1468 :         attachmentFlags.set(ACTUALDURATIONS);
     608        1468 :         binaryPartPtrPtr = (char**) &sdmDataSubset.actualDurations_;
     609        1468 :         sdmDataSubset.nActualDurations_ = binaryPartSize[binaryPartName];
     610        1468 :         sdmDataSubset.actualDurationsPosition_ = f.tellg();
     611        1468 :         numberOfCharsPerValue = 8;
     612             :       }
     613      102824 :       else if (binaryPartName == "actualTimes") {
     614        1468 :         attachmentFlags.set(ACTUALTIMES);
     615        1468 :         binaryPartPtrPtr = (char **) &sdmDataSubset.actualTimes_;
     616        1468 :         sdmDataSubset.nActualTimes_ = binaryPartSize[binaryPartName];
     617        1468 :         sdmDataSubset.actualTimesPosition_ = f.tellg();
     618        1468 :         numberOfCharsPerValue = 8;
     619             :       }
     620      101356 :       else if (binaryPartName == "autoData") {
     621       41606 :         attachmentFlags.set(AUTODATA);
     622       41606 :         binaryPartPtrPtr = (char **) &sdmDataSubset.autoData_;
     623       41606 :         sdmDataSubset.nAutoData_ = binaryPartSize[binaryPartName];
     624       41606 :         sdmDataSubset.autoDataPosition_ = f.tellg();
     625       41606 :         numberOfCharsPerValue = 4;
     626             :       }
     627       59750 :       else if (binaryPartName == "crossData") {
     628       27831 :         attachmentFlags.set(CROSSDATA);
     629       27831 :         sdmDataSubset.shortCrossData_ = 0;
     630       27831 :         sdmDataSubset.longCrossData_  = 0;
     631       27831 :         sdmDataSubset.floatCrossData_ = 0;
     632       27831 :         sdmDataSubset.nCrossData_ = binaryPartSize[binaryPartName];
     633       27831 :         sdmDataSubset.crossDataPosition_ = f.tellg();
     634       27831 :         PrimitiveDataType pdt = sdmDataSubset.crossDataType();
     635       27831 :         switch (pdt) {
     636       23155 :         case  INT16_TYPE:
     637       23155 :           binaryPartPtrPtr = (char **) &sdmDataSubset.shortCrossData_;
     638       23155 :           numberOfCharsPerValue = 2;
     639       23155 :           break;
     640        2952 :         case INT32_TYPE:
     641        2952 :           binaryPartPtrPtr = (char **) &sdmDataSubset.longCrossData_;
     642        2952 :           numberOfCharsPerValue = 4;
     643        2952 :           break;
     644        1724 :         case FLOAT32_TYPE:
     645        1724 :           binaryPartPtrPtr = (char **) &sdmDataSubset.floatCrossData_;
     646        1724 :           numberOfCharsPerValue = 4;
     647        1724 :             break;
     648           0 :         default:
     649           0 :           throw SDMDataObjectStreamReaderException("Invalid data type for cross data '" + CPrimitiveDataType::name(pdt) + "'.");
     650             :         }
     651             :       }
     652       31919 :       else if (binaryPartName == "flags") {
     653        9760 :         attachmentFlags.set(FLAGS);
     654        9760 :         binaryPartPtrPtr = (char **) &sdmDataSubset.flags_;
     655        9760 :         sdmDataSubset.nFlags_ = binaryPartSize[binaryPartName];
     656        9760 :         sdmDataSubset.flagsPosition_ = f.tellg();
     657        9760 :         numberOfCharsPerValue = 4;
     658             :       }
     659       22159 :       else if (binaryPartName == "zeroLags") {
     660       22159 :         attachmentFlags.set(ZEROLAGS);
     661       22159 :         binaryPartPtrPtr = (char **) &sdmDataSubset.zeroLags_;
     662       22159 :         sdmDataSubset.nZeroLags_ = binaryPartSize[binaryPartName];
     663       22159 :         sdmDataSubset.zeroLagsPosition_ = f.tellg();
     664       22159 :         numberOfCharsPerValue = 4;
     665             :       }
     666             : 
     667      104292 :       int64_t numberOfCharsToRead = numberOfCharsPerValue * binaryPartSize[binaryPartName];
     668      104292 :       *binaryPartPtrPtr = new char[numberOfCharsToRead * sizeof(char)];
     669      104292 :       if (*binaryPartPtrPtr == 0) {
     670           0 :         ostringstream oss;
     671           0 :         oss << "Processing integration # " << integrationIndex << ": I could not get memory to store '" << binaryPartName << "'." << endl;
     672           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     673           0 :       }
     674             : 
     675      104292 :       f.read(*binaryPartPtrPtr, numberOfCharsToRead * sizeof(char));
     676      104292 :       if (f.fail()) {
     677           0 :         ostringstream oss;
     678           0 :         oss << "Processing integration # " << integrationIndex << ": a problem occurred while reading '" << binaryPartName << "'." << endl;
     679           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     680           0 :       }
     681      104292 :       if (f.eof()) {
     682           0 :         ostringstream oss;
     683           0 :         oss << "Processing integration # " << integrationIndex << ": a unexpected end of file occurred while reading '" << binaryPartName  << "'." << endl;
     684           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     685           0 :       }
     686             : 
     687      104292 :       string line = nextLine(); // Absorb the nl right after the last byte of the binary attachment
     688      104292 :       line = nextLine();   // This should boundary_2                            
     689             :    
     690      104292 :       if (line.find("--" + boundary_2) != 0) {
     691           0 :         ostringstream oss;
     692           0 :         oss << "Processing integration # " << integrationIndex << ": unexpected '" <<  line << "' after the binary part '" << binaryPartName << "'." << endl;
     693           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     694           0 :       }
     695             : 
     696      104292 :       done = line.compare("--" + boundary_2+"--") == 0;
     697      104292 :     }
     698             : 
     699             :     // Now check if the binary attachments found are compatible with the correlation mode
     700             :     // and if their sizes are equal to what is announced in the global header.
     701             :     //
     702             :     // The presence of crossData and autoData depends on the correlation mode.
     703             :     
     704       41718 :     switch (sdmDataObject.correlationMode()) {  
     705         112 :     case CROSS_ONLY:
     706         112 :       if (!attachmentFlags.test(CROSSDATA)) {
     707           0 :         ostringstream oss;
     708           0 :         oss << "Data subset '"<<sdmDataSubset.projectPath()<<"': ";
     709           0 :         oss << "a binary attachment 'crossData' was expected in integration #" << integrationIndex;
     710           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     711           0 :       }
     712             :       
     713         112 :       if (attachmentFlags.test(AUTODATA)) {
     714           0 :         ostringstream oss;
     715           0 :         oss << "Data subset '"<<sdmDataSubset.projectPath()<<"': ";
     716           0 :         oss << "found an unexpected attachment 'autoData' in integration #" << integrationIndex << ".";
     717           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     718           0 :       }
     719         112 :       break;
     720             :       
     721       13887 :     case AUTO_ONLY:
     722       13887 :       if (!attachmentFlags.test(AUTODATA)) {
     723           0 :         ostringstream oss;
     724           0 :         oss << "Data subset '"<<sdmDataSubset.projectPath()<<"': ";
     725           0 :         oss << "a binary attachment 'autoData' was expected.in integration #" << integrationIndex << ".";
     726           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     727           0 :       }
     728             : 
     729       13887 :       if (attachmentFlags.test(CROSSDATA)) {
     730           0 :         ostringstream oss;
     731           0 :         oss << "Data subset '"<<sdmDataSubset.projectPath()<<"': ";
     732           0 :         oss << "found an unexpected attachment 'crossData' in integration #" << integrationIndex << ".";
     733           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     734           0 :       }
     735       13887 :       break;
     736             :       
     737       27719 :     case CROSS_AND_AUTO:
     738       27719 :       if (!attachmentFlags.test(AUTODATA)) {
     739           0 :         ostringstream oss;
     740           0 :         oss << "Data subset '"<<sdmDataSubset.projectPath()<<"': ";
     741           0 :         oss << "a binary attachment 'autoData' was expected in integration #" << integrationIndex << ".";
     742           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     743           0 :       }
     744             :       
     745       27719 :       if (!attachmentFlags.test(CROSSDATA)) {
     746           0 :         ostringstream oss;
     747           0 :         oss << "Data subset '"<<sdmDataSubset.projectPath()<<"': ";
     748           0 :         oss << "a binary attachment 'crossData' was expected in integration #" << integrationIndex << ".";
     749           0 :         throw SDMDataObjectStreamReaderException(oss.str());
     750           0 :       }      
     751       27719 :       break;
     752             : 
     753           0 :     default:
     754           0 :       throw SDMDataObjectStreamReaderException("Data subset '"+sdmDataSubset.projectPath()+"': unrecognized correlation mode");
     755             :       break;
     756             :     }
     757             :     
     758             :     
     759       41718 :     if (attachmentFlags.test(ZEROLAGS)) {
     760             :       // Refuse the zeroLags attachment if it's not a Correlator or if the correlator is a CORRELATOR_FX (ACA).
     761       22159 :       if ((sdmDataObject.processorType_ != CORRELATOR) || (sdmDataObject.correlatorType() == FX))
     762           0 :         throw SDMDataObjectStreamReaderException("zeroLags are not expected from a correlator CORRELATOR_FX");
     763             :     }
     764       41718 :   }
     765             : 
     766       82524 :   void SDMDataObjectStreamReader::releaseMemory(SDMDataSubset & sdmDataSubset) {
     767             :     // cout << "SDMDataObjectStreamReader::releaseMemory : entering." << endl;
     768       82524 :     if (sdmDataSubset.actualTimes_ != 0) {
     769             :     // cout << "actualTimes" << endl;
     770        1468 :       delete[] sdmDataSubset.actualTimes_;
     771        1468 :       sdmDataSubset.actualTimes_  = 0;    
     772             :     }
     773       82524 :     sdmDataSubset.nActualTimes_ = 0;
     774             : 
     775             : 
     776       82524 :     if (sdmDataSubset.actualDurations_ != 0) {
     777             :     // cout << "actualDurations" << endl;
     778        1468 :       delete[] sdmDataSubset.actualDurations_;
     779        1468 :       sdmDataSubset.actualDurations_  = 0;    
     780             :     }
     781       82524 :     sdmDataSubset.nActualDurations_ = 0;
     782             : 
     783       82524 :     if (sdmDataSubset.flags_ != 0) {
     784             :     // cout << "Flags" << endl;
     785        9760 :       delete[] sdmDataSubset.flags_;
     786        9760 :       sdmDataSubset.flags_  = 0;    
     787             :     }
     788       82524 :     sdmDataSubset.nFlags_ = 0;
     789             : 
     790       82524 :     if (sdmDataSubset.zeroLags_ != 0) {
     791             :     // cout << "zeroLags" << endl;
     792       22159 :       delete[] sdmDataSubset.zeroLags_;
     793       22159 :       sdmDataSubset.zeroLags_  = 0;    
     794             :     }
     795       82524 :     sdmDataSubset.nZeroLags_ = 0;
     796             : 
     797       82524 :     if (sdmDataSubset.autoData_ != 0) {
     798             :       //cout << "autoData_ = " << sdmDataSubset.autoData_ << " before deletion" <<  endl;
     799       41606 :       delete[] sdmDataSubset.autoData_;
     800       41606 :       sdmDataSubset.autoData_  = 0;    
     801             :     }
     802       82524 :     sdmDataSubset.nAutoData_ = 0;
     803             : 
     804       82524 :     if (sdmDataSubset.shortCrossData_ != 0) {
     805             :       //cout << "shortCrossData_ = " << sdmDataSubset.shortCrossData_ << " before deletion" <<  endl;
     806       23155 :       delete[] sdmDataSubset.shortCrossData_;
     807       23155 :       sdmDataSubset.shortCrossData_ = 0;
     808             :     }
     809             : 
     810       82524 :     if (sdmDataSubset.longCrossData_ != 0) {
     811             :     // cout << "longCrossData" << endl;
     812        2952 :       delete[] sdmDataSubset.longCrossData_;
     813        2952 :       sdmDataSubset.longCrossData_ = 0;
     814             :     }
     815             : 
     816       82524 :     if (sdmDataSubset.floatCrossData_ != 0) {
     817             :     // cout << "floatCrossData" << endl;
     818        1724 :       delete[] sdmDataSubset.floatCrossData_;
     819        1724 :       sdmDataSubset.floatCrossData_ = 0;
     820             :     }
     821       82524 :     sdmDataSubset.nCrossData_ = 0;
     822             : 
     823             :   // cout << "SDMDataObjectStreamReader::releaseMemory : exiting." << endl;
     824       82524 :     return;
     825             :   }
     826             : }  // end namespace asdmbinaries
     827             : 
     828             : using namespace asdmbinaries;
     829             : 
     830             : #ifdef TEST_CLASS
     831             : int main (int argC, char* argV[]) {
     832             :   if (argC < 2) {
     833             :     cout << "a.out filename" << endl;
     834             :     exit(1);
     835             :   }
     836             : 
     837             :   SDMDataObjectStreamReader ssr;
     838             :   try {
     839             :     ssr.open(argV[1]);
     840             :     cout << ssr.toString() << endl;
     841             :     while (ssr.hasSubset()) {
     842             :       const SDMDataSubset & sdmdss = ssr.getSubset();
     843             :       cout << "block of data #" << ssr.currentIntegrationIndex() << " starting at byte #" << ssr.currentIntegrationStartsAt() << endl; 
     844             :       cout << sdmdss.toString(128) << endl;
     845             :     }
     846             :   }
     847             :   catch (SDMDataObjectStreamReaderException& e) {
     848             :     cout << e.getMessage() << endl;
     849             :   }
     850             :   catch (SDMDataObjectParserException& e) {
     851             :     cout << e.getMessage() << endl;
     852             :   }
     853             :   catch (SDMDataObjectException& e) {
     854             :     cout << e.getMessage() << endl;
     855             :   }
     856             : }
     857             : #endif

Generated by: LCOV version 1.16