Coverage Report

Created: 2026-08-14 09:29

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