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

Generated by: LCOV version 1.14