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

Generated by: LCOV version 1.14