LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/osm - osm_parser.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1071 1327 80.7 %
Date: 2026-02-27 12:28:37 Functions: 32 38 84.2 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       5             :  * Purpose:  OSM XML and OSM PBF parser
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2012-2013, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "osm_parser.h"
      14             : #include "gpb.h"
      15             : 
      16             : #include <climits>
      17             : #include <cstddef>
      18             : #include <cstdio>
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : #include <algorithm>
      22             : #include <exception>
      23             : #include <string>
      24             : #include <vector>
      25             : 
      26             : #include "cpl_config.h"
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_multiproc.h"
      30             : #include "cpl_string.h"
      31             : #include "cpl_vsi.h"
      32             : #include "cpl_worker_thread_pool.h"
      33             : #include "gdal_thread_pool.h"
      34             : 
      35             : #ifdef HAVE_EXPAT
      36             : #include "ogr_expat.h"
      37             : #endif
      38             : 
      39             : // The buffer that are passed to GPB decoding are extended with 0's
      40             : // to be sure that we will be able to read a single 64bit value without
      41             : // doing checks for each byte.
      42             : constexpr int EXTRA_BYTES = 1;
      43             : 
      44             : #ifdef HAVE_EXPAT
      45             : constexpr int XML_BUFSIZE = 64 * 1024;
      46             : #endif
      47             : 
      48             : // Per OSM PBF spec
      49             : constexpr unsigned int MAX_BLOB_HEADER_SIZE = 64 * 1024;
      50             : 
      51             : // Per OSM PBF spec (usually much smaller !)
      52             : constexpr unsigned int MAX_BLOB_SIZE = 64 * 1024 * 1024;
      53             : 
      54             : // GDAL implementation limits
      55             : constexpr unsigned int MAX_ACC_BLOB_SIZE = 50 * 1024 * 1024;
      56             : constexpr unsigned int MAX_ACC_UNCOMPRESSED_SIZE = 100 * 1024 * 1024;
      57             : constexpr int N_MAX_JOBS = 1024;
      58             : 
      59             : #if defined(__GNUC__)
      60             : #define CPL_NO_INLINE __attribute__((noinline))
      61             : #else
      62             : #define CPL_NO_INLINE
      63             : #endif
      64             : 
      65             : class OSMParsingException : public std::exception
      66             : {
      67             :     std::string m_osMessage;
      68             : 
      69             :   public:
      70           0 :     explicit OSMParsingException(int nLine)
      71           0 :         : m_osMessage(CPLSPrintf("Parsing error occurred at line %d", nLine))
      72             :     {
      73           0 :     }
      74             : 
      75             :     const char *what() const noexcept override;
      76             : };
      77             : 
      78           0 : const char *OSMParsingException::what() const noexcept
      79             : {
      80           0 :     return m_osMessage.c_str();
      81             : }
      82             : 
      83             : #define THROW_OSM_PARSING_EXCEPTION throw OSMParsingException(__LINE__)
      84             : 
      85             : /************************************************************************/
      86             : /*                             INIT_INFO()                              */
      87             : /************************************************************************/
      88             : 
      89         981 : static void INIT_INFO(OSMInfo *sInfo)
      90             : {
      91         981 :     sInfo->ts.nTimeStamp = 0;
      92         981 :     sInfo->nChangeset = 0;
      93         981 :     sInfo->nVersion = 0;
      94         981 :     sInfo->nUID = 0;
      95         981 :     sInfo->bTimeStampIsStr = false;
      96         981 :     sInfo->pszUserSID = nullptr;
      97         981 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                             _OSMContext                              */
     101             : /************************************************************************/
     102             : 
     103             : typedef struct
     104             : {
     105             :     const GByte *pabySrc;
     106             :     size_t nSrcSize;
     107             :     GByte *pabyDstBase;
     108             :     size_t nDstOffset;
     109             :     size_t nDstSize;
     110             :     bool bStatus;
     111             : } DecompressionJob;
     112             : 
     113             : struct _OSMContext
     114             : {
     115             :     char *pszStrBuf;
     116             :     int *panStrOff;
     117             :     unsigned int nStrCount;
     118             :     unsigned int nStrAllocated;
     119             : 
     120             :     OSMNode *pasNodes;
     121             :     unsigned int nNodesAllocated;
     122             : 
     123             :     OSMTag *pasTags;
     124             :     unsigned int nTagsAllocated;
     125             : 
     126             :     OSMMember *pasMembers;
     127             :     unsigned int nMembersAllocated;
     128             : 
     129             :     GIntBig *panNodeRefs;
     130             :     unsigned int nNodeRefsAllocated;
     131             : 
     132             :     int nGranularity;
     133             :     int nDateGranularity;
     134             :     GIntBig nLatOffset;
     135             :     GIntBig nLonOffset;
     136             : 
     137             :     // concatenated protocol buffer messages BLOB_OSMDATA, or single
     138             :     // BLOB_OSMHEADER
     139             :     GByte *pabyBlob;
     140             :     unsigned int nBlobSizeAllocated;
     141             :     unsigned int nBlobOffset;
     142             :     unsigned int nBlobSize;
     143             : 
     144             :     GByte *pabyBlobHeader;  // MAX_BLOB_HEADER_SIZE+EXTRA_BYTES large
     145             : 
     146             :     CPLWorkerThreadPool *poWTP;
     147             : 
     148             :     GByte *pabyUncompressed;
     149             :     unsigned int nUncompressedAllocated;
     150             :     unsigned int nTotalUncompressedSize;
     151             : 
     152             :     DecompressionJob asJobs[N_MAX_JOBS];
     153             :     int nJobs;
     154             :     int iNextJob;
     155             : 
     156             : #ifdef HAVE_EXPAT
     157             :     XML_Parser hXMLParser;
     158             :     bool bEOF;
     159             :     bool bStopParsing;
     160             :     bool bHasFoundFeature;
     161             :     int nWithoutEventCounter;
     162             :     int nDataHandlerCounter;
     163             : 
     164             :     unsigned int nStrLength;
     165             :     unsigned int nTags;
     166             : 
     167             :     bool bInNode;
     168             :     bool bInWay;
     169             :     bool bInRelation;
     170             : 
     171             :     OSMWay sWay;
     172             :     OSMRelation sRelation;
     173             : 
     174             :     bool bTryToFetchBounds;
     175             : #endif
     176             : 
     177             :     VSILFILE *fp;
     178             : 
     179             :     bool bPBF;
     180             : 
     181             :     double dfLeft;
     182             :     double dfRight;
     183             :     double dfTop;
     184             :     double dfBottom;
     185             : 
     186             :     GUIntBig nBytesRead;
     187             : 
     188             :     NotifyNodesFunc pfnNotifyNodes;
     189             :     NotifyWayFunc pfnNotifyWay;
     190             :     NotifyRelationFunc pfnNotifyRelation;
     191             :     NotifyBoundsFunc pfnNotifyBounds;
     192             :     void *user_data;
     193             : };
     194             : 
     195             : /************************************************************************/
     196             : /*                           ReadBlobHeader()                           */
     197             : /************************************************************************/
     198             : 
     199             : constexpr int BLOBHEADER_IDX_TYPE = 1;
     200             : constexpr int BLOBHEADER_IDX_INDEXDATA = 2;
     201             : constexpr int BLOBHEADER_IDX_DATASIZE = 3;
     202             : 
     203             : typedef enum
     204             : {
     205             :     BLOB_UNKNOWN,
     206             :     BLOB_OSMHEADER,
     207             :     BLOB_OSMDATA
     208             : } BlobType;
     209             : 
     210          92 : static bool ReadBlobHeader(const GByte *pabyData, const GByte *pabyDataLimit,
     211             :                            unsigned int *pnBlobSize, BlobType *peBlobType)
     212             : {
     213          92 :     *pnBlobSize = 0;
     214          92 :     *peBlobType = BLOB_UNKNOWN;
     215             : 
     216             :     try
     217             :     {
     218         276 :         while (pabyData < pabyDataLimit)
     219             :         {
     220         184 :             int nKey = 0;
     221         184 :             READ_FIELD_KEY(nKey);
     222             : 
     223         184 :             if (nKey == MAKE_KEY(BLOBHEADER_IDX_TYPE, WT_DATA))
     224             :             {
     225          92 :                 unsigned int nDataLength = 0;
     226          92 :                 READ_SIZE(pabyData, pabyDataLimit, nDataLength);
     227             : 
     228          92 :                 if (nDataLength == 7 && memcmp(pabyData, "OSMData", 7) == 0)
     229             :                 {
     230          46 :                     *peBlobType = BLOB_OSMDATA;
     231             :                 }
     232          46 :                 else if (nDataLength == 9 &&
     233          46 :                          memcmp(pabyData, "OSMHeader", 9) == 0)
     234             :                 {
     235          46 :                     *peBlobType = BLOB_OSMHEADER;
     236             :                 }
     237             : 
     238          92 :                 pabyData += nDataLength;
     239             :             }
     240          92 :             else if (nKey == MAKE_KEY(BLOBHEADER_IDX_INDEXDATA, WT_DATA))
     241             :             {
     242             :                 // Ignored if found.
     243           0 :                 unsigned int nDataLength = 0;
     244           0 :                 READ_SIZE(pabyData, pabyDataLimit, nDataLength);
     245           0 :                 pabyData += nDataLength;
     246             :             }
     247          92 :             else if (nKey == MAKE_KEY(BLOBHEADER_IDX_DATASIZE, WT_VARINT))
     248             :             {
     249          92 :                 unsigned int nBlobSize = 0;
     250          92 :                 READ_VARUINT32(pabyData, pabyDataLimit, nBlobSize);
     251             :                 // printf("nBlobSize = %d\n", nBlobSize);
     252          92 :                 *pnBlobSize = nBlobSize;
     253             :             }
     254             :             else
     255             :             {
     256           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
     257             :             }
     258             :         }
     259             : 
     260          92 :         return pabyData == pabyDataLimit;
     261             :     }
     262           0 :     catch (const std::exception &e)
     263             :     {
     264           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     265           0 :         return false;
     266             :     }
     267             : }
     268             : 
     269             : /************************************************************************/
     270             : /*                           ReadHeaderBBox()                           */
     271             : /************************************************************************/
     272             : 
     273             : constexpr int HEADERBBOX_IDX_LEFT = 1;
     274             : constexpr int HEADERBBOX_IDX_RIGHT = 2;
     275             : constexpr int HEADERBBOX_IDX_TOP = 3;
     276             : constexpr int HEADERBBOX_IDX_BOTTOM = 4;
     277             : 
     278           3 : static bool ReadHeaderBBox(const GByte *pabyData, const GByte *pabyDataLimit,
     279             :                            OSMContext *psCtxt)
     280             : {
     281           3 :     psCtxt->dfLeft = 0.0;
     282           3 :     psCtxt->dfRight = 0.0;
     283           3 :     psCtxt->dfTop = 0.0;
     284           3 :     psCtxt->dfBottom = 0.0;
     285             : 
     286             :     try
     287             :     {
     288          15 :         while (pabyData < pabyDataLimit)
     289             :         {
     290          12 :             int nKey = 0;
     291          12 :             READ_FIELD_KEY(nKey);
     292             : 
     293          12 :             if (nKey == MAKE_KEY(HEADERBBOX_IDX_LEFT, WT_VARINT))
     294             :             {
     295           3 :                 GIntBig nLeft = 0;
     296           3 :                 READ_VARSINT64(pabyData, pabyDataLimit, nLeft);
     297           3 :                 psCtxt->dfLeft = nLeft * 1e-9;
     298             :             }
     299           9 :             else if (nKey == MAKE_KEY(HEADERBBOX_IDX_RIGHT, WT_VARINT))
     300             :             {
     301           3 :                 GIntBig nRight = 0;
     302           3 :                 READ_VARSINT64(pabyData, pabyDataLimit, nRight);
     303           3 :                 psCtxt->dfRight = nRight * 1e-9;
     304             :             }
     305           6 :             else if (nKey == MAKE_KEY(HEADERBBOX_IDX_TOP, WT_VARINT))
     306             :             {
     307           3 :                 GIntBig nTop = 0;
     308           3 :                 READ_VARSINT64(pabyData, pabyDataLimit, nTop);
     309           3 :                 psCtxt->dfTop = nTop * 1e-9;
     310             :             }
     311           3 :             else if (nKey == MAKE_KEY(HEADERBBOX_IDX_BOTTOM, WT_VARINT))
     312             :             {
     313           3 :                 GIntBig nBottom = 0;
     314           3 :                 READ_VARSINT64(pabyData, pabyDataLimit, nBottom);
     315           3 :                 psCtxt->dfBottom = nBottom * 1e-9;
     316             :             }
     317             :             else
     318             :             {
     319           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
     320             :             }
     321             :         }
     322             : 
     323           3 :         psCtxt->pfnNotifyBounds(psCtxt->dfLeft, psCtxt->dfBottom,
     324             :                                 psCtxt->dfRight, psCtxt->dfTop, psCtxt,
     325             :                                 psCtxt->user_data);
     326             : 
     327           3 :         return pabyData == pabyDataLimit;
     328             :     }
     329           0 :     catch (const std::exception &e)
     330             :     {
     331           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     332           0 :         return false;
     333             :     }
     334             : }
     335             : 
     336             : /************************************************************************/
     337             : /*                           ReadOSMHeader()                            */
     338             : /************************************************************************/
     339             : 
     340             : constexpr int OSMHEADER_IDX_BBOX = 1;
     341             : constexpr int OSMHEADER_IDX_REQUIRED_FEATURES = 4;
     342             : constexpr int OSMHEADER_IDX_OPTIONAL_FEATURES = 5;
     343             : constexpr int OSMHEADER_IDX_WRITING_PROGRAM = 16;
     344             : constexpr int OSMHEADER_IDX_SOURCE = 17;
     345             : 
     346             : /* Ignored */
     347             : constexpr int OSMHEADER_IDX_OSMOSIS_REPLICATION_TIMESTAMP = 32;
     348             : constexpr int OSMHEADER_IDX_OSMOSIS_REPLICATION_SEQ_NUMBER = 33;
     349             : constexpr int OSMHEADER_IDX_OSMOSIS_REPLICATION_BASE_URL = 34;
     350             : 
     351          46 : static bool ReadOSMHeader(const GByte *pabyData, const GByte *pabyDataLimit,
     352             :                           OSMContext *psCtxt)
     353             : {
     354          46 :     char *pszTxt = nullptr;
     355             : 
     356             :     try
     357             :     {
     358         188 :         while (pabyData < pabyDataLimit)
     359             :         {
     360         142 :             int nKey = 0;
     361         142 :             READ_FIELD_KEY(nKey);
     362             : 
     363         142 :             if (nKey == MAKE_KEY(OSMHEADER_IDX_BBOX, WT_DATA))
     364             :             {
     365           3 :                 unsigned int nBBOXSize = 0;
     366           3 :                 READ_SIZE(pabyData, pabyDataLimit, nBBOXSize);
     367             : 
     368           3 :                 if (!ReadHeaderBBox(pabyData, pabyData + nBBOXSize, psCtxt))
     369           0 :                     THROW_OSM_PARSING_EXCEPTION;
     370             : 
     371           3 :                 pabyData += nBBOXSize;
     372             :             }
     373         139 :             else if (nKey == MAKE_KEY(OSMHEADER_IDX_REQUIRED_FEATURES, WT_DATA))
     374             :             {
     375          91 :                 READ_TEXT(pabyData, pabyDataLimit, pszTxt);
     376             :                 // printf("OSMHEADER_IDX_REQUIRED_FEATURES = %s\n", pszTxt)
     377          91 :                 if (!(strcmp(pszTxt, "OsmSchema-V0.6") == 0 ||
     378          45 :                       strcmp(pszTxt, "DenseNodes") == 0))
     379             :                 {
     380           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     381             :                              "Error: unsupported required feature : %s",
     382             :                              pszTxt);
     383           0 :                     VSIFree(pszTxt);
     384           0 :                     THROW_OSM_PARSING_EXCEPTION;
     385             :                 }
     386          91 :                 VSIFree(pszTxt);
     387             :             }
     388          48 :             else if (nKey == MAKE_KEY(OSMHEADER_IDX_OPTIONAL_FEATURES, WT_DATA))
     389             :             {
     390           0 :                 READ_TEXT(pabyData, pabyDataLimit, pszTxt);
     391             :                 // printf("OSMHEADER_IDX_OPTIONAL_FEATURES = %s\n", pszTxt);
     392           0 :                 VSIFree(pszTxt);
     393             :             }
     394          48 :             else if (nKey == MAKE_KEY(OSMHEADER_IDX_WRITING_PROGRAM, WT_DATA))
     395             :             {
     396          46 :                 READ_TEXT(pabyData, pabyDataLimit, pszTxt);
     397             :                 // printf("OSMHEADER_IDX_WRITING_PROGRAM = %s\n", pszTxt);
     398          46 :                 VSIFree(pszTxt);
     399             :             }
     400           2 :             else if (nKey == MAKE_KEY(OSMHEADER_IDX_SOURCE, WT_DATA))
     401             :             {
     402           2 :                 READ_TEXT(pabyData, pabyDataLimit, pszTxt);
     403             :                 // printf("OSMHEADER_IDX_SOURCE = %s\n", pszTxt);
     404           2 :                 VSIFree(pszTxt);
     405             :             }
     406           0 :             else if (nKey ==
     407             :                      MAKE_KEY(OSMHEADER_IDX_OSMOSIS_REPLICATION_TIMESTAMP,
     408             :                               WT_VARINT))
     409             :             {
     410           0 :                 SKIP_VARINT(pabyData, pabyDataLimit);
     411             :             }
     412           0 :             else if (nKey ==
     413             :                      MAKE_KEY(OSMHEADER_IDX_OSMOSIS_REPLICATION_SEQ_NUMBER,
     414             :                               WT_VARINT))
     415             :             {
     416           0 :                 SKIP_VARINT(pabyData, pabyDataLimit);
     417             :             }
     418           0 :             else if (nKey ==
     419             :                      MAKE_KEY(OSMHEADER_IDX_OSMOSIS_REPLICATION_BASE_URL,
     420             :                               WT_DATA))
     421             :             {
     422           0 :                 READ_TEXT(pabyData, pabyDataLimit, pszTxt);
     423             :                 /* printf("OSMHEADER_IDX_OSMOSIS_REPLICATION_BASE_URL = %s\n",
     424             :                  * pszTxt); */
     425           0 :                 VSIFree(pszTxt);
     426             :             }
     427             :             else
     428             :             {
     429           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
     430             :             }
     431             :         }
     432             : 
     433          46 :         return pabyData == pabyDataLimit;
     434             :     }
     435           0 :     catch (const std::exception &e)
     436             :     {
     437           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     438           0 :         return false;
     439             :     }
     440             : }
     441             : 
     442             : /************************************************************************/
     443             : /*                          ReadStringTable()                           */
     444             : /************************************************************************/
     445             : 
     446             : constexpr int READSTRINGTABLE_IDX_STRING = 1;
     447             : 
     448          45 : static bool ReadStringTable(const GByte *pabyData, const GByte *pabyDataLimit,
     449             :                             OSMContext *psCtxt)
     450             : {
     451          45 :     const GByte *const pabyDataStart = pabyData;
     452             : 
     453          45 :     unsigned int nStrCount = 0;
     454          45 :     int *panStrOff = psCtxt->panStrOff;
     455             : 
     456          45 :     psCtxt->pszStrBuf = reinterpret_cast<char *>(const_cast<GByte *>(pabyData));
     457             : 
     458             :     try
     459             :     {
     460          45 :         if (static_cast<unsigned>(pabyDataLimit - pabyData) >
     461          45 :             psCtxt->nStrAllocated)
     462             :         {
     463          28 :             psCtxt->nStrAllocated =
     464          56 :                 std::max(psCtxt->nStrAllocated * 2,
     465          28 :                          static_cast<unsigned>(pabyDataLimit - pabyData));
     466          28 :             int *panStrOffNew = static_cast<int *>(VSI_REALLOC_VERBOSE(
     467             :                 panStrOff, psCtxt->nStrAllocated * sizeof(int)));
     468          28 :             if (panStrOffNew == nullptr)
     469           0 :                 THROW_OSM_PARSING_EXCEPTION;
     470          28 :             panStrOff = panStrOffNew;
     471             :         }
     472             : 
     473          90 :         while (pabyData < pabyDataLimit)
     474             :         {
     475          45 :             int nKey = 0;
     476          45 :             READ_FIELD_KEY(nKey);
     477             : 
     478        1274 :             while (nKey == MAKE_KEY(READSTRINGTABLE_IDX_STRING, WT_DATA))
     479             :             {
     480        1274 :                 unsigned int nDataLength = 0;
     481        1274 :                 READ_SIZE(pabyData, pabyDataLimit, nDataLength);
     482             : 
     483        1274 :                 panStrOff[nStrCount++] =
     484        1274 :                     static_cast<int>(pabyData - pabyDataStart);
     485        1274 :                 GByte *pbSaved = const_cast<GByte *>(&pabyData[nDataLength]);
     486             : 
     487        1274 :                 pabyData += nDataLength;
     488             : 
     489        1274 :                 if (pabyData < pabyDataLimit)
     490             :                 {
     491        1229 :                     READ_FIELD_KEY(nKey);
     492        1229 :                     *pbSaved = 0;
     493             :                     /* printf("string[%d] = %s\n", nStrCount-1, pbSaved -
     494             :                      * nDataLength); */
     495             :                 }
     496             :                 else
     497             :                 {
     498          45 :                     *pbSaved = 0;
     499             :                     /* printf("string[%d] = %s\n", nStrCount-1, pbSaved -
     500             :                      * nDataLength); */
     501          45 :                     break;
     502             :                 }
     503             :             }
     504             : 
     505          45 :             if (pabyData < pabyDataLimit)
     506             :             {
     507           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
     508             :             }
     509             :         }
     510             : 
     511          45 :         psCtxt->panStrOff = panStrOff;
     512          45 :         psCtxt->nStrCount = nStrCount;
     513             : 
     514          45 :         return pabyData == pabyDataLimit;
     515             :     }
     516           0 :     catch (const std::exception &e)
     517             :     {
     518           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     519           0 :         psCtxt->panStrOff = panStrOff;
     520           0 :         psCtxt->nStrCount = nStrCount;
     521           0 :         return false;
     522             :     }
     523             : }
     524             : 
     525             : /************************************************************************/
     526             : /*                      AddWithOverflowAccepted()                       */
     527             : /************************************************************************/
     528             : 
     529             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
     530       24974 : static GIntBig AddWithOverflowAccepted(GIntBig a, GIntBig b)
     531             : {
     532             :     // Assumes complement-to-two signed integer representation and that
     533             :     // the compiler will safely cast a negative number to unsigned and a
     534             :     // big unsigned to negative integer.
     535       24974 :     return static_cast<GIntBig>(static_cast<GUIntBig>(a) +
     536       24974 :                                 static_cast<GUIntBig>(b));
     537             : }
     538             : 
     539             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
     540        3847 : static int AddWithOverflowAccepted(int a, int b)
     541             : {
     542             :     // Assumes complement-to-two signed integer representation and that
     543             :     // the compiler will safely cast a negative number to unsigned and a
     544             :     // big unsigned to negative integer.
     545        3847 :     return static_cast<int>(static_cast<unsigned>(a) +
     546        3847 :                             static_cast<unsigned>(b));
     547             : }
     548             : 
     549             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
     550        3847 : static unsigned AddWithOverflowAccepted(unsigned a, int b)
     551             : {
     552             :     // Assumes complement-to-two signed integer representation and that
     553             :     // the compiler will safely cast a negative number to unsigned.
     554        3847 :     return a + static_cast<unsigned>(b);
     555             : }
     556             : 
     557             : /************************************************************************/
     558             : /*                           ReadDenseNodes()                           */
     559             : /************************************************************************/
     560             : 
     561             : constexpr int DENSEINFO_IDX_VERSION = 1;
     562             : constexpr int DENSEINFO_IDX_TIMESTAMP = 2;
     563             : constexpr int DENSEINFO_IDX_CHANGESET = 3;
     564             : constexpr int DENSEINFO_IDX_UID = 4;
     565             : constexpr int DENSEINFO_IDX_USER_SID = 5;
     566             : constexpr int DENSEINFO_IDX_VISIBLE = 6;
     567             : 
     568             : constexpr int DENSENODES_IDX_ID = 1;
     569             : constexpr int DENSENODES_IDX_DENSEINFO = 5;
     570             : constexpr int DENSENODES_IDX_LAT = 8;
     571             : constexpr int DENSENODES_IDX_LON = 9;
     572             : constexpr int DENSENODES_IDX_KEYVALS = 10;
     573             : 
     574          44 : static bool ReadDenseNodes(const GByte *pabyData, const GByte *pabyDataLimit,
     575             :                            OSMContext *psCtxt)
     576             : {
     577          44 :     const GByte *pabyDataIDs = nullptr;
     578          44 :     const GByte *pabyDataIDsLimit = nullptr;
     579          44 :     const GByte *pabyDataLat = nullptr;
     580          44 :     const GByte *pabyDataLon = nullptr;
     581          44 :     const GByte *apabyData[DENSEINFO_IDX_VISIBLE] = {nullptr, nullptr, nullptr,
     582             :                                                      nullptr, nullptr, nullptr};
     583          44 :     const GByte *pabyDataKeyVal = nullptr;
     584          44 :     unsigned int nMaxTags = 0;
     585             : 
     586             :     try
     587             :     {
     588         263 :         while (pabyData < pabyDataLimit)
     589             :         {
     590         219 :             int nKey = 0;
     591         219 :             READ_FIELD_KEY(nKey);
     592             : 
     593         219 :             if (nKey == MAKE_KEY(DENSENODES_IDX_ID, WT_DATA))
     594             :             {
     595          44 :                 unsigned int nSize = 0;
     596             : 
     597          44 :                 if (pabyDataIDs != nullptr)
     598           0 :                     THROW_OSM_PARSING_EXCEPTION;
     599          44 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
     600             : 
     601          44 :                 if (nSize > psCtxt->nNodesAllocated)
     602             :                 {
     603          27 :                     psCtxt->nNodesAllocated =
     604          27 :                         std::max(psCtxt->nNodesAllocated * 2, nSize);
     605             :                     OSMNode *pasNodesNew =
     606          27 :                         static_cast<OSMNode *>(VSI_REALLOC_VERBOSE(
     607             :                             psCtxt->pasNodes,
     608             :                             psCtxt->nNodesAllocated * sizeof(OSMNode)));
     609          27 :                     if (pasNodesNew == nullptr)
     610           0 :                         THROW_OSM_PARSING_EXCEPTION;
     611          27 :                     psCtxt->pasNodes = pasNodesNew;
     612             :                 }
     613             : 
     614          44 :                 pabyDataIDs = pabyData;
     615          44 :                 pabyDataIDsLimit = pabyData + nSize;
     616          44 :                 pabyData += nSize;
     617             :             }
     618         175 :             else if (nKey == MAKE_KEY(DENSENODES_IDX_DENSEINFO, WT_DATA))
     619             :             {
     620          43 :                 unsigned int nSize = 0;
     621             : 
     622          43 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
     623             : 
     624             :                 /* Inline reading of DenseInfo structure */
     625             : 
     626          43 :                 const GByte *pabyDataNewLimit = pabyData + nSize;
     627         258 :                 while (pabyData < pabyDataNewLimit)
     628             :                 {
     629         215 :                     READ_FIELD_KEY(nKey);
     630             : 
     631         215 :                     const int nFieldNumber = GET_FIELDNUMBER(nKey);
     632         215 :                     if (GET_WIRETYPE(nKey) == WT_DATA &&
     633         215 :                         nFieldNumber >= DENSEINFO_IDX_VERSION &&
     634             :                         nFieldNumber <= DENSEINFO_IDX_VISIBLE)
     635             :                     {
     636         215 :                         if (apabyData[nFieldNumber - 1] != nullptr)
     637           0 :                             THROW_OSM_PARSING_EXCEPTION;
     638         215 :                         READ_SIZE(pabyData, pabyDataNewLimit, nSize);
     639             : 
     640         215 :                         apabyData[nFieldNumber - 1] = pabyData;
     641         215 :                         pabyData += nSize;
     642             :                     }
     643             :                     else
     644             :                     {
     645           0 :                         SKIP_UNKNOWN_FIELD(pabyData, pabyDataNewLimit, TRUE);
     646             :                     }
     647             :                 }
     648             : 
     649          43 :                 if (pabyData != pabyDataNewLimit)
     650           0 :                     THROW_OSM_PARSING_EXCEPTION;
     651             :             }
     652         132 :             else if (nKey == MAKE_KEY(DENSENODES_IDX_LAT, WT_DATA))
     653             :             {
     654          44 :                 if (pabyDataLat != nullptr)
     655           0 :                     THROW_OSM_PARSING_EXCEPTION;
     656          44 :                 unsigned int nSize = 0;
     657          44 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
     658          44 :                 pabyDataLat = pabyData;
     659          44 :                 pabyData += nSize;
     660             :             }
     661          88 :             else if (nKey == MAKE_KEY(DENSENODES_IDX_LON, WT_DATA))
     662             :             {
     663          44 :                 if (pabyDataLon != nullptr)
     664           0 :                     THROW_OSM_PARSING_EXCEPTION;
     665          44 :                 unsigned int nSize = 0;
     666          44 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
     667          44 :                 pabyDataLon = pabyData;
     668          44 :                 pabyData += nSize;
     669             :             }
     670          44 :             else if (nKey == MAKE_KEY(DENSENODES_IDX_KEYVALS, WT_DATA))
     671             :             {
     672          44 :                 if (pabyDataKeyVal != nullptr)
     673           0 :                     THROW_OSM_PARSING_EXCEPTION;
     674          44 :                 unsigned int nSize = 0;
     675          44 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
     676             : 
     677          44 :                 pabyDataKeyVal = pabyData;
     678          44 :                 nMaxTags = nSize / 2;
     679             : 
     680          44 :                 if (nMaxTags > psCtxt->nTagsAllocated)
     681             :                 {
     682             : 
     683          27 :                     psCtxt->nTagsAllocated =
     684          27 :                         std::max(psCtxt->nTagsAllocated * 2, nMaxTags);
     685             :                     OSMTag *pasTagsNew =
     686          27 :                         static_cast<OSMTag *>(VSI_REALLOC_VERBOSE(
     687             :                             psCtxt->pasTags,
     688             :                             psCtxt->nTagsAllocated * sizeof(OSMTag)));
     689          27 :                     if (pasTagsNew == nullptr)
     690           0 :                         THROW_OSM_PARSING_EXCEPTION;
     691          27 :                     psCtxt->pasTags = pasTagsNew;
     692             :                 }
     693             : 
     694          44 :                 pabyData += nSize;
     695             :             }
     696             :             else
     697             :             {
     698           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
     699             :             }
     700             :         }
     701             : 
     702          44 :         if (pabyData != pabyDataLimit)
     703           0 :             THROW_OSM_PARSING_EXCEPTION;
     704             : 
     705          44 :         if (pabyDataIDs != nullptr && pabyDataLat != nullptr &&
     706          44 :             pabyDataLon != nullptr)
     707             :         {
     708          44 :             const GByte *pabyDataVersion = apabyData[DENSEINFO_IDX_VERSION - 1];
     709          44 :             const GByte *pabyDataTimeStamp =
     710          44 :                 apabyData[DENSEINFO_IDX_TIMESTAMP - 1];
     711          44 :             const GByte *pabyDataChangeset =
     712          44 :                 apabyData[DENSEINFO_IDX_CHANGESET - 1];
     713          44 :             const GByte *pabyDataUID = apabyData[DENSEINFO_IDX_UID - 1];
     714          44 :             const GByte *pabyDataUserSID =
     715          44 :                 apabyData[DENSEINFO_IDX_USER_SID - 1];
     716             :             /* GByte* pabyDataVisible = apabyData[DENSEINFO_IDX_VISIBLE - 1]; */
     717             : 
     718          44 :             GIntBig nID = 0;
     719          44 :             GIntBig nLat = 0;
     720          44 :             GIntBig nLon = 0;
     721          44 :             GIntBig nTimeStamp = 0;
     722          44 :             GIntBig nChangeset = 0;
     723          44 :             int nUID = 0;
     724          44 :             unsigned int nUserSID = 0;
     725          44 :             int nTags = 0;
     726          44 :             int nNodes = 0;
     727             : 
     728          44 :             const char *pszStrBuf = psCtxt->pszStrBuf;
     729          44 :             int *panStrOff = psCtxt->panStrOff;
     730          44 :             const unsigned int nStrCount = psCtxt->nStrCount;
     731          44 :             OSMTag *pasTags = psCtxt->pasTags;
     732          44 :             OSMNode *pasNodes = psCtxt->pasNodes;
     733             : 
     734          44 :             int nVersion = 0;
     735             :             /* int nVisible = 1; */
     736             : 
     737        3900 :             while (pabyDataIDs < pabyDataIDsLimit)
     738             :             {
     739             :                 GIntBig nDelta1, nDelta2;
     740        3856 :                 int nKVIndexStart = nTags;
     741             : 
     742        3856 :                 READ_VARSINT64_NOCHECK(pabyDataIDs, pabyDataIDsLimit, nDelta1);
     743        3856 :                 READ_VARSINT64(pabyDataLat, pabyDataLimit, nDelta2);
     744        3856 :                 nID = AddWithOverflowAccepted(nID, nDelta1);
     745        3856 :                 nLat = AddWithOverflowAccepted(nLat, nDelta2);
     746             : 
     747        3856 :                 READ_VARSINT64(pabyDataLon, pabyDataLimit, nDelta1);
     748        3856 :                 nLon = AddWithOverflowAccepted(nLon, nDelta1);
     749             : 
     750        3856 :                 if (pabyDataTimeStamp)
     751             :                 {
     752        3847 :                     READ_VARSINT64(pabyDataTimeStamp, pabyDataLimit, nDelta2);
     753        3847 :                     nTimeStamp = AddWithOverflowAccepted(nTimeStamp, nDelta2);
     754             :                 }
     755        3856 :                 if (pabyDataChangeset)
     756             :                 {
     757        3847 :                     READ_VARSINT64(pabyDataChangeset, pabyDataLimit, nDelta1);
     758        3847 :                     nChangeset = AddWithOverflowAccepted(nChangeset, nDelta1);
     759             :                 }
     760        3856 :                 if (pabyDataVersion)
     761             :                 {
     762        3847 :                     READ_VARINT32(pabyDataVersion, pabyDataLimit, nVersion);
     763             :                 }
     764        3856 :                 if (pabyDataUID)
     765             :                 {
     766        3847 :                     int nDeltaUID = 0;
     767        3847 :                     READ_VARSINT32(pabyDataUID, pabyDataLimit, nDeltaUID);
     768        3847 :                     nUID = AddWithOverflowAccepted(nUID, nDeltaUID);
     769             :                 }
     770        3856 :                 if (pabyDataUserSID)
     771             :                 {
     772        3847 :                     int nDeltaUserSID = 0;
     773        3847 :                     READ_VARSINT32(pabyDataUserSID, pabyDataLimit,
     774             :                                    nDeltaUserSID);
     775        3847 :                     nUserSID = AddWithOverflowAccepted(nUserSID, nDeltaUserSID);
     776        3847 :                     if (nUserSID >= nStrCount)
     777           0 :                         THROW_OSM_PARSING_EXCEPTION;
     778             :                 }
     779             :                 /* if( pabyDataVisible )
     780             :                     READ_VARINT32(pabyDataVisible, pabyDataLimit, nVisible); */
     781             : 
     782        3856 :                 if (pabyDataKeyVal != nullptr && pasTags != nullptr)
     783             :                 {
     784        4046 :                     while (static_cast<unsigned>(nTags) < nMaxTags)
     785             :                     {
     786             :                         unsigned int nKey, nVal;
     787        4043 :                         READ_VARUINT32(pabyDataKeyVal, pabyDataLimit, nKey);
     788        4043 :                         if (nKey == 0)
     789        3853 :                             break;
     790         190 :                         if (nKey >= nStrCount)
     791           0 :                             THROW_OSM_PARSING_EXCEPTION;
     792             : 
     793         190 :                         READ_VARUINT32(pabyDataKeyVal, pabyDataLimit, nVal);
     794         190 :                         if (nVal >= nStrCount)
     795           0 :                             THROW_OSM_PARSING_EXCEPTION;
     796             : 
     797         190 :                         pasTags[nTags].pszK = pszStrBuf + panStrOff[nKey];
     798         190 :                         pasTags[nTags].pszV = pszStrBuf + panStrOff[nVal];
     799         190 :                         nTags++;
     800             : 
     801             :                         /* printf("nKey = %d, nVal = %d\n", nKey, nVal); */
     802             :                     }
     803             :                 }
     804             : 
     805        3856 :                 if (pasTags != nullptr && nTags > nKVIndexStart)
     806          70 :                     pasNodes[nNodes].pasTags = pasTags + nKVIndexStart;
     807             :                 else
     808        3786 :                     pasNodes[nNodes].pasTags = nullptr;
     809        3856 :                 pasNodes[nNodes].nTags = nTags - nKVIndexStart;
     810             : 
     811        3856 :                 pasNodes[nNodes].nID = nID;
     812        3856 :                 pasNodes[nNodes].dfLat =
     813        3856 :                     .000000001 *
     814        3856 :                     (psCtxt->nLatOffset +
     815        3856 :                      (static_cast<double>(psCtxt->nGranularity) * nLat));
     816        3856 :                 pasNodes[nNodes].dfLon =
     817        3856 :                     .000000001 *
     818        3856 :                     (psCtxt->nLonOffset +
     819        3856 :                      (static_cast<double>(psCtxt->nGranularity) * nLon));
     820        3856 :                 if (pasNodes[nNodes].dfLon < -180 ||
     821        3856 :                     pasNodes[nNodes].dfLon > 180 ||
     822        3856 :                     pasNodes[nNodes].dfLat < -90 || pasNodes[nNodes].dfLat > 90)
     823           0 :                     THROW_OSM_PARSING_EXCEPTION;
     824        3856 :                 pasNodes[nNodes].sInfo.bTimeStampIsStr = false;
     825        3856 :                 pasNodes[nNodes].sInfo.ts.nTimeStamp = nTimeStamp;
     826        3856 :                 pasNodes[nNodes].sInfo.nChangeset = nChangeset;
     827        3856 :                 pasNodes[nNodes].sInfo.nVersion = nVersion;
     828        3856 :                 pasNodes[nNodes].sInfo.nUID = nUID;
     829        3856 :                 if (nUserSID >= nStrCount)
     830           0 :                     pasNodes[nNodes].sInfo.pszUserSID = "";
     831             :                 else
     832        3856 :                     pasNodes[nNodes].sInfo.pszUserSID =
     833        3856 :                         pszStrBuf + panStrOff[nUserSID];
     834             :                 /* pasNodes[nNodes].sInfo.nVisible = nVisible; */
     835        3856 :                 nNodes++;
     836             :                 /* printf("nLat = " CPL_FRMT_GIB "\n", nLat); printf("nLon = "
     837             :                  * CPL_FRMT_GIB "\n", nLon); */
     838             :             }
     839             : 
     840          44 :             psCtxt->pfnNotifyNodes(nNodes, pasNodes, psCtxt, psCtxt->user_data);
     841             : 
     842          44 :             if (pabyDataIDs != pabyDataIDsLimit)
     843           0 :                 THROW_OSM_PARSING_EXCEPTION;
     844             :         }
     845             : 
     846          44 :         return true;
     847             :     }
     848           0 :     catch (const std::exception &e)
     849             :     {
     850           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     851           0 :         return false;
     852             :     }
     853             : }
     854             : 
     855             : /************************************************************************/
     856             : /*                            ReadOSMInfo()                             */
     857             : /************************************************************************/
     858             : 
     859             : constexpr int INFO_IDX_VERSION = 1;
     860             : constexpr int INFO_IDX_TIMESTAMP = 2;
     861             : constexpr int INFO_IDX_CHANGESET = 3;
     862             : constexpr int INFO_IDX_UID = 4;
     863             : constexpr int INFO_IDX_USER_SID = 5;
     864             : constexpr int INFO_IDX_VISIBLE = 6;
     865             : 
     866             : static bool ReadOSMInfo(const GByte *pabyData, const GByte *pabyDataLimit,
     867             :                         OSMInfo *psInfo, OSMContext *psContext) CPL_NO_INLINE;
     868             : 
     869        5676 : static bool ReadOSMInfo(const GByte *pabyData, const GByte *pabyDataLimit,
     870             :                         OSMInfo *psInfo, OSMContext *psContext)
     871             : {
     872             :     try
     873             :     {
     874        5676 :         while (pabyData < pabyDataLimit)
     875             :         {
     876        4730 :             int nKey = 0;
     877        4730 :             READ_FIELD_KEY(nKey);
     878             : 
     879        4730 :             if (nKey == MAKE_KEY(INFO_IDX_VERSION, WT_VARINT))
     880             :             {
     881         946 :                 READ_VARINT32(pabyData, pabyDataLimit, psInfo->nVersion);
     882             :             }
     883        3784 :             else if (nKey == MAKE_KEY(INFO_IDX_TIMESTAMP, WT_VARINT))
     884             :             {
     885         946 :                 READ_VARINT64(pabyData, pabyDataLimit, psInfo->ts.nTimeStamp);
     886             :             }
     887        2838 :             else if (nKey == MAKE_KEY(INFO_IDX_CHANGESET, WT_VARINT))
     888             :             {
     889         946 :                 READ_VARINT64(pabyData, pabyDataLimit, psInfo->nChangeset);
     890             :             }
     891        1892 :             else if (nKey == MAKE_KEY(INFO_IDX_UID, WT_VARINT))
     892             :             {
     893         946 :                 READ_VARINT32(pabyData, pabyDataLimit, psInfo->nUID);
     894             :             }
     895         946 :             else if (nKey == MAKE_KEY(INFO_IDX_USER_SID, WT_VARINT))
     896             :             {
     897         946 :                 unsigned int nUserSID = 0;
     898         946 :                 READ_VARUINT32(pabyData, pabyDataLimit, nUserSID);
     899         946 :                 if (nUserSID < psContext->nStrCount)
     900         946 :                     psInfo->pszUserSID =
     901         946 :                         psContext->pszStrBuf + psContext->panStrOff[nUserSID];
     902             :             }
     903           0 :             else if (nKey == MAKE_KEY(INFO_IDX_VISIBLE, WT_VARINT))
     904             :             {
     905           0 :                 SKIP_VARINT(pabyData, pabyDataLimit);
     906             :                 // int nVisible = 0;
     907             :                 // READ_VARINT32(pabyData, pabyDataLimit, /*psInfo->*/nVisible);
     908             :             }
     909             :             else
     910             :             {
     911           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
     912             :             }
     913             :         }
     914             : 
     915         946 :         return pabyData == pabyDataLimit;
     916             :     }
     917           0 :     catch (const std::exception &e)
     918             :     {
     919           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
     920           0 :         return false;
     921             :     }
     922             : }
     923             : 
     924             : /************************************************************************/
     925             : /*                              ReadNode()                              */
     926             : /************************************************************************/
     927             : 
     928             : /* From
     929             :  * https://github.com/openstreetmap/osmosis/blob/master/osmosis-osm-binary/src/main/protobuf/osmformat.proto
     930             :  */
     931             : /* The one advertized in http://wiki.openstreetmap.org/wiki/PBF_Format and */
     932             : /* used previously seem wrong/old-dated */
     933             : 
     934             : constexpr int NODE_IDX_ID = 1;
     935             : constexpr int NODE_IDX_LAT = 8;
     936             : constexpr int NODE_IDX_LON = 9;
     937             : constexpr int NODE_IDX_KEYS = 2;
     938             : constexpr int NODE_IDX_VALS = 3;
     939             : constexpr int NODE_IDX_INFO = 4;
     940             : 
     941           9 : static bool ReadNode(const GByte *pabyData, const GByte *pabyDataLimit,
     942             :                      OSMContext *psCtxt)
     943             : {
     944             :     OSMNode sNode;
     945             : 
     946           9 :     sNode.nID = 0;
     947           9 :     sNode.dfLat = 0.0;
     948           9 :     sNode.dfLon = 0.0;
     949           9 :     INIT_INFO(&(sNode.sInfo));
     950           9 :     sNode.nTags = 0;
     951           9 :     sNode.pasTags = nullptr;
     952             : 
     953             :     try
     954             :     {
     955          38 :         while (pabyData < pabyDataLimit)
     956             :         {
     957          29 :             int nKey = 0;
     958          29 :             READ_FIELD_KEY(nKey);
     959             : 
     960          29 :             if (nKey == MAKE_KEY(NODE_IDX_ID, WT_VARINT))
     961             :             {
     962           9 :                 READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, sNode.nID);
     963             :             }
     964          20 :             else if (nKey == MAKE_KEY(NODE_IDX_LAT, WT_VARINT))
     965             :             {
     966           9 :                 GIntBig nLat = 0;
     967           9 :                 READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, nLat);
     968           9 :                 sNode.dfLat =
     969           9 :                     0.000000001 *
     970           9 :                     (psCtxt->nLatOffset +
     971           9 :                      (static_cast<double>(psCtxt->nGranularity) * nLat));
     972             :             }
     973          11 :             else if (nKey == MAKE_KEY(NODE_IDX_LON, WT_VARINT))
     974             :             {
     975           9 :                 GIntBig nLon = 0;
     976           9 :                 READ_VARSINT64_NOCHECK(pabyData, pabyDataLimit, nLon);
     977           9 :                 sNode.dfLon =
     978           9 :                     0.000000001 *
     979           9 :                     (psCtxt->nLonOffset +
     980           9 :                      (static_cast<double>(psCtxt->nGranularity) * nLon));
     981             :             }
     982           2 :             else if (nKey == MAKE_KEY(NODE_IDX_KEYS, WT_DATA))
     983             :             {
     984           1 :                 unsigned int nSize = 0;
     985           1 :                 const GByte *pabyDataNewLimit = nullptr;
     986           1 :                 if (sNode.nTags != 0)
     987           0 :                     THROW_OSM_PARSING_EXCEPTION;
     988           1 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
     989             : 
     990           1 :                 if (nSize > psCtxt->nTagsAllocated)
     991             :                 {
     992           1 :                     psCtxt->nTagsAllocated =
     993           1 :                         std::max(psCtxt->nTagsAllocated * 2, nSize);
     994             :                     OSMTag *pasTagsNew =
     995           1 :                         static_cast<OSMTag *>(VSI_REALLOC_VERBOSE(
     996             :                             psCtxt->pasTags,
     997             :                             psCtxt->nTagsAllocated * sizeof(OSMTag)));
     998           1 :                     if (pasTagsNew == nullptr)
     999           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1000           1 :                     psCtxt->pasTags = pasTagsNew;
    1001             :                 }
    1002             : 
    1003           1 :                 pabyDataNewLimit = pabyData + nSize;
    1004           4 :                 while (pabyData < pabyDataNewLimit)
    1005             :                 {
    1006           3 :                     unsigned int nKey2 = 0;
    1007           3 :                     READ_VARUINT32(pabyData, pabyDataNewLimit, nKey2);
    1008             : 
    1009           3 :                     if (nKey2 >= psCtxt->nStrCount)
    1010           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1011             : 
    1012           3 :                     psCtxt->pasTags[sNode.nTags].pszK =
    1013           3 :                         psCtxt->pszStrBuf + psCtxt->panStrOff[nKey2];
    1014           3 :                     psCtxt->pasTags[sNode.nTags].pszV = "";
    1015           3 :                     sNode.nTags++;
    1016             :                 }
    1017           1 :                 if (pabyData != pabyDataNewLimit)
    1018           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1019             :             }
    1020           1 :             else if (nKey == MAKE_KEY(NODE_IDX_VALS, WT_DATA))
    1021             :             {
    1022           1 :                 unsigned int nIter = 0;
    1023           1 :                 if (sNode.nTags == 0)
    1024           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1025             :                 // unsigned int nSize = 0;
    1026             :                 // READ_VARUINT32(pabyData, pabyDataLimit, nSize);
    1027           1 :                 SKIP_VARINT(pabyData, pabyDataLimit);
    1028             : 
    1029           4 :                 for (; nIter < sNode.nTags; nIter++)
    1030             :                 {
    1031           3 :                     unsigned int nVal = 0;
    1032           3 :                     READ_VARUINT32(pabyData, pabyDataLimit, nVal);
    1033             : 
    1034           3 :                     if (nVal >= psCtxt->nStrCount)
    1035           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1036             : 
    1037           3 :                     psCtxt->pasTags[nIter].pszV =
    1038           3 :                         psCtxt->pszStrBuf + psCtxt->panStrOff[nVal];
    1039             :                 }
    1040             :             }
    1041           0 :             else if (nKey == MAKE_KEY(NODE_IDX_INFO, WT_DATA))
    1042             :             {
    1043           0 :                 unsigned int nSize = 0;
    1044           0 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1045             : 
    1046           0 :                 if (!ReadOSMInfo(pabyData, pabyDataLimit + nSize, &sNode.sInfo,
    1047             :                                  psCtxt))
    1048           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1049             : 
    1050           0 :                 pabyData += nSize;
    1051             :             }
    1052             :             else
    1053             :             {
    1054           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
    1055             :             }
    1056             :         }
    1057             : 
    1058           9 :         if (sNode.dfLon < -180 || sNode.dfLon > 180 || sNode.dfLat < -90 ||
    1059           9 :             sNode.dfLat > 90)
    1060           0 :             THROW_OSM_PARSING_EXCEPTION;
    1061             : 
    1062           9 :         if (pabyData != pabyDataLimit)
    1063           0 :             THROW_OSM_PARSING_EXCEPTION;
    1064             : 
    1065           9 :         if (sNode.nTags)
    1066           1 :             sNode.pasTags = psCtxt->pasTags;
    1067             :         else
    1068           8 :             sNode.pasTags = nullptr;
    1069           9 :         psCtxt->pfnNotifyNodes(1, &sNode, psCtxt, psCtxt->user_data);
    1070             : 
    1071           9 :         return true;
    1072             :     }
    1073           0 :     catch (const std::exception &e)
    1074             :     {
    1075           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1076           0 :         return false;
    1077             :     }
    1078             : }
    1079             : 
    1080             : /************************************************************************/
    1081             : /*                              ReadWay()                               */
    1082             : /************************************************************************/
    1083             : 
    1084             : constexpr int WAY_IDX_ID = 1;
    1085             : constexpr int WAY_IDX_KEYS = 2;
    1086             : constexpr int WAY_IDX_VALS = 3;
    1087             : constexpr int WAY_IDX_INFO = 4;
    1088             : constexpr int WAY_IDX_REFS = 8;
    1089             : 
    1090         772 : static bool ReadWay(const GByte *pabyData, const GByte *pabyDataLimit,
    1091             :                     OSMContext *psCtxt)
    1092             : {
    1093             :     OSMWay sWay;
    1094         772 :     sWay.nID = 0;
    1095         772 :     INIT_INFO(&(sWay.sInfo));
    1096         772 :     sWay.nTags = 0;
    1097         772 :     sWay.nRefs = 0;
    1098             : 
    1099             :     try
    1100             :     {
    1101        4536 :         while (pabyData < pabyDataLimit)
    1102             :         {
    1103        3764 :             int nKey = 0;
    1104        3764 :             READ_FIELD_KEY(nKey);
    1105             : 
    1106        3764 :             if (nKey == MAKE_KEY(WAY_IDX_ID, WT_VARINT))
    1107             :             {
    1108         772 :                 READ_VARINT64(pabyData, pabyDataLimit, sWay.nID);
    1109             :             }
    1110        2992 :             else if (nKey == MAKE_KEY(WAY_IDX_KEYS, WT_DATA))
    1111             :             {
    1112         732 :                 unsigned int nSize = 0;
    1113         732 :                 const GByte *pabyDataNewLimit = nullptr;
    1114         732 :                 if (sWay.nTags != 0)
    1115           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1116         732 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1117             : 
    1118         732 :                 if (nSize > psCtxt->nTagsAllocated)
    1119             :                 {
    1120           0 :                     psCtxt->nTagsAllocated =
    1121           0 :                         std::max(psCtxt->nTagsAllocated * 2, nSize);
    1122             :                     OSMTag *pasTagsNew =
    1123           0 :                         static_cast<OSMTag *>(VSI_REALLOC_VERBOSE(
    1124             :                             psCtxt->pasTags,
    1125             :                             psCtxt->nTagsAllocated * sizeof(OSMTag)));
    1126           0 :                     if (pasTagsNew == nullptr)
    1127           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1128           0 :                     psCtxt->pasTags = pasTagsNew;
    1129             :                 }
    1130             : 
    1131         732 :                 pabyDataNewLimit = pabyData + nSize;
    1132        1862 :                 while (pabyData < pabyDataNewLimit)
    1133             :                 {
    1134        1130 :                     unsigned int nKey2 = 0;
    1135        1130 :                     READ_VARUINT32(pabyData, pabyDataNewLimit, nKey2);
    1136             : 
    1137        1130 :                     if (nKey2 >= psCtxt->nStrCount)
    1138           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1139             : 
    1140        1130 :                     psCtxt->pasTags[sWay.nTags].pszK =
    1141        1130 :                         psCtxt->pszStrBuf + psCtxt->panStrOff[nKey2];
    1142        1130 :                     psCtxt->pasTags[sWay.nTags].pszV = "";
    1143        1130 :                     sWay.nTags++;
    1144             :                 }
    1145         732 :                 if (pabyData != pabyDataNewLimit)
    1146           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1147             :             }
    1148        2260 :             else if (nKey == MAKE_KEY(WAY_IDX_VALS, WT_DATA))
    1149             :             {
    1150         732 :                 unsigned int nIter = 0;
    1151         732 :                 if (sWay.nTags == 0)
    1152           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1153             :                 // unsigned int nSize = 0;
    1154             :                 // READ_VARUINT32(pabyData, pabyDataLimit, nSize);
    1155         732 :                 SKIP_VARINT(pabyData, pabyDataLimit);
    1156             : 
    1157        1862 :                 for (; nIter < sWay.nTags; nIter++)
    1158             :                 {
    1159        1130 :                     unsigned int nVal = 0;
    1160        1130 :                     READ_VARUINT32(pabyData, pabyDataLimit, nVal);
    1161             : 
    1162        1130 :                     if (nVal >= psCtxt->nStrCount)
    1163           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1164             : 
    1165        1130 :                     psCtxt->pasTags[nIter].pszV =
    1166        1130 :                         psCtxt->pszStrBuf + psCtxt->panStrOff[nVal];
    1167             :                 }
    1168             :             }
    1169        1528 :             else if (nKey == MAKE_KEY(WAY_IDX_INFO, WT_DATA))
    1170             :             {
    1171         756 :                 unsigned int nSize = 0;
    1172         756 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1173             : 
    1174         756 :                 if (!ReadOSMInfo(pabyData, pabyData + nSize, &sWay.sInfo,
    1175             :                                  psCtxt))
    1176           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1177             : 
    1178         756 :                 pabyData += nSize;
    1179             :             }
    1180         772 :             else if (nKey == MAKE_KEY(WAY_IDX_REFS, WT_DATA))
    1181             :             {
    1182         772 :                 GIntBig nRefVal = 0;
    1183         772 :                 unsigned int nSize = 0;
    1184         772 :                 const GByte *pabyDataNewLimit = nullptr;
    1185         772 :                 if (sWay.nRefs != 0)
    1186           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1187         772 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1188             : 
    1189         772 :                 if (nSize > psCtxt->nNodeRefsAllocated)
    1190             :                 {
    1191          80 :                     psCtxt->nNodeRefsAllocated =
    1192          80 :                         std::max(psCtxt->nNodeRefsAllocated * 2, nSize);
    1193             :                     GIntBig *panNodeRefsNew =
    1194          80 :                         static_cast<GIntBig *>(VSI_REALLOC_VERBOSE(
    1195             :                             psCtxt->panNodeRefs,
    1196             :                             psCtxt->nNodeRefsAllocated * sizeof(GIntBig)));
    1197          80 :                     if (panNodeRefsNew == nullptr)
    1198           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1199          80 :                     psCtxt->panNodeRefs = panNodeRefsNew;
    1200             :                 }
    1201             : 
    1202         772 :                 pabyDataNewLimit = pabyData + nSize;
    1203        6124 :                 while (pabyData < pabyDataNewLimit)
    1204             :                 {
    1205        5352 :                     GIntBig nDeltaRef = 0;
    1206        5352 :                     READ_VARSINT64_NOCHECK(pabyData, pabyDataNewLimit,
    1207             :                                            nDeltaRef);
    1208        5352 :                     nRefVal = AddWithOverflowAccepted(nRefVal, nDeltaRef);
    1209             : 
    1210        5352 :                     psCtxt->panNodeRefs[sWay.nRefs++] = nRefVal;
    1211             :                 }
    1212             : 
    1213         772 :                 if (pabyData != pabyDataNewLimit)
    1214           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1215             :             }
    1216             :             else
    1217             :             {
    1218           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
    1219             :             }
    1220             :         }
    1221             : 
    1222         772 :         if (pabyData != pabyDataLimit)
    1223           0 :             THROW_OSM_PARSING_EXCEPTION;
    1224             : 
    1225         772 :         if (sWay.nTags)
    1226         732 :             sWay.pasTags = psCtxt->pasTags;
    1227             :         else
    1228          40 :             sWay.pasTags = nullptr;
    1229         772 :         sWay.panNodeRefs = psCtxt->panNodeRefs;
    1230             : 
    1231         772 :         psCtxt->pfnNotifyWay(&sWay, psCtxt, psCtxt->user_data);
    1232             : 
    1233         772 :         return true;
    1234             :     }
    1235           0 :     catch (const std::exception &e)
    1236             :     {
    1237           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1238           0 :         return false;
    1239             :     }
    1240             : }
    1241             : 
    1242             : /************************************************************************/
    1243             : /*                            ReadRelation()                            */
    1244             : /************************************************************************/
    1245             : 
    1246             : constexpr int RELATION_IDX_ID = 1;
    1247             : constexpr int RELATION_IDX_KEYS = 2;
    1248             : constexpr int RELATION_IDX_VALS = 3;
    1249             : constexpr int RELATION_IDX_INFO = 4;
    1250             : constexpr int RELATION_IDX_ROLES_SID = 8;
    1251             : constexpr int RELATION_IDX_MEMIDS = 9;
    1252             : constexpr int RELATION_IDX_TYPES = 10;
    1253             : 
    1254         200 : static bool ReadRelation(const GByte *pabyData, const GByte *pabyDataLimit,
    1255             :                          OSMContext *psCtxt)
    1256             : {
    1257             :     OSMRelation sRelation;
    1258         200 :     sRelation.nID = 0;
    1259         200 :     INIT_INFO(&(sRelation.sInfo));
    1260         200 :     sRelation.nTags = 0;
    1261         200 :     sRelation.nMembers = 0;
    1262             : 
    1263             :     try
    1264             :     {
    1265        1590 :         while (pabyData < pabyDataLimit)
    1266             :         {
    1267        1390 :             int nKey = 0;
    1268        1390 :             READ_FIELD_KEY(nKey);
    1269             : 
    1270        1390 :             if (nKey == MAKE_KEY(RELATION_IDX_ID, WT_VARINT))
    1271             :             {
    1272         200 :                 READ_VARINT64(pabyData, pabyDataLimit, sRelation.nID);
    1273             :             }
    1274        1190 :             else if (nKey == MAKE_KEY(RELATION_IDX_KEYS, WT_DATA))
    1275             :             {
    1276         200 :                 unsigned int nSize = 0;
    1277         200 :                 const GByte *pabyDataNewLimit = nullptr;
    1278         200 :                 if (sRelation.nTags != 0)
    1279           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1280         200 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1281             : 
    1282         200 :                 if (nSize > psCtxt->nTagsAllocated)
    1283             :                 {
    1284           0 :                     psCtxt->nTagsAllocated =
    1285           0 :                         std::max(psCtxt->nTagsAllocated * 2, nSize);
    1286             :                     OSMTag *pasTagsNew =
    1287           0 :                         static_cast<OSMTag *>(VSI_REALLOC_VERBOSE(
    1288             :                             psCtxt->pasTags,
    1289             :                             psCtxt->nTagsAllocated * sizeof(OSMTag)));
    1290           0 :                     if (pasTagsNew == nullptr)
    1291           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1292           0 :                     psCtxt->pasTags = pasTagsNew;
    1293             :                 }
    1294             : 
    1295         200 :                 pabyDataNewLimit = pabyData + nSize;
    1296         440 :                 while (pabyData < pabyDataNewLimit)
    1297             :                 {
    1298         240 :                     unsigned int nKey2 = 0;
    1299         240 :                     READ_VARUINT32(pabyData, pabyDataNewLimit, nKey2);
    1300             : 
    1301         240 :                     if (nKey2 >= psCtxt->nStrCount)
    1302           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1303             : 
    1304         240 :                     psCtxt->pasTags[sRelation.nTags].pszK =
    1305         240 :                         psCtxt->pszStrBuf + psCtxt->panStrOff[nKey2];
    1306         240 :                     psCtxt->pasTags[sRelation.nTags].pszV = "";
    1307         240 :                     sRelation.nTags++;
    1308             :                 }
    1309         200 :                 if (pabyData != pabyDataNewLimit)
    1310           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1311             :             }
    1312         990 :             else if (nKey == MAKE_KEY(RELATION_IDX_VALS, WT_DATA))
    1313             :             {
    1314         200 :                 unsigned int nIter = 0;
    1315         200 :                 if (sRelation.nTags == 0)
    1316           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1317             :                 // unsigned int nSize = 0;
    1318             :                 // READ_VARUINT32(pabyData, pabyDataLimit, nSize);
    1319         200 :                 SKIP_VARINT(pabyData, pabyDataLimit);
    1320             : 
    1321         440 :                 for (; nIter < sRelation.nTags; nIter++)
    1322             :                 {
    1323         240 :                     unsigned int nVal = 0;
    1324         240 :                     READ_VARUINT32(pabyData, pabyDataLimit, nVal);
    1325             : 
    1326         240 :                     if (nVal >= psCtxt->nStrCount)
    1327           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1328             : 
    1329         240 :                     psCtxt->pasTags[nIter].pszV =
    1330         240 :                         psCtxt->pszStrBuf + psCtxt->panStrOff[nVal];
    1331             :                 }
    1332             :             }
    1333         790 :             else if (nKey == MAKE_KEY(RELATION_IDX_INFO, WT_DATA))
    1334             :             {
    1335         190 :                 unsigned int nSize = 0;
    1336         190 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1337             : 
    1338         190 :                 if (!ReadOSMInfo(pabyData, pabyData + nSize, &sRelation.sInfo,
    1339             :                                  psCtxt))
    1340           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1341             : 
    1342         190 :                 pabyData += nSize;
    1343             :             }
    1344         600 :             else if (nKey == MAKE_KEY(RELATION_IDX_ROLES_SID, WT_DATA))
    1345             :             {
    1346         200 :                 unsigned int nSize = 0;
    1347         200 :                 const GByte *pabyDataNewLimit = nullptr;
    1348         200 :                 if (sRelation.nMembers != 0)
    1349           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1350         200 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1351             : 
    1352         200 :                 if (nSize > psCtxt->nMembersAllocated)
    1353             :                 {
    1354          24 :                     psCtxt->nMembersAllocated =
    1355          24 :                         std::max(psCtxt->nMembersAllocated * 2, nSize);
    1356             :                     OSMMember *pasMembersNew =
    1357          24 :                         static_cast<OSMMember *>(VSI_REALLOC_VERBOSE(
    1358             :                             psCtxt->pasMembers,
    1359             :                             psCtxt->nMembersAllocated * sizeof(OSMMember)));
    1360          24 :                     if (pasMembersNew == nullptr)
    1361           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1362          24 :                     psCtxt->pasMembers = pasMembersNew;
    1363             :                 }
    1364             : 
    1365         200 :                 pabyDataNewLimit = pabyData + nSize;
    1366         560 :                 while (pabyData < pabyDataNewLimit)
    1367             :                 {
    1368         360 :                     unsigned int nRoleSID = 0;
    1369         360 :                     READ_VARUINT32(pabyData, pabyDataNewLimit, nRoleSID);
    1370         360 :                     if (nRoleSID >= psCtxt->nStrCount)
    1371           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1372             : 
    1373         360 :                     psCtxt->pasMembers[sRelation.nMembers].pszRole =
    1374         360 :                         psCtxt->pszStrBuf + psCtxt->panStrOff[nRoleSID];
    1375         360 :                     psCtxt->pasMembers[sRelation.nMembers].nID = 0;
    1376         360 :                     psCtxt->pasMembers[sRelation.nMembers].eType = MEMBER_NODE;
    1377         360 :                     sRelation.nMembers++;
    1378             :                 }
    1379             : 
    1380         200 :                 if (pabyData != pabyDataNewLimit)
    1381           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1382             :             }
    1383         400 :             else if (nKey == MAKE_KEY(RELATION_IDX_MEMIDS, WT_DATA))
    1384             :             {
    1385         200 :                 unsigned int nIter = 0;
    1386         200 :                 GIntBig nMemID = 0;
    1387         200 :                 if (sRelation.nMembers == 0)
    1388           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1389             :                 // unsigned int nSize = 0;
    1390             :                 // READ_VARUINT32(pabyData, pabyDataLimit, nSize);
    1391         200 :                 SKIP_VARINT(pabyData, pabyDataLimit);
    1392             : 
    1393         560 :                 for (; nIter < sRelation.nMembers; nIter++)
    1394             :                 {
    1395         360 :                     GIntBig nDeltaMemID = 0;
    1396         360 :                     READ_VARSINT64(pabyData, pabyDataLimit, nDeltaMemID);
    1397         360 :                     nMemID = AddWithOverflowAccepted(nMemID, nDeltaMemID);
    1398             : 
    1399         360 :                     psCtxt->pasMembers[nIter].nID = nMemID;
    1400             :                 }
    1401             :             }
    1402         200 :             else if (nKey == MAKE_KEY(RELATION_IDX_TYPES, WT_DATA))
    1403             :             {
    1404         200 :                 unsigned int nIter = 0;
    1405         200 :                 if (sRelation.nMembers == 0)
    1406           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1407         200 :                 unsigned int nSize = 0;
    1408         200 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1409         200 :                 if (nSize != sRelation.nMembers)
    1410           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1411             : 
    1412         560 :                 for (; nIter < sRelation.nMembers; nIter++)
    1413             :                 {
    1414         360 :                     unsigned int nType = pabyData[nIter];
    1415         360 :                     if (nType > MEMBER_RELATION)
    1416           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1417             : 
    1418         360 :                     psCtxt->pasMembers[nIter].eType =
    1419             :                         static_cast<OSMMemberType>(nType);
    1420             :                 }
    1421         200 :                 pabyData += nSize;
    1422             :             }
    1423             :             else
    1424             :             {
    1425           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
    1426             :             }
    1427             :         }
    1428             :         /* printf("<ReadRelation\n"); */
    1429             : 
    1430         200 :         if (pabyData != pabyDataLimit)
    1431           0 :             THROW_OSM_PARSING_EXCEPTION;
    1432             : 
    1433         200 :         if (sRelation.nTags)
    1434         200 :             sRelation.pasTags = psCtxt->pasTags;
    1435             :         else
    1436           0 :             sRelation.pasTags = nullptr;
    1437             : 
    1438         200 :         sRelation.pasMembers = psCtxt->pasMembers;
    1439             : 
    1440         200 :         psCtxt->pfnNotifyRelation(&sRelation, psCtxt, psCtxt->user_data);
    1441             : 
    1442         200 :         return true;
    1443             :     }
    1444           0 :     catch (const std::exception &e)
    1445             :     {
    1446           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1447           0 :         return false;
    1448             :     }
    1449             : }
    1450             : 
    1451             : /************************************************************************/
    1452             : /*                         ReadPrimitiveGroup()                         */
    1453             : /************************************************************************/
    1454             : 
    1455             : constexpr int PRIMITIVEGROUP_IDX_NODES = 1;
    1456             : // constexpr int PRIMITIVEGROUP_IDX_DENSE = 2;
    1457             : // constexpr int PRIMITIVEGROUP_IDX_WAYS = 3;
    1458             : constexpr int PRIMITIVEGROUP_IDX_RELATIONS = 4;
    1459             : // constexpr int PRIMITIVEGROUP_IDX_CHANGESETS = 5;
    1460             : 
    1461             : typedef bool (*PrimitiveFuncType)(const GByte *pabyData,
    1462             :                                   const GByte *pabyDataLimit,
    1463             :                                   OSMContext *psCtxt);
    1464             : 
    1465             : static const PrimitiveFuncType apfnPrimitives[] = {ReadNode, ReadDenseNodes,
    1466             :                                                    ReadWay, ReadRelation};
    1467             : 
    1468        1152 : static bool ReadPrimitiveGroup(const GByte *pabyData,
    1469             :                                const GByte *pabyDataLimit, OSMContext *psCtxt)
    1470             : {
    1471             :     try
    1472             :     {
    1473        1152 :         while (pabyData < pabyDataLimit)
    1474             :         {
    1475        1025 :             int nKey = 0;
    1476        1025 :             READ_FIELD_KEY(nKey);
    1477             : 
    1478        1025 :             const int nFieldNumber = GET_FIELDNUMBER(nKey) - 1;
    1479        1025 :             if (GET_WIRETYPE(nKey) == WT_DATA &&
    1480        1025 :                 nFieldNumber >= PRIMITIVEGROUP_IDX_NODES - 1 &&
    1481             :                 nFieldNumber <= PRIMITIVEGROUP_IDX_RELATIONS - 1)
    1482             :             {
    1483        1025 :                 unsigned int nSize = 0;
    1484        1025 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1485             : 
    1486        1025 :                 if (!apfnPrimitives[nFieldNumber](pabyData, pabyData + nSize,
    1487             :                                                   psCtxt))
    1488           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1489             : 
    1490        1025 :                 pabyData += nSize;
    1491             :             }
    1492             :             else
    1493             :             {
    1494           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
    1495             :             }
    1496             :         }
    1497             : 
    1498         127 :         return pabyData == pabyDataLimit;
    1499             :     }
    1500           0 :     catch (const std::exception &e)
    1501             :     {
    1502           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1503           0 :         return false;
    1504             :     }
    1505             : }
    1506             : 
    1507             : /************************************************************************/
    1508             : /*                         ReadPrimitiveBlock()                         */
    1509             : /************************************************************************/
    1510             : 
    1511             : constexpr int PRIMITIVEBLOCK_IDX_STRINGTABLE = 1;
    1512             : constexpr int PRIMITIVEBLOCK_IDX_PRIMITIVEGROUP = 2;
    1513             : constexpr int PRIMITIVEBLOCK_IDX_GRANULARITY = 17;
    1514             : constexpr int PRIMITIVEBLOCK_IDX_DATE_GRANULARITY = 18;
    1515             : constexpr int PRIMITIVEBLOCK_IDX_LAT_OFFSET = 19;
    1516             : constexpr int PRIMITIVEBLOCK_IDX_LON_OFFSET = 20;
    1517             : 
    1518          45 : static bool ReadPrimitiveBlock(const GByte *pabyData,
    1519             :                                const GByte *pabyDataLimit, OSMContext *psCtxt)
    1520             : {
    1521          45 :     const GByte *pabyDataSave = pabyData;
    1522             : 
    1523          45 :     psCtxt->pszStrBuf = nullptr;
    1524          45 :     psCtxt->nStrCount = 0;
    1525          45 :     psCtxt->nGranularity = 100;
    1526          45 :     psCtxt->nDateGranularity = 1000;
    1527          45 :     psCtxt->nLatOffset = 0;
    1528          45 :     psCtxt->nLonOffset = 0;
    1529             : 
    1530             :     try
    1531             :     {
    1532         305 :         while (pabyData < pabyDataLimit)
    1533             :         {
    1534         260 :             int nKey = 0;
    1535         260 :             READ_FIELD_KEY(nKey);
    1536             : 
    1537         260 :             if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_GRANULARITY, WT_VARINT))
    1538             :             {
    1539          44 :                 READ_VARINT32(pabyData, pabyDataLimit, psCtxt->nGranularity);
    1540          44 :                 if (psCtxt->nGranularity <= 0)
    1541           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1542             :             }
    1543         216 :             else if (nKey ==
    1544             :                      MAKE_KEY(PRIMITIVEBLOCK_IDX_DATE_GRANULARITY, WT_VARINT))
    1545             :             {
    1546          44 :                 READ_VARINT32(pabyData, pabyDataLimit,
    1547             :                               psCtxt->nDateGranularity);
    1548             :             }
    1549         172 :             else if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_LAT_OFFSET, WT_VARINT))
    1550             :             {
    1551           0 :                 READ_VARINT64(pabyData, pabyDataLimit, psCtxt->nLatOffset);
    1552             :             }
    1553         172 :             else if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_LON_OFFSET, WT_VARINT))
    1554             :             {
    1555           0 :                 READ_VARINT64(pabyData, pabyDataLimit, psCtxt->nLonOffset);
    1556             :             }
    1557             :             else
    1558             :             {
    1559         172 :                 SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, FALSE);
    1560             :             }
    1561             :         }
    1562             : 
    1563          45 :         if (pabyData != pabyDataLimit)
    1564           0 :             THROW_OSM_PARSING_EXCEPTION;
    1565             : 
    1566          45 :         pabyData = pabyDataSave;
    1567         260 :         while (pabyData < pabyDataLimit)
    1568             :         {
    1569         215 :             int nKey = 0;
    1570         215 :             READ_FIELD_KEY(nKey);
    1571             : 
    1572         215 :             if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_STRINGTABLE, WT_DATA))
    1573             :             {
    1574          45 :                 GByte bSaveAfterByte = 0;
    1575          45 :                 GByte *pbSaveAfterByte = nullptr;
    1576          45 :                 if (psCtxt->nStrCount != 0)
    1577           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1578          45 :                 unsigned int nSize = 0;
    1579          45 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1580             : 
    1581             :                 // Dirty little trick:
    1582             :                 // ReadStringTable() will over-write the byte after the
    1583             :                 // StringTable message with a NUL character, so we backup
    1584             :                 // it to be able to restore it just before issuing the next
    1585             :                 // READ_FIELD_KEY. Then we will re-NUL it to have valid
    1586             :                 // NUL terminated strings.
    1587             :                 // This trick enable us to keep the strings where there are
    1588             :                 // in RAM.
    1589          45 :                 pbSaveAfterByte = const_cast<GByte *>(pabyData + nSize);
    1590          45 :                 bSaveAfterByte = *pbSaveAfterByte;
    1591             : 
    1592          45 :                 if (!ReadStringTable(pabyData, pabyData + nSize, psCtxt))
    1593           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1594             : 
    1595          45 :                 pabyData += nSize;
    1596             : 
    1597          45 :                 *pbSaveAfterByte = bSaveAfterByte;
    1598          45 :                 if (pabyData == pabyDataLimit)
    1599           0 :                     break;
    1600             : 
    1601          45 :                 READ_FIELD_KEY(nKey);
    1602          45 :                 *pbSaveAfterByte = 0;
    1603             : 
    1604          45 :                 if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_STRINGTABLE, WT_DATA))
    1605           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1606             : 
    1607             :                 /* Yes we go on ! */
    1608             :             }
    1609             : 
    1610         215 :             if (nKey == MAKE_KEY(PRIMITIVEBLOCK_IDX_PRIMITIVEGROUP, WT_DATA))
    1611             :             {
    1612         127 :                 unsigned int nSize = 0;
    1613         127 :                 READ_SIZE(pabyData, pabyDataLimit, nSize);
    1614             : 
    1615         127 :                 if (!ReadPrimitiveGroup(pabyData, pabyData + nSize, psCtxt))
    1616           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1617             : 
    1618         127 :                 pabyData += nSize;
    1619             :             }
    1620             :             else
    1621             :             {
    1622          88 :                 SKIP_UNKNOWN_FIELD_INLINE(pabyData, pabyDataLimit, FALSE);
    1623             :             }
    1624             :         }
    1625             : 
    1626          45 :         return pabyData == pabyDataLimit;
    1627             :     }
    1628           0 :     catch (const std::exception &e)
    1629             :     {
    1630           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1631           0 :         return false;
    1632             :     }
    1633             : }
    1634             : 
    1635             : /************************************************************************/
    1636             : /*                         DecompressFunction()                         */
    1637             : /************************************************************************/
    1638             : 
    1639          87 : static void DecompressFunction(void *pDataIn)
    1640             : {
    1641          87 :     DecompressionJob *psJob = static_cast<DecompressionJob *>(pDataIn);
    1642         174 :     psJob->bStatus = CPLZLibInflate(psJob->pabySrc, psJob->nSrcSize,
    1643          87 :                                     psJob->pabyDstBase + psJob->nDstOffset,
    1644          87 :                                     psJob->nDstSize, nullptr) != nullptr;
    1645          87 : }
    1646             : 
    1647             : /************************************************************************/
    1648             : /*                        RunDecompressionJobs()                        */
    1649             : /************************************************************************/
    1650             : 
    1651          86 : static bool RunDecompressionJobs(OSMContext *psCtxt)
    1652             : {
    1653          86 :     psCtxt->nTotalUncompressedSize = 0;
    1654             : 
    1655          86 :     GByte *pabyDstBase = psCtxt->pabyUncompressed;
    1656          86 :     std::vector<void *> ahJobs;
    1657         173 :     for (int i = 0; i < psCtxt->nJobs; i++)
    1658             :     {
    1659          87 :         psCtxt->asJobs[i].pabyDstBase = pabyDstBase;
    1660          87 :         if (psCtxt->poWTP)
    1661          87 :             ahJobs.push_back(&psCtxt->asJobs[i]);
    1662             :         else
    1663           0 :             DecompressFunction(&psCtxt->asJobs[i]);
    1664             :     }
    1665          86 :     if (psCtxt->poWTP)
    1666             :     {
    1667          86 :         psCtxt->poWTP->SubmitJobs(DecompressFunction, ahJobs);
    1668          86 :         psCtxt->poWTP->WaitCompletion();
    1669             :     }
    1670             : 
    1671          86 :     bool bRet = true;
    1672         173 :     for (int i = 0; bRet && i < psCtxt->nJobs; i++)
    1673             :     {
    1674          87 :         bRet &= psCtxt->asJobs[i].bStatus;
    1675             :     }
    1676         172 :     return bRet;
    1677             : }
    1678             : 
    1679             : /************************************************************************/
    1680             : /*                         ProcessSingleBlob()                          */
    1681             : /************************************************************************/
    1682             : 
    1683             : // cppcheck-suppress constParameterReference
    1684          87 : static bool ProcessSingleBlob(OSMContext *psCtxt, DecompressionJob &sJob,
    1685             :                               BlobType eType)
    1686             : {
    1687          87 :     if (eType == BLOB_OSMHEADER)
    1688             :     {
    1689          88 :         return ReadOSMHeader(sJob.pabyDstBase + sJob.nDstOffset,
    1690          44 :                              sJob.pabyDstBase + sJob.nDstOffset + sJob.nDstSize,
    1691          44 :                              psCtxt);
    1692             :     }
    1693             :     else
    1694             :     {
    1695          43 :         CPLAssert(eType == BLOB_OSMDATA);
    1696          86 :         return ReadPrimitiveBlock(
    1697          43 :             sJob.pabyDstBase + sJob.nDstOffset,
    1698          43 :             sJob.pabyDstBase + sJob.nDstOffset + sJob.nDstSize, psCtxt);
    1699             :     }
    1700             : }
    1701             : 
    1702             : /************************************************************************/
    1703             : /*                 RunDecompressionJobsAndProcessAll()                  */
    1704             : /************************************************************************/
    1705             : 
    1706          44 : static bool RunDecompressionJobsAndProcessAll(OSMContext *psCtxt,
    1707             :                                               BlobType eType)
    1708             : {
    1709          44 :     if (!RunDecompressionJobs(psCtxt))
    1710             :     {
    1711           0 :         return false;
    1712             :     }
    1713          88 :     for (int i = 0; i < psCtxt->nJobs; i++)
    1714             :     {
    1715          44 :         if (!ProcessSingleBlob(psCtxt, psCtxt->asJobs[i], eType))
    1716             :         {
    1717           0 :             return false;
    1718             :         }
    1719             :     }
    1720          44 :     psCtxt->iNextJob = 0;
    1721          44 :     psCtxt->nJobs = 0;
    1722          44 :     return true;
    1723             : }
    1724             : 
    1725             : /************************************************************************/
    1726             : /*                              ReadBlob()                              */
    1727             : /************************************************************************/
    1728             : 
    1729             : constexpr int BLOB_IDX_RAW = 1;
    1730             : constexpr int BLOB_IDX_RAW_SIZE = 2;
    1731             : constexpr int BLOB_IDX_ZLIB_DATA = 3;
    1732             : 
    1733          90 : static bool ReadBlob(OSMContext *psCtxt, BlobType eType)
    1734             : {
    1735          90 :     unsigned int nUncompressedSize = 0;
    1736          90 :     bool bRet = true;
    1737          90 :     const GByte *pabyData = psCtxt->pabyBlob + psCtxt->nBlobOffset;
    1738          90 :     const GByte *pabyLastCheckpointData = pabyData;
    1739          90 :     const GByte *pabyDataLimit = psCtxt->pabyBlob + psCtxt->nBlobSize;
    1740             : 
    1741             :     try
    1742             :     {
    1743         272 :         while (pabyData < pabyDataLimit)
    1744             :         {
    1745         182 :             int nKey = 0;
    1746         182 :             READ_FIELD_KEY(nKey);
    1747             : 
    1748         182 :             if (nKey == MAKE_KEY(BLOB_IDX_RAW, WT_DATA))
    1749             :             {
    1750           4 :                 if (psCtxt->nJobs > 0 &&
    1751           0 :                     !RunDecompressionJobsAndProcessAll(psCtxt, eType))
    1752             :                 {
    1753           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1754             :                 }
    1755             : 
    1756           4 :                 unsigned int nDataLength = 0;
    1757           4 :                 READ_SIZE(pabyData, pabyDataLimit, nDataLength);
    1758           4 :                 if (nDataLength > MAX_BLOB_SIZE)
    1759           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1760             : 
    1761             :                 // printf("raw data size = %d\n", nDataLength);
    1762             : 
    1763           4 :                 if (eType == BLOB_OSMHEADER)
    1764             :                 {
    1765             :                     bRet =
    1766           2 :                         ReadOSMHeader(pabyData, pabyData + nDataLength, psCtxt);
    1767             :                 }
    1768           2 :                 else if (eType == BLOB_OSMDATA)
    1769             :                 {
    1770           2 :                     bRet = ReadPrimitiveBlock(pabyData, pabyData + nDataLength,
    1771             :                                               psCtxt);
    1772             :                 }
    1773             : 
    1774           4 :                 pabyData += nDataLength;
    1775             :             }
    1776         178 :             else if (nKey == MAKE_KEY(BLOB_IDX_RAW_SIZE, WT_VARINT))
    1777             :             {
    1778          91 :                 READ_VARUINT32(pabyData, pabyDataLimit, nUncompressedSize);
    1779             :                 // printf("nUncompressedSize = %d\n", nUncompressedSize);
    1780             :             }
    1781          87 :             else if (nKey == MAKE_KEY(BLOB_IDX_ZLIB_DATA, WT_DATA))
    1782             :             {
    1783          87 :                 unsigned int nZlibCompressedSize = 0;
    1784          87 :                 READ_VARUINT32(pabyData, pabyDataLimit, nZlibCompressedSize);
    1785          87 :                 if (CHECK_OOB && nZlibCompressedSize >
    1786          87 :                                      psCtxt->nBlobSize - psCtxt->nBlobOffset)
    1787             :                 {
    1788           0 :                     THROW_OSM_PARSING_EXCEPTION;
    1789             :                 }
    1790             : 
    1791             :                 // printf("nZlibCompressedSize = %d\n", nZlibCompressedSize);
    1792             : 
    1793          87 :                 if (nUncompressedSize != 0)
    1794             :                 {
    1795          87 :                     if (nUncompressedSize / 100 > nZlibCompressedSize)
    1796             :                     {
    1797             :                         // Too prevent excessive memory allocations
    1798           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1799             :                                  "Excessive uncompressed vs compressed ratio");
    1800           0 :                         THROW_OSM_PARSING_EXCEPTION;
    1801             :                     }
    1802          87 :                     if (psCtxt->nJobs > 0 &&
    1803           1 :                         (psCtxt->nTotalUncompressedSize >
    1804           1 :                              UINT_MAX - nUncompressedSize ||
    1805           1 :                          psCtxt->nTotalUncompressedSize + nUncompressedSize >
    1806             :                              MAX_ACC_UNCOMPRESSED_SIZE))
    1807             :                     {
    1808           0 :                         pabyData = pabyLastCheckpointData;
    1809           0 :                         break;
    1810             :                     }
    1811          87 :                     unsigned nSizeNeeded =
    1812          87 :                         psCtxt->nTotalUncompressedSize + nUncompressedSize;
    1813          87 :                     if (nSizeNeeded > psCtxt->nUncompressedAllocated)
    1814             :                     {
    1815             : 
    1816          54 :                         GByte *pabyUncompressedNew = nullptr;
    1817          54 :                         if (psCtxt->nUncompressedAllocated <=
    1818          54 :                                 UINT_MAX - psCtxt->nUncompressedAllocated / 3 &&
    1819          54 :                             psCtxt->nUncompressedAllocated +
    1820          54 :                                     psCtxt->nUncompressedAllocated / 3 <
    1821             :                                 MAX_ACC_UNCOMPRESSED_SIZE)
    1822             :                         {
    1823          54 :                             psCtxt->nUncompressedAllocated =
    1824          54 :                                 std::max(psCtxt->nUncompressedAllocated +
    1825          54 :                                              psCtxt->nUncompressedAllocated / 3,
    1826          54 :                                          nSizeNeeded);
    1827             :                         }
    1828             :                         else
    1829             :                         {
    1830           0 :                             psCtxt->nUncompressedAllocated = nSizeNeeded;
    1831             :                         }
    1832          54 :                         if (psCtxt->nUncompressedAllocated >
    1833             :                             UINT_MAX - EXTRA_BYTES)
    1834           0 :                             THROW_OSM_PARSING_EXCEPTION;
    1835             :                         pabyUncompressedNew =
    1836          54 :                             static_cast<GByte *>(VSI_REALLOC_VERBOSE(
    1837             :                                 psCtxt->pabyUncompressed,
    1838             :                                 psCtxt->nUncompressedAllocated + EXTRA_BYTES));
    1839          54 :                         if (pabyUncompressedNew == nullptr)
    1840           0 :                             THROW_OSM_PARSING_EXCEPTION;
    1841          54 :                         psCtxt->pabyUncompressed = pabyUncompressedNew;
    1842             :                     }
    1843          87 :                     memset(psCtxt->pabyUncompressed + nSizeNeeded, 0,
    1844             :                            EXTRA_BYTES);
    1845             : 
    1846          87 :                     psCtxt->asJobs[psCtxt->nJobs].pabySrc = pabyData;
    1847          87 :                     psCtxt->asJobs[psCtxt->nJobs].nSrcSize =
    1848          87 :                         nZlibCompressedSize;
    1849          87 :                     psCtxt->asJobs[psCtxt->nJobs].nDstOffset =
    1850          87 :                         psCtxt->nTotalUncompressedSize;
    1851          87 :                     psCtxt->asJobs[psCtxt->nJobs].nDstSize = nUncompressedSize;
    1852          87 :                     psCtxt->nJobs++;
    1853          87 :                     if (psCtxt->poWTP == nullptr || eType != BLOB_OSMDATA)
    1854             :                     {
    1855          44 :                         if (!RunDecompressionJobsAndProcessAll(psCtxt, eType))
    1856             :                         {
    1857           0 :                             THROW_OSM_PARSING_EXCEPTION;
    1858             :                         }
    1859             :                     }
    1860             :                     else
    1861             :                     {
    1862             :                         // Make sure that uncompressed blobs are separated by
    1863             :                         // EXTRA_BYTES in the case where in the future we would
    1864             :                         // implement parallel decoding of them (not sure if
    1865             :                         // that's doable)
    1866          43 :                         psCtxt->nTotalUncompressedSize +=
    1867             :                             nUncompressedSize + EXTRA_BYTES;
    1868             :                     }
    1869             :                 }
    1870             : 
    1871          87 :                 nUncompressedSize = 0;
    1872          87 :                 pabyData += nZlibCompressedSize;
    1873          87 :                 pabyLastCheckpointData = pabyData;
    1874          87 :                 if (psCtxt->nJobs == N_MAX_JOBS)
    1875           0 :                     break;
    1876             :             }
    1877             :             else
    1878             :             {
    1879           0 :                 SKIP_UNKNOWN_FIELD(pabyData, pabyDataLimit, TRUE);
    1880             :             }
    1881             :         }
    1882             : 
    1883          90 :         if (psCtxt->nJobs > 0)
    1884             :         {
    1885          42 :             if (!RunDecompressionJobs(psCtxt))
    1886             :             {
    1887           0 :                 THROW_OSM_PARSING_EXCEPTION;
    1888             :             }
    1889             :             // Just process one blob at a time
    1890          42 :             if (!ProcessSingleBlob(psCtxt, psCtxt->asJobs[0], eType))
    1891             :             {
    1892           0 :                 THROW_OSM_PARSING_EXCEPTION;
    1893             :             }
    1894          42 :             psCtxt->iNextJob = 1;
    1895             :         }
    1896             : 
    1897          90 :         psCtxt->nBlobOffset =
    1898          90 :             static_cast<unsigned>(pabyData - psCtxt->pabyBlob);
    1899          90 :         return bRet;
    1900             :     }
    1901           0 :     catch (const std::exception &e)
    1902             :     {
    1903           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", e.what());
    1904           0 :         return false;
    1905             :     }
    1906             : }
    1907             : 
    1908             : /************************************************************************/
    1909             : /*                          OSM_ProcessBlock()                          */
    1910             : /************************************************************************/
    1911             : 
    1912          94 : static OSMRetCode PBF_ProcessBlock(OSMContext *psCtxt)
    1913             : {
    1914             :     // Process any remaining queued jobs one by one
    1915          94 :     if (psCtxt->iNextJob < psCtxt->nJobs)
    1916             :     {
    1917           1 :         if (!(ProcessSingleBlob(psCtxt, psCtxt->asJobs[psCtxt->iNextJob],
    1918             :                                 BLOB_OSMDATA)))
    1919             :         {
    1920           0 :             return OSM_ERROR;
    1921             :         }
    1922           1 :         psCtxt->iNextJob++;
    1923           1 :         return OSM_OK;
    1924             :     }
    1925          93 :     psCtxt->iNextJob = 0;
    1926          93 :     psCtxt->nJobs = 0;
    1927             : 
    1928             :     // Make sure to finish parsing the last concatenated blocks
    1929          93 :     if (psCtxt->nBlobOffset < psCtxt->nBlobSize)
    1930             :     {
    1931           0 :         return ReadBlob(psCtxt, BLOB_OSMDATA) ? OSM_OK : OSM_ERROR;
    1932             :     }
    1933          93 :     psCtxt->nBlobOffset = 0;
    1934          93 :     psCtxt->nBlobSize = 0;
    1935             : 
    1936          93 :     int nBlobCount = 0;
    1937          93 :     OSMRetCode eRetCode = OSM_OK;
    1938          93 :     unsigned int nBlobSizeAcc = 0;
    1939          93 :     BlobType eType = BLOB_UNKNOWN;
    1940             :     while (true)
    1941             :     {
    1942             :         GByte abyHeaderSize[4];
    1943         138 :         unsigned int nBlobSize = 0;
    1944             : 
    1945         138 :         if (VSIFReadL(abyHeaderSize, 4, 1, psCtxt->fp) != 1)
    1946             :         {
    1947          45 :             eRetCode = OSM_EOF;
    1948          45 :             break;
    1949             :         }
    1950          93 :         const unsigned int nHeaderSize =
    1951          93 :             (static_cast<unsigned int>(abyHeaderSize[0]) << 24) |
    1952          93 :             (abyHeaderSize[1] << 16) | (abyHeaderSize[2] << 8) |
    1953          93 :             abyHeaderSize[3];
    1954             : 
    1955          93 :         psCtxt->nBytesRead += 4;
    1956             : 
    1957             :         /* printf("nHeaderSize = %d\n", nHeaderSize); */
    1958          93 :         if (nHeaderSize > MAX_BLOB_HEADER_SIZE)
    1959             :         {
    1960           1 :             eRetCode = OSM_ERROR;
    1961           1 :             break;
    1962             :         }
    1963          92 :         if (VSIFReadL(psCtxt->pabyBlobHeader, 1, nHeaderSize, psCtxt->fp) !=
    1964          92 :             nHeaderSize)
    1965             :         {
    1966           0 :             eRetCode = OSM_ERROR;
    1967           0 :             break;
    1968             :         }
    1969             : 
    1970          92 :         psCtxt->nBytesRead += nHeaderSize;
    1971             : 
    1972          92 :         memset(psCtxt->pabyBlobHeader + nHeaderSize, 0, EXTRA_BYTES);
    1973         184 :         const bool bRet = ReadBlobHeader(psCtxt->pabyBlobHeader,
    1974          92 :                                          psCtxt->pabyBlobHeader + nHeaderSize,
    1975             :                                          &nBlobSize, &eType);
    1976          92 :         if (!bRet || eType == BLOB_UNKNOWN)
    1977             :         {
    1978           0 :             eRetCode = OSM_ERROR;
    1979           0 :             break;
    1980             :         }
    1981             : 
    1982             :         // Limit in OSM PBF spec
    1983          92 :         if (nBlobSize > MAX_BLOB_SIZE)
    1984             :         {
    1985           0 :             eRetCode = OSM_ERROR;
    1986           0 :             break;
    1987             :         }
    1988          92 :         if (nBlobSize + nBlobSizeAcc > psCtxt->nBlobSizeAllocated)
    1989             :         {
    1990           0 :             psCtxt->nBlobSizeAllocated = std::max(
    1991           0 :                 std::min(MAX_ACC_BLOB_SIZE, psCtxt->nBlobSizeAllocated * 2),
    1992           0 :                 nBlobSize + nBlobSizeAcc);
    1993           0 :             GByte *pabyBlobNew = static_cast<GByte *>(VSI_REALLOC_VERBOSE(
    1994             :                 psCtxt->pabyBlob, psCtxt->nBlobSizeAllocated + EXTRA_BYTES));
    1995           0 :             if (pabyBlobNew == nullptr)
    1996             :             {
    1997           0 :                 eRetCode = OSM_ERROR;
    1998           0 :                 break;
    1999             :             }
    2000           0 :             psCtxt->pabyBlob = pabyBlobNew;
    2001             :         }
    2002             :         // Given how Protocol buffer work, we can merge several buffers
    2003             :         // by just appending them to the previous ones.
    2004          92 :         if (VSIFReadL(psCtxt->pabyBlob + nBlobSizeAcc, 1, nBlobSize,
    2005          92 :                       psCtxt->fp) != nBlobSize)
    2006             :         {
    2007           1 :             eRetCode = OSM_ERROR;
    2008           1 :             break;
    2009             :         }
    2010          91 :         psCtxt->nBytesRead += nBlobSize;
    2011          91 :         nBlobSizeAcc += nBlobSize;
    2012          91 :         memset(psCtxt->pabyBlob + nBlobSizeAcc, 0, EXTRA_BYTES);
    2013             : 
    2014          91 :         nBlobCount++;
    2015             : 
    2016          91 :         if (eType == BLOB_OSMDATA && psCtxt->poWTP != nullptr)
    2017             :         {
    2018             :             // Accumulate BLOB_OSMDATA until we reach either the maximum
    2019             :             // number of jobs or a threshold in bytes
    2020          45 :             if (nBlobCount == N_MAX_JOBS || nBlobSizeAcc > MAX_ACC_BLOB_SIZE)
    2021             :             {
    2022             :                 break;
    2023             :             }
    2024             :         }
    2025             :         else
    2026             :         {
    2027             :             break;
    2028             :         }
    2029          45 :     }
    2030             : 
    2031          93 :     if (nBlobCount > 0)
    2032             :     {
    2033          90 :         psCtxt->nBlobOffset = 0;
    2034          90 :         psCtxt->nBlobSize = nBlobSizeAcc;
    2035          90 :         const bool bRet = ReadBlob(psCtxt, eType);
    2036          90 :         if (bRet)
    2037             :         {
    2038          90 :             if (eRetCode == OSM_EOF &&
    2039          44 :                 (psCtxt->iNextJob < psCtxt->nJobs ||
    2040          43 :                  psCtxt->nBlobOffset < psCtxt->nBlobSize))
    2041             :             {
    2042           1 :                 eRetCode = OSM_OK;
    2043             :             }
    2044          90 :             CPLAssert(psCtxt->iNextJob == psCtxt->nJobs ||
    2045             :                       eType == BLOB_OSMDATA);
    2046             :         }
    2047             :         else
    2048             :         {
    2049           0 :             eRetCode = OSM_ERROR;
    2050             :         }
    2051             :     }
    2052             : 
    2053          93 :     return eRetCode;
    2054             : }
    2055             : 
    2056             : /************************************************************************/
    2057             : /*                        EmptyNotifyNodesFunc()                        */
    2058             : /************************************************************************/
    2059             : 
    2060           0 : static void EmptyNotifyNodesFunc(unsigned int /* nNodes */,
    2061             :                                  OSMNode * /* pasNodes */,
    2062             :                                  OSMContext * /* psCtxt */,
    2063             :                                  void * /* user_data */)
    2064             : {
    2065           0 : }
    2066             : 
    2067             : /************************************************************************/
    2068             : /*                         EmptyNotifyWayFunc()                         */
    2069             : /************************************************************************/
    2070             : 
    2071           0 : static void EmptyNotifyWayFunc(OSMWay * /* psWay */, OSMContext * /* psCtxt */,
    2072             :                                void * /* user_data */)
    2073             : {
    2074           0 : }
    2075             : 
    2076             : /************************************************************************/
    2077             : /*                      EmptyNotifyRelationFunc()                       */
    2078             : /************************************************************************/
    2079             : 
    2080           0 : static void EmptyNotifyRelationFunc(OSMRelation * /* psRelation */,
    2081             :                                     OSMContext * /* psCtxt */,
    2082             :                                     void * /* user_data */)
    2083             : {
    2084           0 : }
    2085             : 
    2086             : /************************************************************************/
    2087             : /*                       EmptyNotifyBoundsFunc()                        */
    2088             : /************************************************************************/
    2089             : 
    2090           0 : static void EmptyNotifyBoundsFunc(double /* dfXMin */, double /* dfYMin */,
    2091             :                                   double /* dfXMax */, double /* dfYMax */,
    2092             :                                   OSMContext * /*psCtxt */,
    2093             :                                   void * /* user_data */)
    2094             : {
    2095           0 : }
    2096             : 
    2097             : #ifdef HAVE_EXPAT
    2098             : 
    2099             : /************************************************************************/
    2100             : /*                           OSM_AddString()                            */
    2101             : /************************************************************************/
    2102             : 
    2103         420 : static const char *OSM_AddString(OSMContext *psCtxt, const char *pszStr)
    2104             : {
    2105         420 :     const auto nLen = strlen(pszStr);
    2106         420 :     if (psCtxt->nStrLength + nLen + 1 > psCtxt->nStrAllocated)
    2107             :     {
    2108           0 :         CPLError(CE_Failure, CPLE_AppDefined, "String buffer too small");
    2109           0 :         return "";
    2110             :     }
    2111         420 :     char *pszRet = psCtxt->pszStrBuf + psCtxt->nStrLength;
    2112         420 :     memcpy(pszRet, pszStr, nLen);
    2113         420 :     pszRet[nLen] = '\0';
    2114         420 :     psCtxt->nStrLength += static_cast<unsigned>(nLen) + 1;
    2115         420 :     return pszRet;
    2116             : }
    2117             : 
    2118             : /************************************************************************/
    2119             : /*                             OSM_Atoi64()                             */
    2120             : /************************************************************************/
    2121             : 
    2122         402 : static GIntBig OSM_Atoi64(const char *pszString)
    2123             : {
    2124         402 :     return atoll(pszString);
    2125             : }
    2126             : 
    2127             : /************************************************************************/
    2128             : /*                      OSM_XML_startElementCbk()                       */
    2129             : /************************************************************************/
    2130             : 
    2131         395 : static void XMLCALL OSM_XML_startElementCbk(void *pUserData,
    2132             :                                             const char *pszName,
    2133             :                                             const char **ppszAttr)
    2134             : {
    2135         395 :     OSMContext *psCtxt = static_cast<OSMContext *>(pUserData);
    2136         395 :     const char **ppszIter = ppszAttr;
    2137             : 
    2138         395 :     if (psCtxt->bStopParsing)
    2139           0 :         return;
    2140             : 
    2141         395 :     psCtxt->nWithoutEventCounter = 0;
    2142             : 
    2143         395 :     if (psCtxt->bTryToFetchBounds)
    2144             :     {
    2145          19 :         if (strcmp(pszName, "bounds") == 0 ||
    2146          14 :             strcmp(pszName, "bound") == 0 /* osmosis uses bound */)
    2147             :         {
    2148           5 :             int nCountCoords = 0;
    2149             : 
    2150           5 :             psCtxt->bTryToFetchBounds = false;
    2151             : 
    2152           5 :             if (ppszIter)
    2153             :             {
    2154          25 :                 while (ppszIter[0] != nullptr)
    2155             :                 {
    2156          20 :                     if (strcmp(ppszIter[0], "minlon") == 0)
    2157             :                     {
    2158           5 :                         psCtxt->dfLeft = CPLAtof(ppszIter[1]);
    2159           5 :                         nCountCoords++;
    2160             :                     }
    2161          15 :                     else if (strcmp(ppszIter[0], "minlat") == 0)
    2162             :                     {
    2163           5 :                         psCtxt->dfBottom = CPLAtof(ppszIter[1]);
    2164           5 :                         nCountCoords++;
    2165             :                     }
    2166          10 :                     else if (strcmp(ppszIter[0], "maxlon") == 0)
    2167             :                     {
    2168           5 :                         psCtxt->dfRight = CPLAtof(ppszIter[1]);
    2169           5 :                         nCountCoords++;
    2170             :                     }
    2171           5 :                     else if (strcmp(ppszIter[0], "maxlat") == 0)
    2172             :                     {
    2173           5 :                         psCtxt->dfTop = CPLAtof(ppszIter[1]);
    2174           5 :                         nCountCoords++;
    2175             :                     }
    2176           0 :                     else if (strcmp(ppszIter[0], "box") ==
    2177             :                              0 /* osmosis uses box */)
    2178             :                     {
    2179             :                         char **papszTokens =
    2180           0 :                             CSLTokenizeString2(ppszIter[1], ",", 0);
    2181           0 :                         if (CSLCount(papszTokens) == 4)
    2182             :                         {
    2183           0 :                             psCtxt->dfBottom = CPLAtof(papszTokens[0]);
    2184           0 :                             psCtxt->dfLeft = CPLAtof(papszTokens[1]);
    2185           0 :                             psCtxt->dfTop = CPLAtof(papszTokens[2]);
    2186           0 :                             psCtxt->dfRight = CPLAtof(papszTokens[3]);
    2187           0 :                             nCountCoords = 4;
    2188             :                         }
    2189           0 :                         CSLDestroy(papszTokens);
    2190             :                     }
    2191          20 :                     ppszIter += 2;
    2192             :                 }
    2193             :             }
    2194             : 
    2195           5 :             if (nCountCoords == 4)
    2196             :             {
    2197           5 :                 psCtxt->pfnNotifyBounds(psCtxt->dfLeft, psCtxt->dfBottom,
    2198             :                                         psCtxt->dfRight, psCtxt->dfTop, psCtxt,
    2199             :                                         psCtxt->user_data);
    2200             :             }
    2201             :         }
    2202             :     }
    2203             : 
    2204         395 :     if (!psCtxt->bInNode && !psCtxt->bInWay && !psCtxt->bInRelation &&
    2205         129 :         strcmp(pszName, "node") == 0)
    2206             :     {
    2207          53 :         psCtxt->bInNode = true;
    2208          53 :         psCtxt->bTryToFetchBounds = false;
    2209             : 
    2210          53 :         psCtxt->nStrLength = 0;
    2211          53 :         psCtxt->pszStrBuf[0] = '\0';
    2212          53 :         psCtxt->nTags = 0;
    2213             : 
    2214          53 :         memset(&(psCtxt->pasNodes[0]), 0, sizeof(OSMNode));
    2215          53 :         psCtxt->pasNodes[0].sInfo.pszUserSID = "";
    2216             : 
    2217          53 :         if (ppszIter)
    2218             :         {
    2219         452 :             while (ppszIter[0] != nullptr)
    2220             :             {
    2221         399 :                 if (strcmp(ppszIter[0], "id") == 0)
    2222             :                 {
    2223          53 :                     psCtxt->pasNodes[0].nID = OSM_Atoi64(ppszIter[1]);
    2224             :                 }
    2225         346 :                 else if (strcmp(ppszIter[0], "lat") == 0)
    2226             :                 {
    2227          53 :                     psCtxt->pasNodes[0].dfLat = CPLAtof(ppszIter[1]);
    2228             :                 }
    2229         293 :                 else if (strcmp(ppszIter[0], "lon") == 0)
    2230             :                 {
    2231          53 :                     psCtxt->pasNodes[0].dfLon = CPLAtof(ppszIter[1]);
    2232             :                 }
    2233         240 :                 else if (strcmp(ppszIter[0], "version") == 0)
    2234             :                 {
    2235          48 :                     psCtxt->pasNodes[0].sInfo.nVersion = atoi(ppszIter[1]);
    2236             :                 }
    2237         192 :                 else if (strcmp(ppszIter[0], "changeset") == 0)
    2238             :                 {
    2239          48 :                     psCtxt->pasNodes[0].sInfo.nChangeset =
    2240          48 :                         OSM_Atoi64(ppszIter[1]);
    2241             :                 }
    2242         144 :                 else if (strcmp(ppszIter[0], "user") == 0)
    2243             :                 {
    2244          96 :                     psCtxt->pasNodes[0].sInfo.pszUserSID =
    2245          48 :                         OSM_AddString(psCtxt, ppszIter[1]);
    2246             :                 }
    2247          96 :                 else if (strcmp(ppszIter[0], "uid") == 0)
    2248             :                 {
    2249          48 :                     psCtxt->pasNodes[0].sInfo.nUID = atoi(ppszIter[1]);
    2250             :                 }
    2251          48 :                 else if (strcmp(ppszIter[0], "timestamp") == 0)
    2252             :                 {
    2253          96 :                     psCtxt->pasNodes[0].sInfo.ts.pszTimeStamp =
    2254          48 :                         OSM_AddString(psCtxt, ppszIter[1]);
    2255          48 :                     psCtxt->pasNodes[0].sInfo.bTimeStampIsStr = true;
    2256             :                 }
    2257         399 :                 ppszIter += 2;
    2258             :             }
    2259             :         }
    2260             :     }
    2261             : 
    2262         342 :     else if (!psCtxt->bInNode && !psCtxt->bInWay && !psCtxt->bInRelation &&
    2263          76 :              strcmp(pszName, "way") == 0)
    2264             :     {
    2265          38 :         psCtxt->bInWay = true;
    2266             : 
    2267          38 :         psCtxt->nStrLength = 0;
    2268          38 :         psCtxt->pszStrBuf[0] = '\0';
    2269          38 :         psCtxt->nTags = 0;
    2270             : 
    2271          38 :         memset(&(psCtxt->sWay), 0, sizeof(OSMWay));
    2272          38 :         psCtxt->sWay.sInfo.pszUserSID = "";
    2273             : 
    2274          38 :         if (ppszIter)
    2275             :         {
    2276         261 :             while (ppszIter[0] != nullptr)
    2277             :             {
    2278         223 :                 if (strcmp(ppszIter[0], "id") == 0)
    2279             :                 {
    2280          38 :                     psCtxt->sWay.nID = OSM_Atoi64(ppszIter[1]);
    2281             :                 }
    2282         185 :                 else if (strcmp(ppszIter[0], "version") == 0)
    2283             :                 {
    2284          37 :                     psCtxt->sWay.sInfo.nVersion = atoi(ppszIter[1]);
    2285             :                 }
    2286         148 :                 else if (strcmp(ppszIter[0], "changeset") == 0)
    2287             :                 {
    2288          37 :                     psCtxt->sWay.sInfo.nChangeset = OSM_Atoi64(ppszIter[1]);
    2289             :                 }
    2290         111 :                 else if (strcmp(ppszIter[0], "user") == 0)
    2291             :                 {
    2292          37 :                     psCtxt->sWay.sInfo.pszUserSID =
    2293          37 :                         OSM_AddString(psCtxt, ppszIter[1]);
    2294             :                 }
    2295          74 :                 else if (strcmp(ppszIter[0], "uid") == 0)
    2296             :                 {
    2297          37 :                     psCtxt->sWay.sInfo.nUID = atoi(ppszIter[1]);
    2298             :                 }
    2299          37 :                 else if (strcmp(ppszIter[0], "timestamp") == 0)
    2300             :                 {
    2301          37 :                     psCtxt->sWay.sInfo.ts.pszTimeStamp =
    2302          37 :                         OSM_AddString(psCtxt, ppszIter[1]);
    2303          37 :                     psCtxt->sWay.sInfo.bTimeStampIsStr = true;
    2304             :                 }
    2305         223 :                 ppszIter += 2;
    2306             :             }
    2307             :         }
    2308             :     }
    2309             : 
    2310         304 :     else if (!psCtxt->bInNode && !psCtxt->bInWay && !psCtxt->bInRelation &&
    2311          38 :              strcmp(pszName, "relation") == 0)
    2312             :     {
    2313          22 :         psCtxt->bInRelation = true;
    2314             : 
    2315          22 :         psCtxt->nStrLength = 0;
    2316          22 :         psCtxt->pszStrBuf[0] = '\0';
    2317          22 :         psCtxt->nTags = 0;
    2318             : 
    2319          22 :         memset(&(psCtxt->sRelation), 0, sizeof(OSMRelation));
    2320          22 :         psCtxt->sRelation.sInfo.pszUserSID = "";
    2321             : 
    2322          22 :         if (ppszIter)
    2323             :         {
    2324         149 :             while (ppszIter[0] != nullptr)
    2325             :             {
    2326         127 :                 if (strcmp(ppszIter[0], "id") == 0)
    2327             :                 {
    2328          22 :                     psCtxt->sRelation.nID = OSM_Atoi64(ppszIter[1]);
    2329             :                 }
    2330         105 :                 else if (strcmp(ppszIter[0], "version") == 0)
    2331             :                 {
    2332          21 :                     psCtxt->sRelation.sInfo.nVersion = atoi(ppszIter[1]);
    2333             :                 }
    2334          84 :                 else if (strcmp(ppszIter[0], "changeset") == 0)
    2335             :                 {
    2336          21 :                     psCtxt->sRelation.sInfo.nChangeset =
    2337          21 :                         OSM_Atoi64(ppszIter[1]);
    2338             :                 }
    2339          63 :                 else if (strcmp(ppszIter[0], "user") == 0)
    2340             :                 {
    2341          21 :                     psCtxt->sRelation.sInfo.pszUserSID =
    2342          21 :                         OSM_AddString(psCtxt, ppszIter[1]);
    2343             :                 }
    2344          42 :                 else if (strcmp(ppszIter[0], "uid") == 0)
    2345             :                 {
    2346          21 :                     psCtxt->sRelation.sInfo.nUID = atoi(ppszIter[1]);
    2347             :                 }
    2348          21 :                 else if (strcmp(ppszIter[0], "timestamp") == 0)
    2349             :                 {
    2350          21 :                     psCtxt->sRelation.sInfo.ts.pszTimeStamp =
    2351          21 :                         OSM_AddString(psCtxt, ppszIter[1]);
    2352          21 :                     psCtxt->sRelation.sInfo.bTimeStampIsStr = true;
    2353             :                 }
    2354         127 :                 ppszIter += 2;
    2355             :             }
    2356             :         }
    2357             :     }
    2358             : 
    2359         282 :     else if (psCtxt->bInWay && strcmp(pszName, "nd") == 0)
    2360             :     {
    2361         141 :         if (ppszAttr != nullptr && ppszAttr[0] != nullptr &&
    2362         141 :             strcmp(ppszAttr[0], "ref") == 0)
    2363             :         {
    2364         141 :             if (psCtxt->sWay.nRefs < psCtxt->nNodeRefsAllocated)
    2365             :             {
    2366         282 :                 psCtxt->panNodeRefs[psCtxt->sWay.nRefs] =
    2367         141 :                     OSM_Atoi64(ppszAttr[1]);
    2368         141 :                 psCtxt->sWay.nRefs++;
    2369             :             }
    2370             :             else
    2371             :             {
    2372           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2373             :                          "Too many nodes referenced in way " CPL_FRMT_GIB,
    2374             :                          psCtxt->sWay.nID);
    2375             :             }
    2376             :         }
    2377             :     }
    2378             : 
    2379         141 :     else if (psCtxt->bInRelation && strcmp(pszName, "member") == 0)
    2380             :     {
    2381             :         /* 300 is the recommended value, but there are files with more than 2000
    2382             :          * so we should be able */
    2383             :         /* to realloc over that value */
    2384          42 :         if (psCtxt->sRelation.nMembers >= psCtxt->nMembersAllocated)
    2385             :         {
    2386           0 :             int nMembersAllocated = std::max(psCtxt->nMembersAllocated * 2,
    2387           0 :                                              psCtxt->sRelation.nMembers + 1);
    2388             :             OSMMember *pasMembersNew =
    2389           0 :                 static_cast<OSMMember *>(VSI_REALLOC_VERBOSE(
    2390             :                     psCtxt->pasMembers, nMembersAllocated * sizeof(OSMMember)));
    2391           0 :             if (pasMembersNew == nullptr)
    2392             :             {
    2393           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    2394             :                          "Cannot allocate enough memory to store members of "
    2395             :                          "relation " CPL_FRMT_GIB,
    2396             :                          psCtxt->sRelation.nID);
    2397           0 :                 return;
    2398             :             }
    2399           0 :             psCtxt->nMembersAllocated = nMembersAllocated;
    2400           0 :             psCtxt->pasMembers = pasMembersNew;
    2401             :         }
    2402             : 
    2403          42 :         OSMMember *psMember = &(psCtxt->pasMembers[psCtxt->sRelation.nMembers]);
    2404          42 :         psCtxt->sRelation.nMembers++;
    2405             : 
    2406          42 :         psMember->nID = 0;
    2407          42 :         psMember->pszRole = "";
    2408          42 :         psMember->eType = MEMBER_NODE;
    2409             : 
    2410          42 :         if (ppszIter)
    2411             :         {
    2412         168 :             while (ppszIter[0] != nullptr)
    2413             :             {
    2414         126 :                 if (strcmp(ppszIter[0], "ref") == 0)
    2415             :                 {
    2416          42 :                     psMember->nID = OSM_Atoi64(ppszIter[1]);
    2417             :                 }
    2418          84 :                 else if (strcmp(ppszIter[0], "type") == 0)
    2419             :                 {
    2420          42 :                     if (strcmp(ppszIter[1], "node") == 0)
    2421           4 :                         psMember->eType = MEMBER_NODE;
    2422          38 :                     else if (strcmp(ppszIter[1], "way") == 0)
    2423          38 :                         psMember->eType = MEMBER_WAY;
    2424           0 :                     else if (strcmp(ppszIter[1], "relation") == 0)
    2425           0 :                         psMember->eType = MEMBER_RELATION;
    2426             :                 }
    2427          42 :                 else if (strcmp(ppszIter[0], "role") == 0)
    2428             :                 {
    2429          42 :                     psMember->pszRole = OSM_AddString(psCtxt, ppszIter[1]);
    2430             :                 }
    2431         126 :                 ppszIter += 2;
    2432             :             }
    2433          42 :         }
    2434             :     }
    2435          99 :     else if ((psCtxt->bInNode || psCtxt->bInWay || psCtxt->bInRelation) &&
    2436          83 :              strcmp(pszName, "tag") == 0)
    2437             :     {
    2438          83 :         if (psCtxt->nTags == psCtxt->nTagsAllocated)
    2439             :         {
    2440           0 :             psCtxt->nTagsAllocated = psCtxt->nTagsAllocated * 2;
    2441           0 :             OSMTag *pasTagsNew = static_cast<OSMTag *>(VSI_REALLOC_VERBOSE(
    2442             :                 psCtxt->pasTags, psCtxt->nTagsAllocated * sizeof(OSMTag)));
    2443           0 :             if (pasTagsNew == nullptr)
    2444             :             {
    2445           0 :                 if (psCtxt->bInNode)
    2446           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2447             :                              "Too many tags in node " CPL_FRMT_GIB,
    2448           0 :                              psCtxt->pasNodes[0].nID);
    2449           0 :                 else if (psCtxt->bInWay)
    2450           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2451             :                              "Too many tags in way " CPL_FRMT_GIB,
    2452             :                              psCtxt->sWay.nID);
    2453           0 :                 else if (psCtxt->bInRelation)
    2454           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    2455             :                              "Too many tags in relation " CPL_FRMT_GIB,
    2456             :                              psCtxt->sRelation.nID);
    2457           0 :                 return;
    2458             :             }
    2459           0 :             psCtxt->pasTags = pasTagsNew;
    2460             :         }
    2461             : 
    2462          83 :         OSMTag *psTag = &(psCtxt->pasTags[psCtxt->nTags]);
    2463          83 :         psCtxt->nTags++;
    2464             : 
    2465          83 :         psTag->pszK = "";
    2466          83 :         psTag->pszV = "";
    2467             : 
    2468          83 :         if (ppszIter)
    2469             :         {
    2470         249 :             while (ppszIter[0] != nullptr)
    2471             :             {
    2472         166 :                 if (ppszIter[0][0] == 'k')
    2473             :                 {
    2474          83 :                     psTag->pszK = OSM_AddString(psCtxt, ppszIter[1]);
    2475             :                 }
    2476          83 :                 else if (ppszIter[0][0] == 'v')
    2477             :                 {
    2478          83 :                     psTag->pszV = OSM_AddString(psCtxt, ppszIter[1]);
    2479             :                 }
    2480         166 :                 ppszIter += 2;
    2481             :             }
    2482             :         }
    2483             :     }
    2484             : }
    2485             : 
    2486             : /************************************************************************/
    2487             : /*                       OSM_XML_endElementCbk()                        */
    2488             : /************************************************************************/
    2489             : 
    2490         393 : static void XMLCALL OSM_XML_endElementCbk(void *pUserData, const char *pszName)
    2491             : {
    2492         393 :     OSMContext *psCtxt = static_cast<OSMContext *>(pUserData);
    2493             : 
    2494         393 :     if (psCtxt->bStopParsing)
    2495           0 :         return;
    2496             : 
    2497         393 :     psCtxt->nWithoutEventCounter = 0;
    2498             : 
    2499         393 :     if (psCtxt->bInNode && strcmp(pszName, "node") == 0)
    2500             :     {
    2501             :         // Written this way to deal with NaN
    2502          53 :         if (!(psCtxt->pasNodes[0].dfLon >= -180 &&
    2503          53 :               psCtxt->pasNodes[0].dfLon <= 180 &&
    2504          53 :               psCtxt->pasNodes[0].dfLat >= -90 &&
    2505          53 :               psCtxt->pasNodes[0].dfLat <= 90))
    2506             :         {
    2507           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Invalid lon=%f lat=%f",
    2508           0 :                      psCtxt->pasNodes[0].dfLon, psCtxt->pasNodes[0].dfLat);
    2509             :         }
    2510             :         else
    2511             :         {
    2512          53 :             psCtxt->pasNodes[0].nTags = psCtxt->nTags;
    2513          53 :             psCtxt->pasNodes[0].pasTags = psCtxt->pasTags;
    2514             : 
    2515          53 :             psCtxt->pfnNotifyNodes(1, psCtxt->pasNodes, psCtxt,
    2516             :                                    psCtxt->user_data);
    2517             : 
    2518          53 :             psCtxt->bHasFoundFeature = true;
    2519             :         }
    2520          53 :         psCtxt->bInNode = false;
    2521             :     }
    2522             : 
    2523         340 :     else if (psCtxt->bInWay && strcmp(pszName, "way") == 0)
    2524             :     {
    2525          38 :         psCtxt->sWay.nTags = psCtxt->nTags;
    2526          38 :         psCtxt->sWay.pasTags = psCtxt->pasTags;
    2527             : 
    2528          38 :         psCtxt->sWay.panNodeRefs = psCtxt->panNodeRefs;
    2529             : 
    2530          38 :         psCtxt->pfnNotifyWay(&(psCtxt->sWay), psCtxt, psCtxt->user_data);
    2531             : 
    2532          38 :         psCtxt->bHasFoundFeature = true;
    2533             : 
    2534          38 :         psCtxt->bInWay = false;
    2535             :     }
    2536             : 
    2537         302 :     else if (psCtxt->bInRelation && strcmp(pszName, "relation") == 0)
    2538             :     {
    2539          22 :         psCtxt->sRelation.nTags = psCtxt->nTags;
    2540          22 :         psCtxt->sRelation.pasTags = psCtxt->pasTags;
    2541             : 
    2542          22 :         psCtxt->sRelation.pasMembers = psCtxt->pasMembers;
    2543             : 
    2544          22 :         psCtxt->pfnNotifyRelation(&(psCtxt->sRelation), psCtxt,
    2545             :                                   psCtxt->user_data);
    2546             : 
    2547          22 :         psCtxt->bHasFoundFeature = true;
    2548             : 
    2549          22 :         psCtxt->bInRelation = false;
    2550             :     }
    2551             : }
    2552             : 
    2553             : /************************************************************************/
    2554             : /*                           dataHandlerCbk()                           */
    2555             : /************************************************************************/
    2556             : 
    2557       66473 : static void XMLCALL OSM_XML_dataHandlerCbk(void *pUserData,
    2558             :                                            const char * /* data */,
    2559             :                                            int /* nLen */)
    2560             : {
    2561       66473 :     OSMContext *psCtxt = static_cast<OSMContext *>(pUserData);
    2562             : 
    2563       66473 :     if (psCtxt->bStopParsing)
    2564           0 :         return;
    2565             : 
    2566       66473 :     psCtxt->nWithoutEventCounter = 0;
    2567             : 
    2568       66473 :     psCtxt->nDataHandlerCounter++;
    2569       66473 :     if (psCtxt->nDataHandlerCounter >= XML_BUFSIZE)
    2570             :     {
    2571           1 :         CPLError(CE_Failure, CPLE_AppDefined,
    2572             :                  "File probably corrupted (million laugh pattern)");
    2573           1 :         XML_StopParser(psCtxt->hXMLParser, false);
    2574           1 :         psCtxt->bStopParsing = true;
    2575           1 :         return;
    2576             :     }
    2577             : }
    2578             : 
    2579             : /************************************************************************/
    2580             : /*                          XML_ProcessBlock()                          */
    2581             : /************************************************************************/
    2582             : 
    2583          14 : static OSMRetCode XML_ProcessBlock(OSMContext *psCtxt)
    2584             : {
    2585          14 :     if (psCtxt->bEOF)
    2586           3 :         return OSM_EOF;
    2587          11 :     if (psCtxt->bStopParsing)
    2588           0 :         return OSM_ERROR;
    2589             : 
    2590          11 :     psCtxt->bHasFoundFeature = false;
    2591          11 :     psCtxt->nWithoutEventCounter = 0;
    2592             : 
    2593           0 :     do
    2594             :     {
    2595          11 :         psCtxt->nDataHandlerCounter = 0;
    2596             : 
    2597             :         const unsigned int nLen = static_cast<unsigned int>(
    2598          11 :             VSIFReadL(psCtxt->pabyBlob, 1, XML_BUFSIZE, psCtxt->fp));
    2599             : 
    2600          11 :         psCtxt->nBytesRead += nLen;
    2601             : 
    2602          11 :         psCtxt->bEOF = nLen < XML_BUFSIZE;
    2603             :         const int eErr =
    2604          22 :             XML_Parse(psCtxt->hXMLParser,
    2605          11 :                       reinterpret_cast<const char *>(psCtxt->pabyBlob), nLen,
    2606          11 :                       psCtxt->bEOF);
    2607             : 
    2608          11 :         if (eErr == XML_STATUS_ERROR)
    2609             :         {
    2610           2 :             CPLError(
    2611             :                 CE_Failure, CPLE_AppDefined,
    2612             :                 "XML parsing of OSM file failed : %s "
    2613             :                 "at line %d, column %d",
    2614             :                 XML_ErrorString(XML_GetErrorCode(psCtxt->hXMLParser)),
    2615           2 :                 static_cast<int>(XML_GetCurrentLineNumber(psCtxt->hXMLParser)),
    2616             :                 static_cast<int>(
    2617           2 :                     XML_GetCurrentColumnNumber(psCtxt->hXMLParser)));
    2618           2 :             psCtxt->bStopParsing = true;
    2619             :         }
    2620          11 :         psCtxt->nWithoutEventCounter++;
    2621           0 :     } while (!psCtxt->bEOF && !psCtxt->bStopParsing &&
    2622          11 :              !psCtxt->bHasFoundFeature && psCtxt->nWithoutEventCounter < 10);
    2623             : 
    2624          11 :     if (psCtxt->nWithoutEventCounter == 10)
    2625             :     {
    2626           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2627             :                  "Too much data inside one element. File probably corrupted");
    2628           0 :         psCtxt->bStopParsing = true;
    2629             :     }
    2630             : 
    2631          11 :     return psCtxt->bStopParsing ? OSM_ERROR : psCtxt->bEOF ? OSM_EOF : OSM_OK;
    2632             : }
    2633             : 
    2634             : #endif
    2635             : 
    2636             : /************************************************************************/
    2637             : /*                              OSM_Open()                              */
    2638             : /************************************************************************/
    2639             : 
    2640          44 : OSMContext *OSM_Open(const char *pszFilename, NotifyNodesFunc pfnNotifyNodes,
    2641             :                      NotifyWayFunc pfnNotifyWay,
    2642             :                      NotifyRelationFunc pfnNotifyRelation,
    2643             :                      NotifyBoundsFunc pfnNotifyBounds, void *user_data)
    2644             : {
    2645             : 
    2646          44 :     VSILFILE *fp = VSIFOpenL(pszFilename, "rb");
    2647          44 :     if (fp == nullptr)
    2648           0 :         return nullptr;
    2649             : 
    2650             :     GByte abyHeader[1024];
    2651             :     int nRead =
    2652          44 :         static_cast<int>(VSIFReadL(abyHeader, 1, sizeof(abyHeader) - 1, fp));
    2653          44 :     abyHeader[nRead] = '\0';
    2654             : 
    2655          44 :     bool bPBF = false;
    2656             : 
    2657          44 :     if (strstr(reinterpret_cast<const char *>(abyHeader), "<osm") != nullptr)
    2658             :     {
    2659             :         /* OSM XML */
    2660             : #ifndef HAVE_EXPAT
    2661             :         CPLError(CE_Failure, CPLE_AppDefined,
    2662             :                  "OSM XML detected, but Expat parser not available");
    2663             :         VSIFCloseL(fp);
    2664             :         return nullptr;
    2665             : #endif
    2666             :     }
    2667             :     else
    2668             :     {
    2669          32 :         const int nLimitI = nRead - static_cast<int>(strlen("OSMHeader"));
    2670         218 :         for (int i = 0; i < nLimitI; i++)
    2671             :         {
    2672         218 :             if (memcmp(abyHeader + i, "OSMHeader", strlen("OSMHeader")) == 0)
    2673             :             {
    2674          32 :                 bPBF = true;
    2675          32 :                 break;
    2676             :             }
    2677             :         }
    2678          32 :         if (!bPBF)
    2679             :         {
    2680           0 :             VSIFCloseL(fp);
    2681           0 :             return nullptr;
    2682             :         }
    2683             :     }
    2684             : 
    2685          44 :     VSIFSeekL(fp, 0, SEEK_SET);
    2686             : 
    2687             :     OSMContext *psCtxt =
    2688          44 :         static_cast<OSMContext *>(VSI_MALLOC_VERBOSE(sizeof(OSMContext)));
    2689          44 :     if (psCtxt == nullptr)
    2690             :     {
    2691           0 :         VSIFCloseL(fp);
    2692           0 :         return nullptr;
    2693             :     }
    2694          44 :     memset(psCtxt, 0, sizeof(OSMContext));
    2695          44 :     psCtxt->bPBF = bPBF;
    2696          44 :     psCtxt->fp = fp;
    2697          44 :     psCtxt->pfnNotifyNodes = pfnNotifyNodes;
    2698          44 :     if (pfnNotifyNodes == nullptr)
    2699           0 :         psCtxt->pfnNotifyNodes = EmptyNotifyNodesFunc;
    2700          44 :     psCtxt->pfnNotifyWay = pfnNotifyWay;
    2701          44 :     if (pfnNotifyWay == nullptr)
    2702           0 :         psCtxt->pfnNotifyWay = EmptyNotifyWayFunc;
    2703          44 :     psCtxt->pfnNotifyRelation = pfnNotifyRelation;
    2704          44 :     if (pfnNotifyRelation == nullptr)
    2705           0 :         psCtxt->pfnNotifyRelation = EmptyNotifyRelationFunc;
    2706          44 :     psCtxt->pfnNotifyBounds = pfnNotifyBounds;
    2707          44 :     if (pfnNotifyBounds == nullptr)
    2708           0 :         psCtxt->pfnNotifyBounds = EmptyNotifyBoundsFunc;
    2709          44 :     psCtxt->user_data = user_data;
    2710             : 
    2711          44 :     if (bPBF)
    2712             :     {
    2713          32 :         psCtxt->nBlobSizeAllocated = 64 * 1024 + EXTRA_BYTES;
    2714             :     }
    2715             : #ifdef HAVE_EXPAT
    2716             :     else
    2717             :     {
    2718          12 :         psCtxt->nBlobSizeAllocated = XML_BUFSIZE;
    2719             : 
    2720          12 :         psCtxt->nStrAllocated = 1024 * 1024;
    2721          12 :         psCtxt->pszStrBuf =
    2722          12 :             static_cast<char *>(VSI_MALLOC_VERBOSE(psCtxt->nStrAllocated));
    2723          12 :         if (psCtxt->pszStrBuf)
    2724          12 :             psCtxt->pszStrBuf[0] = '\0';
    2725             : 
    2726          12 :         psCtxt->hXMLParser = OGRCreateExpatXMLParser();
    2727          12 :         XML_SetUserData(psCtxt->hXMLParser, psCtxt);
    2728          12 :         XML_SetElementHandler(psCtxt->hXMLParser, OSM_XML_startElementCbk,
    2729             :                               OSM_XML_endElementCbk);
    2730          12 :         XML_SetCharacterDataHandler(psCtxt->hXMLParser, OSM_XML_dataHandlerCbk);
    2731             : 
    2732          12 :         psCtxt->bTryToFetchBounds = true;
    2733             : 
    2734          12 :         psCtxt->nNodesAllocated = 1;
    2735          12 :         psCtxt->pasNodes = static_cast<OSMNode *>(
    2736          12 :             VSI_MALLOC_VERBOSE(sizeof(OSMNode) * psCtxt->nNodesAllocated));
    2737             : 
    2738          12 :         psCtxt->nTagsAllocated = 256;
    2739          12 :         psCtxt->pasTags = static_cast<OSMTag *>(
    2740          12 :             VSI_MALLOC_VERBOSE(sizeof(OSMTag) * psCtxt->nTagsAllocated));
    2741             : 
    2742             :         /* 300 is the recommended value, but there are files with more than 2000
    2743             :          * so we should be able */
    2744             :         /* to realloc over that value */
    2745          12 :         psCtxt->nMembersAllocated = 2000;
    2746          12 :         psCtxt->pasMembers = static_cast<OSMMember *>(
    2747          12 :             VSI_MALLOC_VERBOSE(sizeof(OSMMember) * psCtxt->nMembersAllocated));
    2748             : 
    2749          12 :         psCtxt->nNodeRefsAllocated = 10000;
    2750          12 :         psCtxt->panNodeRefs = static_cast<GIntBig *>(
    2751          12 :             VSI_MALLOC_VERBOSE(sizeof(GIntBig) * psCtxt->nNodeRefsAllocated));
    2752             : 
    2753          12 :         if (psCtxt->pszStrBuf == nullptr || psCtxt->pasNodes == nullptr ||
    2754          12 :             psCtxt->pasTags == nullptr || psCtxt->pasMembers == nullptr ||
    2755          12 :             psCtxt->panNodeRefs == nullptr)
    2756             :         {
    2757           0 :             OSM_Close(psCtxt);
    2758           0 :             return nullptr;
    2759             :         }
    2760             :     }
    2761             : #endif
    2762             : 
    2763          44 :     psCtxt->pabyBlob =
    2764          44 :         static_cast<GByte *>(VSI_MALLOC_VERBOSE(psCtxt->nBlobSizeAllocated));
    2765          44 :     if (psCtxt->pabyBlob == nullptr)
    2766             :     {
    2767           0 :         OSM_Close(psCtxt);
    2768           0 :         return nullptr;
    2769             :     }
    2770          44 :     psCtxt->pabyBlobHeader = static_cast<GByte *>(
    2771          44 :         VSI_MALLOC_VERBOSE(MAX_BLOB_HEADER_SIZE + EXTRA_BYTES));
    2772          44 :     if (psCtxt->pabyBlobHeader == nullptr)
    2773             :     {
    2774           0 :         OSM_Close(psCtxt);
    2775           0 :         return nullptr;
    2776             :     }
    2777          44 :     const int nNumThreads = GDALGetNumThreads(GDAL_DEFAULT_MAX_THREAD_COUNT,
    2778             :                                               /* bDefaultToAllCPUs = */ true);
    2779          44 :     if (nNumThreads > 1)
    2780             :     {
    2781          44 :         psCtxt->poWTP = new CPLWorkerThreadPool();
    2782             :         // coverity[tainted_data]
    2783          44 :         if (!psCtxt->poWTP->Setup(nNumThreads, nullptr, nullptr))
    2784             :         {
    2785           0 :             delete psCtxt->poWTP;
    2786           0 :             psCtxt->poWTP = nullptr;
    2787             :         }
    2788             :     }
    2789             : 
    2790          44 :     return psCtxt;
    2791             : }
    2792             : 
    2793             : /************************************************************************/
    2794             : /*                             OSM_Close()                              */
    2795             : /************************************************************************/
    2796             : 
    2797          44 : void OSM_Close(OSMContext *psCtxt)
    2798             : {
    2799          44 :     if (psCtxt == nullptr)
    2800           0 :         return;
    2801             : 
    2802             : #ifdef HAVE_EXPAT
    2803          44 :     if (!psCtxt->bPBF)
    2804             :     {
    2805          12 :         if (psCtxt->hXMLParser)
    2806          12 :             XML_ParserFree(psCtxt->hXMLParser);
    2807             : 
    2808          12 :         CPLFree(psCtxt->pszStrBuf); /* only for XML case ! */
    2809             :     }
    2810             : #endif
    2811             : 
    2812          44 :     VSIFree(psCtxt->pabyBlob);
    2813          44 :     VSIFree(psCtxt->pabyBlobHeader);
    2814          44 :     VSIFree(psCtxt->pabyUncompressed);
    2815          44 :     VSIFree(psCtxt->panStrOff);
    2816          44 :     VSIFree(psCtxt->pasNodes);
    2817          44 :     VSIFree(psCtxt->pasTags);
    2818          44 :     VSIFree(psCtxt->pasMembers);
    2819          44 :     VSIFree(psCtxt->panNodeRefs);
    2820          44 :     delete psCtxt->poWTP;
    2821             : 
    2822          44 :     VSIFCloseL(psCtxt->fp);
    2823          44 :     VSIFree(psCtxt);
    2824             : }
    2825             : 
    2826             : /************************************************************************/
    2827             : /*                          OSM_ResetReading()                          */
    2828             : /************************************************************************/
    2829             : 
    2830          65 : void OSM_ResetReading(OSMContext *psCtxt)
    2831             : {
    2832          65 :     VSIFSeekL(psCtxt->fp, 0, SEEK_SET);
    2833             : 
    2834          65 :     psCtxt->nBytesRead = 0;
    2835          65 :     psCtxt->nJobs = 0;
    2836          65 :     psCtxt->iNextJob = 0;
    2837          65 :     psCtxt->nBlobOffset = 0;
    2838          65 :     psCtxt->nBlobSize = 0;
    2839          65 :     psCtxt->nTotalUncompressedSize = 0;
    2840             : 
    2841             : #ifdef HAVE_EXPAT
    2842          65 :     if (!psCtxt->bPBF)
    2843             :     {
    2844           3 :         XML_ParserFree(psCtxt->hXMLParser);
    2845           3 :         psCtxt->hXMLParser = OGRCreateExpatXMLParser();
    2846           3 :         XML_SetUserData(psCtxt->hXMLParser, psCtxt);
    2847           3 :         XML_SetElementHandler(psCtxt->hXMLParser, OSM_XML_startElementCbk,
    2848             :                               OSM_XML_endElementCbk);
    2849           3 :         XML_SetCharacterDataHandler(psCtxt->hXMLParser, OSM_XML_dataHandlerCbk);
    2850           3 :         psCtxt->bEOF = false;
    2851           3 :         psCtxt->bStopParsing = false;
    2852           3 :         psCtxt->nStrLength = 0;
    2853           3 :         psCtxt->pszStrBuf[0] = '\0';
    2854           3 :         psCtxt->nTags = 0;
    2855             : 
    2856           3 :         psCtxt->bTryToFetchBounds = true;
    2857           3 :         psCtxt->bInNode = false;
    2858           3 :         psCtxt->bInWay = false;
    2859           3 :         psCtxt->bInRelation = false;
    2860             :     }
    2861             : #endif
    2862          65 : }
    2863             : 
    2864             : /************************************************************************/
    2865             : /*                          OSM_ProcessBlock()                          */
    2866             : /************************************************************************/
    2867             : 
    2868         108 : OSMRetCode OSM_ProcessBlock(OSMContext *psCtxt)
    2869             : {
    2870             : #ifdef HAVE_EXPAT
    2871         108 :     if (psCtxt->bPBF)
    2872          94 :         return PBF_ProcessBlock(psCtxt);
    2873             :     else
    2874          14 :         return XML_ProcessBlock(psCtxt);
    2875             : #else
    2876             :     return PBF_ProcessBlock(psCtxt);
    2877             : #endif
    2878             : }
    2879             : 
    2880             : /************************************************************************/
    2881             : /*                          OSM_GetBytesRead()                          */
    2882             : /************************************************************************/
    2883             : 
    2884          71 : GUIntBig OSM_GetBytesRead(OSMContext *psCtxt)
    2885             : {
    2886          71 :     return psCtxt->nBytesRead;
    2887             : }

Generated by: LCOV version 1.14