LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s57 - s57classregistrar.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 162 224 72.3 %
Date: 2025-01-18 12:42:00 Functions: 17 18 94.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  S-57 Translator
       4             :  * Purpose:  Implements S57ClassRegistrar class for keeping track of
       5             :  *           information on S57 object classes.
       6             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 1999, Frank Warmerdam
      10             :  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "cpl_conv.h"
      16             : #include "cpl_string.h"
      17             : #include "s57.h"
      18             : 
      19             : #ifdef EMBED_RESOURCE_FILES
      20             : #include "embedded_resources.h"
      21             : #endif
      22             : 
      23             : /************************************************************************/
      24             : /*                         S57ClassRegistrar()                          */
      25             : /************************************************************************/
      26             : 
      27           3 : S57ClassRegistrar::S57ClassRegistrar()
      28           3 :     : nClasses(0), nAttrCount(0), papszNextLine(nullptr)
      29             : {
      30           3 : }
      31             : 
      32             : /************************************************************************/
      33             : /*                         ~S57ClassRegistrar()                         */
      34             : /************************************************************************/
      35             : 
      36           2 : S57ClassRegistrar::~S57ClassRegistrar()
      37             : 
      38             : {
      39           2 :     nClasses = 0;
      40       80004 :     for (size_t i = 0; i < aoAttrInfos.size(); i++)
      41       80002 :         delete aoAttrInfos[i];
      42           2 :     aoAttrInfos.resize(0);
      43           2 :     nAttrCount = 0;
      44           2 : }
      45             : 
      46             : /************************************************************************/
      47             : /*                        S57ClassContentExplorer()                     */
      48             : /************************************************************************/
      49             : 
      50          55 : S57ClassContentExplorer::S57ClassContentExplorer(
      51          55 :     S57ClassRegistrar *poRegistrarIn)
      52             :     : poRegistrar(poRegistrarIn), papapszClassesFields(nullptr),
      53          55 :       iCurrentClass(-1), papszCurrentFields(nullptr), papszTempResult(nullptr)
      54             : {
      55          55 : }
      56             : 
      57             : /************************************************************************/
      58             : /*                        ~S57ClassContentExplorer()                    */
      59             : /************************************************************************/
      60             : 
      61         110 : S57ClassContentExplorer::~S57ClassContentExplorer()
      62             : {
      63          55 :     CSLDestroy(papszTempResult);
      64             : 
      65          55 :     if (papapszClassesFields != nullptr)
      66             :     {
      67       15675 :         for (int i = 0; i < poRegistrar->nClasses; i++)
      68       15620 :             CSLDestroy(papapszClassesFields[i]);
      69          55 :         CPLFree(papapszClassesFields);
      70             :     }
      71          55 : }
      72             : 
      73             : /************************************************************************/
      74             : /*                              FindFile()                              */
      75             : /************************************************************************/
      76             : 
      77           6 : bool S57ClassRegistrar::FindFile(const char *pszTarget,
      78             :                                  const char *pszDirectory, bool bReportErr,
      79             :                                  VSILFILE **pfp)
      80             : 
      81             : {
      82          12 :     std::string osFilename;
      83             : 
      84           6 :     *pfp = nullptr;
      85             : 
      86           6 :     if (pszDirectory == nullptr)
      87             :     {
      88             : #if defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
      89             :         const char *pszFilename = pszTarget;
      90             : #else
      91           6 :         const char *pszFilename = CPLFindFile("s57", pszTarget);
      92           6 :         if (pszFilename == nullptr)
      93           0 :             pszFilename = pszTarget;
      94             : #endif
      95           6 :         osFilename = pszFilename;
      96           6 :         if (EQUAL(osFilename.c_str(), pszTarget))
      97             :         {
      98             : #ifdef EMBED_RESOURCE_FILES
      99             :             const char *pszContent = S57GetEmbeddedCSV(pszTarget);
     100             :             if (pszContent)
     101             :             {
     102             :                 CPLDebug("S57", "Using embedded %s", pszTarget);
     103             :                 *pfp = VSIFileFromMemBuffer(
     104             :                     nullptr,
     105             :                     const_cast<GByte *>(
     106             :                         reinterpret_cast<const GByte *>(pszContent)),
     107             :                     static_cast<int>(strlen(pszContent)),
     108             :                     /* bTakeOwnership = */ false);
     109             :             }
     110             : #endif
     111             :         }
     112             :     }
     113             :     else
     114             :     {
     115           0 :         osFilename = CPLFormFilenameSafe(pszDirectory, pszTarget, nullptr);
     116             :     }
     117             : 
     118             : #ifdef EMBED_RESOURCE_FILES
     119             :     if (!(*pfp))
     120             : #endif
     121             :     {
     122           6 :         *pfp = VSIFOpenL(osFilename.c_str(), "rb");
     123             :     }
     124             : 
     125           6 :     if (*pfp == nullptr)
     126             :     {
     127           0 :         if (bReportErr)
     128           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.\n",
     129             :                      osFilename.c_str());
     130           0 :         return FALSE;
     131             :     }
     132             : 
     133           6 :     return TRUE;
     134             : }
     135             : 
     136             : /************************************************************************/
     137             : /*                              ReadLine()                              */
     138             : /*                                                                      */
     139             : /*      Read a line from the provided file, or from the "built-in"      */
     140             : /*      configuration file line list if the file is NULL.               */
     141             : /************************************************************************/
     142             : 
     143        2319 : const char *S57ClassRegistrar::ReadLine(VSILFILE *fp)
     144             : 
     145             : {
     146        2319 :     if (fp != nullptr)
     147        2319 :         return CPLReadLineL(fp);
     148             : 
     149           0 :     if (papszNextLine == nullptr)
     150           0 :         return nullptr;
     151             : 
     152           0 :     if (*papszNextLine == nullptr)
     153             :     {
     154           0 :         papszNextLine = nullptr;
     155           0 :         return nullptr;
     156             :     }
     157             : 
     158           0 :     return *(papszNextLine++);
     159             : }
     160             : 
     161             : /************************************************************************/
     162             : /*                              LoadInfo()                              */
     163             : /************************************************************************/
     164             : 
     165           3 : bool S57ClassRegistrar::LoadInfo(const char *pszDirectory,
     166             :                                  const char *pszProfile, bool bReportErr)
     167             : 
     168             : {
     169           3 :     if (pszDirectory == nullptr)
     170           3 :         pszDirectory = CPLGetConfigOption("S57_CSV", nullptr);
     171             : 
     172             :     /* ==================================================================== */
     173             :     /*      Read the s57objectclasses file.                                 */
     174             :     /* ==================================================================== */
     175           3 :     if (pszProfile == nullptr)
     176           3 :         pszProfile = CPLGetConfigOption("S57_PROFILE", "");
     177             : 
     178             :     char szTargetFile[1024];  // TODO: Get this off of the stack.
     179           3 :     if (EQUAL(pszProfile, "Additional_Military_Layers"))
     180             :     {
     181             :         // Has been suppressed in GDAL data/
     182           0 :         snprintf(szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv",
     183             :                  "aml");
     184             :     }
     185           3 :     else if (EQUAL(pszProfile, "Inland_Waterways"))
     186             :     {
     187             :         // Has been suppressed in GDAL data/
     188           0 :         snprintf(szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv",
     189             :                  "iw");
     190             :     }
     191           3 :     else if (strlen(pszProfile) > 0)
     192             :     {
     193           0 :         snprintf(szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv",
     194             :                  pszProfile);
     195             :     }
     196             :     else
     197             :     {
     198           3 :         strcpy(szTargetFile, "s57objectclasses.csv");
     199             :     }
     200             : 
     201           3 :     VSILFILE *fp = nullptr;
     202           3 :     if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
     203             :     {
     204           0 :         if (EQUAL(pszProfile, "Additional_Military_Layers") ||
     205           0 :             EQUAL(pszProfile, "Inland_Waterways"))
     206             :         {
     207           0 :             strcpy(szTargetFile, "s57objectclasses.csv");
     208           0 :             if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
     209           0 :                 return false;
     210             :         }
     211           0 :         return false;
     212             :     }
     213             : 
     214             :     /* -------------------------------------------------------------------- */
     215             :     /*      Skip the line defining the column titles.                       */
     216             :     /* -------------------------------------------------------------------- */
     217           3 :     const char *pszLine = ReadLine(fp);
     218             : 
     219           3 :     if (!EQUAL(pszLine,
     220             :                "\"Code\",\"ObjectClass\",\"Acronym\",\"Attribute_A\","
     221             :                "\"Attribute_B\",\"Attribute_C\",\"Class\",\"Primitives\""))
     222             :     {
     223           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     224             :                  "s57objectclasses columns don't match expected format!\n");
     225           0 :         if (fp != nullptr)
     226           0 :             VSIFCloseL(fp);
     227           0 :         return false;
     228             :     }
     229             : 
     230             :     /* -------------------------------------------------------------------- */
     231             :     /*      Read and form string list.                                      */
     232             :     /* -------------------------------------------------------------------- */
     233           3 :     apszClassesInfo.Clear();
     234         861 :     while ((pszLine = ReadLine(fp)) != nullptr)
     235             :     {
     236         858 :         if (strstr(pszLine, "###") != nullptr)
     237           6 :             continue;
     238         852 :         apszClassesInfo.AddString(pszLine);
     239             :     }
     240             : 
     241             :     /* -------------------------------------------------------------------- */
     242             :     /*      Cleanup, and establish state.                                   */
     243             :     /* -------------------------------------------------------------------- */
     244           3 :     if (fp != nullptr)
     245           3 :         VSIFCloseL(fp);
     246             : 
     247           3 :     nClasses = apszClassesInfo.size();
     248           3 :     if (nClasses == 0)
     249           0 :         return false;
     250             : 
     251             :     /* ==================================================================== */
     252             :     /*      Read the attributes list.                                       */
     253             :     /* ==================================================================== */
     254             : 
     255           3 :     if (EQUAL(pszProfile, "Additional_Military_Layers"))
     256             :     {
     257             :         // Has been suppressed in GDAL data/
     258           0 :         snprintf(szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv",
     259             :                  "aml");
     260             :     }
     261           3 :     else if (EQUAL(pszProfile, "Inland_Waterways"))
     262             :     {
     263             :         // Has been suppressed in GDAL data/
     264           0 :         snprintf(szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv",
     265             :                  "iw");
     266             :     }
     267           3 :     else if (strlen(pszProfile) > 0)
     268             :     {
     269           0 :         snprintf(szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv",
     270             :                  pszProfile);
     271             :     }
     272             :     else
     273             :     {
     274           3 :         strcpy(szTargetFile, "s57attributes.csv");
     275             :     }
     276             : 
     277           3 :     if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
     278             :     {
     279           0 :         if (EQUAL(pszProfile, "Additional_Military_Layers") ||
     280           0 :             EQUAL(pszProfile, "Inland_Waterways"))
     281             :         {
     282           0 :             strcpy(szTargetFile, "s57attributes.csv");
     283           0 :             if (!FindFile(szTargetFile, pszDirectory, bReportErr, &fp))
     284           0 :                 return false;
     285             :         }
     286           0 :         return false;
     287             :     }
     288             : 
     289             :     /* -------------------------------------------------------------------- */
     290             :     /*      Skip the line defining the column titles.                       */
     291             :     /* -------------------------------------------------------------------- */
     292           3 :     pszLine = ReadLine(fp);
     293             : 
     294           3 :     if (!EQUAL(
     295             :             pszLine,
     296             :             "\"Code\",\"Attribute\",\"Acronym\",\"Attributetype\",\"Class\""))
     297             :     {
     298           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     299             :                  "s57attributes columns don't match expected format!\n");
     300           0 :         if (fp != nullptr)
     301           0 :             VSIFCloseL(fp);
     302           0 :         return false;
     303             :     }
     304             : 
     305             :     /* -------------------------------------------------------------------- */
     306             :     /*      Read and form string list.                                      */
     307             :     /* -------------------------------------------------------------------- */
     308        1452 :     while ((pszLine = ReadLine(fp)) != nullptr)
     309             :     {
     310        1449 :         if (strstr(pszLine, "###") != nullptr)
     311           9 :             continue;
     312             : 
     313        1440 :         char **papszTokens = CSLTokenizeStringComplex(pszLine, ",", TRUE, TRUE);
     314             : 
     315        1440 :         if (CSLCount(papszTokens) < 5)
     316             :         {
     317           0 :             CSLDestroy(papszTokens);
     318           0 :             continue;
     319             :         }
     320             : 
     321        1440 :         int iAttr = atoi(papszTokens[0]);
     322        1440 :         if (iAttr >= (int)aoAttrInfos.size())
     323        1440 :             aoAttrInfos.resize(iAttr + 1);
     324             : 
     325        1440 :         if (iAttr < 0 || aoAttrInfos[iAttr] != nullptr)
     326             :         {
     327           0 :             CPLDebug("S57", "Duplicate/corrupt definition for attribute %d:%s",
     328           0 :                      iAttr, papszTokens[2]);
     329           0 :             CSLDestroy(papszTokens);
     330           0 :             continue;
     331             :         }
     332             : 
     333        1440 :         aoAttrInfos[iAttr] = new S57AttrInfo();
     334        1440 :         aoAttrInfos[iAttr]->osName = papszTokens[1];
     335        1440 :         aoAttrInfos[iAttr]->osAcronym = papszTokens[2];
     336        1440 :         aoAttrInfos[iAttr]->chType = papszTokens[3][0];
     337        1440 :         aoAttrInfos[iAttr]->chClass = papszTokens[4][0];
     338        1440 :         anAttrIndex.push_back(iAttr);
     339        1440 :         CSLDestroy(papszTokens);
     340             :     }
     341             : 
     342           3 :     if (fp != nullptr)
     343           3 :         VSIFCloseL(fp);
     344             : 
     345           3 :     nAttrCount = static_cast<int>(anAttrIndex.size());
     346             : 
     347             :     /* -------------------------------------------------------------------- */
     348             :     /*      Sort index by acronym.                                          */
     349             :     /* -------------------------------------------------------------------- */
     350           3 :     bool bModified = false;
     351        1050 :     do
     352             :     {
     353        1050 :         bModified = false;
     354      504000 :         for (int iAttr = 0; iAttr < nAttrCount - 1; iAttr++)
     355             :         {
     356      502950 :             if (strcmp(aoAttrInfos[anAttrIndex[iAttr]]->osAcronym,
     357     1005900 :                        aoAttrInfos[anAttrIndex[iAttr + 1]]->osAcronym) > 0)
     358             :             {
     359       51513 :                 int nTemp = anAttrIndex[iAttr];
     360       51513 :                 anAttrIndex[iAttr] = anAttrIndex[iAttr + 1];
     361       51513 :                 anAttrIndex[iAttr + 1] = nTemp;
     362       51513 :                 bModified = true;
     363             :             }
     364             :         }
     365             :     } while (bModified);
     366             : 
     367           3 :     return true;
     368             : }
     369             : 
     370             : /************************************************************************/
     371             : /*                         SelectClassByIndex()                         */
     372             : /************************************************************************/
     373             : 
     374       46851 : bool S57ClassContentExplorer::SelectClassByIndex(int nNewIndex)
     375             : 
     376             : {
     377       46851 :     if (nNewIndex < 0 || nNewIndex >= poRegistrar->nClasses)
     378          18 :         return false;
     379             : 
     380             :     /* -------------------------------------------------------------------- */
     381             :     /*      Do we have our cache of class information field lists?          */
     382             :     /* -------------------------------------------------------------------- */
     383       46833 :     if (papapszClassesFields == nullptr)
     384             :     {
     385          55 :         papapszClassesFields =
     386          55 :             (char ***)CPLCalloc(sizeof(void *), poRegistrar->nClasses);
     387             :     }
     388             : 
     389             :     /* -------------------------------------------------------------------- */
     390             :     /*      Has this info been parsed yet?                                  */
     391             :     /* -------------------------------------------------------------------- */
     392       46833 :     if (papapszClassesFields[nNewIndex] == nullptr)
     393       15620 :         papapszClassesFields[nNewIndex] = CSLTokenizeStringComplex(
     394       15620 :             poRegistrar->apszClassesInfo[nNewIndex], ",", TRUE, TRUE);
     395             : 
     396       46833 :     papszCurrentFields = papapszClassesFields[nNewIndex];
     397             : 
     398       46833 :     iCurrentClass = nNewIndex;
     399             : 
     400       46833 :     return true;
     401             : }
     402             : 
     403             : /************************************************************************/
     404             : /*                             SelectClass()                            */
     405             : /************************************************************************/
     406             : 
     407        5281 : bool S57ClassContentExplorer::SelectClass(int nOBJL)
     408             : 
     409             : {
     410      748695 :     for (int i = 0; i < poRegistrar->nClasses; i++)
     411             :     {
     412      748695 :         if (atoi(poRegistrar->apszClassesInfo[i]) == nOBJL)
     413        5281 :             return SelectClassByIndex(i);
     414             :     }
     415             : 
     416           0 :     return FALSE;
     417             : }
     418             : 
     419             : /************************************************************************/
     420             : /*                            SelectClass()                             */
     421             : /************************************************************************/
     422             : 
     423         260 : bool S57ClassContentExplorer::SelectClass(const char *pszAcronym)
     424             : 
     425             : {
     426       36489 :     for (int i = 0; i < poRegistrar->nClasses; i++)
     427             :     {
     428       36440 :         if (!SelectClassByIndex(i))
     429           0 :             continue;
     430             : 
     431       36440 :         const char *pszClassAcronym = GetAcronym();
     432       36440 :         if (pszClassAcronym != nullptr &&
     433       36440 :             strcmp(pszClassAcronym, pszAcronym) == 0)
     434         211 :             return true;
     435             :     }
     436             : 
     437          49 :     return false;
     438             : }
     439             : 
     440             : /************************************************************************/
     441             : /*                              GetOBJL()                               */
     442             : /************************************************************************/
     443             : 
     444        5281 : int S57ClassContentExplorer::GetOBJL()
     445             : 
     446             : {
     447        5281 :     if (iCurrentClass >= 0)
     448        5281 :         return atoi(poRegistrar->apszClassesInfo[iCurrentClass]);
     449             : 
     450           0 :     return -1;
     451             : }
     452             : 
     453             : /************************************************************************/
     454             : /*                           GetDescription()                           */
     455             : /************************************************************************/
     456             : 
     457          54 : const char *S57ClassContentExplorer::GetDescription() const
     458             : 
     459             : {
     460          54 :     if (iCurrentClass >= 0 && papszCurrentFields[0] != nullptr)
     461          54 :         return papszCurrentFields[1];
     462             : 
     463           0 :     return nullptr;
     464             : }
     465             : 
     466             : /************************************************************************/
     467             : /*                             GetAcronym()                             */
     468             : /************************************************************************/
     469             : 
     470       48074 : const char *S57ClassContentExplorer::GetAcronym() const
     471             : 
     472             : {
     473       48074 :     if (iCurrentClass >= 0 && papszCurrentFields[0] != nullptr &&
     474       48074 :         papszCurrentFields[1] != nullptr)
     475       48074 :         return papszCurrentFields[2];
     476             : 
     477           0 :     return nullptr;
     478             : }
     479             : 
     480             : /************************************************************************/
     481             : /*                          GetAttributeList()                          */
     482             : /*                                                                      */
     483             : /*      The passed string can be "a", "b", "c" or NULL for all.  The    */
     484             : /*      returned list remained owned by this object, not the caller.    */
     485             : /************************************************************************/
     486             : 
     487        5305 : char **S57ClassContentExplorer::GetAttributeList(const char *pszType)
     488             : 
     489             : {
     490        5305 :     if (iCurrentClass < 0)
     491           0 :         return nullptr;
     492             : 
     493        5305 :     CSLDestroy(papszTempResult);
     494        5305 :     papszTempResult = nullptr;
     495             : 
     496       21220 :     for (int iColumn = 3; iColumn < 6; iColumn++)
     497             :     {
     498       15915 :         if (pszType != nullptr && iColumn == 3 && !EQUAL(pszType, "a"))
     499           0 :             continue;
     500             : 
     501       15915 :         if (pszType != nullptr && iColumn == 4 && !EQUAL(pszType, "b"))
     502           0 :             continue;
     503             : 
     504       15915 :         if (pszType != nullptr && iColumn == 5 && !EQUAL(pszType, "c"))
     505           0 :             continue;
     506             : 
     507       31830 :         char **papszTokens = CSLTokenizeStringComplex(
     508       15915 :             papszCurrentFields[iColumn], ";", TRUE, FALSE);
     509             : 
     510       15915 :         papszTempResult = CSLInsertStrings(papszTempResult, -1, papszTokens);
     511             : 
     512       15915 :         CSLDestroy(papszTokens);
     513             :     }
     514             : 
     515        5305 :     return papszTempResult;
     516             : }
     517             : 
     518             : /************************************************************************/
     519             : /*                            GetClassCode()                            */
     520             : /************************************************************************/
     521             : 
     522           0 : char S57ClassContentExplorer::GetClassCode() const
     523             : 
     524             : {
     525           0 :     if (iCurrentClass >= 0 && papszCurrentFields[0] != nullptr &&
     526           0 :         papszCurrentFields[1] != nullptr && papszCurrentFields[2] != nullptr &&
     527           0 :         papszCurrentFields[3] != nullptr && papszCurrentFields[4] != nullptr &&
     528           0 :         papszCurrentFields[5] != nullptr && papszCurrentFields[6] != nullptr)
     529           0 :         return papszCurrentFields[6][0];
     530             : 
     531           0 :     return '\0';
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                           GetPrimitives()                            */
     536             : /************************************************************************/
     537             : 
     538        5281 : char **S57ClassContentExplorer::GetPrimitives()
     539             : 
     540             : {
     541        5281 :     if (iCurrentClass >= 0 && CSLCount(papszCurrentFields) > 7)
     542             :     {
     543        5281 :         CSLDestroy(papszTempResult);
     544        5281 :         papszTempResult =
     545        5281 :             CSLTokenizeStringComplex(papszCurrentFields[7], ";", TRUE, FALSE);
     546        5281 :         return papszTempResult;
     547             :     }
     548             : 
     549           0 :     return nullptr;
     550             : }
     551             : 
     552             : /************************************************************************/
     553             : /*                            GetAttrInfo()                             */
     554             : /************************************************************************/
     555             : 
     556      109511 : const S57AttrInfo *S57ClassRegistrar::GetAttrInfo(int iAttr)
     557             : {
     558      109511 :     if (iAttr < 0 || iAttr >= (int)aoAttrInfos.size())
     559           0 :         return nullptr;
     560             : 
     561      109511 :     return aoAttrInfos[iAttr];
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                         FindAttrByAcronym()                          */
     566             : /************************************************************************/
     567             : 
     568      108045 : int S57ClassRegistrar::FindAttrByAcronym(const char *pszName)
     569             : 
     570             : {
     571      108045 :     int iStart = 0;
     572      108045 :     int iEnd = nAttrCount - 1;
     573             : 
     574      842530 :     while (iStart <= iEnd)
     575             :     {
     576      842476 :         const int iCandidate = (iStart + iEnd) / 2;
     577             :         int nCompareValue =
     578      842476 :             strcmp(pszName, aoAttrInfos[anAttrIndex[iCandidate]]->osAcronym);
     579             : 
     580      842476 :         if (nCompareValue < 0)
     581             :         {
     582      393427 :             iEnd = iCandidate - 1;
     583             :         }
     584      449049 :         else if (nCompareValue > 0)
     585             :         {
     586      341058 :             iStart = iCandidate + 1;
     587             :         }
     588             :         else
     589      107991 :             return anAttrIndex[iCandidate];
     590             :     }
     591             : 
     592          54 :     return -1;
     593             : }

Generated by: LCOV version 1.14