LCOV - code coverage report
Current view: top level - ogr - ogr_wkb.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 632 715 88.4 %
Date: 2026-05-07 23:23:29 Functions: 36 36 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OGR
       4             :  * Purpose:  WKB geometry related methods
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_error.h"
      14             : #include "ogr_wkb.h"
      15             : #include "ogr_core.h"
      16             : #include "ogr_geometry.h"
      17             : #include "ogr_p.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <cmath>
      21             : #include <climits>
      22             : #include <limits>
      23             : 
      24             : #include <algorithm>
      25             : #include <limits>
      26             : 
      27             : #define USE_FAST_FLOAT
      28             : #ifdef USE_FAST_FLOAT
      29             : #include "include_fast_float.h"
      30             : #endif
      31             : 
      32             : /************************************************************************/
      33             : /*                           OGRWKBNeedSwap()                           */
      34             : /************************************************************************/
      35             : 
      36         208 : static inline bool OGRWKBNeedSwap(GByte b)
      37             : {
      38             :     if constexpr (CPL_IS_LSB)
      39         208 :         return b != 1;
      40             :     else
      41             :         return b != 0;
      42             : }
      43             : 
      44             : /************************************************************************/
      45             : /*                          OGRWKBReadUInt32()                          */
      46             : /************************************************************************/
      47             : 
      48         231 : static inline uint32_t OGRWKBReadUInt32(const GByte *pabyWkb, bool bNeedSwap)
      49             : {
      50             :     uint32_t nVal;
      51         231 :     memcpy(&nVal, pabyWkb, sizeof(nVal));
      52         231 :     if (bNeedSwap)
      53           0 :         CPL_SWAP32PTR(&nVal);
      54         231 :     return nVal;
      55             : }
      56             : 
      57             : /************************************************************************/
      58             : /*                         OGRWKBReadFloat64()                          */
      59             : /************************************************************************/
      60             : 
      61         100 : static inline double OGRWKBReadFloat64(const GByte *pabyWkb, bool bNeedSwap)
      62             : {
      63             :     double dfVal;
      64         100 :     memcpy(&dfVal, pabyWkb, sizeof(dfVal));
      65         100 :     if (bNeedSwap)
      66           0 :         CPL_SWAP64PTR(&dfVal);
      67         100 :     return dfVal;
      68             : }
      69             : 
      70             : /************************************************************************/
      71             : /*                         OGRWKBRingGetArea()                          */
      72             : /************************************************************************/
      73             : 
      74          12 : static bool OGRWKBRingGetArea(const GByte *&pabyWkb, size_t &nWKBSize, int nDim,
      75             :                               bool bNeedSwap, double &dfArea)
      76             : {
      77          12 :     const uint32_t nPoints = OGRWKBReadUInt32(pabyWkb, bNeedSwap);
      78          12 :     if (nPoints >= 4 &&
      79          12 :         (nWKBSize - sizeof(uint32_t)) / (nDim * sizeof(double)) >= nPoints)
      80             :     {
      81          12 :         nWKBSize -= sizeof(uint32_t) + nDim * sizeof(double);
      82          12 :         pabyWkb += sizeof(uint32_t);
      83             :         // Computation according to Green's Theorem
      84             :         // Cf OGRSimpleCurve::get_LinearArea()
      85          12 :         double x_m1 = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
      86          12 :         double y_m1 = OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
      87          12 :         double y_m2 = y_m1;
      88          12 :         dfArea = 0;
      89          12 :         pabyWkb += nDim * sizeof(double);
      90          50 :         for (uint32_t i = 1; i < nPoints; ++i)
      91             :         {
      92          38 :             const double x = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
      93             :             const double y =
      94          38 :                 OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
      95          38 :             pabyWkb += nDim * sizeof(double);
      96          38 :             dfArea += x_m1 * (y - y_m2);
      97          38 :             y_m2 = y_m1;
      98          38 :             x_m1 = x;
      99          38 :             y_m1 = y;
     100             :         }
     101          12 :         dfArea += x_m1 * (y_m1 - y_m2);
     102          12 :         dfArea = 0.5 * std::fabs(dfArea);
     103          12 :         return true;
     104             :     }
     105           0 :     return false;
     106             : }
     107             : 
     108             : /************************************************************************/
     109             : /*                         OGRWKBGetGeomType()                          */
     110             : /************************************************************************/
     111             : 
     112         203 : bool OGRWKBGetGeomType(const GByte *pabyWkb, size_t nWKBSize, bool &bNeedSwap,
     113             :                        uint32_t &nType)
     114             : {
     115         203 :     if (nWKBSize >= 5)
     116             :     {
     117         203 :         bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
     118         203 :         nType = OGRWKBReadUInt32(pabyWkb + 1, bNeedSwap);
     119         203 :         return true;
     120             :     }
     121           0 :     return false;
     122             : }
     123             : 
     124             : /************************************************************************/
     125             : /*                        OGRWKBPolygonGetArea()                        */
     126             : /************************************************************************/
     127             : 
     128          11 : bool OGRWKBPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
     129             :                           double &dfArea)
     130             : {
     131             :     bool bNeedSwap;
     132             :     uint32_t nType;
     133          11 :     if (nWKBSize < 9 || !OGRWKBGetGeomType(pabyWkb, nWKBSize, bNeedSwap, nType))
     134           0 :         return false;
     135             : 
     136          11 :     int nDims = 2;
     137          11 :     if (nType == wkbPolygon)
     138             :     {
     139             :         // do nothing
     140             :     }
     141           6 :     else if (nType == wkbPolygon + 1000 ||  // wkbPolygonZ
     142           4 :              nType == wkbPolygon25D || nType == wkbPolygonM)
     143             :     {
     144           4 :         nDims = 3;
     145             :     }
     146           2 :     else if (nType == wkbPolygonZM)
     147             :     {
     148           2 :         nDims = 4;
     149             :     }
     150             :     else
     151             :     {
     152           0 :         return false;
     153             :     }
     154             : 
     155          11 :     const uint32_t nRings = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
     156          11 :     if ((nWKBSize - 9) / sizeof(uint32_t) >= nRings)
     157             :     {
     158          11 :         pabyWkb += 9;
     159          11 :         nWKBSize -= 9;
     160          11 :         dfArea = 0;
     161          11 :         if (nRings > 0)
     162             :         {
     163          11 :             if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap, dfArea))
     164           0 :                 return false;
     165          12 :             for (uint32_t i = 1; i < nRings; ++i)
     166             :             {
     167             :                 double dfRingArea;
     168           1 :                 if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap,
     169             :                                        dfRingArea))
     170           0 :                     return false;
     171           1 :                 dfArea -= dfRingArea;
     172             :             }
     173             :         }
     174          11 :         return true;
     175             :     }
     176           0 :     return false;
     177             : }
     178             : 
     179             : /************************************************************************/
     180             : /*                     OGRWKBMultiPolygonGetArea()                      */
     181             : /************************************************************************/
     182             : 
     183           5 : bool OGRWKBMultiPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
     184             :                                double &dfArea)
     185             : {
     186           5 :     if (nWKBSize < 9)
     187           0 :         return false;
     188             : 
     189           5 :     const bool bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
     190           5 :     const uint32_t nPolys = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
     191           5 :     if ((nWKBSize - 9) / 9 >= nPolys)
     192             :     {
     193           5 :         pabyWkb += 9;
     194           5 :         nWKBSize -= 9;
     195           5 :         dfArea = 0;
     196          11 :         for (uint32_t i = 0; i < nPolys; ++i)
     197             :         {
     198             :             double dfPolyArea;
     199           6 :             if (!OGRWKBPolygonGetArea(pabyWkb, nWKBSize, dfPolyArea))
     200           0 :                 return false;
     201           6 :             dfArea += dfPolyArea;
     202             :         }
     203           5 :         return true;
     204             :     }
     205           0 :     return false;
     206             : }
     207             : 
     208             : /************************************************************************/
     209             : /*                            WKBFromEWKB()                             */
     210             : /************************************************************************/
     211             : 
     212        1445 : const GByte *WKBFromEWKB(GByte *pabyEWKB, size_t nEWKBSize, size_t &nWKBSizeOut,
     213             :                          int *pnSRIDOut)
     214             : {
     215        1445 :     if (nEWKBSize < 5U)
     216             :     {
     217           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid EWKB content : %u bytes",
     218             :                  static_cast<unsigned>(nEWKBSize));
     219           0 :         return nullptr;
     220             :     }
     221             : 
     222        1445 :     const GByte *pabyWKB = pabyEWKB;
     223             : 
     224             :     /* -------------------------------------------------------------------- */
     225             :     /*      PostGIS EWKB format includes an SRID, but this won't be         */
     226             :     /*      understood by OGR, so if the SRID flag is set, we remove the    */
     227             :     /*      SRID (bytes at offset 5 to 8).                                  */
     228             :     /* -------------------------------------------------------------------- */
     229        1445 :     if (nEWKBSize > 9 &&
     230        1438 :         ((pabyEWKB[0] == 0 /* big endian */ && (pabyEWKB[1] & 0x20)) ||
     231        1438 :          (pabyEWKB[0] != 0 /* little endian */ && (pabyEWKB[4] & 0x20))))
     232             :     {
     233         526 :         if (pnSRIDOut)
     234             :         {
     235           0 :             memcpy(pnSRIDOut, pabyEWKB + 5, 4);
     236           0 :             const OGRwkbByteOrder eByteOrder =
     237           0 :                 (pabyEWKB[0] == 0 ? wkbXDR : wkbNDR);
     238           0 :             if (OGR_SWAP(eByteOrder))
     239           0 :                 *pnSRIDOut = CPL_SWAP32(*pnSRIDOut);
     240             :         }
     241             : 
     242             :         // Drop the SRID flag
     243         526 :         if (pabyEWKB[0] == 0)
     244           0 :             pabyEWKB[1] &= (~0x20);
     245             :         else
     246         526 :             pabyEWKB[4] &= (~0x20);
     247             : 
     248             :         // Move 5 first bytes of EWKB 4 bytes later to create regular WKB
     249         526 :         memmove(pabyEWKB + 4, pabyEWKB, 5);
     250         526 :         memset(pabyEWKB, 0, 4);
     251             :         // and make pabyWKB point to that
     252         526 :         pabyWKB += 4;
     253         526 :         nWKBSizeOut = nEWKBSize - 4;
     254             :     }
     255             :     else
     256             :     {
     257         919 :         if (pnSRIDOut)
     258             :         {
     259           0 :             *pnSRIDOut = INT_MIN;
     260             :         }
     261         919 :         nWKBSizeOut = nEWKBSize;
     262             :     }
     263             : 
     264        1445 :     return pabyWKB;
     265             : }
     266             : 
     267             : /************************************************************************/
     268             : /*                      OGRWKBReadUInt32AtOffset()                      */
     269             : /************************************************************************/
     270             : 
     271        8505 : static uint32_t OGRWKBReadUInt32AtOffset(const uint8_t *data,
     272             :                                          OGRwkbByteOrder eByteOrder,
     273             :                                          size_t &iOffset)
     274             : {
     275             :     uint32_t v;
     276        8505 :     memcpy(&v, data + iOffset, sizeof(v));
     277        8505 :     iOffset += sizeof(v);
     278        8505 :     if (OGR_SWAP(eByteOrder))
     279             :     {
     280        1666 :         CPL_SWAP32PTR(&v);
     281             :     }
     282        8505 :     return v;
     283             : }
     284             : 
     285             : /************************************************************************/
     286             : /*                        ReadWKBPointSequence()                        */
     287             : /************************************************************************/
     288             : 
     289             : template <bool INCLUDE_Z, typename EnvelopeType>
     290         852 : static bool ReadWKBPointSequence(const uint8_t *data, size_t size,
     291             :                                  OGRwkbByteOrder eByteOrder, int nDim,
     292             :                                  bool bHasZ, size_t &iOffset,
     293             :                                  EnvelopeType &sEnvelope)
     294             : {
     295             :     const uint32_t nPoints =
     296         852 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     297         852 :     if (nPoints > (size - iOffset) / (nDim * sizeof(double)))
     298           0 :         return false;
     299         852 :     double dfX = 0;
     300         852 :     double dfY = 0;
     301         852 :     [[maybe_unused]] double dfZ = 0;
     302       14540 :     for (uint32_t j = 0; j < nPoints; j++)
     303             :     {
     304       13688 :         memcpy(&dfX, data + iOffset, sizeof(double));
     305       13688 :         memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
     306             :         if constexpr (INCLUDE_Z)
     307             :         {
     308          52 :             if (bHasZ)
     309          34 :                 memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
     310             :                        sizeof(double));
     311             :         }
     312       13688 :         iOffset += nDim * sizeof(double);
     313       13688 :         if (OGR_SWAP(eByteOrder))
     314             :         {
     315         560 :             CPL_SWAP64PTR(&dfX);
     316         560 :             CPL_SWAP64PTR(&dfY);
     317             :             if constexpr (INCLUDE_Z)
     318             :             {
     319           0 :                 CPL_SWAP64PTR(&dfZ);
     320             :             }
     321             :         }
     322       13688 :         sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
     323       13688 :         sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
     324       13688 :         sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
     325       13688 :         sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
     326             :         if constexpr (INCLUDE_Z)
     327             :         {
     328          52 :             if (bHasZ)
     329             :             {
     330          34 :                 sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
     331          34 :                 sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
     332             :             }
     333             :         }
     334             :     }
     335         852 :     return true;
     336             : }
     337             : 
     338             : /************************************************************************/
     339             : /*                        ReadWKBRingSequence()                         */
     340             : /************************************************************************/
     341             : 
     342             : template <bool INCLUDE_Z, typename EnvelopeType>
     343         600 : static bool ReadWKBRingSequence(const uint8_t *data, size_t size,
     344             :                                 OGRwkbByteOrder eByteOrder, int nDim,
     345             :                                 bool bHasZ, size_t &iOffset,
     346             :                                 EnvelopeType &sEnvelope)
     347             : {
     348         600 :     const uint32_t nRings = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     349         600 :     if (nRings > (size - iOffset) / sizeof(uint32_t))
     350           0 :         return false;
     351        1258 :     for (uint32_t i = 0; i < nRings; i++)
     352             :     {
     353         658 :         if (iOffset + sizeof(uint32_t) > size)
     354           0 :             return false;
     355         658 :         if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     356             :                                              bHasZ, iOffset, sEnvelope))
     357           0 :             return false;
     358             :     }
     359         600 :     return true;
     360             : }
     361             : 
     362             : /************************************************************************/
     363             : /*                        OGRWKBGetBoundingBox()                        */
     364             : /************************************************************************/
     365             : 
     366             : constexpr uint32_t WKB_PREFIX_SIZE = 1 + sizeof(uint32_t);
     367             : constexpr uint32_t MIN_WKB_SIZE = WKB_PREFIX_SIZE + sizeof(uint32_t);
     368             : 
     369             : template <bool INCLUDE_Z, typename EnvelopeType>
     370      990738 : static bool OGRWKBGetBoundingBox(const uint8_t *data, size_t size,
     371             :                                  size_t &iOffset, EnvelopeType &sEnvelope,
     372             :                                  int nRec)
     373             : {
     374      990738 :     if (size - iOffset < MIN_WKB_SIZE)
     375           0 :         return false;
     376      990738 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffset]);
     377      990738 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
     378           0 :         return false;
     379      990738 :     const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
     380             : 
     381      990738 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
     382      990738 :     OGRReadWKBGeometryType(data + iOffset, wkbVariantIso, &eGeometryType);
     383      990738 :     iOffset += 5;
     384      990738 :     const auto eFlatType = wkbFlatten(eGeometryType);
     385      990738 :     const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeometryType));
     386      990738 :     const int nDim = 2 + (bHasZ ? 1 : 0) + (OGR_GT_HasM(eGeometryType) ? 1 : 0);
     387             : 
     388      990738 :     if (eFlatType == wkbPoint)
     389             :     {
     390      990166 :         if (size - iOffset < nDim * sizeof(double))
     391           0 :             return false;
     392      990166 :         double dfX = 0;
     393      990166 :         double dfY = 0;
     394      990166 :         [[maybe_unused]] double dfZ = 0;
     395      990166 :         memcpy(&dfX, data + iOffset, sizeof(double));
     396      990166 :         memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
     397             :         if constexpr (INCLUDE_Z)
     398             :         {
     399          11 :             if (bHasZ)
     400           4 :                 memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
     401             :                        sizeof(double));
     402             :         }
     403      990166 :         iOffset += nDim * sizeof(double);
     404      990166 :         if (OGR_SWAP(eByteOrder))
     405             :         {
     406          40 :             CPL_SWAP64PTR(&dfX);
     407          40 :             CPL_SWAP64PTR(&dfY);
     408             :             if constexpr (INCLUDE_Z)
     409             :             {
     410           0 :                 CPL_SWAP64PTR(&dfZ);
     411             :             }
     412             :         }
     413      990166 :         if (std::isnan(dfX))
     414             :         {
     415             :             // Point empty
     416           6 :             sEnvelope = EnvelopeType();
     417             :         }
     418             :         else
     419             :         {
     420      990160 :             sEnvelope.MinX = dfX;
     421      990160 :             sEnvelope.MinY = dfY;
     422      990160 :             sEnvelope.MaxX = dfX;
     423      990160 :             sEnvelope.MaxY = dfY;
     424             :             if constexpr (INCLUDE_Z)
     425             :             {
     426          10 :                 if (bHasZ)
     427             :                 {
     428           4 :                     sEnvelope.MinZ = dfZ;
     429           4 :                     sEnvelope.MaxZ = dfZ;
     430             :                 }
     431             :             }
     432             :         }
     433      990166 :         return true;
     434             :     }
     435             : 
     436         572 :     if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
     437             :     {
     438         106 :         sEnvelope = EnvelopeType();
     439             : 
     440         106 :         return ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     441         106 :                                                bHasZ, iOffset, sEnvelope);
     442             :     }
     443             : 
     444         466 :     if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
     445             :     {
     446         190 :         sEnvelope = EnvelopeType();
     447             : 
     448         190 :         return ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     449         190 :                                               bHasZ, iOffset, sEnvelope);
     450             :     }
     451             : 
     452         276 :     if (eFlatType == wkbMultiPoint)
     453             :     {
     454          57 :         sEnvelope = EnvelopeType();
     455             : 
     456          57 :         uint32_t nParts = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     457          57 :         if (nParts >
     458          57 :             (size - iOffset) / (WKB_PREFIX_SIZE + nDim * sizeof(double)))
     459           0 :             return false;
     460          57 :         double dfX = 0;
     461          57 :         double dfY = 0;
     462          57 :         [[maybe_unused]] double dfZ = 0;
     463         170 :         for (uint32_t k = 0; k < nParts; k++)
     464             :         {
     465         113 :             iOffset += WKB_PREFIX_SIZE;
     466         113 :             memcpy(&dfX, data + iOffset, sizeof(double));
     467         113 :             memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
     468             :             if constexpr (INCLUDE_Z)
     469             :             {
     470           5 :                 if (bHasZ)
     471           3 :                     memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
     472             :                            sizeof(double));
     473             :             }
     474         113 :             iOffset += nDim * sizeof(double);
     475         113 :             if (OGR_SWAP(eByteOrder))
     476             :             {
     477          40 :                 CPL_SWAP64PTR(&dfX);
     478          40 :                 CPL_SWAP64PTR(&dfY);
     479             :                 if constexpr (INCLUDE_Z)
     480             :                 {
     481           0 :                     CPL_SWAP64PTR(&dfZ);
     482             :                 }
     483             :             }
     484         113 :             sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
     485         113 :             sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
     486         113 :             sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
     487         113 :             sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
     488             :             if constexpr (INCLUDE_Z)
     489             :             {
     490           5 :                 if (bHasZ)
     491             :                 {
     492           3 :                     sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
     493           3 :                     sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
     494             :                 }
     495             :             }
     496             :         }
     497          57 :         return true;
     498             :     }
     499             : 
     500         219 :     if (eFlatType == wkbMultiLineString)
     501             :     {
     502          59 :         sEnvelope = EnvelopeType();
     503             : 
     504             :         const uint32_t nParts =
     505          59 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     506          59 :         if (nParts > (size - iOffset) / MIN_WKB_SIZE)
     507           0 :             return false;
     508         147 :         for (uint32_t k = 0; k < nParts; k++)
     509             :         {
     510          88 :             if (iOffset + MIN_WKB_SIZE > size)
     511           0 :                 return false;
     512          88 :             iOffset += WKB_PREFIX_SIZE;
     513          88 :             if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     514             :                                                  bHasZ, iOffset, sEnvelope))
     515           0 :                 return false;
     516             :         }
     517          59 :         return true;
     518             :     }
     519             : 
     520         160 :     if (eFlatType == wkbMultiPolygon)
     521             :     {
     522          82 :         sEnvelope = EnvelopeType();
     523             : 
     524             :         const uint32_t nParts =
     525          82 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     526          82 :         if (nParts > (size - iOffset) / MIN_WKB_SIZE)
     527           0 :             return false;
     528         492 :         for (uint32_t k = 0; k < nParts; k++)
     529             :         {
     530         410 :             if (iOffset + MIN_WKB_SIZE > size)
     531           0 :                 return false;
     532         410 :             CPLAssert(data[iOffset] == eByteOrder);
     533         410 :             iOffset += WKB_PREFIX_SIZE;
     534         410 :             if (!ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     535             :                                                 bHasZ, iOffset, sEnvelope))
     536           0 :                 return false;
     537             :         }
     538          82 :         return true;
     539             :     }
     540             : 
     541          78 :     if (eFlatType == wkbGeometryCollection || eFlatType == wkbCompoundCurve ||
     542           4 :         eFlatType == wkbCurvePolygon || eFlatType == wkbMultiCurve ||
     543           2 :         eFlatType == wkbMultiSurface || eFlatType == wkbPolyhedralSurface ||
     544             :         eFlatType == wkbTIN)
     545             :     {
     546          78 :         if (nRec == 128)
     547           0 :             return false;
     548          78 :         sEnvelope = EnvelopeType();
     549             : 
     550             :         const uint32_t nParts =
     551          78 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     552          78 :         if (nParts > (size - iOffset) / MIN_WKB_SIZE)
     553           0 :             return false;
     554          78 :         EnvelopeType sEnvelopeSubGeom;
     555         176 :         for (uint32_t k = 0; k < nParts; k++)
     556             :         {
     557          98 :             if (!OGRWKBGetBoundingBox<INCLUDE_Z>(data, size, iOffset,
     558             :                                                  sEnvelopeSubGeom, nRec + 1))
     559           0 :                 return false;
     560          98 :             sEnvelope.Merge(sEnvelopeSubGeom);
     561             :         }
     562          78 :         return true;
     563             :     }
     564             : 
     565           0 :     return false;
     566             : }
     567             : 
     568             : /************************************************************************/
     569             : /*                        OGRWKBGetBoundingBox()                        */
     570             : /************************************************************************/
     571             : 
     572      990605 : bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
     573             :                           OGREnvelope &sEnvelope)
     574             : {
     575      990605 :     size_t iOffset = 0;
     576      990605 :     return OGRWKBGetBoundingBox<false>(pabyWkb, nWKBSize, iOffset, sEnvelope,
     577     1981210 :                                        0);
     578             : }
     579             : 
     580             : /************************************************************************/
     581             : /*                        OGRWKBGetBoundingBox()                        */
     582             : /************************************************************************/
     583             : 
     584          35 : bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
     585             :                           OGREnvelope3D &sEnvelope)
     586             : {
     587          35 :     size_t iOffset = 0;
     588          70 :     return OGRWKBGetBoundingBox<true>(pabyWkb, nWKBSize, iOffset, sEnvelope, 0);
     589             : }
     590             : 
     591             : /************************************************************************/
     592             : /*              OGRWKBIntersectsPointSequencePessimistic()              */
     593             : /************************************************************************/
     594             : 
     595         199 : static bool OGRWKBIntersectsPointSequencePessimistic(
     596             :     const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
     597             :     const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
     598             :     bool &bErrorOut)
     599             : {
     600             :     const uint32_t nPoints =
     601         199 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     602         199 :     if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
     603             :     {
     604          12 :         bErrorOut = true;
     605          12 :         return false;
     606             :     }
     607             : 
     608         187 :     double dfX = 0;
     609         187 :     double dfY = 0;
     610        1318 :     for (uint32_t j = 0; j < nPoints; j++)
     611             :     {
     612        1256 :         memcpy(&dfX, data + iOffsetInOut, sizeof(double));
     613        1256 :         memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
     614        1256 :         iOffsetInOut += nDim * sizeof(double);
     615        1256 :         if (OGR_SWAP(eByteOrder))
     616             :         {
     617           3 :             CPL_SWAP64PTR(&dfX);
     618           3 :             CPL_SWAP64PTR(&dfY);
     619             :         }
     620        1256 :         if (dfX >= sEnvelope.MinX && dfY >= sEnvelope.MinY &&
     621         775 :             dfX <= sEnvelope.MaxX && dfY <= sEnvelope.MaxY)
     622             :         {
     623         125 :             return true;
     624             :         }
     625             :     }
     626             : 
     627          62 :     return false;
     628             : }
     629             : 
     630             : /************************************************************************/
     631             : /*              OGRWKBIntersectsRingSequencePessimistic()               */
     632             : /************************************************************************/
     633             : 
     634         149 : static bool OGRWKBIntersectsRingSequencePessimistic(
     635             :     const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
     636             :     const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
     637             :     bool &bErrorOut)
     638             : {
     639             :     const uint32_t nRings =
     640         149 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     641         149 :     if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
     642             :     {
     643          16 :         bErrorOut = true;
     644          16 :         return false;
     645             :     }
     646         133 :     if (nRings == 0)
     647           2 :         return false;
     648         131 :     if (iOffsetInOut + sizeof(uint32_t) > size)
     649             :     {
     650           0 :         bErrorOut = true;
     651           0 :         return false;
     652             :     }
     653         131 :     if (OGRWKBIntersectsPointSequencePessimistic(
     654             :             data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut))
     655             :     {
     656          98 :         return true;
     657             :     }
     658          33 :     if (bErrorOut)
     659           0 :         return false;
     660             : 
     661             :     // skip inner rings
     662          39 :     for (uint32_t i = 1; i < nRings; ++i)
     663             :     {
     664           6 :         if (iOffsetInOut + sizeof(uint32_t) > size)
     665             :         {
     666           0 :             bErrorOut = true;
     667           0 :             return false;
     668             :         }
     669             :         const uint32_t nPoints =
     670           6 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     671           6 :         if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
     672             :         {
     673           0 :             bErrorOut = true;
     674           0 :             return false;
     675             :         }
     676           6 :         iOffsetInOut += sizeof(double) * nPoints * nDim;
     677             :     }
     678          33 :     return false;
     679             : }
     680             : 
     681             : /************************************************************************/
     682             : /*                    OGRWKBIntersectsPessimistic()                     */
     683             : /************************************************************************/
     684             : 
     685       23538 : static bool OGRWKBIntersectsPessimistic(const GByte *data, const size_t size,
     686             :                                         size_t &iOffsetInOut,
     687             :                                         const OGREnvelope &sEnvelope,
     688             :                                         const int nRec, bool &bErrorOut)
     689             : {
     690       23538 :     if (size - iOffsetInOut < MIN_WKB_SIZE)
     691             :     {
     692           0 :         bErrorOut = true;
     693           0 :         return false;
     694             :     }
     695       23538 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
     696       23538 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
     697             :     {
     698           0 :         bErrorOut = true;
     699           0 :         return false;
     700             :     }
     701       23538 :     const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
     702             : 
     703       23538 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
     704       23538 :     OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
     705       23538 :     iOffsetInOut += 5;
     706       23538 :     const auto eFlatType = wkbFlatten(eGeometryType);
     707       23538 :     const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
     708       23538 :                      (OGR_GT_HasM(eGeometryType) ? 1 : 0);
     709             : 
     710       23538 :     if (eFlatType == wkbPoint)
     711             :     {
     712       23171 :         if (size - iOffsetInOut < nDim * sizeof(double))
     713           8 :             return false;
     714       23163 :         double dfX = 0;
     715       23163 :         double dfY = 0;
     716       23163 :         memcpy(&dfX, data + iOffsetInOut, sizeof(double));
     717       23163 :         memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
     718       23163 :         iOffsetInOut += nDim * sizeof(double);
     719       23163 :         if (OGR_SWAP(eByteOrder))
     720             :         {
     721           0 :             CPL_SWAP64PTR(&dfX);
     722           0 :             CPL_SWAP64PTR(&dfY);
     723             :         }
     724       23163 :         if (std::isnan(dfX))
     725             :         {
     726           1 :             return false;
     727             :         }
     728             :         else
     729             :         {
     730       23155 :             return dfX >= sEnvelope.MinX && dfX <= sEnvelope.MaxX &&
     731       46317 :                    dfY >= sEnvelope.MinY && dfY <= sEnvelope.MaxY;
     732             :         }
     733             :     }
     734             : 
     735         367 :     if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
     736             :     {
     737          68 :         return OGRWKBIntersectsPointSequencePessimistic(
     738          68 :             data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
     739             :     }
     740             : 
     741         299 :     if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
     742             :     {
     743         149 :         return OGRWKBIntersectsRingSequencePessimistic(
     744         149 :             data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
     745             :     }
     746             : 
     747         150 :     if (eFlatType == wkbMultiPoint || eFlatType == wkbMultiLineString ||
     748          81 :         eFlatType == wkbMultiPolygon || eFlatType == wkbGeometryCollection ||
     749          65 :         eFlatType == wkbCompoundCurve || eFlatType == wkbCurvePolygon ||
     750          39 :         eFlatType == wkbMultiCurve || eFlatType == wkbMultiSurface ||
     751          13 :         eFlatType == wkbPolyhedralSurface || eFlatType == wkbTIN)
     752             :     {
     753         150 :         if (nRec == 128)
     754             :         {
     755           0 :             bErrorOut = true;
     756           0 :             return false;
     757             :         }
     758             :         const uint32_t nParts =
     759         150 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     760         150 :         if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
     761             :         {
     762          80 :             bErrorOut = true;
     763          80 :             return false;
     764             :         }
     765         115 :         for (uint32_t k = 0; k < nParts; k++)
     766             :         {
     767          79 :             if (OGRWKBIntersectsPessimistic(data, size, iOffsetInOut, sEnvelope,
     768             :                                             nRec + 1, bErrorOut))
     769             :             {
     770          34 :                 return true;
     771             :             }
     772          45 :             else if (bErrorOut)
     773             :             {
     774           0 :                 return false;
     775             :             }
     776             :         }
     777          36 :         return false;
     778             :     }
     779             : 
     780           0 :     bErrorOut = true;
     781           0 :     return false;
     782             : }
     783             : 
     784             : /************************************************************************/
     785             : /*                    OGRWKBIntersectsPessimistic()                     */
     786             : /************************************************************************/
     787             : 
     788             : /* Returns whether the geometry (pabyWkb, nWKBSize) intersects, for sure,
     789             :  * the passed envelope.
     790             :  * When it returns true, the geometry intersects the envelope.
     791             :  * When it returns false, the geometry may or may not intersect the envelope.
     792             :  */
     793       23459 : bool OGRWKBIntersectsPessimistic(const GByte *pabyWkb, size_t nWKBSize,
     794             :                                  const OGREnvelope &sEnvelope)
     795             : {
     796       23459 :     size_t iOffsetInOut = 0;
     797       23459 :     bool bErrorOut = false;
     798       23459 :     bool bRet = OGRWKBIntersectsPessimistic(pabyWkb, nWKBSize, iOffsetInOut,
     799             :                                             sEnvelope, 0, bErrorOut);
     800       23459 :     if (!bRet && !bErrorOut)
     801             :     {
     802             :         // The following assert only holds if there is no trailing data
     803             :         // after the WKB
     804             :         // CPLAssert(iOffsetInOut == nWKBSize);
     805             :     }
     806       23459 :     return bRet;
     807             : }
     808             : 
     809             : /************************************************************************/
     810             : /*                            epsilonEqual()                            */
     811             : /************************************************************************/
     812             : 
     813          92 : static inline bool epsilonEqual(double a, double b, double eps)
     814             : {
     815          92 :     return ::fabs(a - b) < eps;
     816             : }
     817             : 
     818             : /************************************************************************/
     819             : /*                       OGRWKBIsClockwiseRing()                        */
     820             : /************************************************************************/
     821             : 
     822         382 : static inline double GetX(const GByte *data, uint32_t i, int nDim,
     823             :                           bool bNeedSwap)
     824             : {
     825             :     double dfX;
     826         382 :     memcpy(&dfX, data + static_cast<size_t>(i) * nDim * sizeof(double),
     827             :            sizeof(double));
     828         382 :     if (bNeedSwap)
     829           0 :         CPL_SWAP64PTR(&dfX);
     830         382 :     return dfX;
     831             : }
     832             : 
     833         653 : static inline double GetY(const GByte *data, uint32_t i, int nDim,
     834             :                           bool bNeedSwap)
     835             : {
     836             :     double dfY;
     837         653 :     memcpy(&dfY, data + (static_cast<size_t>(i) * nDim + 1) * sizeof(double),
     838             :            sizeof(double));
     839         653 :     if (bNeedSwap)
     840           0 :         CPL_SWAP64PTR(&dfY);
     841         653 :     return dfY;
     842             : }
     843             : 
     844          37 : static bool OGRWKBIsClockwiseRing(const GByte *data, const uint32_t nPoints,
     845             :                                   const int nDim, const bool bNeedSwap)
     846             : {
     847          37 :     constexpr double EPSILON = 1.0E-5;
     848             : 
     849             :     // WARNING: keep in sync OGRLineString::isClockwise(),
     850             :     // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
     851             : 
     852          37 :     bool bUseFallback = false;
     853             : 
     854             :     // Find the lowest rightmost vertex.
     855          37 :     uint32_t v = 0;  // Used after for.
     856          37 :     double vX = GetX(data, v, nDim, bNeedSwap);
     857          37 :     double vY = GetY(data, v, nDim, bNeedSwap);
     858         535 :     for (uint32_t i = 1; i < nPoints - 1; i++)
     859             :     {
     860             :         // => v < end.
     861         498 :         const double y = GetY(data, i, nDim, bNeedSwap);
     862         498 :         if (y < vY)
     863             :         {
     864         163 :             v = i;
     865         163 :             vX = GetX(data, i, nDim, bNeedSwap);
     866         163 :             vY = y;
     867         163 :             bUseFallback = false;
     868             :         }
     869         335 :         else if (y == vY)
     870             :         {
     871          18 :             const double x = GetX(data, i, nDim, bNeedSwap);
     872          18 :             if (x > vX)
     873             :             {
     874           8 :                 v = i;
     875           8 :                 vX = x;
     876             :                 // vY = y;
     877           8 :                 bUseFallback = false;
     878             :             }
     879          10 :             else if (x == vX)
     880             :             {
     881             :                 // Two vertex with same coordinates are the lowest rightmost
     882             :                 // vertex.  Cannot use that point as the pivot (#5342).
     883           2 :                 bUseFallback = true;
     884             :             }
     885             :         }
     886             :     }
     887             : 
     888             :     // Previous.
     889          37 :     uint32_t next = (v == 0) ? nPoints - 2 : v - 1;
     890          49 :     if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
     891          12 :         epsilonEqual(GetY(data, next, nDim, bNeedSwap), vY, EPSILON))
     892             :     {
     893             :         // Don't try to be too clever by retrying with a next point.
     894             :         // This can lead to false results as in the case of #3356.
     895           0 :         bUseFallback = true;
     896             :     }
     897             : 
     898          37 :     const double dx0 = GetX(data, next, nDim, bNeedSwap) - vX;
     899          37 :     const double dy0 = GetY(data, next, nDim, bNeedSwap) - vY;
     900             : 
     901             :     // Following.
     902          37 :     next = v + 1;
     903          37 :     if (next >= nPoints - 1)
     904             :     {
     905           2 :         next = 0;
     906             :     }
     907             : 
     908          43 :     if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
     909           6 :         epsilonEqual(GetY(data, next, nDim, bNeedSwap), vY, EPSILON))
     910             :     {
     911             :         // Don't try to be too clever by retrying with a next point.
     912             :         // This can lead to false results as in the case of #3356.
     913           2 :         bUseFallback = true;
     914             :     }
     915             : 
     916          37 :     const double dx1 = GetX(data, next, nDim, bNeedSwap) - vX;
     917          37 :     const double dy1 = GetY(data, next, nDim, bNeedSwap) - vY;
     918             : 
     919          37 :     const double crossproduct = dx1 * dy0 - dx0 * dy1;
     920             : 
     921          37 :     if (!bUseFallback)
     922             :     {
     923          35 :         if (crossproduct > 0)  // CCW
     924          23 :             return false;
     925          12 :         else if (crossproduct < 0)  // CW
     926          12 :             return true;
     927             :     }
     928             : 
     929             :     // This is a degenerate case: the extent of the polygon is less than EPSILON
     930             :     // or 2 nearly identical points were found.
     931             :     // Try with Green Formula as a fallback, but this is not a guarantee
     932             :     // as we'll probably be affected by numerical instabilities.
     933             : 
     934           2 :     double dfSum = GetX(data, 0, nDim, bNeedSwap) *
     935           2 :                    (GetY(data, 1, nDim, bNeedSwap) -
     936           2 :                     GetY(data, nPoints - 1, nDim, bNeedSwap));
     937             : 
     938          12 :     for (uint32_t i = 1; i < nPoints - 1; i++)
     939             :     {
     940          10 :         dfSum += GetX(data, i, nDim, bNeedSwap) *
     941          20 :                  (GetY(data, i + 1, nDim, bNeedSwap) -
     942          10 :                   GetY(data, i - 1, nDim, bNeedSwap));
     943             :     }
     944             : 
     945           2 :     dfSum += GetX(data, nPoints - 1, nDim, bNeedSwap) *
     946           2 :              (GetY(data, 0, nDim, bNeedSwap) -
     947           2 :               GetX(data, nPoints - 2, nDim, bNeedSwap));
     948             : 
     949           2 :     return dfSum < 0;
     950             : }
     951             : 
     952             : /************************************************************************/
     953             : /*              OGRWKBFixupCounterClockWiseExternalRing()               */
     954             : /************************************************************************/
     955             : 
     956          63 : static bool OGRWKBFixupCounterClockWiseExternalRingInternal(
     957             :     GByte *data, size_t size, size_t &iOffsetInOut, const int nRec)
     958             : {
     959          63 :     if (size - iOffsetInOut < MIN_WKB_SIZE)
     960             :     {
     961           0 :         return false;
     962             :     }
     963          63 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
     964          63 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
     965             :     {
     966           0 :         return false;
     967             :     }
     968          63 :     const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
     969             : 
     970          63 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
     971          63 :     OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
     972          63 :     iOffsetInOut += 5;
     973          63 :     const auto eFlatType = wkbFlatten(eGeometryType);
     974          63 :     const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
     975          63 :                      (OGR_GT_HasM(eGeometryType) ? 1 : 0);
     976             : 
     977          63 :     if (eFlatType == wkbPolygon)
     978             :     {
     979             :         const uint32_t nRings =
     980          34 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     981          34 :         if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
     982             :         {
     983           0 :             return false;
     984             :         }
     985          71 :         for (uint32_t iRing = 0; iRing < nRings; ++iRing)
     986             :         {
     987          37 :             if (iOffsetInOut + sizeof(uint32_t) > size)
     988           0 :                 return false;
     989             :             const uint32_t nPoints =
     990          37 :                 OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     991          37 :             const size_t sizeOfPoint = nDim * sizeof(double);
     992          37 :             if (nPoints > (size - iOffsetInOut) / sizeOfPoint)
     993             :             {
     994           0 :                 return false;
     995             :             }
     996             : 
     997          37 :             if (nPoints >= 4)
     998             :             {
     999          37 :                 const bool bIsClockwiseRing = OGRWKBIsClockwiseRing(
    1000          37 :                     data + iOffsetInOut, nPoints, nDim, OGR_SWAP(eByteOrder));
    1001          37 :                 if ((bIsClockwiseRing && iRing == 0) ||
    1002          25 :                     (!bIsClockwiseRing && iRing > 0))
    1003             :                 {
    1004             :                     GByte abyTmp[4 * sizeof(double)];
    1005          43 :                     for (uint32_t i = 0; i < nPoints / 2; ++i)
    1006             :                     {
    1007          29 :                         GByte *pBegin = data + iOffsetInOut + i * sizeOfPoint;
    1008          29 :                         GByte *pEnd = data + iOffsetInOut +
    1009          29 :                                       (nPoints - 1 - i) * sizeOfPoint;
    1010          29 :                         memcpy(abyTmp, pBegin, sizeOfPoint);
    1011          29 :                         memcpy(pBegin, pEnd, sizeOfPoint);
    1012          29 :                         memcpy(pEnd, abyTmp, sizeOfPoint);
    1013             :                     }
    1014             :                 }
    1015             :             }
    1016             : 
    1017          37 :             iOffsetInOut += nPoints * sizeOfPoint;
    1018             :         }
    1019             :     }
    1020             : 
    1021          63 :     if (eFlatType == wkbGeometryCollection || eFlatType == wkbMultiPolygon ||
    1022             :         eFlatType == wkbMultiSurface)
    1023             :     {
    1024           6 :         if (nRec == 128)
    1025             :         {
    1026           0 :             return false;
    1027             :         }
    1028             :         const uint32_t nParts =
    1029           6 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
    1030           6 :         if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
    1031             :         {
    1032           0 :             return false;
    1033             :         }
    1034          11 :         for (uint32_t k = 0; k < nParts; k++)
    1035             :         {
    1036           5 :             if (!OGRWKBFixupCounterClockWiseExternalRingInternal(
    1037             :                     data, size, iOffsetInOut, nRec))
    1038             :             {
    1039           0 :                 return false;
    1040             :             }
    1041             :         }
    1042             :     }
    1043             : 
    1044          63 :     return true;
    1045             : }
    1046             : 
    1047             : /** Modifies the geometry such that exterior rings of polygons are
    1048             :  * counter-clockwise oriented and inner rings clockwise oriented.
    1049             :  */
    1050          58 : void OGRWKBFixupCounterClockWiseExternalRing(GByte *pabyWkb, size_t nWKBSize)
    1051             : {
    1052          58 :     size_t iOffsetInOut = 0;
    1053          58 :     OGRWKBFixupCounterClockWiseExternalRingInternal(
    1054             :         pabyWkb, nWKBSize, iOffsetInOut, /* nRec = */ 0);
    1055          58 : }
    1056             : 
    1057             : /************************************************************************/
    1058             : /*                         OGRWKBPointUpdater()                         */
    1059             : /************************************************************************/
    1060             : 
    1061             : OGRWKBPointUpdater::OGRWKBPointUpdater() = default;
    1062             : 
    1063             : OGRWKBPointUpdater::~OGRWKBPointUpdater() = default;
    1064             : 
    1065             : /************************************************************************/
    1066             : /*              OGRWKBIntersectsPointSequencePessimistic()              */
    1067             : /************************************************************************/
    1068             : 
    1069        3029 : 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        3029 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
    1077        3029 :     if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
    1078             :     {
    1079        1497 :         return false;
    1080             :     }
    1081        1532 :     const bool bNeedSwap = OGR_SWAP(eByteOrder);
    1082        6067 :     for (uint32_t j = 0; j < nPoints; j++)
    1083             :     {
    1084        4537 :         void *pdfX = data + iOffsetInOut;
    1085        4537 :         void *pdfY = data + iOffsetInOut + sizeof(double);
    1086        4537 :         void *pdfZ = bHasZ ? data + iOffsetInOut + 2 * sizeof(double) : nullptr;
    1087        4537 :         void *pdfM =
    1088        4537 :             bHasM ? data + iOffsetInOut + (bHasZ ? 3 : 2) * sizeof(double)
    1089             :                   : nullptr;
    1090        4537 :         if (!oUpdater.update(bNeedSwap, pdfX, pdfY, pdfZ, pdfM))
    1091           2 :             return false;
    1092             : 
    1093        4535 :         iOffsetInOut += nDim * sizeof(double);
    1094             :     }
    1095             : 
    1096        1530 :     return true;
    1097             : }
    1098             : 
    1099             : /************************************************************************/
    1100             : /*                      OGRWKBVisitRingSequence()                       */
    1101             : /************************************************************************/
    1102             : 
    1103        1776 : 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        1776 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
    1111        1776 :     if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
    1112             :     {
    1113         150 :         return false;
    1114             :     }
    1115             : 
    1116        2534 :     for (uint32_t i = 0; i < nRings; ++i)
    1117             :     {
    1118        1727 :         if (iOffsetInOut + sizeof(uint32_t) > size)
    1119             :         {
    1120           4 :             return false;
    1121             :         }
    1122        1723 :         if (!OGRWKBUpdatePointsSequence(data, size, oUpdater, eByteOrder, nDim,
    1123             :                                         bHasZ, bHasM, iOffsetInOut))
    1124             :         {
    1125         815 :             return false;
    1126             :         }
    1127             :     }
    1128         807 :     return true;
    1129             : }
    1130             : 
    1131             : /************************************************************************/
    1132             : /*                         OGRWKBUpdatePoints()                         */
    1133             : /************************************************************************/
    1134             : 
    1135       26303 : static bool OGRWKBUpdatePoints(uint8_t *data, const size_t size,
    1136             :                                OGRWKBPointUpdater &oUpdater,
    1137             :                                size_t &iOffsetInOut, const int nRec)
    1138             : {
    1139       26303 :     if (size - iOffsetInOut < MIN_WKB_SIZE)
    1140             :     {
    1141         514 :         return false;
    1142             :     }
    1143       25789 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
    1144       25789 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
    1145             :     {
    1146          68 :         return false;
    1147             :     }
    1148       25721 :     const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
    1149             : 
    1150       25721 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
    1151       25721 :     OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
    1152       25721 :     iOffsetInOut += 5;
    1153       25721 :     const auto eFlatType = wkbFlatten(eGeometryType);
    1154             : 
    1155       25721 :     if (eFlatType == wkbGeometryCollection || eFlatType == wkbCompoundCurve ||
    1156       25250 :         eFlatType == wkbCurvePolygon || eFlatType == wkbMultiPoint ||
    1157       24939 :         eFlatType == wkbMultiLineString || eFlatType == wkbMultiPolygon ||
    1158       24751 :         eFlatType == wkbMultiCurve || eFlatType == wkbMultiSurface ||
    1159       24491 :         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       24329 :     const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeometryType));
    1180       24329 :     const bool bHasM = CPL_TO_BOOL(OGR_GT_HasM(eGeometryType));
    1181       24329 :     const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
    1182             : 
    1183       24329 :     if (eFlatType == wkbPoint)
    1184             :     {
    1185       21044 :         if (size - iOffsetInOut < nDim * sizeof(double))
    1186         376 :             return false;
    1187       20668 :         void *pdfX = data + iOffsetInOut;
    1188       20668 :         void *pdfY = data + iOffsetInOut + sizeof(double);
    1189       20668 :         void *pdfZ = bHasZ ? data + iOffsetInOut + 2 * sizeof(double) : nullptr;
    1190       20668 :         void *pdfM =
    1191       20668 :             bHasM ? data + iOffsetInOut + (bHasZ ? 3 : 2) * sizeof(double)
    1192             :                   : nullptr;
    1193       20668 :         const bool bNeedSwap = OGR_SWAP(eByteOrder);
    1194       20668 :         if (!oUpdater.update(bNeedSwap, pdfX, pdfY, pdfZ, pdfM))
    1195           1 :             return false;
    1196       20667 :         iOffsetInOut += nDim * sizeof(double);
    1197       20667 :         return true;
    1198             :     }
    1199             : 
    1200        3285 :     if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
    1201             :     {
    1202        1306 :         return OGRWKBUpdatePointsSequence(data, size, oUpdater, eByteOrder,
    1203        1306 :                                           nDim, bHasZ, bHasM, iOffsetInOut);
    1204             :     }
    1205             : 
    1206        1979 :     if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
    1207             :     {
    1208        1776 :         return OGRWKBVisitRingSequence(data, size, oUpdater, eByteOrder, nDim,
    1209        1776 :                                        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       24945 : bool OGRWKBUpdatePoints(GByte *pabyWkb, size_t nWKBSize,
    1219             :                         OGRWKBPointUpdater &oUpdater)
    1220             : {
    1221       24945 :     size_t iOffsetInOut = 0;
    1222       24945 :     return OGRWKBUpdatePoints(pabyWkb, nWKBSize, oUpdater, iOffsetInOut,
    1223       49890 :                               /* 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       14918 : 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       14918 :         explicit OGRWKBPointUpdaterReproj(OGRCoordinateTransformation *poCTIn,
    1265             :                                           OGREnvelope3D &sEnvelopeIn)
    1266       14918 :             : m_poCT(poCTIn), m_sEnvelope(sEnvelopeIn)
    1267             :         {
    1268       14918 :         }
    1269             : 
    1270       14943 :         bool update(bool bNeedSwap, void *x, void *y, void *z,
    1271             :                     void * /* m */) override
    1272             :         {
    1273             :             double dfX, dfY, dfZ;
    1274       14943 :             memcpy(&dfX, x, sizeof(double));
    1275       14943 :             memcpy(&dfY, y, sizeof(double));
    1276       14943 :             if (bNeedSwap)
    1277             :             {
    1278        1116 :                 CPL_SWAP64PTR(&dfX);
    1279        1116 :                 CPL_SWAP64PTR(&dfY);
    1280             :             }
    1281       14943 :             if (!(std::isnan(dfX) && std::isnan(dfY)))
    1282             :             {
    1283       14743 :                 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       13577 :                     dfZ = 0;
    1293       14743 :                 int nErrorCode = 0;
    1294       14743 :                 m_poCT->TransformWithErrorCodes(1, &dfX, &dfY, &dfZ, nullptr,
    1295       14743 :                                                 &nErrorCode);
    1296       14743 :                 if (nErrorCode)
    1297           3 :                     return false;
    1298       14740 :                 m_sEnvelope.Merge(dfX, dfY, dfZ);
    1299       14740 :                 if (bNeedSwap)
    1300             :                 {
    1301        1016 :                     CPL_SWAP64PTR(&dfX);
    1302        1016 :                     CPL_SWAP64PTR(&dfY);
    1303        1016 :                     CPL_SWAP64PTR(&dfZ);
    1304             :                 }
    1305       14740 :                 memcpy(x, &dfX, sizeof(double));
    1306       14740 :                 memcpy(y, &dfY, sizeof(double));
    1307       14740 :                 if (z)
    1308        1166 :                     memcpy(z, &dfZ, sizeof(double));
    1309             :             }
    1310       14940 :             return true;
    1311             :         }
    1312             : 
    1313             :       private:
    1314             :         OGRWKBPointUpdaterReproj(const OGRWKBPointUpdaterReproj &) = delete;
    1315             :         OGRWKBPointUpdaterReproj &
    1316             :         operator=(const OGRWKBPointUpdaterReproj &) = delete;
    1317             :     };
    1318             : 
    1319       14918 :     sEnvelope = OGREnvelope3D();
    1320       29836 :     OGRWKBPointUpdaterReproj oUpdater(poCT, sEnvelope);
    1321       29836 :     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         236 :     std::unique_ptr<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 :         poGeometry = OGRGeometryFactory::createFromWkt(pszPtrStart).first;
    1632             :         // cppcheck-suppress selfAssignment
    1633         100 :         *pszEnd = chBackup;
    1634             :     }
    1635             :     else
    1636             :     {
    1637          18 :         std::string osTmp;
    1638          18 :         osTmp.assign(pszPtrStart, nLength);
    1639          18 :         poGeometry = OGRGeometryFactory::createFromWkt(osTmp.c_str()).first;
    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 :     return nWKBSize;
    1655             : }

Generated by: LCOV version 1.14