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

Generated by: LCOV version 1.14