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

Generated by: LCOV version 1.14