LCOV - code coverage report
Current view: top level - ogr - ogr_wkb.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 516 605 85.3 %
Date: 2024-05-03 15:49:35 Functions: 29 29 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             :  * Permission is hereby granted, free of charge, to any person obtaining a
      11             :  * copy of this software and associated documentation files (the "Software"),
      12             :  * to deal in the Software without restriction, including without limitation
      13             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      14             :  * and/or sell copies of the Software, and to permit persons to whom the
      15             :  * Software is furnished to do so, subject to the following conditions:
      16             :  *
      17             :  * The above copyright notice and this permission notice shall be included
      18             :  * in all copies or substantial portions of the Software.
      19             :  *
      20             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      21             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      22             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      23             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      24             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      25             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      26             :  * DEALINGS IN THE SOFTWARE.
      27             :  ****************************************************************************/
      28             : 
      29             : #include "cpl_error.h"
      30             : #include "ogr_wkb.h"
      31             : #include "ogr_core.h"
      32             : #include "ogr_geometry.h"
      33             : #include "ogr_p.h"
      34             : 
      35             : #include <algorithm>
      36             : #include <cmath>
      37             : #include <climits>
      38             : #include <limits>
      39             : 
      40             : #include <algorithm>
      41             : #include <limits>
      42             : 
      43             : #define USE_FAST_FLOAT
      44             : #ifdef USE_FAST_FLOAT
      45             : #include "include_fast_float.h"
      46             : #endif
      47             : 
      48             : /************************************************************************/
      49             : /*                          OGRWKBNeedSwap()                            */
      50             : /************************************************************************/
      51             : 
      52         192 : static inline bool OGRWKBNeedSwap(GByte b)
      53             : {
      54             : #if CPL_IS_LSB
      55         192 :     const bool bNeedSwap = b == 0;
      56             : #else
      57             :     const bool bNeedSwap = b == 1;
      58             : #endif
      59         192 :     return bNeedSwap;
      60             : }
      61             : 
      62             : /************************************************************************/
      63             : /*                        OGRWKBReadUInt32()                            */
      64             : /************************************************************************/
      65             : 
      66         215 : static inline uint32_t OGRWKBReadUInt32(const GByte *pabyWkb, bool bNeedSwap)
      67             : {
      68             :     uint32_t nVal;
      69         215 :     memcpy(&nVal, pabyWkb, sizeof(nVal));
      70         215 :     if (bNeedSwap)
      71           0 :         CPL_SWAP32PTR(&nVal);
      72         215 :     return nVal;
      73             : }
      74             : 
      75             : /************************************************************************/
      76             : /*                        OGRWKBReadFloat64()                           */
      77             : /************************************************************************/
      78             : 
      79         100 : static inline double OGRWKBReadFloat64(const GByte *pabyWkb, bool bNeedSwap)
      80             : {
      81             :     double dfVal;
      82         100 :     memcpy(&dfVal, pabyWkb, sizeof(dfVal));
      83         100 :     if (bNeedSwap)
      84           0 :         CPL_SWAP64PTR(&dfVal);
      85         100 :     return dfVal;
      86             : }
      87             : 
      88             : /************************************************************************/
      89             : /*                        OGRWKBRingGetArea()                           */
      90             : /************************************************************************/
      91             : 
      92          12 : static bool OGRWKBRingGetArea(const GByte *&pabyWkb, size_t &nWKBSize, int nDim,
      93             :                               bool bNeedSwap, double &dfArea)
      94             : {
      95          12 :     const uint32_t nPoints = OGRWKBReadUInt32(pabyWkb, bNeedSwap);
      96          12 :     if (nPoints >= 4 &&
      97          12 :         (nWKBSize - sizeof(uint32_t)) / (nDim * sizeof(double)) >= nPoints)
      98             :     {
      99          12 :         nWKBSize -= sizeof(uint32_t) + nDim * sizeof(double);
     100          12 :         pabyWkb += sizeof(uint32_t);
     101             :         // Computation according to Green's Theorem
     102             :         // Cf OGRSimpleCurve::get_LinearArea()
     103          12 :         double x_m1 = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
     104          12 :         double y_m1 = OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
     105          12 :         double y_m2 = y_m1;
     106          12 :         dfArea = 0;
     107          12 :         pabyWkb += nDim * sizeof(double);
     108          50 :         for (uint32_t i = 1; i < nPoints; ++i)
     109             :         {
     110          38 :             const double x = OGRWKBReadFloat64(pabyWkb, bNeedSwap);
     111             :             const double y =
     112          38 :                 OGRWKBReadFloat64(pabyWkb + sizeof(double), bNeedSwap);
     113          38 :             pabyWkb += nDim * sizeof(double);
     114          38 :             dfArea += x_m1 * (y - y_m2);
     115          38 :             y_m2 = y_m1;
     116          38 :             x_m1 = x;
     117          38 :             y_m1 = y;
     118             :         }
     119          12 :         dfArea += x_m1 * (y_m1 - y_m2);
     120          12 :         dfArea = 0.5 * std::fabs(dfArea);
     121          12 :         return true;
     122             :     }
     123           0 :     return false;
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                         OGRWKBGetGeomType()                          */
     128             : /************************************************************************/
     129             : 
     130         187 : bool OGRWKBGetGeomType(const GByte *pabyWkb, size_t nWKBSize, bool &bNeedSwap,
     131             :                        uint32_t &nType)
     132             : {
     133         187 :     if (nWKBSize >= 5)
     134             :     {
     135         187 :         bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
     136         187 :         nType = OGRWKBReadUInt32(pabyWkb + 1, bNeedSwap);
     137         187 :         return true;
     138             :     }
     139           0 :     return false;
     140             : }
     141             : 
     142             : /************************************************************************/
     143             : /*                        OGRWKBPolygonGetArea()                        */
     144             : /************************************************************************/
     145             : 
     146          11 : bool OGRWKBPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
     147             :                           double &dfArea)
     148             : {
     149             :     bool bNeedSwap;
     150             :     uint32_t nType;
     151          11 :     if (nWKBSize < 9 || !OGRWKBGetGeomType(pabyWkb, nWKBSize, bNeedSwap, nType))
     152           0 :         return false;
     153             : 
     154          11 :     int nDims = 2;
     155          11 :     if (nType == wkbPolygon)
     156             :     {
     157             :         // do nothing
     158             :     }
     159           6 :     else if (nType == wkbPolygon + 1000 ||  // wkbPolygonZ
     160           4 :              nType == wkbPolygon25D || nType == wkbPolygonM)
     161             :     {
     162           4 :         nDims = 3;
     163             :     }
     164           2 :     else if (nType == wkbPolygonZM)
     165             :     {
     166           2 :         nDims = 4;
     167             :     }
     168             :     else
     169             :     {
     170           0 :         return false;
     171             :     }
     172             : 
     173          11 :     const uint32_t nRings = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
     174          11 :     if ((nWKBSize - 9) / sizeof(uint32_t) >= nRings)
     175             :     {
     176          11 :         pabyWkb += 9;
     177          11 :         nWKBSize -= 9;
     178          11 :         dfArea = 0;
     179          11 :         if (nRings > 0)
     180             :         {
     181          11 :             if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap, dfArea))
     182           0 :                 return false;
     183          12 :             for (uint32_t i = 1; i < nRings; ++i)
     184             :             {
     185             :                 double dfRingArea;
     186           1 :                 if (!OGRWKBRingGetArea(pabyWkb, nWKBSize, nDims, bNeedSwap,
     187             :                                        dfRingArea))
     188           0 :                     return false;
     189           1 :                 dfArea -= dfRingArea;
     190             :             }
     191             :         }
     192          11 :         return true;
     193             :     }
     194           0 :     return false;
     195             : }
     196             : 
     197             : /************************************************************************/
     198             : /*                    OGRWKBMultiPolygonGetArea()                       */
     199             : /************************************************************************/
     200             : 
     201           5 : bool OGRWKBMultiPolygonGetArea(const GByte *&pabyWkb, size_t &nWKBSize,
     202             :                                double &dfArea)
     203             : {
     204           5 :     if (nWKBSize < 9)
     205           0 :         return false;
     206             : 
     207           5 :     const bool bNeedSwap = OGRWKBNeedSwap(pabyWkb[0]);
     208           5 :     const uint32_t nPolys = OGRWKBReadUInt32(pabyWkb + 5, bNeedSwap);
     209           5 :     if ((nWKBSize - 9) / 9 >= nPolys)
     210             :     {
     211           5 :         pabyWkb += 9;
     212           5 :         nWKBSize -= 9;
     213           5 :         dfArea = 0;
     214          11 :         for (uint32_t i = 0; i < nPolys; ++i)
     215             :         {
     216             :             double dfPolyArea;
     217           6 :             if (!OGRWKBPolygonGetArea(pabyWkb, nWKBSize, dfPolyArea))
     218           0 :                 return false;
     219           6 :             dfArea += dfPolyArea;
     220             :         }
     221           5 :         return true;
     222             :     }
     223           0 :     return false;
     224             : }
     225             : 
     226             : /************************************************************************/
     227             : /*                            WKBFromEWKB()                             */
     228             : /************************************************************************/
     229             : 
     230        1412 : const GByte *WKBFromEWKB(GByte *pabyEWKB, size_t nEWKBSize, size_t &nWKBSizeOut,
     231             :                          int *pnSRIDOut)
     232             : {
     233        1412 :     if (nEWKBSize < 5U)
     234             :     {
     235           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid EWKB content : %u bytes",
     236             :                  static_cast<unsigned>(nEWKBSize));
     237           0 :         return nullptr;
     238             :     }
     239             : 
     240        1412 :     const GByte *pabyWKB = pabyEWKB;
     241             : 
     242             :     /* -------------------------------------------------------------------- */
     243             :     /*      PostGIS EWKB format includes an SRID, but this won't be         */
     244             :     /*      understood by OGR, so if the SRID flag is set, we remove the    */
     245             :     /*      SRID (bytes at offset 5 to 8).                                  */
     246             :     /* -------------------------------------------------------------------- */
     247        1412 :     if (nEWKBSize > 9 &&
     248        1405 :         ((pabyEWKB[0] == 0 /* big endian */ && (pabyEWKB[1] & 0x20)) ||
     249        1405 :          (pabyEWKB[0] != 0 /* little endian */ && (pabyEWKB[4] & 0x20))))
     250             :     {
     251         514 :         if (pnSRIDOut)
     252             :         {
     253           0 :             memcpy(pnSRIDOut, pabyEWKB + 5, 4);
     254           0 :             const OGRwkbByteOrder eByteOrder =
     255           0 :                 (pabyEWKB[0] == 0 ? wkbXDR : wkbNDR);
     256           0 :             if (OGR_SWAP(eByteOrder))
     257           0 :                 *pnSRIDOut = CPL_SWAP32(*pnSRIDOut);
     258             :         }
     259             : 
     260             :         // Drop the SRID flag
     261         514 :         if (pabyEWKB[0] == 0)
     262           0 :             pabyEWKB[1] &= (~0x20);
     263             :         else
     264         514 :             pabyEWKB[4] &= (~0x20);
     265             : 
     266             :         // Move 5 first bytes of EWKB 4 bytes later to create regular WKB
     267         514 :         memmove(pabyEWKB + 4, pabyEWKB, 5);
     268         514 :         memset(pabyEWKB, 0, 4);
     269             :         // and make pabyWKB point to that
     270         514 :         pabyWKB += 4;
     271         514 :         nWKBSizeOut = nEWKBSize - 4;
     272             :     }
     273             :     else
     274             :     {
     275         898 :         if (pnSRIDOut)
     276             :         {
     277           0 :             *pnSRIDOut = INT_MIN;
     278             :         }
     279         898 :         nWKBSizeOut = nEWKBSize;
     280             :     }
     281             : 
     282        1412 :     return pabyWKB;
     283             : }
     284             : 
     285             : /************************************************************************/
     286             : /*                     OGRWKBReadUInt32AtOffset()                       */
     287             : /************************************************************************/
     288             : 
     289        2001 : static uint32_t OGRWKBReadUInt32AtOffset(const uint8_t *data,
     290             :                                          OGRwkbByteOrder eByteOrder,
     291             :                                          size_t &iOffset)
     292             : {
     293             :     uint32_t v;
     294        2001 :     memcpy(&v, data + iOffset, sizeof(v));
     295        2001 :     iOffset += sizeof(v);
     296        2001 :     if (OGR_SWAP(eByteOrder))
     297             :     {
     298         154 :         CPL_SWAP32PTR(&v);
     299             :     }
     300        2001 :     return v;
     301             : }
     302             : 
     303             : /************************************************************************/
     304             : /*                         ReadWKBPointSequence()                       */
     305             : /************************************************************************/
     306             : 
     307             : template <bool INCLUDE_Z, typename EnvelopeType>
     308         772 : static bool ReadWKBPointSequence(const uint8_t *data, size_t size,
     309             :                                  OGRwkbByteOrder eByteOrder, int nDim,
     310             :                                  bool bHasZ, size_t &iOffset,
     311             :                                  EnvelopeType &sEnvelope)
     312             : {
     313             :     const uint32_t nPoints =
     314         772 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     315         772 :     if (nPoints > (size - iOffset) / (nDim * sizeof(double)))
     316           0 :         return false;
     317         772 :     double dfX = 0;
     318         772 :     double dfY = 0;
     319         772 :     [[maybe_unused]] double dfZ = 0;
     320       14168 :     for (uint32_t j = 0; j < nPoints; j++)
     321             :     {
     322       13396 :         memcpy(&dfX, data + iOffset, sizeof(double));
     323       13396 :         memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
     324             :         if constexpr (INCLUDE_Z)
     325             :         {
     326          52 :             if (bHasZ)
     327          34 :                 memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
     328             :                        sizeof(double));
     329             :         }
     330       13396 :         iOffset += nDim * sizeof(double);
     331       13396 :         if (OGR_SWAP(eByteOrder))
     332             :         {
     333         308 :             CPL_SWAP64PTR(&dfX);
     334         308 :             CPL_SWAP64PTR(&dfY);
     335             :             if constexpr (INCLUDE_Z)
     336             :             {
     337           0 :                 CPL_SWAP64PTR(&dfZ);
     338             :             }
     339             :         }
     340       13396 :         sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
     341       13396 :         sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
     342       13396 :         sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
     343       13396 :         sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
     344             :         if constexpr (INCLUDE_Z)
     345             :         {
     346          52 :             if (bHasZ)
     347             :             {
     348          34 :                 sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
     349          34 :                 sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
     350             :             }
     351             :         }
     352             :     }
     353         772 :     return true;
     354             : }
     355             : 
     356             : /************************************************************************/
     357             : /*                         ReadWKBRingSequence()                        */
     358             : /************************************************************************/
     359             : 
     360             : template <bool INCLUDE_Z, typename EnvelopeType>
     361         574 : static bool ReadWKBRingSequence(const uint8_t *data, size_t size,
     362             :                                 OGRwkbByteOrder eByteOrder, int nDim,
     363             :                                 bool bHasZ, size_t &iOffset,
     364             :                                 EnvelopeType &sEnvelope)
     365             : {
     366         574 :     const uint32_t nRings = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     367         574 :     if (nRings > (size - iOffset) / sizeof(uint32_t))
     368           0 :         return false;
     369        1188 :     for (uint32_t i = 0; i < nRings; i++)
     370             :     {
     371         614 :         if (iOffset + sizeof(uint32_t) > size)
     372           0 :             return false;
     373         614 :         if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     374             :                                              bHasZ, iOffset, sEnvelope))
     375           0 :             return false;
     376             :     }
     377         574 :     return true;
     378             : }
     379             : 
     380             : /************************************************************************/
     381             : /*                        OGRWKBGetBoundingBox()                        */
     382             : /************************************************************************/
     383             : 
     384             : constexpr uint32_t WKB_PREFIX_SIZE = 1 + sizeof(uint32_t);
     385             : constexpr uint32_t MIN_WKB_SIZE = WKB_PREFIX_SIZE + sizeof(uint32_t);
     386             : 
     387             : template <bool INCLUDE_Z, typename EnvelopeType>
     388      958427 : static bool OGRWKBGetBoundingBox(const uint8_t *data, size_t size,
     389             :                                  size_t &iOffset, EnvelopeType &sEnvelope,
     390             :                                  int nRec)
     391             : {
     392      958427 :     if (size - iOffset < MIN_WKB_SIZE)
     393           0 :         return false;
     394      958427 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffset]);
     395      958427 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
     396           0 :         return false;
     397      958427 :     const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
     398             : 
     399      958427 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
     400      958427 :     OGRReadWKBGeometryType(data + iOffset, wkbVariantIso, &eGeometryType);
     401      958427 :     iOffset += 5;
     402      958427 :     const auto eFlatType = wkbFlatten(eGeometryType);
     403      958427 :     const bool bHasZ = CPL_TO_BOOL(OGR_GT_HasZ(eGeometryType));
     404      958427 :     const int nDim = 2 + (bHasZ ? 1 : 0) + (OGR_GT_HasM(eGeometryType) ? 1 : 0);
     405             : 
     406      958427 :     if (eFlatType == wkbPoint)
     407             :     {
     408      957926 :         if (size - iOffset < nDim * sizeof(double))
     409           0 :             return false;
     410      957926 :         double dfX = 0;
     411      957926 :         double dfY = 0;
     412      957926 :         [[maybe_unused]] double dfZ = 0;
     413      957926 :         memcpy(&dfX, data + iOffset, sizeof(double));
     414      957926 :         memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
     415             :         if constexpr (INCLUDE_Z)
     416             :         {
     417          11 :             if (bHasZ)
     418           4 :                 memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
     419             :                        sizeof(double));
     420             :         }
     421      957926 :         iOffset += nDim * sizeof(double);
     422      957926 :         if (OGR_SWAP(eByteOrder))
     423             :         {
     424          22 :             CPL_SWAP64PTR(&dfX);
     425          22 :             CPL_SWAP64PTR(&dfY);
     426             :             if constexpr (INCLUDE_Z)
     427             :             {
     428           0 :                 CPL_SWAP64PTR(&dfZ);
     429             :             }
     430             :         }
     431      957926 :         if (std::isnan(dfX))
     432             :         {
     433             :             // Point empty
     434           6 :             sEnvelope = EnvelopeType();
     435             :         }
     436             :         else
     437             :         {
     438      957920 :             sEnvelope.MinX = dfX;
     439      957920 :             sEnvelope.MinY = dfY;
     440      957920 :             sEnvelope.MaxX = dfX;
     441      957920 :             sEnvelope.MaxY = dfY;
     442             :             if constexpr (INCLUDE_Z)
     443             :             {
     444          10 :                 if (bHasZ)
     445             :                 {
     446           4 :                     sEnvelope.MinZ = dfZ;
     447           4 :                     sEnvelope.MaxZ = dfZ;
     448             :                 }
     449             :             }
     450             :         }
     451      957926 :         return true;
     452             :     }
     453             : 
     454         501 :     if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
     455             :     {
     456          88 :         sEnvelope = EnvelopeType();
     457             : 
     458          88 :         return ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     459          88 :                                                bHasZ, iOffset, sEnvelope);
     460             :     }
     461             : 
     462         413 :     if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
     463             :     {
     464         173 :         sEnvelope = EnvelopeType();
     465             : 
     466         173 :         return ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     467         173 :                                               bHasZ, iOffset, sEnvelope);
     468             :     }
     469             : 
     470         240 :     if (eFlatType == wkbMultiPoint)
     471             :     {
     472          48 :         sEnvelope = EnvelopeType();
     473             : 
     474          48 :         uint32_t nParts = OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     475          48 :         if (nParts >
     476          48 :             (size - iOffset) / (WKB_PREFIX_SIZE + nDim * sizeof(double)))
     477           0 :             return false;
     478          48 :         double dfX = 0;
     479          48 :         double dfY = 0;
     480          48 :         [[maybe_unused]] double dfZ = 0;
     481         143 :         for (uint32_t k = 0; k < nParts; k++)
     482             :         {
     483          95 :             iOffset += WKB_PREFIX_SIZE;
     484          95 :             memcpy(&dfX, data + iOffset, sizeof(double));
     485          95 :             memcpy(&dfY, data + iOffset + sizeof(double), sizeof(double));
     486             :             if constexpr (INCLUDE_Z)
     487             :             {
     488           5 :                 if (bHasZ)
     489           3 :                     memcpy(&dfZ, data + iOffset + 2 * sizeof(double),
     490             :                            sizeof(double));
     491             :             }
     492          95 :             iOffset += nDim * sizeof(double);
     493          95 :             if (OGR_SWAP(eByteOrder))
     494             :             {
     495          22 :                 CPL_SWAP64PTR(&dfX);
     496          22 :                 CPL_SWAP64PTR(&dfY);
     497             :                 if constexpr (INCLUDE_Z)
     498             :                 {
     499           0 :                     CPL_SWAP64PTR(&dfZ);
     500             :                 }
     501             :             }
     502          95 :             sEnvelope.MinX = std::min(sEnvelope.MinX, dfX);
     503          95 :             sEnvelope.MinY = std::min(sEnvelope.MinY, dfY);
     504          95 :             sEnvelope.MaxX = std::max(sEnvelope.MaxX, dfX);
     505          95 :             sEnvelope.MaxY = std::max(sEnvelope.MaxY, dfY);
     506             :             if constexpr (INCLUDE_Z)
     507             :             {
     508           5 :                 if (bHasZ)
     509             :                 {
     510           3 :                     sEnvelope.MinZ = std::min(sEnvelope.MinZ, dfZ);
     511           3 :                     sEnvelope.MaxZ = std::max(sEnvelope.MaxZ, dfZ);
     512             :                 }
     513             :             }
     514             :         }
     515          48 :         return true;
     516             :     }
     517             : 
     518         192 :     if (eFlatType == wkbMultiLineString)
     519             :     {
     520          50 :         sEnvelope = EnvelopeType();
     521             : 
     522             :         const uint32_t nParts =
     523          50 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     524          50 :         if (nParts > (size - iOffset) / MIN_WKB_SIZE)
     525           0 :             return false;
     526         120 :         for (uint32_t k = 0; k < nParts; k++)
     527             :         {
     528          70 :             if (iOffset + MIN_WKB_SIZE > size)
     529           0 :                 return false;
     530          70 :             iOffset += WKB_PREFIX_SIZE;
     531          70 :             if (!ReadWKBPointSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     532             :                                                  bHasZ, iOffset, sEnvelope))
     533           0 :                 return false;
     534             :         }
     535          50 :         return true;
     536             :     }
     537             : 
     538         142 :     if (eFlatType == wkbMultiPolygon)
     539             :     {
     540          73 :         sEnvelope = EnvelopeType();
     541             : 
     542             :         const uint32_t nParts =
     543          73 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     544          73 :         if (nParts > (size - iOffset) / MIN_WKB_SIZE)
     545           0 :             return false;
     546         474 :         for (uint32_t k = 0; k < nParts; k++)
     547             :         {
     548         401 :             if (iOffset + MIN_WKB_SIZE > size)
     549           0 :                 return false;
     550         401 :             CPLAssert(data[iOffset] == eByteOrder);
     551         401 :             iOffset += WKB_PREFIX_SIZE;
     552         401 :             if (!ReadWKBRingSequence<INCLUDE_Z>(data, size, eByteOrder, nDim,
     553             :                                                 bHasZ, iOffset, sEnvelope))
     554           0 :                 return false;
     555             :         }
     556          73 :         return true;
     557             :     }
     558             : 
     559          69 :     if (eFlatType == wkbGeometryCollection || eFlatType == wkbCompoundCurve ||
     560           4 :         eFlatType == wkbCurvePolygon || eFlatType == wkbMultiCurve ||
     561           2 :         eFlatType == wkbMultiSurface || eFlatType == wkbPolyhedralSurface ||
     562             :         eFlatType == wkbTIN)
     563             :     {
     564          69 :         if (nRec == 128)
     565           0 :             return false;
     566          69 :         sEnvelope = EnvelopeType();
     567             : 
     568             :         const uint32_t nParts =
     569          69 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffset);
     570          69 :         if (nParts > (size - iOffset) / MIN_WKB_SIZE)
     571           0 :             return false;
     572          69 :         EnvelopeType sEnvelopeSubGeom;
     573         149 :         for (uint32_t k = 0; k < nParts; k++)
     574             :         {
     575          80 :             if (!OGRWKBGetBoundingBox<INCLUDE_Z>(data, size, iOffset,
     576             :                                                  sEnvelopeSubGeom, nRec + 1))
     577           0 :                 return false;
     578          80 :             sEnvelope.Merge(sEnvelopeSubGeom);
     579             :         }
     580          69 :         return true;
     581             :     }
     582             : 
     583           0 :     return false;
     584             : }
     585             : 
     586             : /************************************************************************/
     587             : /*                        OGRWKBGetBoundingBox()                        */
     588             : /************************************************************************/
     589             : 
     590      958312 : bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
     591             :                           OGREnvelope &sEnvelope)
     592             : {
     593      958312 :     size_t iOffset = 0;
     594      958312 :     return OGRWKBGetBoundingBox<false>(pabyWkb, nWKBSize, iOffset, sEnvelope,
     595     1916620 :                                        0);
     596             : }
     597             : 
     598             : /************************************************************************/
     599             : /*                        OGRWKBGetBoundingBox()                        */
     600             : /************************************************************************/
     601             : 
     602          35 : bool OGRWKBGetBoundingBox(const GByte *pabyWkb, size_t nWKBSize,
     603             :                           OGREnvelope3D &sEnvelope)
     604             : {
     605          35 :     size_t iOffset = 0;
     606          70 :     return OGRWKBGetBoundingBox<true>(pabyWkb, nWKBSize, iOffset, sEnvelope, 0);
     607             : }
     608             : 
     609             : /************************************************************************/
     610             : /*              OGRWKBIntersectsPointSequencePessimistic()              */
     611             : /************************************************************************/
     612             : 
     613         134 : static bool OGRWKBIntersectsPointSequencePessimistic(
     614             :     const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
     615             :     const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
     616             :     bool &bErrorOut)
     617             : {
     618             :     const uint32_t nPoints =
     619         134 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     620         134 :     if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
     621             :     {
     622          12 :         bErrorOut = true;
     623          12 :         return false;
     624             :     }
     625             : 
     626         122 :     double dfX = 0;
     627         122 :     double dfY = 0;
     628         434 :     for (uint32_t j = 0; j < nPoints; j++)
     629             :     {
     630         381 :         memcpy(&dfX, data + iOffsetInOut, sizeof(double));
     631         381 :         memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
     632         381 :         iOffsetInOut += nDim * sizeof(double);
     633         381 :         if (OGR_SWAP(eByteOrder))
     634             :         {
     635           0 :             CPL_SWAP64PTR(&dfX);
     636           0 :             CPL_SWAP64PTR(&dfY);
     637             :         }
     638         381 :         if (dfX >= sEnvelope.MinX && dfY >= sEnvelope.MinY &&
     639         307 :             dfX <= sEnvelope.MaxX && dfY <= sEnvelope.MaxY)
     640             :         {
     641          69 :             return true;
     642             :         }
     643             :     }
     644             : 
     645          53 :     return false;
     646             : }
     647             : 
     648             : /************************************************************************/
     649             : /*               OGRWKBIntersectsRingSequencePessimistic()              */
     650             : /************************************************************************/
     651             : 
     652          87 : static bool OGRWKBIntersectsRingSequencePessimistic(
     653             :     const uint8_t *data, const size_t size, const OGRwkbByteOrder eByteOrder,
     654             :     const int nDim, size_t &iOffsetInOut, const OGREnvelope &sEnvelope,
     655             :     bool &bErrorOut)
     656             : {
     657             :     const uint32_t nRings =
     658          87 :         OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     659          87 :     if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
     660             :     {
     661          16 :         bErrorOut = true;
     662          16 :         return false;
     663             :     }
     664          71 :     if (nRings == 0)
     665           2 :         return false;
     666          69 :     if (iOffsetInOut + sizeof(uint32_t) > size)
     667             :     {
     668           0 :         bErrorOut = true;
     669           0 :         return false;
     670             :     }
     671          69 :     if (OGRWKBIntersectsPointSequencePessimistic(
     672             :             data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut))
     673             :     {
     674          45 :         return true;
     675             :     }
     676          24 :     if (bErrorOut)
     677           0 :         return false;
     678             : 
     679             :     // skip inner rings
     680          30 :     for (uint32_t i = 1; i < nRings; ++i)
     681             :     {
     682           6 :         if (iOffsetInOut + sizeof(uint32_t) > size)
     683             :         {
     684           0 :             bErrorOut = true;
     685           0 :             return false;
     686             :         }
     687             :         const uint32_t nPoints =
     688           6 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     689           6 :         if (nPoints > (size - iOffsetInOut) / (nDim * sizeof(double)))
     690             :         {
     691           0 :             bErrorOut = true;
     692           0 :             return false;
     693             :         }
     694           6 :         iOffsetInOut += sizeof(double) * nPoints * nDim;
     695             :     }
     696          24 :     return false;
     697             : }
     698             : 
     699             : /************************************************************************/
     700             : /*                  OGRWKBIntersectsPessimistic()                         */
     701             : /************************************************************************/
     702             : 
     703       23369 : static bool OGRWKBIntersectsPessimistic(const GByte *data, const size_t size,
     704             :                                         size_t &iOffsetInOut,
     705             :                                         const OGREnvelope &sEnvelope,
     706             :                                         const int nRec, bool &bErrorOut)
     707             : {
     708       23369 :     if (size - iOffsetInOut < MIN_WKB_SIZE)
     709             :     {
     710           0 :         bErrorOut = true;
     711           0 :         return false;
     712             :     }
     713       23369 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
     714       23369 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
     715             :     {
     716           0 :         bErrorOut = true;
     717           0 :         return false;
     718             :     }
     719       23369 :     const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
     720             : 
     721       23369 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
     722       23369 :     OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
     723       23369 :     iOffsetInOut += 5;
     724       23369 :     const auto eFlatType = wkbFlatten(eGeometryType);
     725       23369 :     const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
     726       23369 :                      (OGR_GT_HasM(eGeometryType) ? 1 : 0);
     727             : 
     728       23369 :     if (eFlatType == wkbPoint)
     729             :     {
     730       23070 :         if (size - iOffsetInOut < nDim * sizeof(double))
     731           8 :             return false;
     732       23062 :         double dfX = 0;
     733       23062 :         double dfY = 0;
     734       23062 :         memcpy(&dfX, data + iOffsetInOut, sizeof(double));
     735       23062 :         memcpy(&dfY, data + iOffsetInOut + sizeof(double), sizeof(double));
     736       23062 :         iOffsetInOut += nDim * sizeof(double);
     737       23062 :         if (OGR_SWAP(eByteOrder))
     738             :         {
     739           0 :             CPL_SWAP64PTR(&dfX);
     740           0 :             CPL_SWAP64PTR(&dfY);
     741             :         }
     742       23062 :         if (std::isnan(dfX))
     743             :         {
     744           1 :             return false;
     745             :         }
     746             :         else
     747             :         {
     748       23054 :             return dfX >= sEnvelope.MinX && dfX <= sEnvelope.MaxX &&
     749       46115 :                    dfY >= sEnvelope.MinY && dfY <= sEnvelope.MaxY;
     750             :         }
     751             :     }
     752             : 
     753         299 :     if (eFlatType == wkbLineString || eFlatType == wkbCircularString)
     754             :     {
     755          65 :         return OGRWKBIntersectsPointSequencePessimistic(
     756          65 :             data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
     757             :     }
     758             : 
     759         234 :     if (eFlatType == wkbPolygon || eFlatType == wkbTriangle)
     760             :     {
     761          87 :         return OGRWKBIntersectsRingSequencePessimistic(
     762          87 :             data, size, eByteOrder, nDim, iOffsetInOut, sEnvelope, bErrorOut);
     763             :     }
     764             : 
     765         147 :     if (eFlatType == wkbMultiPoint || eFlatType == wkbMultiLineString ||
     766          81 :         eFlatType == wkbMultiPolygon || eFlatType == wkbGeometryCollection ||
     767          65 :         eFlatType == wkbCompoundCurve || eFlatType == wkbCurvePolygon ||
     768          39 :         eFlatType == wkbMultiCurve || eFlatType == wkbMultiSurface ||
     769          13 :         eFlatType == wkbPolyhedralSurface || eFlatType == wkbTIN)
     770             :     {
     771         147 :         if (nRec == 128)
     772             :         {
     773           0 :             bErrorOut = true;
     774           0 :             return false;
     775             :         }
     776             :         const uint32_t nParts =
     777         147 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     778         147 :         if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
     779             :         {
     780          80 :             bErrorOut = true;
     781          80 :             return false;
     782             :         }
     783         112 :         for (uint32_t k = 0; k < nParts; k++)
     784             :         {
     785          76 :             if (OGRWKBIntersectsPessimistic(data, size, iOffsetInOut, sEnvelope,
     786             :                                             nRec + 1, bErrorOut))
     787             :             {
     788          31 :                 return true;
     789             :             }
     790          45 :             else if (bErrorOut)
     791             :             {
     792           0 :                 return false;
     793             :             }
     794             :         }
     795          36 :         return false;
     796             :     }
     797             : 
     798           0 :     bErrorOut = true;
     799           0 :     return false;
     800             : }
     801             : 
     802             : /************************************************************************/
     803             : /*                  OGRWKBIntersectsPessimistic()                       */
     804             : /************************************************************************/
     805             : 
     806             : /* Returns whether the geometry (pabyWkb, nWKBSize) intersects, for sure,
     807             :  * the passed envelope.
     808             :  * When it returns true, the geometry intersects the envelope.
     809             :  * When it returns false, the geometry may or may not intersect the envelope.
     810             :  */
     811       23293 : bool OGRWKBIntersectsPessimistic(const GByte *pabyWkb, size_t nWKBSize,
     812             :                                  const OGREnvelope &sEnvelope)
     813             : {
     814       23293 :     size_t iOffsetInOut = 0;
     815       23293 :     bool bErrorOut = false;
     816       23293 :     bool bRet = OGRWKBIntersectsPessimistic(pabyWkb, nWKBSize, iOffsetInOut,
     817             :                                             sEnvelope, 0, bErrorOut);
     818       23293 :     if (!bRet && !bErrorOut)
     819             :     {
     820             :         // The following assert only holds if there is no trailing data
     821             :         // after the WKB
     822             :         // CPLAssert(iOffsetInOut == nWKBSize);
     823             :     }
     824       23293 :     return bRet;
     825             : }
     826             : 
     827             : /************************************************************************/
     828             : /*                            epsilonEqual()                            */
     829             : /************************************************************************/
     830             : 
     831             : constexpr double EPSILON = 1.0E-5;
     832             : 
     833          48 : static inline bool epsilonEqual(double a, double b, double eps)
     834             : {
     835          48 :     return ::fabs(a - b) < eps;
     836             : }
     837             : 
     838             : /************************************************************************/
     839             : /*                     OGRWKBIsClockwiseRing()                          */
     840             : /************************************************************************/
     841             : 
     842         191 : static inline double GetX(const GByte *data, uint32_t i, int nDim,
     843             :                           bool bNeedSwap)
     844             : {
     845             :     double dfX;
     846         191 :     memcpy(&dfX, data + static_cast<size_t>(i) * nDim * sizeof(double),
     847             :            sizeof(double));
     848         191 :     if (bNeedSwap)
     849           0 :         CPL_SWAP64PTR(&dfX);
     850         191 :     return dfX;
     851             : }
     852             : 
     853         342 : static inline double GetY(const GByte *data, uint32_t i, int nDim,
     854             :                           bool bNeedSwap)
     855             : {
     856             :     double dfY;
     857         342 :     memcpy(&dfY, data + (static_cast<size_t>(i) * nDim + 1) * sizeof(double),
     858             :            sizeof(double));
     859         342 :     if (bNeedSwap)
     860           0 :         CPL_SWAP64PTR(&dfY);
     861         342 :     return dfY;
     862             : }
     863             : 
     864          19 : static bool OGRWKBIsClockwiseRing(const GByte *data, const uint32_t nPoints,
     865             :                                   const int nDim, const bool bNeedSwap)
     866             : {
     867             :     // WARNING: keep in sync OGRLineString::isClockwise(),
     868             :     // OGRCurve::isClockwise() and OGRWKBIsClockwiseRing()
     869             : 
     870          19 :     bool bUseFallback = false;
     871             : 
     872             :     // Find the lowest rightmost vertex.
     873          19 :     uint32_t v = 0;  // Used after for.
     874          19 :     double vX = GetX(data, v, nDim, bNeedSwap);
     875          19 :     double vY = GetY(data, v, nDim, bNeedSwap);
     876         268 :     for (uint32_t i = 1; i < nPoints - 1; i++)
     877             :     {
     878             :         // => v < end.
     879         249 :         const double y = GetY(data, i, nDim, bNeedSwap);
     880         249 :         if (y < vY)
     881             :         {
     882          78 :             v = i;
     883          78 :             vX = GetX(data, i, nDim, bNeedSwap);
     884          78 :             vY = y;
     885          78 :             bUseFallback = false;
     886             :         }
     887         171 :         else if (y == vY)
     888             :         {
     889           2 :             const double x = GetX(data, i, nDim, bNeedSwap);
     890           2 :             if (x > vX)
     891             :             {
     892           0 :                 v = i;
     893           0 :                 vX = x;
     894           0 :                 vY = y;
     895           0 :                 bUseFallback = false;
     896             :             }
     897           2 :             else if (x == vX)
     898             :             {
     899             :                 // Two vertex with same coordinates are the lowest rightmost
     900             :                 // vertex.  Cannot use that point as the pivot (#5342).
     901           2 :                 bUseFallback = true;
     902             :             }
     903             :         }
     904             :     }
     905             : 
     906             :     // Previous.
     907          19 :     uint32_t next = (v == 0) ? nPoints - 2 : v - 1;
     908          23 :     if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
     909           4 :         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           0 :         bUseFallback = true;
     914             :     }
     915             : 
     916          19 :     const double dx0 = GetX(data, next, nDim, bNeedSwap) - vX;
     917          19 :     const double dy0 = GetY(data, next, nDim, bNeedSwap) - vY;
     918             : 
     919             :     // Following.
     920          19 :     next = v + 1;
     921          19 :     if (next >= nPoints - 1)
     922             :     {
     923           1 :         next = 0;
     924             :     }
     925             : 
     926          25 :     if (epsilonEqual(GetX(data, next, nDim, bNeedSwap), vX, EPSILON) &&
     927           6 :         epsilonEqual(GetY(data, next, nDim, bNeedSwap), vY, EPSILON))
     928             :     {
     929             :         // Don't try to be too clever by retrying with a next point.
     930             :         // This can lead to false results as in the case of #3356.
     931           2 :         bUseFallback = true;
     932             :     }
     933             : 
     934          19 :     const double dx1 = GetX(data, next, nDim, bNeedSwap) - vX;
     935          19 :     const double dy1 = GetY(data, next, nDim, bNeedSwap) - vY;
     936             : 
     937          19 :     const double crossproduct = dx1 * dy0 - dx0 * dy1;
     938             : 
     939          19 :     if (!bUseFallback)
     940             :     {
     941          17 :         if (crossproduct > 0)  // CCW
     942          13 :             return false;
     943           4 :         else if (crossproduct < 0)  // CW
     944           4 :             return true;
     945             :     }
     946             : 
     947             :     // This is a degenerate case: the extent of the polygon is less than EPSILON
     948             :     // or 2 nearly identical points were found.
     949             :     // Try with Green Formula as a fallback, but this is not a guarantee
     950             :     // as we'll probably be affected by numerical instabilities.
     951             : 
     952           2 :     double dfSum = GetX(data, 0, nDim, bNeedSwap) *
     953           2 :                    (GetY(data, 1, nDim, bNeedSwap) -
     954           2 :                     GetY(data, nPoints - 1, nDim, bNeedSwap));
     955             : 
     956          12 :     for (uint32_t i = 1; i < nPoints - 1; i++)
     957             :     {
     958          10 :         dfSum += GetX(data, i, nDim, bNeedSwap) *
     959          20 :                  (GetY(data, i + 1, nDim, bNeedSwap) -
     960          10 :                   GetY(data, i - 1, nDim, bNeedSwap));
     961             :     }
     962             : 
     963           2 :     dfSum += GetX(data, nPoints - 1, nDim, bNeedSwap) *
     964           2 :              (GetY(data, 0, nDim, bNeedSwap) -
     965           2 :               GetX(data, nPoints - 2, nDim, bNeedSwap));
     966             : 
     967           2 :     return dfSum < 0;
     968             : }
     969             : 
     970             : /************************************************************************/
     971             : /*                OGRWKBFixupCounterClockWiseExternalRing()             */
     972             : /************************************************************************/
     973             : 
     974          37 : static bool OGRWKBFixupCounterClockWiseExternalRingInternal(
     975             :     GByte *data, size_t size, size_t &iOffsetInOut, const int nRec)
     976             : {
     977          37 :     if (size - iOffsetInOut < MIN_WKB_SIZE)
     978             :     {
     979           0 :         return false;
     980             :     }
     981          37 :     const int nByteOrder = DB2_V72_FIX_BYTE_ORDER(data[iOffsetInOut]);
     982          37 :     if (!(nByteOrder == wkbXDR || nByteOrder == wkbNDR))
     983             :     {
     984           0 :         return false;
     985             :     }
     986          37 :     const OGRwkbByteOrder eByteOrder = static_cast<OGRwkbByteOrder>(nByteOrder);
     987             : 
     988          37 :     OGRwkbGeometryType eGeometryType = wkbUnknown;
     989          37 :     OGRReadWKBGeometryType(data + iOffsetInOut, wkbVariantIso, &eGeometryType);
     990          37 :     iOffsetInOut += 5;
     991          37 :     const auto eFlatType = wkbFlatten(eGeometryType);
     992          37 :     const int nDim = 2 + (OGR_GT_HasZ(eGeometryType) ? 1 : 0) +
     993          37 :                      (OGR_GT_HasM(eGeometryType) ? 1 : 0);
     994             : 
     995          37 :     if (eFlatType == wkbPolygon)
     996             :     {
     997             :         const uint32_t nRings =
     998          16 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
     999          16 :         if (nRings > (size - iOffsetInOut) / sizeof(uint32_t))
    1000             :         {
    1001           0 :             return false;
    1002             :         }
    1003          35 :         for (uint32_t iRing = 0; iRing < nRings; ++iRing)
    1004             :         {
    1005          19 :             if (iOffsetInOut + sizeof(uint32_t) > size)
    1006           0 :                 return false;
    1007             :             const uint32_t nPoints =
    1008          19 :                 OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
    1009          19 :             const size_t sizeOfPoint = nDim * sizeof(double);
    1010          19 :             if (nPoints > (size - iOffsetInOut) / sizeOfPoint)
    1011             :             {
    1012           0 :                 return false;
    1013             :             }
    1014             : 
    1015          19 :             if (nPoints >= 4)
    1016             :             {
    1017          19 :                 const bool bIsClockwiseRing = OGRWKBIsClockwiseRing(
    1018          19 :                     data + iOffsetInOut, nPoints, nDim, OGR_SWAP(eByteOrder));
    1019          19 :                 if ((bIsClockwiseRing && iRing == 0) ||
    1020          15 :                     (!bIsClockwiseRing && iRing > 0))
    1021             :                 {
    1022             :                     GByte abyTmp[4 * sizeof(double)];
    1023          19 :                     for (uint32_t i = 0; i < nPoints / 2; ++i)
    1024             :                     {
    1025          13 :                         GByte *pBegin = data + iOffsetInOut + i * sizeOfPoint;
    1026          13 :                         GByte *pEnd = data + iOffsetInOut +
    1027          13 :                                       (nPoints - 1 - i) * sizeOfPoint;
    1028          13 :                         memcpy(abyTmp, pBegin, sizeOfPoint);
    1029          13 :                         memcpy(pBegin, pEnd, sizeOfPoint);
    1030          13 :                         memcpy(pEnd, abyTmp, sizeOfPoint);
    1031             :                     }
    1032             :                 }
    1033             :             }
    1034             : 
    1035          19 :             iOffsetInOut += nPoints * sizeOfPoint;
    1036             :         }
    1037             :     }
    1038             : 
    1039          37 :     if (eFlatType == wkbGeometryCollection || eFlatType == wkbMultiPolygon ||
    1040             :         eFlatType == wkbMultiSurface)
    1041             :     {
    1042           6 :         if (nRec == 128)
    1043             :         {
    1044           0 :             return false;
    1045             :         }
    1046             :         const uint32_t nParts =
    1047           6 :             OGRWKBReadUInt32AtOffset(data, eByteOrder, iOffsetInOut);
    1048           6 :         if (nParts > (size - iOffsetInOut) / MIN_WKB_SIZE)
    1049             :         {
    1050           0 :             return false;
    1051             :         }
    1052          11 :         for (uint32_t k = 0; k < nParts; k++)
    1053             :         {
    1054           5 :             if (!OGRWKBFixupCounterClockWiseExternalRingInternal(
    1055             :                     data, size, iOffsetInOut, nRec))
    1056             :             {
    1057           0 :                 return false;
    1058             :             }
    1059             :         }
    1060             :     }
    1061             : 
    1062          37 :     return true;
    1063             : }
    1064             : 
    1065             : /** Modifies the geometry such that exterior rings of polygons are
    1066             :  * counter-clockwise oriented and inner rings clockwise oriented.
    1067             :  */
    1068          32 : void OGRWKBFixupCounterClockWiseExternalRing(GByte *pabyWkb, size_t nWKBSize)
    1069             : {
    1070          32 :     size_t iOffsetInOut = 0;
    1071          32 :     OGRWKBFixupCounterClockWiseExternalRingInternal(
    1072             :         pabyWkb, nWKBSize, iOffsetInOut, /* nRec = */ 0);
    1073          32 : }
    1074             : 
    1075             : /************************************************************************/
    1076             : /*                         OGRAppendBuffer()                            */
    1077             : /************************************************************************/
    1078             : 
    1079             : OGRAppendBuffer::OGRAppendBuffer() = default;
    1080             : 
    1081             : /************************************************************************/
    1082             : /*                        ~OGRAppendBuffer()                            */
    1083             : /************************************************************************/
    1084             : 
    1085             : OGRAppendBuffer::~OGRAppendBuffer() = default;
    1086             : 
    1087             : /************************************************************************/
    1088             : /*                       OGRWKTToWKBTranslator()                        */
    1089             : /************************************************************************/
    1090             : 
    1091          18 : OGRWKTToWKBTranslator::OGRWKTToWKBTranslator(OGRAppendBuffer &oAppendBuffer)
    1092          18 :     : m_oAppendBuffer(oAppendBuffer)
    1093             : {
    1094             : #ifndef USE_FAST_FLOAT
    1095             :     // Test if current locale decimal separator is decimal point
    1096             :     char szTest[10];
    1097             :     snprintf(szTest, sizeof(szTest), "%f", 1.5);
    1098             :     m_bCanUseStrtod = strchr(szTest, '.') != nullptr;
    1099             : #endif
    1100          18 :     CPL_IGNORE_RET_VAL(m_bCanUseStrtod);
    1101          18 : }
    1102             : 
    1103             : /************************************************************************/
    1104             : /*                          TranslateWKT()                              */
    1105             : /************************************************************************/
    1106             : 
    1107         138 : size_t OGRWKTToWKBTranslator::TranslateWKT(void *pabyWKTStart, size_t nLength,
    1108             :                                            bool bCanAlterByteAfter)
    1109             : {
    1110         138 :     const char *pszPtrStart = static_cast<const char *>(pabyWKTStart);
    1111             :     // Optimize single-part single-ring multipolygon WKT->WKB translation
    1112         138 :     if (bCanAlterByteAfter && nLength > strlen("MULTIPOLYGON") &&
    1113          28 :         EQUALN(pszPtrStart, "MULTIPOLYGON", strlen("MULTIPOLYGON")))
    1114             :     {
    1115          28 :         int nCountOpenPar = 0;
    1116          28 :         size_t nCountComma = 0;
    1117          28 :         bool bHasZ = false;
    1118          28 :         bool bHasM = false;
    1119             : 
    1120          28 :         char *pszEnd = static_cast<char *>(pabyWKTStart) + nLength;
    1121          28 :         const char chBackup = *pszEnd;
    1122          28 :         *pszEnd = 0;
    1123             : 
    1124             :         // Checks that the multipolygon consists of a single part with
    1125             :         // only an exterior ring.
    1126         852 :         for (const char *pszPtr = pszPtrStart + strlen("MULTIPOLYGON"); *pszPtr;
    1127             :              ++pszPtr)
    1128             :         {
    1129         832 :             const char ch = *pszPtr;
    1130         832 :             if (ch == 'Z')
    1131           8 :                 bHasZ = true;
    1132         824 :             else if (ch == 'M')
    1133           8 :                 bHasM = true;
    1134         832 :             if (ch == '(')
    1135             :             {
    1136          84 :                 nCountOpenPar++;
    1137          84 :                 if (nCountOpenPar == 4)
    1138           0 :                     break;
    1139             :             }
    1140         748 :             else if (ch == ')')
    1141             :             {
    1142          72 :                 nCountOpenPar--;
    1143          72 :                 if (nCountOpenPar < 0)
    1144           0 :                     break;
    1145             :             }
    1146         676 :             else if (ch == ',')
    1147             :             {
    1148          92 :                 if (nCountOpenPar < 3)
    1149             :                 {
    1150             :                     // multipart / multi-ring
    1151           8 :                     break;
    1152             :                 }
    1153          84 :                 nCountComma++;
    1154             :             }
    1155             :         }
    1156          28 :         const int nDim = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
    1157          48 :         if (nCountOpenPar == 0 && nCountComma > 0 &&
    1158          20 :             nCountComma < std::numeric_limits<uint32_t>::max())
    1159             :         {
    1160          20 :             const uint32_t nVerticesCount =
    1161          20 :                 static_cast<uint32_t>(nCountComma + 1);
    1162          20 :             const size_t nWKBSize =
    1163             :                 sizeof(GByte) +     // Endianness
    1164             :                 sizeof(uint32_t) +  // multipolygon WKB geometry type
    1165             :                 sizeof(uint32_t) +  // number of parts
    1166             :                 sizeof(GByte) +     // Endianness
    1167             :                 sizeof(uint32_t) +  // polygon WKB geometry type
    1168             :                 sizeof(uint32_t) +  // number of rings
    1169             :                 sizeof(uint32_t) +  // number of vertices
    1170          20 :                 nDim * sizeof(double) * nVerticesCount;
    1171             :             GByte *const pabyCurStart = static_cast<GByte *>(
    1172          20 :                 m_oAppendBuffer.GetPtrForNewBytes(nWKBSize));
    1173          20 :             if (!pabyCurStart)
    1174             :             {
    1175           0 :                 return static_cast<size_t>(-1);
    1176             :             }
    1177          20 :             GByte *pabyCur = pabyCurStart;
    1178             :             // Multipolygon byte order
    1179             :             {
    1180          20 :                 *pabyCur = wkbNDR;
    1181          20 :                 pabyCur++;
    1182             :             }
    1183             :             // Multipolygon geometry type
    1184             :             {
    1185          20 :                 uint32_t nWKBGeomType =
    1186          20 :                     wkbMultiPolygon + (bHasZ ? 1000 : 0) + (bHasM ? 2000 : 0);
    1187          20 :                 CPL_LSBPTR32(&nWKBGeomType);
    1188          20 :                 memcpy(pabyCur, &nWKBGeomType, sizeof(uint32_t));
    1189          20 :                 pabyCur += sizeof(uint32_t);
    1190             :             }
    1191             :             // Number of parts
    1192             :             {
    1193          20 :                 uint32_t nOne = 1;
    1194          20 :                 CPL_LSBPTR32(&nOne);
    1195          20 :                 memcpy(pabyCur, &nOne, sizeof(uint32_t));
    1196          20 :                 pabyCur += sizeof(uint32_t);
    1197             :             }
    1198             :             // Polygon byte order
    1199             :             {
    1200          20 :                 *pabyCur = wkbNDR;
    1201          20 :                 pabyCur++;
    1202             :             }
    1203             :             // Polygon geometry type
    1204             :             {
    1205          20 :                 uint32_t nWKBGeomType =
    1206          20 :                     wkbPolygon + (bHasZ ? 1000 : 0) + (bHasM ? 2000 : 0);
    1207          20 :                 CPL_LSBPTR32(&nWKBGeomType);
    1208          20 :                 memcpy(pabyCur, &nWKBGeomType, sizeof(uint32_t));
    1209          20 :                 pabyCur += sizeof(uint32_t);
    1210             :             }
    1211             :             // Number of rings
    1212             :             {
    1213          20 :                 uint32_t nOne = 1;
    1214          20 :                 CPL_LSBPTR32(&nOne);
    1215          20 :                 memcpy(pabyCur, &nOne, sizeof(uint32_t));
    1216          20 :                 pabyCur += sizeof(uint32_t);
    1217             :             }
    1218             :             // Number of vertices
    1219             :             {
    1220          20 :                 uint32_t nVerticesCountToWrite = nVerticesCount;
    1221          20 :                 CPL_LSBPTR32(&nVerticesCountToWrite);
    1222          20 :                 memcpy(pabyCur, &nVerticesCountToWrite, sizeof(uint32_t));
    1223          20 :                 pabyCur += sizeof(uint32_t);
    1224             :             }
    1225          20 :             uint32_t nDoubleCount = 0;
    1226          20 :             const uint32_t nExpectedDoubleCount = nVerticesCount * nDim;
    1227         616 :             for (const char *pszPtr = pszPtrStart + strlen("MULTIPOLYGON");
    1228         616 :                  *pszPtr;
    1229             :                  /* nothing */)
    1230             :             {
    1231         596 :                 const char ch = *pszPtr;
    1232         596 :                 if (ch == '-' || ch == '.' || (ch >= '0' && ch <= '9'))
    1233             :                 {
    1234         224 :                     nDoubleCount++;
    1235         224 :                     if (nDoubleCount > nExpectedDoubleCount)
    1236             :                     {
    1237           0 :                         break;
    1238             :                     }
    1239             : #ifdef USE_FAST_FLOAT
    1240             :                     double dfVal;
    1241         224 :                     auto answer = fast_float::from_chars(pszPtr, pszEnd, dfVal);
    1242         224 :                     if (answer.ec != std::errc())
    1243             :                     {
    1244           0 :                         nDoubleCount = 0;
    1245           0 :                         break;
    1246             :                     }
    1247         224 :                     pszPtr = answer.ptr;
    1248             : #else
    1249             :                     char *endptr = nullptr;
    1250             :                     const double dfVal =
    1251             :                         m_bCanUseStrtod ? strtod(pszPtr, &endptr)
    1252             :                                         : CPLStrtodDelim(pszPtr, &endptr, '.');
    1253             :                     pszPtr = endptr;
    1254             : #endif
    1255         224 :                     CPL_LSBPTR64(&dfVal);
    1256         224 :                     memcpy(pabyCur, &dfVal, sizeof(double));
    1257         224 :                     pabyCur += sizeof(double);
    1258             :                 }
    1259             :                 else
    1260             :                 {
    1261         372 :                     ++pszPtr;
    1262             :                 }
    1263             :             }
    1264          20 :             if (nDoubleCount == nExpectedDoubleCount)
    1265             :             {
    1266          20 :                 CPLAssert(static_cast<size_t>(pabyCur - pabyCurStart) ==
    1267             :                           nWKBSize);
    1268             :                 // cppcheck-suppress selfAssignment
    1269          20 :                 *pszEnd = chBackup;
    1270          20 :                 return nWKBSize;
    1271             :             }
    1272             :             else
    1273             :             {
    1274           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1275             :                          "Invalid WKT geometry: %s", pszPtrStart);
    1276             :                 // cppcheck-suppress selfAssignment
    1277           0 :                 *pszEnd = chBackup;
    1278           0 :                 return static_cast<size_t>(-1);
    1279             :             }
    1280             :         }
    1281             :         // cppcheck-suppress selfAssignment
    1282           8 :         *pszEnd = chBackup;
    1283             :     }
    1284             : 
    1285             :     // General case going through a OGRGeometry
    1286         118 :     OGRGeometry *poGeometry = nullptr;
    1287         118 :     if (bCanAlterByteAfter)
    1288             :     {
    1289             :         // Slight optimization for all geometries but the final one, to
    1290             :         // avoid creating a new string each time.
    1291             :         // We set the ending byte to '\0' and restore it back after parsing
    1292             :         // the WKT
    1293         100 :         char *pszEnd = static_cast<char *>(pabyWKTStart) + nLength;
    1294         100 :         const char chBackup = *pszEnd;
    1295         100 :         *pszEnd = 0;
    1296         100 :         OGRGeometryFactory::createFromWkt(pszPtrStart, nullptr, &poGeometry);
    1297             :         // cppcheck-suppress selfAssignment
    1298         100 :         *pszEnd = chBackup;
    1299             :     }
    1300             :     else
    1301             :     {
    1302          36 :         std::string osTmp;
    1303          18 :         osTmp.assign(pszPtrStart, nLength);
    1304          18 :         OGRGeometryFactory::createFromWkt(osTmp.c_str(), nullptr, &poGeometry);
    1305             :     }
    1306         118 :     if (!poGeometry)
    1307             :     {
    1308           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid WKT geometry");
    1309           0 :         return static_cast<size_t>(-1);
    1310             :     }
    1311         118 :     const size_t nWKBSize = poGeometry->WkbSize();
    1312             :     GByte *pabyWKB =
    1313         118 :         static_cast<GByte *>(m_oAppendBuffer.GetPtrForNewBytes(nWKBSize));
    1314         118 :     if (!pabyWKB)
    1315             :     {
    1316           0 :         return static_cast<size_t>(-1);
    1317             :     }
    1318         118 :     poGeometry->exportToWkb(wkbNDR, pabyWKB, wkbVariantIso);
    1319         118 :     delete poGeometry;
    1320         118 :     return nWKBSize;
    1321             : }

Generated by: LCOV version 1.14