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

Generated by: LCOV version 1.14