LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - filegdbtable_priv.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 180 189 95.2 %
Date: 2024-11-21 22:18:42 Functions: 28 28 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * $Id$
       3             :  *
       4             :  * Project:  OpenGIS Simple Features Reference Implementation
       5             :  * Purpose:  Implements reading of FileGDB tables
       6             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #ifndef FILEGDBTABLE_PRIV_H_INCLUDED
      15             : #define FILEGDBTABLE_PRIV_H_INCLUDED
      16             : 
      17             : #include "filegdbtable.h"
      18             : 
      19             : #include "cpl_conv.h"
      20             : #include "cpl_error.h"
      21             : #include "cpl_time.h"
      22             : 
      23             : #include <algorithm>
      24             : #include <cwchar>
      25             : #include <vector>
      26             : #include <limits>
      27             : 
      28             : #define DIV_ROUND_UP(a, b) (((a) % (b)) == 0 ? ((a) / (b)) : (((a) / (b)) + 1))
      29             : 
      30             : #define TEST_BIT(ar, bit) (ar[(bit) / 8] & (1 << ((bit) % 8)))
      31             : #define BIT_ARRAY_SIZE_IN_BYTES(bitsize) (((bitsize) + 7) / 8)
      32             : 
      33             : namespace OpenFileGDB
      34             : {
      35             : 
      36             : /************************************************************************/
      37             : /*                              GetInt16()                              */
      38             : /************************************************************************/
      39             : 
      40        7680 : inline GInt16 GetInt16(const GByte *pBaseAddr, int iOffset)
      41             : {
      42             :     GInt16 nVal;
      43        7680 :     memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
      44        7680 :     CPL_LSBPTR16(&nVal);
      45        7680 :     return nVal;
      46             : }
      47             : 
      48             : /************************************************************************/
      49             : /*                              GetUInt16()                             */
      50             : /************************************************************************/
      51             : 
      52        7350 : inline GUInt16 GetUInt16(const GByte *pBaseAddr, int iOffset)
      53             : {
      54             :     GUInt16 nVal;
      55        7350 :     memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
      56        7350 :     CPL_LSBPTR16(&nVal);
      57        7350 :     return nVal;
      58             : }
      59             : 
      60             : /************************************************************************/
      61             : /*                              GetInt32()                              */
      62             : /************************************************************************/
      63             : 
      64       50376 : inline GInt32 GetInt32(const GByte *pBaseAddr, int iOffset)
      65             : {
      66             :     GInt32 nVal;
      67       50376 :     memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
      68       50376 :     CPL_LSBPTR32(&nVal);
      69       50376 :     return nVal;
      70             : }
      71             : 
      72             : /************************************************************************/
      73             : /*                              GetUInt32()                             */
      74             : /************************************************************************/
      75             : 
      76      541255 : inline GUInt32 GetUInt32(const GByte *pBaseAddr, int iOffset)
      77             : {
      78             :     GUInt32 nVal;
      79      541255 :     memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
      80      541255 :     CPL_LSBPTR32(&nVal);
      81      541255 :     return nVal;
      82             : }
      83             : 
      84             : /************************************************************************/
      85             : /*                              GetInt64()                              */
      86             : /************************************************************************/
      87             : 
      88       83111 : inline int64_t GetInt64(const GByte *pBaseAddr, int iOffset)
      89             : {
      90             :     int64_t nVal;
      91       83111 :     memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
      92       83111 :     CPL_LSBPTR64(&nVal);
      93       83111 :     return nVal;
      94             : }
      95             : 
      96             : /************************************************************************/
      97             : /*                              GetUInt64()                             */
      98             : /************************************************************************/
      99             : 
     100        4588 : inline uint64_t GetUInt64(const GByte *pBaseAddr, int iOffset)
     101             : {
     102             :     uint64_t nVal;
     103        4588 :     memcpy(&nVal, pBaseAddr + sizeof(nVal) * iOffset, sizeof(nVal));
     104        4588 :     CPL_LSBPTR64(&nVal);
     105        4588 :     return nVal;
     106             : }
     107             : 
     108             : /************************************************************************/
     109             : /*                             GetFloat32()                             */
     110             : /************************************************************************/
     111             : 
     112        7696 : inline float GetFloat32(const GByte *pBaseAddr, int iOffset)
     113             : {
     114             :     float fVal;
     115        7696 :     memcpy(&fVal, pBaseAddr + sizeof(fVal) * iOffset, sizeof(fVal));
     116        7696 :     CPL_LSBPTR32(&fVal);
     117        7696 :     return fVal;
     118             : }
     119             : 
     120             : /************************************************************************/
     121             : /*                             GetFloat64()                             */
     122             : /************************************************************************/
     123             : 
     124       74087 : inline double GetFloat64(const GByte *pBaseAddr, int iOffset)
     125             : {
     126             :     double dfVal;
     127       74087 :     memcpy(&dfVal, pBaseAddr + sizeof(dfVal) * iOffset, sizeof(dfVal));
     128       74087 :     CPL_LSBPTR64(&dfVal);
     129       74087 :     return dfVal;
     130             : }
     131             : 
     132             : /************************************************************************/
     133             : /*                          ReadUInt32()                                */
     134             : /************************************************************************/
     135             : 
     136        7999 : inline bool ReadUInt32(VSILFILE *fp, uint32_t &nVal)
     137             : {
     138        7999 :     const bool bRet = VSIFReadL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
     139        7999 :     CPL_LSBPTR32(&nVal);
     140        7999 :     return bRet;
     141             : }
     142             : 
     143             : /************************************************************************/
     144             : /*                          WriteUInt32()                               */
     145             : /************************************************************************/
     146             : 
     147       76906 : inline bool WriteUInt32(VSILFILE *fp, uint32_t nVal)
     148             : {
     149       76906 :     CPL_LSBPTR32(&nVal);
     150       76906 :     return VSIFWriteL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                          WriteUInt64()                               */
     155             : /************************************************************************/
     156             : 
     157       10472 : inline bool WriteUInt64(VSILFILE *fp, uint64_t nVal)
     158             : {
     159       10472 :     CPL_LSBPTR64(&nVal);
     160       10472 :     return VSIFWriteL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
     161             : }
     162             : 
     163             : /************************************************************************/
     164             : /*                          WriteFloat64()                               */
     165             : /************************************************************************/
     166             : 
     167         650 : inline bool WriteFloat64(VSILFILE *fp, double dfVal)
     168             : {
     169         650 :     CPL_LSBPTR64(&dfVal);
     170         650 :     return VSIFWriteL(&dfVal, 1, sizeof(dfVal), fp) == sizeof(dfVal);
     171             : }
     172             : 
     173             : /************************************************************************/
     174             : /*                          WriteUInt32()                               */
     175             : /************************************************************************/
     176             : 
     177       21757 : inline void WriteUInt32(std::vector<GByte> &abyBuffer, uint32_t nVal)
     178             : {
     179       21757 :     CPL_LSBPTR32(&nVal);
     180       21757 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&nVal);
     181       21757 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(nVal));
     182       21757 : }
     183             : 
     184             : /************************************************************************/
     185             : /*                          WriteUInt32()                               */
     186             : /************************************************************************/
     187             : 
     188       10190 : inline void WriteUInt32(std::vector<GByte> &abyBuffer, uint32_t nVal,
     189             :                         size_t nPos)
     190             : {
     191       10190 :     CPL_LSBPTR32(&nVal);
     192       10190 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&nVal);
     193       10190 :     memcpy(&abyBuffer[nPos], pabyInput, sizeof(nVal));
     194       10190 : }
     195             : 
     196             : /************************************************************************/
     197             : /*                          WriteFloat32()                               */
     198             : /************************************************************************/
     199             : 
     200          54 : inline void WriteFloat32(std::vector<GByte> &abyBuffer, float fVal)
     201             : {
     202          54 :     CPL_LSBPTR32(&fVal);
     203          54 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&fVal);
     204          54 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(fVal));
     205          54 : }
     206             : 
     207             : /************************************************************************/
     208             : /*                          WriteFloat64()                               */
     209             : /************************************************************************/
     210             : 
     211       13009 : inline void WriteFloat64(std::vector<GByte> &abyBuffer, double dfVal)
     212             : {
     213       13009 :     CPL_LSBPTR64(&dfVal);
     214       13009 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&dfVal);
     215       13009 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(dfVal));
     216       13009 : }
     217             : 
     218             : /************************************************************************/
     219             : /*                          WriteInt32()                                */
     220             : /************************************************************************/
     221             : 
     222        3841 : inline void WriteInt32(std::vector<GByte> &abyBuffer, int32_t nVal)
     223             : {
     224        3841 :     CPL_LSBPTR32(&nVal);
     225        3841 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&nVal);
     226        3841 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(nVal));
     227        3841 : }
     228             : 
     229             : /************************************************************************/
     230             : /*                          WriteInt64()                                */
     231             : /************************************************************************/
     232             : 
     233           3 : inline void WriteInt64(std::vector<GByte> &abyBuffer, int64_t nVal)
     234             : {
     235           3 :     CPL_LSBPTR64(&nVal);
     236           3 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&nVal);
     237           3 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(nVal));
     238           3 : }
     239             : 
     240             : /************************************************************************/
     241             : /*                          WriteUInt16()                               */
     242             : /************************************************************************/
     243             : 
     244        4507 : inline void WriteUInt16(std::vector<GByte> &abyBuffer, uint16_t nVal)
     245             : {
     246        4507 :     CPL_LSBPTR16(&nVal);
     247        4507 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&nVal);
     248        4507 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(nVal));
     249        4507 : }
     250             : 
     251             : /************************************************************************/
     252             : /*                          WriteInt16()                                */
     253             : /************************************************************************/
     254             : 
     255        3740 : inline void WriteInt16(std::vector<GByte> &abyBuffer, int16_t nVal)
     256             : {
     257        3740 :     CPL_LSBPTR16(&nVal);
     258        3740 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&nVal);
     259        3740 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(nVal));
     260        3740 : }
     261             : 
     262             : /************************************************************************/
     263             : /*                          WriteUInt8()                                */
     264             : /************************************************************************/
     265             : 
     266      202845 : inline void WriteUInt8(std::vector<GByte> &abyBuffer, uint8_t nVal)
     267             : {
     268      202845 :     abyBuffer.push_back(nVal);
     269      202845 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                          WriteUInt64()                               */
     273             : /************************************************************************/
     274             : 
     275        4061 : inline void WriteUInt64(std::vector<GByte> &abyBuffer, uint64_t nVal)
     276             : {
     277        4061 :     CPL_LSBPTR64(&nVal);
     278        4061 :     const GByte *pabyInput = reinterpret_cast<const GByte *>(&nVal);
     279        4061 :     abyBuffer.insert(abyBuffer.end(), pabyInput, pabyInput + sizeof(nVal));
     280        4061 : }
     281             : 
     282             : /************************************************************************/
     283             : /*                             WriteVarUInt()                           */
     284             : /************************************************************************/
     285             : 
     286      121449 : inline void WriteVarUInt(std::vector<GByte> &abyBuffer, uint64_t nVal)
     287             : {
     288             :     while (true)
     289             :     {
     290      121449 :         if (nVal >= 0x80)
     291             :         {
     292       54215 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(0x80 | (nVal & 0x7F)));
     293       54215 :             nVal >>= 7;
     294             :         }
     295             :         else
     296             :         {
     297       67234 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(nVal));
     298       67234 :             break;
     299             :         }
     300             :     }
     301       67234 : }
     302             : 
     303             : /************************************************************************/
     304             : /*                             WriteVarInt()                            */
     305             : /************************************************************************/
     306             : 
     307        3389 : inline void WriteVarInt(std::vector<GByte> &abyBuffer, int64_t nVal)
     308             : {
     309             :     uint64_t nUVal;
     310        3389 :     if (nVal < 0)
     311             :     {
     312         427 :         if (nVal == std::numeric_limits<int64_t>::min())
     313           0 :             nUVal = static_cast<uint64_t>(1) << 63;
     314             :         else
     315         427 :             nUVal = -nVal;
     316         427 :         if (nUVal >= 0x40)
     317             :         {
     318         427 :             WriteUInt8(abyBuffer,
     319         427 :                        static_cast<uint8_t>(0x80 | 0x40 | (nUVal & 0x3F)));
     320         427 :             nUVal >>= 6;
     321             :         }
     322             :         else
     323             :         {
     324           0 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(0x40 | (nUVal & 0x3F)));
     325           0 :             return;
     326             :         }
     327             :     }
     328             :     else
     329             :     {
     330        2962 :         nUVal = nVal;
     331        2962 :         if (nUVal >= 0x40)
     332             :         {
     333         705 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(0x80 | (nUVal & 0x3F)));
     334         705 :             nUVal >>= 6;
     335             :         }
     336             :         else
     337             :         {
     338        2257 :             WriteUInt8(abyBuffer, static_cast<uint8_t>((nUVal & 0x3F)));
     339        2257 :             return;
     340             :         }
     341             :     }
     342             : 
     343        1132 :     WriteVarUInt(abyBuffer, nUVal);
     344             : }
     345             : 
     346             : /************************************************************************/
     347             : /*                            ReadUTF16String()                         */
     348             : /************************************************************************/
     349             : 
     350       96147 : inline std::string ReadUTF16String(const GByte *pabyIter, int nCarCount)
     351             : {
     352      192294 :     std::wstring osWideStr;
     353      762238 :     for (int j = 0; j < nCarCount; j++)
     354      666091 :         osWideStr += pabyIter[2 * j] | (pabyIter[2 * j + 1] << 8);
     355             :     char *pszStr =
     356       96147 :         CPLRecodeFromWChar(osWideStr.c_str(), CPL_ENC_UCS2, CPL_ENC_UTF8);
     357       96147 :     std::string osRet(pszStr);
     358       96147 :     CPLFree(pszStr);
     359      192294 :     return osRet;
     360             : }
     361             : 
     362             : /************************************************************************/
     363             : /*                           WriteUTF16String()                         */
     364             : /************************************************************************/
     365             : 
     366             : enum UTF16StringFormat
     367             : {
     368             :     NUMBER_OF_BYTES_ON_UINT16,
     369             :     NUMBER_OF_BYTES_ON_VARUINT,
     370             :     NUMBER_OF_CHARS_ON_UINT8,
     371             :     NUMBER_OF_CHARS_ON_UINT32,
     372             : };
     373             : 
     374       29062 : inline void WriteUTF16String(std::vector<GByte> &abyBuffer, const char *pszStr,
     375             :                              UTF16StringFormat eFormat)
     376             : {
     377       29062 :     wchar_t *pszWStr = CPLRecodeToWChar(pszStr, CPL_ENC_UTF8, CPL_ENC_UCS2);
     378       29062 :     size_t nWLen = wcslen(pszWStr);
     379       29062 :     switch (eFormat)
     380             :     {
     381         544 :         case NUMBER_OF_BYTES_ON_UINT16:
     382             :         {
     383             :             // Write length as bytes
     384             :             const auto nLenToWrite =
     385         544 :                 std::min(static_cast<size_t>(65534), sizeof(uint16_t) * nWLen);
     386         544 :             if (nLenToWrite < sizeof(uint16_t) * nWLen)
     387             :             {
     388           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     389             :                          "String %s truncated to %u bytes", pszStr,
     390             :                          static_cast<uint32_t>(nLenToWrite));
     391           0 :                 nWLen = nLenToWrite / sizeof(uint16_t);
     392             :             }
     393         544 :             WriteUInt16(abyBuffer, static_cast<uint16_t>(nLenToWrite));
     394         544 :             break;
     395             :         }
     396             : 
     397           2 :         case NUMBER_OF_BYTES_ON_VARUINT:
     398             :         {
     399             :             // Write length as bytes
     400           2 :             WriteVarUInt(abyBuffer, sizeof(uint16_t) * nWLen);
     401           2 :             break;
     402             :         }
     403             : 
     404       27532 :         case NUMBER_OF_CHARS_ON_UINT8:
     405             :         {
     406             :             // Write length as number of UTF16 characters
     407       27532 :             const auto nLenToWrite = std::min(static_cast<size_t>(255), nWLen);
     408       27532 :             if (nLenToWrite < nWLen)
     409             :             {
     410           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     411             :                          "String %s truncated to %u UTF16 characters", pszStr,
     412             :                          static_cast<uint32_t>(nLenToWrite));
     413           0 :                 nWLen = nLenToWrite;
     414             :             }
     415       27532 :             WriteUInt8(abyBuffer, static_cast<uint8_t>(nLenToWrite));
     416       27532 :             break;
     417             :         }
     418             : 
     419         984 :         case NUMBER_OF_CHARS_ON_UINT32:
     420             :         {
     421             :             // Write length as number of UTF16 characters
     422         984 :             WriteUInt32(abyBuffer, static_cast<uint32_t>(nWLen));
     423         984 :             break;
     424             :         }
     425             :     }
     426             : 
     427       29062 :     if (nWLen)
     428             :     {
     429       15300 :         std::vector<uint16_t> anChars(nWLen);
     430      180556 :         for (size_t i = 0; i < nWLen; ++i)
     431             :         {
     432      165256 :             anChars[i] = static_cast<uint16_t>(pszWStr[i]);
     433      165256 :             CPL_LSBPTR16(&anChars[i]);
     434             :         }
     435             :         const GByte *pabyInput =
     436       15300 :             reinterpret_cast<const GByte *>(anChars.data());
     437           0 :         abyBuffer.insert(abyBuffer.end(), pabyInput,
     438       15300 :                          pabyInput + nWLen * sizeof(uint16_t));
     439             :     }
     440       29062 :     CPLFree(pszWStr);
     441       29062 : }
     442             : 
     443             : /************************************************************************/
     444             : /*                      FileGDBOGRDateToDoubleDate()                    */
     445             : /************************************************************************/
     446             : 
     447         134 : inline double FileGDBOGRDateToDoubleDate(const OGRField *psField,
     448             :                                          bool bConvertToGMT,
     449             :                                          bool bHighPrecision)
     450             : {
     451             :     struct tm brokendowntime;
     452         134 :     brokendowntime.tm_year = psField->Date.Year - 1900;
     453         134 :     brokendowntime.tm_mon = psField->Date.Month - 1;
     454         134 :     brokendowntime.tm_mday = psField->Date.Day;
     455         134 :     brokendowntime.tm_hour = psField->Date.Hour;
     456         134 :     brokendowntime.tm_min = psField->Date.Minute;
     457         134 :     brokendowntime.tm_sec = bHighPrecision
     458         134 :                                 ? static_cast<int>(psField->Date.Second)
     459         118 :                                 : static_cast<int>(psField->Date.Second + 0.5);
     460         134 :     GIntBig nUnixTime = CPLYMDHMSToUnixTime(&brokendowntime);
     461         134 :     if (bConvertToGMT && psField->Date.TZFlag > 1 &&
     462          39 :         psField->Date.TZFlag != 100)
     463             :     {
     464             :         // Convert to GMT
     465          38 :         const int TZOffset = std::abs(psField->Date.TZFlag - 100) * 15;
     466          38 :         const int TZHour = TZOffset / 60;
     467          38 :         const int TZMinute = TZOffset - TZHour * 60;
     468          38 :         const int nOffset = TZHour * 3600 + TZMinute * 60;
     469          38 :         if (psField->Date.TZFlag >= 100)
     470          38 :             nUnixTime -= nOffset;
     471             :         else
     472           0 :             nUnixTime += nOffset;
     473             :     }
     474             :     // 25569: Number of days between 1899/12/30 00:00:00 and 1970/01/01 00:00:00
     475             :     return static_cast<double>(
     476         268 :                nUnixTime +
     477             :                (bHighPrecision
     478         134 :                     ? fmod(static_cast<double>(psField->Date.Second), 1.0)
     479         134 :                     : 0)) /
     480         134 :                3600.0 / 24.0 +
     481         134 :            25569.0;
     482             : }
     483             : 
     484             : /************************************************************************/
     485             : /*                      FileGDBOGRTimeToDoubleTime()                    */
     486             : /************************************************************************/
     487             : 
     488          12 : inline double FileGDBOGRTimeToDoubleTime(const OGRField *psField)
     489             : {
     490          12 :     return static_cast<double>(psField->Date.Hour * 3600 +
     491          12 :                                psField->Date.Minute * 60 +
     492          12 :                                psField->Date.Second) /
     493          12 :            3600.0 / 24.0;
     494             : }
     495             : 
     496             : void FileGDBTablePrintError(const char *pszFile, int nLineNumber);
     497             : 
     498             : #define PrintError() FileGDBTablePrintError(__FILE__, __LINE__)
     499             : 
     500             : /************************************************************************/
     501             : /*                          returnError()                               */
     502             : /************************************************************************/
     503             : 
     504             : #define returnError()                                                          \
     505             :     do                                                                         \
     506             :     {                                                                          \
     507             :         PrintError();                                                          \
     508             :         return (errorRetValue);                                                \
     509             :     } while (0)
     510             : 
     511             : /************************************************************************/
     512             : /*                         returnErrorIf()                              */
     513             : /************************************************************************/
     514             : 
     515             : #define returnErrorIf(expr)                                                    \
     516             :     do                                                                         \
     517             :     {                                                                          \
     518             :         if ((expr))                                                            \
     519             :             returnError();                                                     \
     520             :     } while (0)
     521             : 
     522             : /************************************************************************/
     523             : /*                       returnErrorAndCleanupIf()                      */
     524             : /************************************************************************/
     525             : 
     526             : #define returnErrorAndCleanupIf(expr, cleanup)                                 \
     527             :     do                                                                         \
     528             :     {                                                                          \
     529             :         if ((expr))                                                            \
     530             :         {                                                                      \
     531             :             cleanup;                                                           \
     532             :             returnError();                                                     \
     533             :         }                                                                      \
     534             :     } while (0)
     535             : 
     536             : } /* namespace OpenFileGDB */
     537             : 
     538             : #endif /* FILEGDBTABLE_PRIV_H_INCLUDED */

Generated by: LCOV version 1.14