LCOV - code coverage report
Current view: top level - ogr - ogr_wkb.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 632 715 88.4 %
Date: 2025-12-02 17:42:13 Functions: 36 36 100.0 %

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

Generated by: LCOV version 1.14