Coverage Report

Created: 2025-11-16 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogr_wkb.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  OGR
4
 * Purpose:  WKB geometry related methods
5
 * Author:   Even Rouault <even dot rouault at spatialys.com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_error.h"
14
#include "ogr_wkb.h"
15
#include "ogr_core.h"
16
#include "ogr_geometry.h"
17
#include "ogr_p.h"
18
19
#include <algorithm>
20
#include <cmath>
21
#include <climits>
22
#include <limits>
23
24
#include <algorithm>
25
#include <limits>
26
27
#define USE_FAST_FLOAT
28
#ifdef USE_FAST_FLOAT
29
#include "include_fast_float.h"
30
#endif
31
32
/************************************************************************/
33
/*                          OGRWKBNeedSwap()                            */
34
/************************************************************************/
35
36
static inline bool OGRWKBNeedSwap(GByte b)
37
0
{
38
0
    return b != CPL_IS_LSB;
39
0
}
40
41
/************************************************************************/
42
/*                        OGRWKBReadUInt32()                            */
43
/************************************************************************/
44
45
static inline uint32_t OGRWKBReadUInt32(const GByte *pabyWkb, bool bNeedSwap)
46
0
{
47
0
    uint32_t nVal;
48
0
    memcpy(&nVal, pabyWkb, sizeof(nVal));
49
0
    if (bNeedSwap)
50
0
        CPL_SWAP32PTR(&nVal);
51
0
    return nVal;
52
0
}
53
54
/************************************************************************/
55
/*                        OGRWKBReadFloat64()                           */
56
/************************************************************************/
57
58
static inline double OGRWKBReadFloat64(const GByte *pabyWkb, bool bNeedSwap)
59
0
{
60
0
    double dfVal;
61
0
    memcpy(&dfVal, pabyWkb, sizeof(dfVal));
62
0
    if (bNeedSwap)
63
0
        CPL_SWAP64PTR(&dfVal);
64
0
    return dfVal;
65
0
}
66
67
/************************************************************************/
68
/*                        OGRWKBRingGetArea()                           */
69
/************************************************************************/
70
71
static bool OGRWKBRingGetArea(const GByte *&pabyWkb, size_t &nWKBSize, int nDim,
72
                              bool bNeedSwap, double &dfArea)
73
0
{
74
0
    const uint32_t nPoints = OGRWKBReadUInt32(pabyWkb, bNeedSwap);
75
0
    if (nPoints >= 4 &&
76
0
        (nWKBSize - sizeof(uint32_t)) / (nDim * sizeof(double)) >= nPoints)
77
0
    {
78
0
        nWKBSize -= sizeof(uint32_t) + nDim * sizeof(double);
79
0
        pabyWkb += sizeof(uint32_t);
80
        // Computation according to Green's Theorem
81
        // Cf OGRSimpleCurve::get_LinearArea()
82
0
        double x_m1 = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
83
0
        double y_m1 = OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
84
0
        double y_m2 = y_m1;
85
0
        dfArea = 0;
86
0
        pabyWkb += nDim * sizeof(double);
87
0
        for (uint32_t i = 1; i < nPoints; ++i)
88
0
        {
89
0
            const double x = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
90
0
            const double y =
91
0
                OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
92
0
            pabyWkb += nDim * sizeof(double);
93
0
            dfArea += x_m1 * (y - y_m2);
94
0
            y_m2 = y_m1;
95
0
            x_m1 = x;
96
0
            y_m1 = y;
97
0
        }
98
0
        dfArea += x_m1 * (y_m1 - y_m2);
99
0
        dfArea = 0.5 * std::fabs(dfArea);
100
0
        return true;
101
0
    }
102
0
    return false;
103
0
}
104
105
/************************************************************************/
106
/*                         OGRWKBGetGeomType()                          */
107
/************************************************************************/
108
109
bool OGRWKBGetGeomType(const GByte *pabyWkb, size_t nWKBSize, bool &bNeedSwap,
110
                       uint32_t &nType)
111
0
{
112
0
    if (nWKBSize >= 5)
113
0
    {
114
0
        bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
115
0
        nType = OGRWKBReadUInt32(pabyWkb + 1, bNeedSwap);
116
0
        return true;
117
0
    }
118
0
    return false;
119
0
}
120
121
/************************************************************************/
122
/*                        OGRWKBPolygonGetArea()                        */
123
/************************************************************************/
124
125
bool OGRWKBPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
126
                          double &dfArea)
127
0
{
128
0
    bool bNeedSwap;
129
0
    uint32_t nType;
130
0
    if (nWKBSize < 9 || !OGRWKBGetGeomType(pabyWkb, nWKBSize, bNeedSwap, nType))
131
0
        return false;
132
133
0
    int nDims = 2;
134
0
    if (nType == wkbPolygon)
135
0
    {
136
        // do nothing
137
0
    }
138
0
    else if (nType == wkbPolygon + 1000 ||  // wkbPolygonZ
139
0
             nType == wkbPolygon25D || nType == wkbPolygonM)
140
0
    {
141
0
        nDims = 3;
142
0
    }
143
0
    else if (nType == wkbPolygonZM)
144
0
    {
145
0
        nDims = 4;
146
0
    }
147
0
    else
148
0
    {
149
0
        return false;
150
0
    }
151
152
0
    const uint32_t nRings = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
153
0
    if ((nWKBSize - 9) / sizeof(uint32_t) >= nRings)
154
0
    {
155
0
        pabyWkb += 9;
156
0
        nWKBSize -= 9;
157
0
        dfArea = 0;
158
0
        if (nRings > 0)
159
0
        {
160
0
            if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap, dfArea))
161
0
                return false;
162
0
            for (uint32_t i = 1; i < nRings; ++i)
163
0
            {
164
0
                double dfRingArea;
165
0
                if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap,
166
0
                                       dfRingArea))
167
0
                    return false;
168
0
                dfArea -= dfRingArea;
169
0
            }
170
0
        }
171
0
        return true;
172
0
    }
173
0
    return false;
174
0
}
175
176
/************************************************************************/
177
/*                    OGRWKBMultiPolygonGetArea()                       */
178
/************************************************************************/
179
180
bool OGRWKBMultiPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
181
                               double &dfArea)
182
0
{
183
0
    if (nWKBSize < 9)
184
0
        return false;
185
186
0
    const bool bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
187
0
    const uint32_t nPolys = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
188
0
    if ((nWKBSize - 9) / 9 >= nPolys)
189
0
    {
190
0
        pabyWkb += 9;
191
0
        nWKBSize -= 9;
192
0
        dfArea = 0;
193
0
        for (uint32_t i = 0; i < nPolys; ++i)
194
0
        {
195
0
            double dfPolyArea;
196
0
            if (!OGRWKBPolygonGetArea(pabyWkb, nWKBSize, dfPolyArea))
197
0
                return false;
198
0
            dfArea += dfPolyArea;
199
0
        }
200
0
        return true;
201
0
    }
202
0
    return false;
203
0
}
204
205
/************************************************************************/
206
/*                            WKBFromEWKB()                             */
207
/************************************************************************/
208
209
const GByte *WKBFromEWKB(GByte *pabyEWKB, size_t nEWKBSize, size_t &nWKBSizeOut,
210
                         int *pnSRIDOut)
211
0
{
212
0
    if (nEWKBSize < 5U)
213
0
    {
214
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid EWKB content : %u bytes",
215
0
                 static_cast<unsigned>(nEWKBSize));
216
0
        return nullptr;
217
0
    }
218
219
0
    const GByte *pabyWKB = pabyEWKB;
220
221
    /* -------------------------------------------------------------------- */
222
    /*      PostGIS EWKB format includes an SRID, but this won't be         */
223
    /*      understood by OGR, so if the SRID flag is set, we remove the    */
224
    /*      SRID (bytes at offset 5 to 8).                                  */
225
    /* -------------------------------------------------------------------- */
226
0
    if (nEWKBSize > 9 &&
227
0
        ((pabyEWKB[0] == 0 /* big endian */ && (pabyEWKB[1] & 0x20)) ||
228
0
         (pabyEWKB[0] != 0 /* little endian */ && (pabyEWKB[4] & 0x20))))
229
0
    {
230
0
        if (pnSRIDOut)
231
0
        {
232
0
            memcpy(pnSRIDOut, pabyEWKB + 5, 4);
233
0
            const OGRwkbByteOrder eByteOrder =
234
0
                (pabyEWKB[0] == 0 ? wkbXDR : wkbNDR);
235
0
            if (OGR_SWAP(eByteOrder))
236
0
                *pnSRIDOut = CPL_SWAP32(*pnSRIDOut);
237
0
        }
238
239
        // Drop the SRID flag
240
0
        if (pabyEWKB[0] == 0)
241
0
            pabyEWKB[1] &= (~0x20);
242
0
        else
243
0
            pabyEWKB[4] &= (~0x20);
244
245
        // Move 5 first bytes of EWKB 4 bytes later to create regular WKB
246
0
        memmove(pabyEWKB + 4, pabyEWKB, 5);
247
0
        memset(pabyEWKB, 0, 4);
248
        // and make pabyWKB point to that
249
0
        pabyWKB += 4;
250
0
        nWKBSizeOut = nEWKBSize - 4;
251
0
    }
252
0
    else
253
0
    {
254
0
        if (pnSRIDOut)
255
0
        {
256
0
            *pnSRIDOut = INT_MIN;
257
0
        }
258
0
        nWKBSizeOut = nEWKBSize;
259
0
    }
260
261
0
    return pabyWKB;
262
0
}
263
264
/************************************************************************/
265
/*                     OGRWKBReadUInt32AtOffset()                       */
266
/************************************************************************/
267
268
static uint32_t OGRWKBReadUInt32AtOffset(const uint8_t *data,
269
                                         OGRwkbByteOrder eByteOrder,
270
                                         size_t &iOffset)
271
0
{
272
0
    uint32_t v;
273
0
    memcpy(&v, data + iOffset, sizeof(v));
274
0
    iOffset += sizeof(v);
275
0
    if (OGR_SWAP(eByteOrder))
276
0
    {
277
0
        CPL_SWAP32PTR(&v);
278
0
    }
279
0
    return v;
280
0
}
281
282
/************************************************************************/
283
/*                         ReadWKBPointSequence()                       */
284
/************************************************************************/
285
286
template <bool INCLUDE_Z, typename EnvelopeType>
287
static bool ReadWKBPointSequence(const uint8_t *data, size_t size,
288
                                 OGRwkbByteOrder eByteOrder, int nDim,
289
                                 bool bHasZ, size_t &iOffset,
290
                                 EnvelopeType &sEnvelope)
291
0
{
292
0
    const uint32_t nPoints =
293
0
        OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
294
0
    if (nPoints > (size - iOffset) / (nDim * sizeof(double)))
295
0
        return false;
296
0
    double dfX = 0;
297
0
    double dfY = 0;
298
0
    [[maybe_unused]] double dfZ = 0;
299
0
    for (uint32_t j = 0; j < nPoints; j++)
300
0
    {
301
0
        memcpy(&dfX, data + iOffset, sizeof(double));
302
0
        memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
303
        if constexpr (INCLUDE_Z)
304
0
        {
305
0
            if (bHasZ)
306
0
                memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
307
0
                       sizeof(double));
308
0
        }
309
0
        iOffset += nDim * sizeof(double);
310
0
        if (OGR_SWAP(eByteOrder))
311
0
        {
312
0
            CPL_SWAP64PTR(&dfX);
313
0
            CPL_SWAP64PTR(&dfY);
314
            if constexpr (INCLUDE_Z)
315
0
            {
316
0
                CPL_SWAP64PTR(&dfZ);
317
0
            }
318
0
        }
319
0
        sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
320
0
        sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
321
0
        sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
322
0
        sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
323
        if constexpr (INCLUDE_Z)
324
0
        {
325
0
            if (bHasZ)
326
0
            {
327
0
                sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
328
0
                sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
329
0
            }
330
0
        }
331
0
    }
332
0
    return true;
333
0
}
Unexecuted instantiation: ogr_wkb.cpp:bool ReadWKBPointSequence<false, OGREnvelope>(unsigned char const*, unsigned long, OGRwkbByteOrder, int, bool, unsigned long&, OGREnvelope&)
Unexecuted instantiation: ogr_wkb.cpp:bool ReadWKBPointSequence<true, OGREnvelope3D>(unsigned char const*, unsigned long, OGRwkbByteOrder, int, bool, unsigned long&, OGREnvelope3D&)
334
335
/************************************************************************/
336
/*                         ReadWKBRingSequence()                        */
337
/************************************************************************/
338
339
template <bool INCLUDE_Z, typename EnvelopeType>
340
static bool ReadWKBRingSequence(const uint8_t *data, size_t size,
341
                                OGRwkbByteOrder eByteOrder, int nDim,
342
                                bool bHasZ, size_t &iOffset,
343
                                EnvelopeType &sEnvelope)
344
0
{
345
0
    const uint32_t nRings = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
346
0
    if (nRings > (size - iOffset) / sizeof(uint32_t))
347
0
        return false;
348
0
    for (uint32_t i = 0; i < nRings; i++)
349
0
    {
350
0
        if (iOffset + sizeof(uint32_t) > size)
351
0
            return false;
352
0
        if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
353
0
                                             bHasZ, iOffset, sEnvelope))
354
0
            return false;
355
0
    }
356
0
    return true;
357
0
}
Unexecuted instantiation: ogr_wkb.cpp:bool ReadWKBRingSequence<false, OGREnvelope>(unsigned char const*, unsigned long, OGRwkbByteOrder, int, bool, unsigned long&, OGREnvelope&)
Unexecuted instantiation: ogr_wkb.cpp:bool ReadWKBRingSequence<true, OGREnvelope3D>(unsigned char const*, unsigned long, OGRwkbByteOrder, int, bool, unsigned long&, OGREnvelope3D&)
358
359
/************************************************************************/
360
/*                        OGRWKBGetBoundingBox()                        */
361
/************************************************************************/
362
363
constexpr uint32_t WKB_PREFIX_SIZE = 1 + sizeof(uint32_t);
364
constexpr uint32_t MIN_WKB_SIZE = WKB_PREFIX_SIZE + sizeof(uint32_t);
365
366
template <bool INCLUDE_Z, typename EnvelopeType>
367
static bool OGRWKBGetBoundingBox(const uint8_t *data, size_t size,
368
                                 size_t &iOffset, EnvelopeType &sEnvelope,
369
                                 int nRec)
370
0
{
371
0
    if (size - iOffset < MIN_WKB_SIZE)
372
0
        return false;
373
0
    const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffset]);
374
0
    if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
375
0
        return false;
376
0
    const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
377
378
0
    OGRwkbGeometryType eGeometryType = wkbUnknown;
379
0
    OGRReadWKBGeometryType(data + iOffset, wkbVariantIso, &eGeometryType);
380
0
    iOffset += 5;
381
0
    const auto eFlatType = wkbFlatten(eGeometryType);
382
0
    const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeometryType));
383
0
    const int nDim = 2 + (bHasZ ? 1 : 0) + (OGR_GT_HasM(eGeometryType) ? 1 : 0);
384
385
0
    if (eFlatType == wkbPoint)
386
0
    {
387
0
        if (size - iOffset < nDim * sizeof(double))
388
0
            return false;
389
0
        double dfX = 0;
390
0
        double dfY = 0;
391
0
        [[maybe_unused]] double dfZ = 0;
392
0
        memcpy(&dfX, data + iOffset, sizeof(double));
393
0
        memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
394
        if constexpr (INCLUDE_Z)
395
0
        {
396
0
            if (bHasZ)
397
0
                memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
398
0
                       sizeof(double));
399
0
        }
400
0
        iOffset += nDim * sizeof(double);
401
0
        if (OGR_SWAP(eByteOrder))
402
0
        {
403
0
            CPL_SWAP64PTR(&dfX);
404
0
            CPL_SWAP64PTR(&dfY);
405
            if constexpr (INCLUDE_Z)
406
0
            {
407
0
                CPL_SWAP64PTR(&dfZ);
408
0
            }
409
0
        }
410
0
        if (std::isnan(dfX))
411
0
        {
412
            // Point empty
413
0
            sEnvelope = EnvelopeType();
414
0
        }
415
0
        else
416
0
        {
417
0
            sEnvelope.MinX = dfX;
418
0
            sEnvelope.MinY = dfY;
419
0
            sEnvelope.MaxX = dfX;
420
0
            sEnvelope.MaxY = dfY;
421
            if constexpr (INCLUDE_Z)
422
0
            {
423
0
                if (bHasZ)
424
0
                {
425
0
                    sEnvelope.MinZ = dfZ;
426
0
                    sEnvelope.MaxZ = dfZ;
427
0
                }
428
0
            }
429
0
        }
430
0
        return true;
431
0
    }
432
433
0
    if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
434
0
    {
435
0
        sEnvelope = EnvelopeType();
436
437
0
        return ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
438
0
                                               bHasZ, iOffset, sEnvelope);
439
0
    }
440
441
0
    if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
442
0
    {
443
0
        sEnvelope = EnvelopeType();
444
445
0
        return ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
446
0
                                              bHasZ, iOffset, sEnvelope);
447
0
    }
448
449
0
    if (eFlatType == wkbMultiPoint)
450
0
    {
451
0
        sEnvelope = EnvelopeType();
452
453
0
        uint32_t nParts = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
454
0
        if (nParts >
455
0
            (size - iOffset) / (WKB_PREFIX_SIZE + nDim * sizeof(double)))
456
0
            return false;
457
0
        double dfX = 0;
458
0
        double dfY = 0;
459
0
        [[maybe_unused]] double dfZ = 0;
460
0
        for (uint32_t k = 0; k < nParts; k++)
461
0
        {
462
0
            iOffset += WKB_PREFIX_SIZE;
463
0
            memcpy(&dfX, data + iOffset, sizeof(double));
464
0
            memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
465
            if constexpr (INCLUDE_Z)
466
0
            {
467
0
                if (bHasZ)
468
0
                    memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
469
0
                           sizeof(double));
470
0
            }
471
0
            iOffset += nDim * sizeof(double);
472
0
            if (OGR_SWAP(eByteOrder))
473
0
            {
474
0
                CPL_SWAP64PTR(&dfX);
475
0
                CPL_SWAP64PTR(&dfY);
476
                if constexpr (INCLUDE_Z)
477
0
                {
478
0
                    CPL_SWAP64PTR(&dfZ);
479
0
                }
480
0
            }
481
0
            sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
482
0
            sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
483
0
            sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
484
0
            sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
485
            if constexpr (INCLUDE_Z)
486
0
            {
487
0
                if (bHasZ)
488
0
                {
489
0
                    sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
490
0
                    sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
491
0
                }
492
0
            }
493
0
        }
494
0
        return true;
495
0
    }
496
497
0
    if (eFlatType == wkbMultiLineString)
498
0
    {
499
0
        sEnvelope = EnvelopeType();
500
501
0
        const uint32_t nParts =
502
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
503
0
        if (nParts > (size - iOffset) / MIN_WKB_SIZE)
504
0
            return false;
505
0
        for (uint32_t k = 0; k < nParts; k++)
506
0
        {
507
0
            if (iOffset + MIN_WKB_SIZE > size)
508
0
                return false;
509
0
            iOffset += WKB_PREFIX_SIZE;
510
0
            if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
511
0
                                                 bHasZ, iOffset, sEnvelope))
512
0
                return false;
513
0
        }
514
0
        return true;
515
0
    }
516
517
0
    if (eFlatType == wkbMultiPolygon)
518
0
    {
519
0
        sEnvelope = EnvelopeType();
520
521
0
        const uint32_t nParts =
522
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
523
0
        if (nParts > (size - iOffset) / MIN_WKB_SIZE)
524
0
            return false;
525
0
        for (uint32_t k = 0; k < nParts; k++)
526
0
        {
527
0
            if (iOffset + MIN_WKB_SIZE > size)
528
0
                return false;
529
0
            CPLAssert(data[iOffset] == eByteOrder);
530
0
            iOffset += WKB_PREFIX_SIZE;
531
0
            if (!ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
532
0
                                                bHasZ, iOffset, sEnvelope))
533
0
                return false;
534
0
        }
535
0
        return true;
536
0
    }
537
538
0
    if (eFlatType == wkbGeometryCollection || eFlatType == wkbCompoundCurve ||
539
0
        eFlatType == wkbCurvePolygon || eFlatType == wkbMultiCurve ||
540
0
        eFlatType == wkbMultiSurface || eFlatType == wkbPolyhedralSurface ||
541
0
        eFlatType == wkbTIN)
542
0
    {
543
0
        if (nRec == 128)
544
0
            return false;
545
0
        sEnvelope = EnvelopeType();
546
547
0
        const uint32_t nParts =
548
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
549
0
        if (nParts > (size - iOffset) / MIN_WKB_SIZE)
550
0
            return false;
551
0
        EnvelopeType sEnvelopeSubGeom;
552
0
        for (uint32_t k = 0; k < nParts; k++)
553
0
        {
554
0
            if (!OGRWKBGetBoundingBox<INCLUDE_Z>(data, size, iOffset,
555
0
                                                 sEnvelopeSubGeom, nRec + 1))
556
0
                return false;
557
0
            sEnvelope.Merge(sEnvelopeSubGeom);
558
0
        }
559
0
        return true;
560
0
    }
561
562
0
    return false;
563
0
}
Unexecuted instantiation: ogr_wkb.cpp:bool OGRWKBGetBoundingBox<false, OGREnvelope>(unsigned char const*, unsigned long, unsigned long&, OGREnvelope&, int)
Unexecuted instantiation: ogr_wkb.cpp:bool OGRWKBGetBoundingBox<true, OGREnvelope3D>(unsigned char const*, unsigned long, unsigned long&, OGREnvelope3D&, int)
564
565
/************************************************************************/
566
/*                        OGRWKBGetBoundingBox()                        */
567
/************************************************************************/
568
569
bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
570
                          OGREnvelope &sEnvelope)
571
0
{
572
0
    size_t iOffset = 0;
573
0
    return OGRWKBGetBoundingBox<false>(pabyWkb, nWKBSize, iOffset, sEnvelope,
574
0
                                       0);
575
0
}
576
577
/************************************************************************/
578
/*                        OGRWKBGetBoundingBox()                        */
579
/************************************************************************/
580
581
bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
582
                          OGREnvelope3D &sEnvelope)
583
0
{
584
0
    size_t iOffset = 0;
585
0
    return OGRWKBGetBoundingBox<true>(pabyWkb, nWKBSize, iOffset, sEnvelope, 0);
586
0
}
587
588
/************************************************************************/
589
/*              OGRWKBIntersectsPointSequencePessimistic()              */
590
/************************************************************************/
591
592
static bool OGRWKBIntersectsPointSequencePessimistic(
593
    const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
594
    const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
595
    bool &bErrorOut)
596
0
{
597
0
    const uint32_t nPoints =
598
0
        OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
599
0
    if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
600
0
    {
601
0
        bErrorOut = true;
602
0
        return false;
603
0
    }
604
605
0
    double dfX = 0;
606
0
    double dfY = 0;
607
0
    for (uint32_t j = 0; j < nPoints; j++)
608
0
    {
609
0
        memcpy(&dfX, data + iOffsetInOut, sizeof(double));
610
0
        memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
611
0
        iOffsetInOut += nDim * sizeof(double);
612
0
        if (OGR_SWAP(eByteOrder))
613
0
        {
614
0
            CPL_SWAP64PTR(&dfX);
615
0
            CPL_SWAP64PTR(&dfY);
616
0
        }
617
0
        if (dfX >= sEnvelope.MinX && dfY >= sEnvelope.MinY &&
618
0
            dfX <= sEnvelope.MaxX && dfY <= sEnvelope.MaxY)
619
0
        {
620
0
            return true;
621
0
        }
622
0
    }
623
624
0
    return false;
625
0
}
626
627
/************************************************************************/
628
/*               OGRWKBIntersectsRingSequencePessimistic()              */
629
/************************************************************************/
630
631
static bool OGRWKBIntersectsRingSequencePessimistic(
632
    const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
633
    const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
634
    bool &bErrorOut)
635
0
{
636
0
    const uint32_t nRings =
637
0
        OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
638
0
    if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
639
0
    {
640
0
        bErrorOut = true;
641
0
        return false;
642
0
    }
643
0
    if (nRings == 0)
644
0
        return false;
645
0
    if (iOffsetInOut + sizeof(uint32_t) > size)
646
0
    {
647
0
        bErrorOut = true;
648
0
        return false;
649
0
    }
650
0
    if (OGRWKBIntersectsPointSequencePessimistic(
651
0
            data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut))
652
0
    {
653
0
        return true;
654
0
    }
655
0
    if (bErrorOut)
656
0
        return false;
657
658
    // skip inner rings
659
0
    for (uint32_t i = 1; i < nRings; ++i)
660
0
    {
661
0
        if (iOffsetInOut + sizeof(uint32_t) > size)
662
0
        {
663
0
            bErrorOut = true;
664
0
            return false;
665
0
        }
666
0
        const uint32_t nPoints =
667
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
668
0
        if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
669
0
        {
670
0
            bErrorOut = true;
671
0
            return false;
672
0
        }
673
0
        iOffsetInOut += sizeof(double) * nPoints * nDim;
674
0
    }
675
0
    return false;
676
0
}
677
678
/************************************************************************/
679
/*                  OGRWKBIntersectsPessimistic()                         */
680
/************************************************************************/
681
682
static bool OGRWKBIntersectsPessimistic(const GByte *data, const size_t size,
683
                                        size_t &iOffsetInOut,
684
                                        const OGREnvelope &sEnvelope,
685
                                        const int nRec, bool &bErrorOut)
686
0
{
687
0
    if (size - iOffsetInOut < MIN_WKB_SIZE)
688
0
    {
689
0
        bErrorOut = true;
690
0
        return false;
691
0
    }
692
0
    const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
693
0
    if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
694
0
    {
695
0
        bErrorOut = true;
696
0
        return false;
697
0
    }
698
0
    const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
699
700
0
    OGRwkbGeometryType eGeometryType = wkbUnknown;
701
0
    OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
702
0
    iOffsetInOut += 5;
703
0
    const auto eFlatType = wkbFlatten(eGeometryType);
704
0
    const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
705
0
                     (OGR_GT_HasM(eGeometryType) ? 1 : 0);
706
707
0
    if (eFlatType == wkbPoint)
708
0
    {
709
0
        if (size - iOffsetInOut < nDim * sizeof(double))
710
0
            return false;
711
0
        double dfX = 0;
712
0
        double dfY = 0;
713
0
        memcpy(&dfX, data + iOffsetInOut, sizeof(double));
714
0
        memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
715
0
        iOffsetInOut += nDim * sizeof(double);
716
0
        if (OGR_SWAP(eByteOrder))
717
0
        {
718
0
            CPL_SWAP64PTR(&dfX);
719
0
            CPL_SWAP64PTR(&dfY);
720
0
        }
721
0
        if (std::isnan(dfX))
722
0
        {
723
0
            return false;
724
0
        }
725
0
        else
726
0
        {
727
0
            return dfX >= sEnvelope.MinX && dfX <= sEnvelope.MaxX &&
728
0
                   dfY >= sEnvelope.MinY && dfY <= sEnvelope.MaxY;
729
0
        }
730
0
    }
731
732
0
    if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
733
0
    {
734
0
        return OGRWKBIntersectsPointSequencePessimistic(
735
0
            data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
736
0
    }
737
738
0
    if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
739
0
    {
740
0
        return OGRWKBIntersectsRingSequencePessimistic(
741
0
            data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
742
0
    }
743
744
0
    if (eFlatType == wkbMultiPoint || eFlatType == wkbMultiLineString ||
745
0
        eFlatType == wkbMultiPolygon || eFlatType == wkbGeometryCollection ||
746
0
        eFlatType == wkbCompoundCurve || eFlatType == wkbCurvePolygon ||
747
0
        eFlatType == wkbMultiCurve || eFlatType == wkbMultiSurface ||
748
0
        eFlatType == wkbPolyhedralSurface || eFlatType == wkbTIN)
749
0
    {
750
0
        if (nRec == 128)
751
0
        {
752
0
            bErrorOut = true;
753
0
            return false;
754
0
        }
755
0
        const uint32_t nParts =
756
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
757
0
        if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
758
0
        {
759
0
            bErrorOut = true;
760
0
            return false;
761
0
        }
762
0
        for (uint32_t k = 0; k < nParts; k++)
763
0
        {
764
0
            if (OGRWKBIntersectsPessimistic(data, size, iOffsetInOut, sEnvelope,
765
0
                                            nRec + 1, bErrorOut))
766
0
            {
767
0
                return true;
768
0
            }
769
0
            else if (bErrorOut)
770
0
            {
771
0
                return false;
772
0
            }
773
0
        }
774
0
        return false;
775
0
    }
776
777
0
    bErrorOut = true;
778
0
    return false;
779
0
}
780
781
/************************************************************************/
782
/*                  OGRWKBIntersectsPessimistic()                       */
783
/************************************************************************/
784
785
/* Returns whether the geometry (pabyWkb, nWKBSize) intersects, for sure,
786
 * the passed envelope.
787
 * When it returns true, the geometry intersects the envelope.
788
 * When it returns false, the geometry may or may not intersect the envelope.
789
 */
790
bool OGRWKBIntersectsPessimistic(const GByte *pabyWkb, size_t nWKBSize,
791
                                 const OGREnvelope &sEnvelope)
792
0
{
793
0
    size_t iOffsetInOut = 0;
794
0
    bool bErrorOut = false;
795
0
    bool bRet = OGRWKBIntersectsPessimistic(pabyWkb, nWKBSize, iOffsetInOut,
796
0
                                            sEnvelope, 0, bErrorOut);
797
0
    if (!bRet && !bErrorOut)
798
0
    {
799
        // The following assert only holds if there is no trailing data
800
        // after the WKB
801
        // CPLAssert(iOffsetInOut == nWKBSize);
802
0
    }
803
0
    return bRet;
804
0
}
805
806
/************************************************************************/
807
/*                            epsilonEqual()                            */
808
/************************************************************************/
809
810
static inline bool epsilonEqual(double a, double b, double eps)
811
0
{
812
0
    return ::fabs(a - b) < eps;
813
0
}
814
815
/************************************************************************/
816
/*                     OGRWKBIsClockwiseRing()                          */
817
/************************************************************************/
818
819
static inline double GetX(const GByte *data, uint32_t i, int nDim,
820
                          bool bNeedSwap)
821
0
{
822
0
    double dfX;
823
0
    memcpy(&dfX, data + static_cast<size_t>(i) * nDim * sizeof(double),
824
0
           sizeof(double));
825
0
    if (bNeedSwap)
826
0
        CPL_SWAP64PTR(&dfX);
827
0
    return dfX;
828
0
}
829
830
static inline double GetY(const GByte *data, uint32_t i, int nDim,
831
                          bool bNeedSwap)
832
0
{
833
0
    double dfY;
834
0
    memcpy(&dfY, data + (static_cast<size_t>(i) * nDim + 1) * sizeof(double),
835
0
           sizeof(double));
836
0
    if (bNeedSwap)
837
0
        CPL_SWAP64PTR(&dfY);
838
0
    return dfY;
839
0
}
840
841
static bool OGRWKBIsClockwiseRing(const GByte *data, const uint32_t nPoints,
842
                                  const int nDim, const bool bNeedSwap)
843
0
{
844
0
    constexpr double EPSILON = 1.0E-5;
845
846
    // WARNING: keep in sync OGRLineString::isClockwise(),
847
    // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
848
849
0
    bool bUseFallback = false;
850
851
    // Find the lowest rightmost vertex.
852
0
    uint32_t v = 0;  // Used after for.
853
0
    double vX = GetX(data, v, nDim, bNeedSwap);
854
0
    double vY = GetY(data, v, nDim, bNeedSwap);
855
0
    for (uint32_t i = 1; i < nPoints - 1; i++)
856
0
    {
857
        // => v < end.
858
0
        const double y = GetY(data, i, nDim, bNeedSwap);
859
0
        if (y < vY)
860
0
        {
861
0
            v = i;
862
0
            vX = GetX(data, i, nDim, bNeedSwap);
863
0
            vY = y;
864
0
            bUseFallback = false;
865
0
        }
866
0
        else if (y == vY)
867
0
        {
868
0
            const double x = GetX(data, i, nDim, bNeedSwap);
869
0
            if (x > vX)
870
0
            {
871
0
                v = i;
872
0
                vX = x;
873
                // vY = y;
874
0
                bUseFallback = false;
875
0
            }
876
0
            else if (x == vX)
877
0
            {
878
                // Two vertex with same coordinates are the lowest rightmost
879
                // vertex.  Cannot use that point as the pivot (#5342).
880
0
                bUseFallback = true;
881
0
            }
882
0
        }
883
0
    }
884
885
    // Previous.
886
0
    uint32_t next = (v == 0) ? nPoints - 2 : v - 1;
887
0
    if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
888
0
        epsilonEqual(GetY(data, next, nDim, bNeedSwap), vY, EPSILON))
889
0
    {
890
        // Don't try to be too clever by retrying with a next point.
891
        // This can lead to false results as in the case of #3356.
892
0
        bUseFallback = true;
893
0
    }
894
895
0
    const double dx0 = GetX(data, next, nDim, bNeedSwap) - vX;
896
0
    const double dy0 = GetY(data, next, nDim, bNeedSwap) - vY;
897
898
    // Following.
899
0
    next = v + 1;
900
0
    if (next >= nPoints - 1)
901
0
    {
902
0
        next = 0;
903
0
    }
904
905
0
    if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
906
0
        epsilonEqual(GetY(data, next, nDim, bNeedSwap), vY, EPSILON))
907
0
    {
908
        // Don't try to be too clever by retrying with a next point.
909
        // This can lead to false results as in the case of #3356.
910
0
        bUseFallback = true;
911
0
    }
912
913
0
    const double dx1 = GetX(data, next, nDim, bNeedSwap) - vX;
914
0
    const double dy1 = GetY(data, next, nDim, bNeedSwap) - vY;
915
916
0
    const double crossproduct = dx1 * dy0 - dx0 * dy1;
917
918
0
    if (!bUseFallback)
919
0
    {
920
0
        if (crossproduct > 0)  // CCW
921
0
            return false;
922
0
        else if (crossproduct < 0)  // CW
923
0
            return true;
924
0
    }
925
926
    // This is a degenerate case: the extent of the polygon is less than EPSILON
927
    // or 2 nearly identical points were found.
928
    // Try with Green Formula as a fallback, but this is not a guarantee
929
    // as we'll probably be affected by numerical instabilities.
930
931
0
    double dfSum = GetX(data, 0, nDim, bNeedSwap) *
932
0
                   (GetY(data, 1, nDim, bNeedSwap) -
933
0
                    GetY(data, nPoints - 1, nDim, bNeedSwap));
934
935
0
    for (uint32_t i = 1; i < nPoints - 1; i++)
936
0
    {
937
0
        dfSum += GetX(data, i, nDim, bNeedSwap) *
938
0
                 (GetY(data, i + 1, nDim, bNeedSwap) -
939
0
                  GetY(data, i - 1, nDim, bNeedSwap));
940
0
    }
941
942
0
    dfSum += GetX(data, nPoints - 1, nDim, bNeedSwap) *
943
0
             (GetY(data, 0, nDim, bNeedSwap) -
944
0
              GetX(data, nPoints - 2, nDim, bNeedSwap));
945
946
0
    return dfSum < 0;
947
0
}
948
949
/************************************************************************/
950
/*                OGRWKBFixupCounterClockWiseExternalRing()             */
951
/************************************************************************/
952
953
static bool OGRWKBFixupCounterClockWiseExternalRingInternal(
954
    GByte *data, size_t size, size_t &iOffsetInOut, const int nRec)
955
0
{
956
0
    if (size - iOffsetInOut < MIN_WKB_SIZE)
957
0
    {
958
0
        return false;
959
0
    }
960
0
    const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
961
0
    if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
962
0
    {
963
0
        return false;
964
0
    }
965
0
    const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
966
967
0
    OGRwkbGeometryType eGeometryType = wkbUnknown;
968
0
    OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
969
0
    iOffsetInOut += 5;
970
0
    const auto eFlatType = wkbFlatten(eGeometryType);
971
0
    const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
972
0
                     (OGR_GT_HasM(eGeometryType) ? 1 : 0);
973
974
0
    if (eFlatType == wkbPolygon)
975
0
    {
976
0
        const uint32_t nRings =
977
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
978
0
        if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
979
0
        {
980
0
            return false;
981
0
        }
982
0
        for (uint32_t iRing = 0; iRing < nRings; ++iRing)
983
0
        {
984
0
            if (iOffsetInOut + sizeof(uint32_t) > size)
985
0
                return false;
986
0
            const uint32_t nPoints =
987
0
                OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
988
0
            const size_t sizeOfPoint = nDim * sizeof(double);
989
0
            if (nPoints > (size - iOffsetInOut) / sizeOfPoint)
990
0
            {
991
0
                return false;
992
0
            }
993
994
0
            if (nPoints >= 4)
995
0
            {
996
0
                const bool bIsClockwiseRing = OGRWKBIsClockwiseRing(
997
0
                    data + iOffsetInOut, nPoints, nDim, OGR_SWAP(eByteOrder));
998
0
                if ((bIsClockwiseRing && iRing == 0) ||
999
0
                    (!bIsClockwiseRing && iRing > 0))
1000
0
                {
1001
0
                    GByte abyTmp[4 * sizeof(double)];
1002
0
                    for (uint32_t i = 0; i < nPoints / 2; ++i)
1003
0
                    {
1004
0
                        GByte *pBegin = data + iOffsetInOut + i * sizeOfPoint;
1005
0
                        GByte *pEnd = data + iOffsetInOut +
1006
0
                                      (nPoints - 1 - i) * sizeOfPoint;
1007
0
                        memcpy(abyTmp, pBegin, sizeOfPoint);
1008
0
                        memcpy(pBegin, pEnd, sizeOfPoint);
1009
0
                        memcpy(pEnd, abyTmp, sizeOfPoint);
1010
0
                    }
1011
0
                }
1012
0
            }
1013
1014
0
            iOffsetInOut += nPoints * sizeOfPoint;
1015
0
        }
1016
0
    }
1017
1018
0
    if (eFlatType == wkbGeometryCollection || eFlatType == wkbMultiPolygon ||
1019
0
        eFlatType == wkbMultiSurface)
1020
0
    {
1021
0
        if (nRec == 128)
1022
0
        {
1023
0
            return false;
1024
0
        }
1025
0
        const uint32_t nParts =
1026
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
1027
0
        if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
1028
0
        {
1029
0
            return false;
1030
0
        }
1031
0
        for (uint32_t k = 0; k < nParts; k++)
1032
0
        {
1033
0
            if (!OGRWKBFixupCounterClockWiseExternalRingInternal(
1034
0
                    data, size, iOffsetInOut, nRec))
1035
0
            {
1036
0
                return false;
1037
0
            }
1038
0
        }
1039
0
    }
1040
1041
0
    return true;
1042
0
}
1043
1044
/** Modifies the geometry such that exterior rings of polygons are
1045
 * counter-clockwise oriented and inner rings clockwise oriented.
1046
 */
1047
void OGRWKBFixupCounterClockWiseExternalRing(GByte *pabyWkb, size_t nWKBSize)
1048
0
{
1049
0
    size_t iOffsetInOut = 0;
1050
0
    OGRWKBFixupCounterClockWiseExternalRingInternal(
1051
0
        pabyWkb, nWKBSize, iOffsetInOut, /* nRec = */ 0);
1052
0
}
1053
1054
/************************************************************************/
1055
/*                        OGRWKBPointUpdater()                          */
1056
/************************************************************************/
1057
1058
0
OGRWKBPointUpdater::OGRWKBPointUpdater() = default;
1059
1060
0
OGRWKBPointUpdater::~OGRWKBPointUpdater() = default;
1061
1062
/************************************************************************/
1063
/*              OGRWKBIntersectsPointSequencePessimistic()              */
1064
/************************************************************************/
1065
1066
static bool OGRWKBUpdatePointsSequence(uint8_t *data, const size_t size,
1067
                                       OGRWKBPointUpdater &oUpdater,
1068
                                       const OGRwkbByteOrder eByteOrder,
1069
                                       const int nDim, const bool bHasZ,
1070
                                       const bool bHasM, size_t &iOffsetInOut)
1071
0
{
1072
0
    const uint32_t nPoints =
1073
0
        OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
1074
0
    if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
1075
0
    {
1076
0
        return false;
1077
0
    }
1078
0
    const bool bNeedSwap = OGR_SWAP(eByteOrder);
1079
0
    for (uint32_t j = 0; j < nPoints; j++)
1080
0
    {
1081
0
        void *pdfX = data + iOffsetInOut;
1082
0
        void *pdfY = data + iOffsetInOut + sizeof(double);
1083
0
        void *pdfZ = bHasZ ? data + iOffsetInOut + 2 * sizeof(double) : nullptr;
1084
0
        void *pdfM =
1085
0
            bHasM ? data + iOffsetInOut + (bHasZ ? 3 : 2) * sizeof(double)
1086
0
                  : nullptr;
1087
0
        if (!oUpdater.update(bNeedSwap, pdfX, pdfY, pdfZ, pdfM))
1088
0
            return false;
1089
1090
0
        iOffsetInOut += nDim * sizeof(double);
1091
0
    }
1092
1093
0
    return true;
1094
0
}
1095
1096
/************************************************************************/
1097
/*                        OGRWKBVisitRingSequence()                     */
1098
/************************************************************************/
1099
1100
static bool OGRWKBVisitRingSequence(uint8_t *data, const size_t size,
1101
                                    OGRWKBPointUpdater &oUpdater,
1102
                                    const OGRwkbByteOrder eByteOrder,
1103
                                    const int nDim, const bool bHasZ,
1104
                                    const bool bHasM, size_t &iOffsetInOut)
1105
0
{
1106
0
    const uint32_t nRings =
1107
0
        OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
1108
0
    if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
1109
0
    {
1110
0
        return false;
1111
0
    }
1112
1113
0
    for (uint32_t i = 0; i < nRings; ++i)
1114
0
    {
1115
0
        if (iOffsetInOut + sizeof(uint32_t) > size)
1116
0
        {
1117
0
            return false;
1118
0
        }
1119
0
        if (!OGRWKBUpdatePointsSequence(data, size, oUpdater, eByteOrder, nDim,
1120
0
                                        bHasZ, bHasM, iOffsetInOut))
1121
0
        {
1122
0
            return false;
1123
0
        }
1124
0
    }
1125
0
    return true;
1126
0
}
1127
1128
/************************************************************************/
1129
/*                      OGRWKBUpdatePoints()                             */
1130
/************************************************************************/
1131
1132
static bool OGRWKBUpdatePoints(uint8_t *data, const size_t size,
1133
                               OGRWKBPointUpdater &oUpdater,
1134
                               size_t &iOffsetInOut, const int nRec)
1135
0
{
1136
0
    if (size - iOffsetInOut < MIN_WKB_SIZE)
1137
0
    {
1138
0
        return false;
1139
0
    }
1140
0
    const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
1141
0
    if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
1142
0
    {
1143
0
        return false;
1144
0
    }
1145
0
    const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
1146
1147
0
    OGRwkbGeometryType eGeometryType = wkbUnknown;
1148
0
    OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
1149
0
    iOffsetInOut += 5;
1150
0
    const auto eFlatType = wkbFlatten(eGeometryType);
1151
1152
0
    if (eFlatType == wkbGeometryCollection || eFlatType == wkbCompoundCurve ||
1153
0
        eFlatType == wkbCurvePolygon || eFlatType == wkbMultiPoint ||
1154
0
        eFlatType == wkbMultiLineString || eFlatType == wkbMultiPolygon ||
1155
0
        eFlatType == wkbMultiCurve || eFlatType == wkbMultiSurface ||
1156
0
        eFlatType == wkbPolyhedralSurface || eFlatType == wkbTIN)
1157
0
    {
1158
0
        if (nRec == 128)
1159
0
            return false;
1160
1161
0
        const uint32_t nParts =
1162
0
            OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
1163
0
        if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
1164
0
        {
1165
0
            return false;
1166
0
        }
1167
0
        for (uint32_t k = 0; k < nParts; k++)
1168
0
        {
1169
0
            if (!OGRWKBUpdatePoints(data, size, oUpdater, iOffsetInOut,
1170
0
                                    nRec + 1))
1171
0
                return false;
1172
0
        }
1173
0
        return true;
1174
0
    }
1175
1176
0
    const bool bHasZ = OGR_GT_HasZ(eGeometryType);
1177
0
    const bool bHasM = OGR_GT_HasM(eGeometryType);
1178
0
    const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
1179
1180
0
    if (eFlatType == wkbPoint)
1181
0
    {
1182
0
        if (size - iOffsetInOut < nDim * sizeof(double))
1183
0
            return false;
1184
0
        void *pdfX = data + iOffsetInOut;
1185
0
        void *pdfY = data + iOffsetInOut + sizeof(double);
1186
0
        void *pdfZ = bHasZ ? data + iOffsetInOut + 2 * sizeof(double) : nullptr;
1187
0
        void *pdfM =
1188
0
            bHasM ? data + iOffsetInOut + (bHasZ ? 3 : 2) * sizeof(double)
1189
0
                  : nullptr;
1190
0
        const bool bNeedSwap = OGR_SWAP(eByteOrder);
1191
0
        if (!oUpdater.update(bNeedSwap, pdfX, pdfY, pdfZ, pdfM))
1192
0
            return false;
1193
0
        iOffsetInOut += nDim * sizeof(double);
1194
0
        return true;
1195
0
    }
1196
1197
0
    if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
1198
0
    {
1199
0
        return OGRWKBUpdatePointsSequence(data, size, oUpdater, eByteOrder,
1200
0
                                          nDim, bHasZ, bHasM, iOffsetInOut);
1201
0
    }
1202
1203
0
    if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
1204
0
    {
1205
0
        return OGRWKBVisitRingSequence(data, size, oUpdater, eByteOrder, nDim,
1206
0
                                       bHasZ, bHasM, iOffsetInOut);
1207
0
    }
1208
1209
0
    CPLDebug("OGR", "Unknown WKB geometry type");
1210
0
    return false;
1211
0
}
1212
1213
/** Visit all points of a WKB geometry to update them.
1214
 */
1215
bool OGRWKBUpdatePoints(GByte *pabyWkb, size_t nWKBSize,
1216
                        OGRWKBPointUpdater &oUpdater)
1217
0
{
1218
0
    size_t iOffsetInOut = 0;
1219
0
    return OGRWKBUpdatePoints(pabyWkb, nWKBSize, oUpdater, iOffsetInOut,
1220
0
                              /* nRec = */ 0);
1221
0
}
1222
1223
/************************************************************************/
1224
/*                    OGRWKBTransformCache::clear()                     */
1225
/************************************************************************/
1226
1227
#ifdef OGR_WKB_TRANSFORM_ALL_AT_ONCE
1228
void OGRWKBTransformCache::clear()
1229
{
1230
    abNeedSwap.clear();
1231
    abIsEmpty.clear();
1232
    apdfX.clear();
1233
    apdfY.clear();
1234
    apdfZ.clear();
1235
    apdfM.clear();
1236
    adfX.clear();
1237
    adfY.clear();
1238
    adfZ.clear();
1239
    adfM.clear();
1240
    anErrorCodes.clear();
1241
}
1242
#endif
1243
1244
/************************************************************************/
1245
/*                         OGRWKBTransform()                            */
1246
/************************************************************************/
1247
1248
/** Visit all points of a WKB geometry to transform them.
1249
 */
1250
bool OGRWKBTransform(GByte *pabyWkb, size_t nWKBSize,
1251
                     OGRCoordinateTransformation *poCT,
1252
                     [[maybe_unused]] OGRWKBTransformCache &oCache,
1253
                     OGREnvelope3D &sEnvelope)
1254
0
{
1255
0
#ifndef OGR_WKB_TRANSFORM_ALL_AT_ONCE
1256
0
    struct OGRWKBPointUpdaterReproj final : public OGRWKBPointUpdater
1257
0
    {
1258
0
        OGRCoordinateTransformation *m_poCT;
1259
0
        OGREnvelope3D &m_sEnvelope;
1260
1261
0
        explicit OGRWKBPointUpdaterReproj(OGRCoordinateTransformation *poCTIn,
1262
0
                                          OGREnvelope3D &sEnvelopeIn)
1263
0
            : m_poCT(poCTIn), m_sEnvelope(sEnvelopeIn)
1264
0
        {
1265
0
        }
1266
1267
0
        bool update(bool bNeedSwap, void *x, void *y, void *z,
1268
0
                    void * /* m */) override
1269
0
        {
1270
0
            double dfX, dfY, dfZ;
1271
0
            memcpy(&dfX, x, sizeof(double));
1272
0
            memcpy(&dfY, y, sizeof(double));
1273
0
            if (bNeedSwap)
1274
0
            {
1275
0
                CPL_SWAP64PTR(&dfX);
1276
0
                CPL_SWAP64PTR(&dfY);
1277
0
            }
1278
0
            if (!(std::isnan(dfX) && std::isnan(dfY)))
1279
0
            {
1280
0
                if (z)
1281
0
                {
1282
0
                    memcpy(&dfZ, z, sizeof(double));
1283
0
                    if (bNeedSwap)
1284
0
                    {
1285
0
                        CPL_SWAP64PTR(&dfZ);
1286
0
                    }
1287
0
                }
1288
0
                else
1289
0
                    dfZ = 0;
1290
0
                int nErrorCode = 0;
1291
0
                m_poCT->TransformWithErrorCodes(1, &dfX, &dfY, &dfZ, nullptr,
1292
0
                                                &nErrorCode);
1293
0
                if (nErrorCode)
1294
0
                    return false;
1295
0
                m_sEnvelope.Merge(dfX, dfY, dfZ);
1296
0
                if (bNeedSwap)
1297
0
                {
1298
0
                    CPL_SWAP64PTR(&dfX);
1299
0
                    CPL_SWAP64PTR(&dfY);
1300
0
                    CPL_SWAP64PTR(&dfZ);
1301
0
                }
1302
0
                memcpy(x, &dfX, sizeof(double));
1303
0
                memcpy(y, &dfY, sizeof(double));
1304
0
                if (z)
1305
0
                    memcpy(z, &dfZ, sizeof(double));
1306
0
            }
1307
0
            return true;
1308
0
        }
1309
1310
0
      private:
1311
0
        OGRWKBPointUpdaterReproj(const OGRWKBPointUpdaterReproj &) = delete;
1312
0
        OGRWKBPointUpdaterReproj &
1313
0
        operator=(const OGRWKBPointUpdaterReproj &) = delete;
1314
0
    };
1315
1316
0
    sEnvelope = OGREnvelope3D();
1317
0
    OGRWKBPointUpdaterReproj oUpdater(poCT, sEnvelope);
1318
0
    return OGRWKBUpdatePoints(pabyWkb, nWKBSize, oUpdater);
1319
1320
#else
1321
    struct OGRWKBPointUpdaterReproj final : public OGRWKBPointUpdater
1322
    {
1323
        OGRWKBTransformCache &m_oCache;
1324
1325
        explicit OGRWKBPointUpdaterReproj(OGRWKBTransformCache &oCacheIn)
1326
            : m_oCache(oCacheIn)
1327
        {
1328
        }
1329
1330
        bool update(bool bNeedSwap, void *x, void *y, void *z,
1331
                    void * /* m */) override
1332
        {
1333
            m_oCache.abNeedSwap.push_back(bNeedSwap);
1334
            m_oCache.apdfX.push_back(x);
1335
            m_oCache.apdfY.push_back(y);
1336
            m_oCache.apdfZ.push_back(z);
1337
            return true;
1338
        }
1339
    };
1340
1341
    oCache.clear();
1342
    OGRWKBPointUpdaterReproj oUpdater(oCache);
1343
    if (!OGRWKBUpdatePoints(pabyWkb, nWKBSize, oUpdater))
1344
        return false;
1345
1346
    oCache.adfX.resize(oCache.apdfX.size());
1347
    oCache.adfY.resize(oCache.apdfX.size());
1348
    oCache.adfZ.resize(oCache.apdfX.size());
1349
1350
    for (size_t i = 0; i < oCache.apdfX.size(); ++i)
1351
    {
1352
        memcpy(&oCache.adfX[i], oCache.apdfX[i], sizeof(double));
1353
        memcpy(&oCache.adfY[i], oCache.apdfY[i], sizeof(double));
1354
        if (oCache.apdfZ[i])
1355
            memcpy(&oCache.adfZ[i], oCache.apdfZ[i], sizeof(double));
1356
        if (oCache.abNeedSwap[i])
1357
        {
1358
            CPL_SWAP64PTR(&oCache.adfX[i]);
1359
            CPL_SWAP64PTR(&oCache.adfY[i]);
1360
            CPL_SWAP64PTR(&oCache.adfZ[i]);
1361
        }
1362
        oCache.abIsEmpty.push_back(std::isnan(oCache.adfX[i]) &&
1363
                                   std::isnan(oCache.adfY[i]));
1364
    }
1365
1366
    oCache.anErrorCodes.resize(oCache.apdfX.size());
1367
    poCT->TransformWithErrorCodes(static_cast<int>(oCache.apdfX.size()),
1368
                                  oCache.adfX.data(), oCache.adfY.data(),
1369
                                  oCache.adfZ.data(), nullptr,
1370
                                  oCache.anErrorCodes.data());
1371
1372
    for (size_t i = 0; i < oCache.apdfX.size(); ++i)
1373
    {
1374
        if (!oCache.abIsEmpty[i] && oCache.anErrorCodes[i])
1375
            return false;
1376
    }
1377
1378
    sEnvelope = OGREnvelope3D();
1379
    for (size_t i = 0; i < oCache.apdfX.size(); ++i)
1380
    {
1381
        if (oCache.abIsEmpty[i])
1382
        {
1383
            oCache.adfX[i] = std::numeric_limits<double>::quiet_NaN();
1384
            oCache.adfY[i] = std::numeric_limits<double>::quiet_NaN();
1385
            oCache.adfZ[i] = std::numeric_limits<double>::quiet_NaN();
1386
        }
1387
        else
1388
        {
1389
            sEnvelope.Merge(oCache.adfX[i], oCache.adfY[i], oCache.adfZ[i]);
1390
        }
1391
        if (oCache.abNeedSwap[i])
1392
        {
1393
            CPL_SWAP64PTR(&oCache.adfX[i]);
1394
            CPL_SWAP64PTR(&oCache.adfY[i]);
1395
            CPL_SWAP64PTR(&oCache.adfZ[i]);
1396
        }
1397
        memcpy(oCache.apdfX[i], &oCache.adfX[i], sizeof(double));
1398
        memcpy(oCache.apdfY[i], &oCache.adfY[i], sizeof(double));
1399
        if (oCache.apdfZ[i])
1400
            memcpy(oCache.apdfZ[i], &oCache.adfZ[i], sizeof(double));
1401
    }
1402
1403
    return true;
1404
#endif
1405
0
}
1406
1407
/************************************************************************/
1408
/*                         OGRAppendBuffer()                            */
1409
/************************************************************************/
1410
1411
0
OGRAppendBuffer::OGRAppendBuffer() = default;
1412
1413
/************************************************************************/
1414
/*                        ~OGRAppendBuffer()                            */
1415
/************************************************************************/
1416
1417
0
OGRAppendBuffer::~OGRAppendBuffer() = default;
1418
1419
/************************************************************************/
1420
/*                       OGRWKTToWKBTranslator()                        */
1421
/************************************************************************/
1422
1423
OGRWKTToWKBTranslator::OGRWKTToWKBTranslator(OGRAppendBuffer &oAppendBuffer)
1424
0
    : m_oAppendBuffer(oAppendBuffer)
1425
0
{
1426
#ifndef USE_FAST_FLOAT
1427
    // Test if current locale decimal separator is decimal point
1428
    char szTest[10];
1429
    snprintf(szTest, sizeof(szTest), "%f", 1.5);
1430
    m_bCanUseStrtod = strchr(szTest, '.') != nullptr;
1431
#endif
1432
0
    CPL_IGNORE_RET_VAL(m_bCanUseStrtod);
1433
0
}
1434
1435
/************************************************************************/
1436
/*                          TranslateWKT()                              */
1437
/************************************************************************/
1438
1439
size_t OGRWKTToWKBTranslator::TranslateWKT(void *pabyWKTStart, size_t nLength,
1440
                                           bool bCanAlterByteAfter)
1441
0
{
1442
0
    const char *pszPtrStart = static_cast<const char *>(pabyWKTStart);
1443
    // Optimize single-part single-ring multipolygon WKT->WKB translation
1444
0
    if (bCanAlterByteAfter && nLength > strlen("MULTIPOLYGON") &&
1445
0
        EQUALN(pszPtrStart, "MULTIPOLYGON", strlen("MULTIPOLYGON")))
1446
0
    {
1447
0
        int nCountOpenPar = 0;
1448
0
        size_t nCountComma = 0;
1449
0
        bool bHasZ = false;
1450
0
        bool bHasM = false;
1451
1452
0
        char *pszEnd = static_cast<char *>(pabyWKTStart) + nLength;
1453
0
        const char chBackup = *pszEnd;
1454
0
        *pszEnd = 0;
1455
1456
        // Checks that the multipolygon consists of a single part with
1457
        // only an exterior ring.
1458
0
        for (const char *pszPtr = pszPtrStart + strlen("MULTIPOLYGON"); *pszPtr;
1459
0
             ++pszPtr)
1460
0
        {
1461
0
            const char ch = *pszPtr;
1462
0
            if (ch == 'Z')
1463
0
                bHasZ = true;
1464
0
            else if (ch == 'M')
1465
0
                bHasM = true;
1466
0
            if (ch == '(')
1467
0
            {
1468
0
                nCountOpenPar++;
1469
0
                if (nCountOpenPar == 4)
1470
0
                    break;
1471
0
            }
1472
0
            else if (ch == ')')
1473
0
            {
1474
0
                nCountOpenPar--;
1475
0
                if (nCountOpenPar < 0)
1476
0
                    break;
1477
0
            }
1478
0
            else if (ch == ',')
1479
0
            {
1480
0
                if (nCountOpenPar < 3)
1481
0
                {
1482
                    // multipart / multi-ring
1483
0
                    break;
1484
0
                }
1485
0
                nCountComma++;
1486
0
            }
1487
0
        }
1488
0
        const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
1489
0
        if (nCountOpenPar == 0 && nCountComma > 0 &&
1490
0
            nCountComma < std::numeric_limits<uint32_t>::max())
1491
0
        {
1492
0
            const uint32_t nVerticesCount =
1493
0
                static_cast<uint32_t>(nCountComma + 1);
1494
0
            const size_t nWKBSize =
1495
0
                sizeof(GByte) +     // Endianness
1496
0
                sizeof(uint32_t) +  // multipolygon WKB geometry type
1497
0
                sizeof(uint32_t) +  // number of parts
1498
0
                sizeof(GByte) +     // Endianness
1499
0
                sizeof(uint32_t) +  // polygon WKB geometry type
1500
0
                sizeof(uint32_t) +  // number of rings
1501
0
                sizeof(uint32_t) +  // number of vertices
1502
0
                nDim * sizeof(double) * nVerticesCount;
1503
0
            GByte *const pabyCurStart = static_cast<GByte *>(
1504
0
                m_oAppendBuffer.GetPtrForNewBytes(nWKBSize));
1505
0
            if (!pabyCurStart)
1506
0
            {
1507
0
                return static_cast<size_t>(-1);
1508
0
            }
1509
0
            GByte *pabyCur = pabyCurStart;
1510
            // Multipolygon byte order
1511
0
            {
1512
0
                *pabyCur = wkbNDR;
1513
0
                pabyCur++;
1514
0
            }
1515
            // Multipolygon geometry type
1516
0
            {
1517
0
                uint32_t nWKBGeomType =
1518
0
                    wkbMultiPolygon + (bHasZ ? 1000 : 0) + (bHasM ? 2000 : 0);
1519
0
                CPL_LSBPTR32(&nWKBGeomType);
1520
0
                memcpy(pabyCur, &nWKBGeomType, sizeof(uint32_t));
1521
0
                pabyCur += sizeof(uint32_t);
1522
0
            }
1523
            // Number of parts
1524
0
            {
1525
0
                uint32_t nOne = 1;
1526
0
                CPL_LSBPTR32(&nOne);
1527
0
                memcpy(pabyCur, &nOne, sizeof(uint32_t));
1528
0
                pabyCur += sizeof(uint32_t);
1529
0
            }
1530
            // Polygon byte order
1531
0
            {
1532
0
                *pabyCur = wkbNDR;
1533
0
                pabyCur++;
1534
0
            }
1535
            // Polygon geometry type
1536
0
            {
1537
0
                uint32_t nWKBGeomType =
1538
0
                    wkbPolygon + (bHasZ ? 1000 : 0) + (bHasM ? 2000 : 0);
1539
0
                CPL_LSBPTR32(&nWKBGeomType);
1540
0
                memcpy(pabyCur, &nWKBGeomType, sizeof(uint32_t));
1541
0
                pabyCur += sizeof(uint32_t);
1542
0
            }
1543
            // Number of rings
1544
0
            {
1545
0
                uint32_t nOne = 1;
1546
0
                CPL_LSBPTR32(&nOne);
1547
0
                memcpy(pabyCur, &nOne, sizeof(uint32_t));
1548
0
                pabyCur += sizeof(uint32_t);
1549
0
            }
1550
            // Number of vertices
1551
0
            {
1552
0
                uint32_t nVerticesCountToWrite = nVerticesCount;
1553
0
                CPL_LSBPTR32(&nVerticesCountToWrite);
1554
0
                memcpy(pabyCur, &nVerticesCountToWrite, sizeof(uint32_t));
1555
0
                pabyCur += sizeof(uint32_t);
1556
0
            }
1557
0
            uint32_t nDoubleCount = 0;
1558
0
            const uint32_t nExpectedDoubleCount = nVerticesCount * nDim;
1559
0
            for (const char *pszPtr = pszPtrStart + strlen("MULTIPOLYGON");
1560
0
                 *pszPtr;
1561
0
                 /* nothing */)
1562
0
            {
1563
0
                const char ch = *pszPtr;
1564
0
                if (ch == '-' || ch == '.' || (ch >= '0' && ch <= '9'))
1565
0
                {
1566
0
                    nDoubleCount++;
1567
0
                    if (nDoubleCount > nExpectedDoubleCount)
1568
0
                    {
1569
0
                        break;
1570
0
                    }
1571
0
#ifdef USE_FAST_FLOAT
1572
0
                    double dfVal;
1573
0
                    auto answer = fast_float::from_chars(pszPtr, pszEnd, dfVal);
1574
0
                    if (answer.ec != std::errc())
1575
0
                    {
1576
0
                        nDoubleCount = 0;
1577
0
                        break;
1578
0
                    }
1579
0
                    pszPtr = answer.ptr;
1580
#else
1581
                    char *endptr = nullptr;
1582
                    const double dfVal =
1583
                        m_bCanUseStrtod ? strtod(pszPtr, &endptr)
1584
                                        : CPLStrtodDelim(pszPtr, &endptr, '.');
1585
                    pszPtr = endptr;
1586
#endif
1587
0
                    CPL_LSBPTR64(&dfVal);
1588
0
                    memcpy(pabyCur, &dfVal, sizeof(double));
1589
0
                    pabyCur += sizeof(double);
1590
0
                }
1591
0
                else
1592
0
                {
1593
0
                    ++pszPtr;
1594
0
                }
1595
0
            }
1596
0
            if (nDoubleCount == nExpectedDoubleCount)
1597
0
            {
1598
0
                CPLAssert(static_cast<size_t>(pabyCur - pabyCurStart) ==
1599
0
                          nWKBSize);
1600
                // cppcheck-suppress selfAssignment
1601
0
                *pszEnd = chBackup;
1602
0
                return nWKBSize;
1603
0
            }
1604
0
            else
1605
0
            {
1606
0
                CPLError(CE_Failure, CPLE_AppDefined,
1607
0
                         "Invalid WKT geometry: %s", pszPtrStart);
1608
                // cppcheck-suppress selfAssignment
1609
0
                *pszEnd = chBackup;
1610
0
                return static_cast<size_t>(-1);
1611
0
            }
1612
0
        }
1613
        // cppcheck-suppress selfAssignment
1614
0
        *pszEnd = chBackup;
1615
0
    }
1616
1617
    // General case going through a OGRGeometry
1618
0
    std::unique_ptr<OGRGeometry> poGeometry = nullptr;
1619
0
    if (bCanAlterByteAfter)
1620
0
    {
1621
        // Slight optimization for all geometries but the final one, to
1622
        // avoid creating a new string each time.
1623
        // We set the ending byte to '\0' and restore it back after parsing
1624
        // the WKT
1625
0
        char *pszEnd = static_cast<char *>(pabyWKTStart) + nLength;
1626
0
        const char chBackup = *pszEnd;
1627
0
        *pszEnd = 0;
1628
0
        poGeometry = OGRGeometryFactory::createFromWkt(pszPtrStart).first;
1629
        // cppcheck-suppress selfAssignment
1630
0
        *pszEnd = chBackup;
1631
0
    }
1632
0
    else
1633
0
    {
1634
0
        std::string osTmp;
1635
0
        osTmp.assign(pszPtrStart, nLength);
1636
0
        poGeometry = OGRGeometryFactory::createFromWkt(osTmp.c_str()).first;
1637
0
    }
1638
0
    if (!poGeometry)
1639
0
    {
1640
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid WKT geometry");
1641
0
        return static_cast<size_t>(-1);
1642
0
    }
1643
0
    const size_t nWKBSize = poGeometry->WkbSize();
1644
0
    GByte *pabyWKB =
1645
0
        static_cast<GByte *>(m_oAppendBuffer.GetPtrForNewBytes(nWKBSize));
1646
0
    if (!pabyWKB)
1647
0
    {
1648
0
        return static_cast<size_t>(-1);
1649
0
    }
1650
0
    poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso);
1651
0
    return nWKBSize;
1652
0
}