LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - filegdbtable.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1616 1909 84.7 %
Date: 2024-05-02 22:57:13 Functions: 93 94 98.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements reading of FileGDB tables
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2014, 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_port.h"
      30             : #include "filegdbtable.h"
      31             : 
      32             : #include <algorithm>
      33             : #include <errno.h>
      34             : #include <limits.h>
      35             : #include <stddef.h>
      36             : #include <stdio.h>
      37             : #include <string.h>
      38             : #include <time.h>
      39             : #include <limits>
      40             : #include <string>
      41             : #include <vector>
      42             : 
      43             : #include "cpl_conv.h"
      44             : #include "cpl_error.h"
      45             : #include "cpl_string.h"
      46             : #include "cpl_time.h"
      47             : #include "cpl_vsi.h"
      48             : #include "filegdbtable_priv.h"
      49             : #include "ogr_api.h"
      50             : #include "ogr_core.h"
      51             : #include "ogr_geometry.h"
      52             : #include "ogrpgeogeometry.h"
      53             : #include "ogr_spatialref.h"
      54             : 
      55             : #define UUID_SIZE_IN_BYTES 16
      56             : 
      57             : #define IS_VALID_LAYER_GEOM_TYPE(byVal)                                        \
      58             :     ((byVal) <= FGTGT_POLYGON || (byVal) == FGTGT_MULTIPATCH)
      59             : 
      60             : /* Reserve one extra byte in case the last field is a string */
      61             : /*       or 2 for 2 ReadVarIntAndAddNoCheck() in a row */
      62             : /*       or 4 for SkipVarUInt() with nIter = 4 */
      63             : /*       or for 4 ReadVarUInt64NoCheck */
      64             : #define ZEROES_AFTER_END_OF_BUFFER 4
      65             : 
      66             : constexpr GUInt32 EXT_SHAPE_Z_FLAG = 0x80000000U;
      67             : constexpr GUInt32 EXT_SHAPE_M_FLAG = 0x40000000U;
      68             : constexpr GUInt32 EXT_SHAPE_CURVE_FLAG = 0x20000000U;
      69             : 
      70             : constexpr GUInt32 EXT_SHAPE_SEGMENT_ARC = 1;
      71             : constexpr GUInt32 EXT_SHAPE_SEGMENT_BEZIER = 4;
      72             : constexpr GUInt32 EXT_SHAPE_SEGMENT_ELLIPSE = 5;
      73             : 
      74             : namespace OpenFileGDB
      75             : {
      76             : 
      77             : /************************************************************************/
      78             : /*                           SanitizeScale()                            */
      79             : /************************************************************************/
      80             : 
      81        3806 : static double SanitizeScale(double dfVal)
      82             : {
      83        3806 :     if (dfVal == 0.0)
      84           0 :         return std::numeric_limits<double>::min();  // to prevent divide by zero
      85        3806 :     return dfVal;
      86             : }
      87             : 
      88             : /************************************************************************/
      89             : /*                      FileGDBTablePrintError()                        */
      90             : /************************************************************************/
      91             : 
      92         125 : void FileGDBTablePrintError(const char *pszFile, int nLineNumber)
      93             : {
      94         125 :     CPLError(CE_Failure, CPLE_AppDefined, "Error occurred in %s at line %d",
      95             :              pszFile, nLineNumber);
      96         125 : }
      97             : 
      98             : /************************************************************************/
      99             : /*                            FileGDBTable()                            */
     100             : /************************************************************************/
     101             : 
     102        6222 : FileGDBTable::FileGDBTable()
     103             : {
     104        6222 :     memset(&m_sCurField, 0, sizeof(m_sCurField));
     105        6222 : }
     106             : 
     107             : /************************************************************************/
     108             : /*                           ~FileGDBTable()                            */
     109             : /************************************************************************/
     110             : 
     111        6222 : FileGDBTable::~FileGDBTable()
     112             : {
     113        6222 :     Close();
     114        6222 : }
     115             : 
     116             : /************************************************************************/
     117             : /*                                Close()                               */
     118             : /************************************************************************/
     119             : 
     120        7034 : void FileGDBTable::Close()
     121             : {
     122        7034 :     Sync();
     123             : 
     124        7034 :     if (m_fpTable)
     125        6209 :         VSIFCloseL(m_fpTable);
     126        7034 :     m_fpTable = nullptr;
     127             : 
     128        7034 :     if (m_fpTableX)
     129        6093 :         VSIFCloseL(m_fpTableX);
     130        7034 :     m_fpTableX = nullptr;
     131        7034 : }
     132             : 
     133             : /************************************************************************/
     134             : /*                              GetFieldIdx()                           */
     135             : /************************************************************************/
     136             : 
     137       36820 : int FileGDBTable::GetFieldIdx(const std::string &osName) const
     138             : {
     139      198878 :     for (size_t i = 0; i < m_apoFields.size(); i++)
     140             :     {
     141      185898 :         if (m_apoFields[i]->GetName() == osName)
     142       23840 :             return static_cast<int>(i);
     143             :     }
     144       12980 :     return -1;
     145             : }
     146             : 
     147             : /************************************************************************/
     148             : /*                          ReadVarUInt()                               */
     149             : /************************************************************************/
     150             : 
     151             : template <class OutType, class ControlType>
     152      277695 : static int ReadVarUInt(GByte *&pabyIter, GByte *pabyEnd, OutType &nOutVal)
     153             : {
     154      277695 :     const int errorRetValue = FALSE;
     155             :     if (!(ControlType::check_bounds))
     156             :     {
     157             :         /* nothing */
     158             :     }
     159             :     else if (ControlType::verbose_error)
     160             :     {
     161      207700 :         returnErrorIf(pabyIter >= pabyEnd);
     162             :     }
     163             :     else
     164             :     {
     165         913 :         if (pabyIter >= pabyEnd)
     166           0 :             return FALSE;
     167             :     }
     168      277695 :     OutType b = *pabyIter;
     169      277695 :     if ((b & 0x80) == 0)
     170             :     {
     171      221317 :         pabyIter++;
     172      221317 :         nOutVal = b;
     173      221317 :         return TRUE;
     174             :     }
     175       56378 :     GByte *pabyLocalIter = pabyIter + 1;
     176       56378 :     int nShift = 7;
     177       56378 :     OutType nVal = (b & 0x7F);
     178             :     while (true)
     179             :     {
     180      185609 :         if (!(ControlType::check_bounds))
     181             :         {
     182             :             /* nothing */
     183             :         }
     184          67 :         else if (ControlType::verbose_error)
     185             :         {
     186       17548 :             returnErrorIf(pabyLocalIter >= pabyEnd);
     187             :         }
     188             :         else
     189             :         {
     190          91 :             if (pabyLocalIter >= pabyEnd)
     191           0 :                 return FALSE;
     192             :         }
     193      242054 :         b = *pabyLocalIter;
     194      242054 :         pabyLocalIter++;
     195      242054 :         nVal |= (b & 0x7F) << nShift;
     196      242054 :         if ((b & 0x80) == 0)
     197             :         {
     198       56378 :             pabyIter = pabyLocalIter;
     199       56378 :             nOutVal = nVal;
     200       56378 :             return TRUE;
     201             :         }
     202      185676 :         nShift += 7;
     203             :         // To avoid undefined behavior later when doing << nShift
     204      185676 :         if (nShift >= static_cast<int>(sizeof(OutType)) * 8)
     205             :         {
     206           0 :             pabyIter = pabyLocalIter;
     207           0 :             nOutVal = nVal;
     208           0 :             returnError();
     209             :         }
     210             :     }
     211             : }
     212             : 
     213             : struct ControlTypeVerboseErrorTrue
     214             : {
     215             :     // cppcheck-suppress unusedStructMember
     216             :     static const bool check_bounds = true;
     217             :     // cppcheck-suppress unusedStructMember
     218             :     static const bool verbose_error = true;
     219             : };
     220             : 
     221             : struct ControlTypeVerboseErrorFalse
     222             : {
     223             :     // cppcheck-suppress unusedStructMember
     224             :     static const bool check_bounds = true;
     225             :     // cppcheck-suppress unusedStructMember
     226             :     static const bool verbose_error = false;
     227             : };
     228             : 
     229             : struct ControlTypeNone
     230             : {
     231             :     // cppcheck-suppress unusedStructMember
     232             :     static const bool check_bounds = false;
     233             :     // cppcheck-suppress unusedStructMember
     234             :     static const bool verbose_error = false;
     235             : };
     236             : 
     237      207700 : static int ReadVarUInt32(GByte *&pabyIter, GByte *pabyEnd, GUInt32 &nOutVal)
     238             : {
     239      207700 :     return ReadVarUInt<GUInt32, ControlTypeVerboseErrorTrue>(pabyIter, pabyEnd,
     240      207700 :                                                              nOutVal);
     241             : }
     242             : 
     243       30609 : static void ReadVarUInt32NoCheck(GByte *&pabyIter, GUInt32 &nOutVal)
     244             : {
     245       30609 :     GByte *pabyEnd = nullptr;
     246       30609 :     ReadVarUInt<GUInt32, ControlTypeNone>(pabyIter, pabyEnd, nOutVal);
     247       30609 : }
     248             : 
     249         913 : static int ReadVarUInt32Silent(GByte *&pabyIter, GByte *pabyEnd,
     250             :                                GUInt32 &nOutVal)
     251             : {
     252         913 :     return ReadVarUInt<GUInt32, ControlTypeVerboseErrorFalse>(pabyIter, pabyEnd,
     253         913 :                                                               nOutVal);
     254             : }
     255             : 
     256       38473 : static void ReadVarUInt64NoCheck(GByte *&pabyIter, GUIntBig &nOutVal)
     257             : {
     258       38473 :     GByte *pabyEnd = nullptr;
     259       38473 :     ReadVarUInt<GUIntBig, ControlTypeNone>(pabyIter, pabyEnd, nOutVal);
     260       38473 : }
     261             : 
     262             : /************************************************************************/
     263             : /*                      IsLikelyFeatureAtOffset()                       */
     264             : /************************************************************************/
     265             : 
     266        3438 : int FileGDBTable::IsLikelyFeatureAtOffset(vsi_l_offset nOffset, GUInt32 *pnSize,
     267             :                                           int *pbDeletedRecord)
     268             : {
     269        3438 :     VSIFSeekL(m_fpTable, nOffset, SEEK_SET);
     270             :     GByte abyBuffer[4];
     271        3438 :     if (VSIFReadL(abyBuffer, 4, 1, m_fpTable) != 1)
     272           3 :         return FALSE;
     273             : 
     274        3435 :     m_nRowBlobLength = GetUInt32(abyBuffer, 0);
     275        3435 :     if (m_nRowBlobLength < static_cast<GUInt32>(m_nNullableFieldsSizeInBytes) ||
     276        3286 :         m_nRowBlobLength > m_nFileSize - nOffset ||
     277         631 :         m_nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER ||
     278         631 :         m_nRowBlobLength > 10 * (m_nFileSize / m_nValidRecordCount))
     279             :     {
     280             :         /* Is it a deleted record ? */
     281        2828 :         if ((m_nRowBlobLength >> (8 * sizeof(m_nRowBlobLength) - 1)) != 0 &&
     282         210 :             m_nRowBlobLength != 0x80000000U)
     283             :         {
     284         210 :             m_nRowBlobLength =
     285         210 :                 static_cast<GUInt32>(-static_cast<int>(m_nRowBlobLength));
     286         210 :             if (m_nRowBlobLength <
     287         210 :                     static_cast<GUInt32>(m_nNullableFieldsSizeInBytes) ||
     288         210 :                 m_nRowBlobLength > m_nFileSize - nOffset ||
     289          53 :                 m_nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER ||
     290          53 :                 m_nRowBlobLength > 10 * (m_nFileSize / m_nValidRecordCount))
     291         157 :                 return FALSE;
     292             :             else
     293          53 :                 *pbDeletedRecord = TRUE;
     294             :         }
     295             :         else
     296        2618 :             return FALSE;
     297             :     }
     298             :     else
     299         607 :         *pbDeletedRecord = FALSE;
     300             : 
     301         660 :     m_nRowBufferMaxSize = std::max(m_nRowBlobLength, m_nRowBufferMaxSize);
     302         660 :     if (m_abyBuffer.size() < m_nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER)
     303             :     {
     304             :         try
     305             :         {
     306           4 :             m_abyBuffer.resize(m_nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER);
     307             :         }
     308           0 :         catch (const std::exception &e)
     309             :         {
     310           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     311           0 :             return FALSE;
     312             :         }
     313             :     }
     314         660 :     if (m_nCountNullableFields > 0)
     315             :     {
     316         610 :         if (VSIFReadL(m_abyBuffer.data(), m_nNullableFieldsSizeInBytes, 1,
     317         610 :                       m_fpTable) != 1)
     318           0 :             return FALSE;
     319             :     }
     320         660 :     m_iAccNullable = 0;
     321         660 :     int bExactSizeKnown = TRUE;
     322         660 :     GUInt32 nRequiredLength = m_nNullableFieldsSizeInBytes;
     323        5007 :     for (int i = 0; i < static_cast<int>(m_apoFields.size()); i++)
     324             :     {
     325        4365 :         if (m_apoFields[i]->m_bNullable)
     326             :         {
     327        3404 :             int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
     328        3404 :             m_iAccNullable++;
     329        3404 :             if (bIsNull)
     330         805 :                 continue;
     331             :         }
     332             : 
     333        3560 :         switch (m_apoFields[i]->m_eType)
     334             :         {
     335           0 :             case FGFT_UNDEFINED:
     336           0 :                 CPLAssert(false);
     337             :                 break;
     338             : 
     339         660 :             case FGFT_OBJECTID:
     340         660 :                 break;
     341             : 
     342        1196 :             case FGFT_STRING:
     343             :             case FGFT_XML:
     344             :             case FGFT_GEOMETRY:
     345             :             case FGFT_BINARY:
     346             :             {
     347        1196 :                 nRequiredLength += 1; /* varuint32 so at least one byte */
     348        1196 :                 bExactSizeKnown = FALSE;
     349        1196 :                 break;
     350             :             }
     351             : 
     352           0 :             case FGFT_RASTER:
     353             :             {
     354             :                 const FileGDBRasterField *rasterField =
     355           0 :                     cpl::down_cast<const FileGDBRasterField *>(
     356           0 :                         m_apoFields[i].get());
     357           0 :                 if (rasterField->GetRasterType() ==
     358             :                     FileGDBRasterField::Type::MANAGED)
     359           0 :                     nRequiredLength += sizeof(GInt32);
     360             :                 else
     361           0 :                     nRequiredLength += 1; /* varuint32 so at least one byte */
     362           0 :                 break;
     363             :             }
     364             : 
     365          95 :             case FGFT_INT16:
     366          95 :                 nRequiredLength += sizeof(GInt16);
     367          95 :                 break;
     368         677 :             case FGFT_INT32:
     369         677 :                 nRequiredLength += sizeof(GInt32);
     370         677 :                 break;
     371          95 :             case FGFT_FLOAT32:
     372          95 :                 nRequiredLength += sizeof(float);
     373          95 :                 break;
     374         436 :             case FGFT_FLOAT64:
     375         436 :                 nRequiredLength += sizeof(double);
     376         436 :                 break;
     377          95 :             case FGFT_DATETIME:
     378             :             case FGFT_DATE:
     379             :             case FGFT_TIME:
     380          95 :                 nRequiredLength += sizeof(double);
     381          95 :                 break;
     382         306 :             case FGFT_GUID:
     383             :             case FGFT_GLOBALID:
     384         306 :                 nRequiredLength += UUID_SIZE_IN_BYTES;
     385         306 :                 break;
     386           0 :             case FGFT_INT64:
     387           0 :                 nRequiredLength += sizeof(int64_t);
     388           0 :                 break;
     389           0 :             case FGFT_DATETIME_WITH_OFFSET:
     390           0 :                 nRequiredLength += sizeof(double) + sizeof(int16_t);
     391           0 :                 break;
     392             :         }
     393        3560 :         if (m_nRowBlobLength < nRequiredLength)
     394          18 :             return FALSE;
     395             :     }
     396         642 :     if (!bExactSizeKnown)
     397             :     {
     398         289 :         if (VSIFReadL(m_abyBuffer.data() + m_nNullableFieldsSizeInBytes,
     399         289 :                       m_nRowBlobLength - m_nNullableFieldsSizeInBytes, 1,
     400         289 :                       m_fpTable) != 1)
     401           0 :             return FALSE;
     402             : 
     403         289 :         m_iAccNullable = 0;
     404         289 :         nRequiredLength = m_nNullableFieldsSizeInBytes;
     405        3132 :         for (int i = 0; i < static_cast<int>(m_apoFields.size()); i++)
     406             :         {
     407        2905 :             if (m_apoFields[i]->m_bNullable)
     408             :             {
     409        2348 :                 int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
     410        2348 :                 m_iAccNullable++;
     411        2348 :                 if (bIsNull)
     412         639 :                     continue;
     413             :             }
     414             : 
     415        2266 :             switch (m_apoFields[i]->m_eType)
     416             :             {
     417           0 :                 case FGFT_UNDEFINED:
     418           0 :                     CPLAssert(false);
     419             :                     break;
     420             : 
     421         279 :                 case FGFT_OBJECTID:
     422         279 :                     break;
     423             : 
     424         603 :                 case FGFT_STRING:
     425             :                 case FGFT_XML:
     426             :                 {
     427         603 :                     GByte *pabyIter = m_abyBuffer.data() + nRequiredLength;
     428             :                     GUInt32 nLength;
     429        1809 :                     if (!ReadVarUInt32Silent(
     430         603 :                             pabyIter, m_abyBuffer.data() + m_nRowBlobLength,
     431        1206 :                             nLength) ||
     432         603 :                         pabyIter - (m_abyBuffer.data() + nRequiredLength) > 5)
     433          50 :                         return FALSE;
     434         603 :                     nRequiredLength =
     435         603 :                         static_cast<GUInt32>(pabyIter - m_abyBuffer.data());
     436         603 :                     if (nLength > m_nRowBlobLength - nRequiredLength)
     437          22 :                         return FALSE;
     438      157797 :                     for (GUInt32 j = 0; j < nLength; j++)
     439             :                     {
     440      157240 :                         if (pabyIter[j] == 0)
     441          24 :                             return FALSE;
     442             :                     }
     443         557 :                     if (!CPLIsUTF8(reinterpret_cast<const char *>(pabyIter),
     444             :                                    nLength))
     445           4 :                         return FALSE;
     446         553 :                     nRequiredLength += nLength;
     447         553 :                     break;
     448             :                 }
     449             : 
     450         310 :                 case FGFT_GEOMETRY:
     451             :                 case FGFT_BINARY:
     452             :                 {
     453         310 :                     GByte *pabyIter = m_abyBuffer.data() + nRequiredLength;
     454             :                     GUInt32 nLength;
     455         930 :                     if (!ReadVarUInt32Silent(
     456         310 :                             pabyIter, m_abyBuffer.data() + m_nRowBlobLength,
     457         620 :                             nLength) ||
     458         310 :                         pabyIter - (m_abyBuffer.data() + nRequiredLength) > 5)
     459          12 :                         return FALSE;
     460         310 :                     nRequiredLength =
     461         310 :                         static_cast<GUInt32>(pabyIter - m_abyBuffer.data());
     462         310 :                     if (nLength > m_nRowBlobLength - nRequiredLength)
     463          12 :                         return FALSE;
     464         298 :                     nRequiredLength += nLength;
     465         298 :                     break;
     466             :                 }
     467             : 
     468           0 :                 case FGFT_RASTER:
     469             :                 {
     470             :                     const FileGDBRasterField *rasterField =
     471           0 :                         cpl::down_cast<const FileGDBRasterField *>(
     472           0 :                             m_apoFields[i].get());
     473           0 :                     if (rasterField->GetRasterType() ==
     474             :                         FileGDBRasterField::Type::MANAGED)
     475           0 :                         nRequiredLength += sizeof(GInt32);
     476             :                     else
     477             :                     {
     478           0 :                         GByte *pabyIter = m_abyBuffer.data() + nRequiredLength;
     479             :                         GUInt32 nLength;
     480           0 :                         if (!ReadVarUInt32Silent(
     481           0 :                                 pabyIter, m_abyBuffer.data() + m_nRowBlobLength,
     482           0 :                                 nLength) ||
     483           0 :                             pabyIter - (m_abyBuffer.data() + nRequiredLength) >
     484             :                                 5)
     485           0 :                             return FALSE;
     486           0 :                         nRequiredLength =
     487           0 :                             static_cast<GUInt32>(pabyIter - m_abyBuffer.data());
     488           0 :                         if (nLength > m_nRowBlobLength - nRequiredLength)
     489           0 :                             return FALSE;
     490           0 :                         nRequiredLength += nLength;
     491             :                     }
     492           0 :                     break;
     493             :                 }
     494             : 
     495          95 :                 case FGFT_INT16:
     496          95 :                     nRequiredLength += sizeof(GInt16);
     497          95 :                     break;
     498         411 :                 case FGFT_INT32:
     499         411 :                     nRequiredLength += sizeof(GInt32);
     500         411 :                     break;
     501          95 :                 case FGFT_FLOAT32:
     502          95 :                     nRequiredLength += sizeof(float);
     503          95 :                     break;
     504          95 :                 case FGFT_FLOAT64:
     505          95 :                     nRequiredLength += sizeof(double);
     506          95 :                     break;
     507          95 :                 case FGFT_DATETIME:
     508             :                 case FGFT_DATE:
     509             :                 case FGFT_TIME:
     510          95 :                     nRequiredLength += sizeof(double);
     511          95 :                     break;
     512         283 :                 case FGFT_GUID:
     513             :                 case FGFT_GLOBALID:
     514         283 :                     nRequiredLength += UUID_SIZE_IN_BYTES;
     515         283 :                     break;
     516           0 :                 case FGFT_INT64:
     517           0 :                     nRequiredLength += sizeof(int64_t);
     518           0 :                     break;
     519           0 :                 case FGFT_DATETIME_WITH_OFFSET:
     520           0 :                     nRequiredLength += sizeof(double) + sizeof(int16_t);
     521           0 :                     break;
     522             :             }
     523        2204 :             if (nRequiredLength > m_nRowBlobLength)
     524           0 :                 return FALSE;
     525             :         }
     526             :     }
     527             : 
     528         580 :     *pnSize = 4 + nRequiredLength;
     529         580 :     return nRequiredLength == m_nRowBlobLength;
     530             : }
     531             : 
     532             : /************************************************************************/
     533             : /*                      GuessFeatureLocations()                         */
     534             : /************************************************************************/
     535             : 
     536             : #define MARK_DELETED(x) ((x) | (static_cast<GUIntBig>(1) << 63))
     537             : #define IS_DELETED(x) (((x) & (static_cast<GUIntBig>(1) << 63)) != 0)
     538             : #define GET_OFFSET(x) ((x) & ~(static_cast<GUIntBig>(1) << 63))
     539             : 
     540          38 : bool FileGDBTable::GuessFeatureLocations()
     541             : {
     542          38 :     VSIFSeekL(m_fpTable, 0, SEEK_END);
     543          38 :     m_nFileSize = VSIFTellL(m_fpTable);
     544             : 
     545          38 :     int bReportDeletedFeatures = CPLTestBool(
     546          38 :         CPLGetConfigOption("OPENFILEGDB_REPORT_DELETED_FEATURES", "NO"));
     547             : 
     548          38 :     vsi_l_offset nOffset = 40 + m_nFieldDescLength;
     549             : 
     550          38 :     if (m_nOffsetFieldDesc != 40)
     551             :     {
     552             :         /* Check if there is a deleted field description at offset 40 */
     553             :         GByte abyBuffer[14];
     554           1 :         VSIFSeekL(m_fpTable, 40, SEEK_SET);
     555           1 :         if (VSIFReadL(abyBuffer, 14, 1, m_fpTable) != 1)
     556           0 :             return FALSE;
     557           1 :         int nSize = GetInt32(abyBuffer, 0);
     558           1 :         int nVersion = GetInt32(abyBuffer + 4, 0);
     559           1 :         if (nSize < 0 && nSize > -1024 * 1024 &&
     560           0 :             (nVersion == 3 || nVersion == 4) &&
     561           0 :             IS_VALID_LAYER_GEOM_TYPE(abyBuffer[8]) && abyBuffer[9] == 3 &&
     562           0 :             abyBuffer[10] == 0 && abyBuffer[11] == 0)
     563             :         {
     564           0 :             nOffset = 40 + (-nSize);
     565             :         }
     566             :         else
     567             :         {
     568           1 :             nOffset = 40;
     569             :         }
     570             :     }
     571             : 
     572          38 :     int nInvalidRecords = 0;
     573        3476 :     while (nOffset < m_nFileSize)
     574             :     {
     575             :         GUInt32 nSize;
     576             :         int bDeletedRecord;
     577        3438 :         if (!IsLikelyFeatureAtOffset(nOffset, &nSize, &bDeletedRecord))
     578             :         {
     579        2869 :             nOffset++;
     580             :         }
     581             :         else
     582             :         {
     583             :             /*CPLDebug("OpenFileGDB", "Feature found at offset %d (size = %d)",
     584             :                      nOffset, nSize);*/
     585         569 :             if (bDeletedRecord)
     586             :             {
     587           9 :                 if (bReportDeletedFeatures)
     588             :                 {
     589           0 :                     m_bHasDeletedFeaturesListed = TRUE;
     590           0 :                     m_anFeatureOffsets.push_back(MARK_DELETED(nOffset));
     591             :                 }
     592             :                 else
     593             :                 {
     594           9 :                     nInvalidRecords++;
     595           9 :                     m_anFeatureOffsets.push_back(0);
     596             :                 }
     597             :             }
     598             :             else
     599         560 :                 m_anFeatureOffsets.push_back(nOffset);
     600         569 :             nOffset += nSize;
     601             :         }
     602             :     }
     603          38 :     m_nTotalRecordCount = static_cast<int>(m_anFeatureOffsets.size());
     604          38 :     if (m_nTotalRecordCount - nInvalidRecords > m_nValidRecordCount)
     605             :     {
     606           0 :         if (!m_bHasDeletedFeaturesListed)
     607             :         {
     608           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     609             :                      "More features found (%d) than declared number of valid "
     610             :                      "features (%d). "
     611             :                      "So deleted features will likely be reported.",
     612           0 :                      m_nTotalRecordCount - nInvalidRecords,
     613             :                      m_nValidRecordCount);
     614             :         }
     615           0 :         m_nValidRecordCount = m_nTotalRecordCount - nInvalidRecords;
     616             :     }
     617             : 
     618          38 :     return m_nTotalRecordCount > 0;
     619             : }
     620             : 
     621             : /************************************************************************/
     622             : /*                            ReadTableXHeader()                        */
     623             : /************************************************************************/
     624             : 
     625        4253 : int FileGDBTable::ReadTableXHeader()
     626             : {
     627        4253 :     const int errorRetValue = FALSE;
     628             :     GByte abyHeader[16];
     629             : 
     630             :     // Read .gdbtablx file header
     631        4253 :     returnErrorIf(VSIFReadL(abyHeader, 16, 1, m_fpTableX) != 1);
     632        4253 :     m_n1024BlocksPresent = GetUInt32(abyHeader + 4, 0);
     633             : 
     634        4253 :     m_nTotalRecordCount = GetInt32(abyHeader + 8, 0);
     635        4253 :     if (m_n1024BlocksPresent == 0)
     636         464 :         returnErrorIf(m_nTotalRecordCount != 0);
     637             :     else
     638        3789 :         returnErrorIf(m_nTotalRecordCount < 0);
     639             : 
     640        4252 :     m_nTablxOffsetSize = GetUInt32(abyHeader + 12, 0);
     641        4252 :     returnErrorIf(m_nTablxOffsetSize < 4 || m_nTablxOffsetSize > 6);
     642             : 
     643        4251 :     m_nOffsetTableXTrailer =
     644        4251 :         16 + m_nTablxOffsetSize * 1024 *
     645        4251 :                  static_cast<vsi_l_offset>(m_n1024BlocksPresent);
     646        4251 :     if (m_n1024BlocksPresent != 0)
     647             :     {
     648             :         GByte abyTrailer[16];
     649             : 
     650        3787 :         VSIFSeekL(m_fpTableX, m_nOffsetTableXTrailer, SEEK_SET);
     651        3792 :         returnErrorIf(VSIFReadL(abyTrailer, 16, 1, m_fpTableX) != 1);
     652             : 
     653        3786 :         GUInt32 nBitmapInt32Words = GetUInt32(abyTrailer, 0);
     654             : 
     655        3786 :         GUInt32 nBitsForBlockMap = GetUInt32(abyTrailer + 4, 0);
     656        3786 :         returnErrorIf(nBitsForBlockMap > 1 + INT_MAX / 1024);
     657             : 
     658        3785 :         GUInt32 n1024BlocksBis = GetUInt32(abyTrailer + 8, 0);
     659        3785 :         returnErrorIf(n1024BlocksBis != m_n1024BlocksPresent);
     660             : 
     661             :         /* GUInt32 nLeadingNonZero32BitWords = GetUInt32(abyTrailer + 12, 0); */
     662             : 
     663        3783 :         if (nBitmapInt32Words == 0)
     664             :         {
     665        3759 :             returnErrorIf(nBitsForBlockMap != m_n1024BlocksPresent);
     666             :             /* returnErrorIf(nLeadingNonZero32BitWords != 0 ); */
     667             :         }
     668             :         else
     669             :         {
     670          24 :             returnErrorIf(static_cast<GUInt32>(m_nTotalRecordCount) >
     671             :                           nBitsForBlockMap * 1024);
     672             : #ifdef DEBUG_VERBOSE
     673             :             CPLDebug("OpenFileGDB", "%s .gdbtablx has block map array",
     674             :                      m_osFilename.c_str());
     675             : #endif
     676             : 
     677             :             // Allocate a bit mask array for blocks of 1024 features.
     678          24 :             uint32_t nSizeInBytes = BIT_ARRAY_SIZE_IN_BYTES(nBitsForBlockMap);
     679             :             try
     680             :             {
     681          24 :                 m_abyTablXBlockMap.resize(nSizeInBytes);
     682             :             }
     683           0 :             catch (const std::exception &e)
     684             :             {
     685           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
     686           0 :                          "Cannot allocate m_abyTablXBlockMap: %s", e.what());
     687           0 :                 return false;
     688             :             }
     689          24 :             returnErrorIf(VSIFReadL(m_abyTablXBlockMap.data(), nSizeInBytes, 1,
     690             :                                     m_fpTableX) != 1);
     691             :             /* returnErrorIf(nMagic2 == 0 ); */
     692             : 
     693             :             // Check that the map is consistent with m_n1024BlocksPresent
     694          23 :             GUInt32 nCountBlocks = 0;
     695     4204160 :             for (GUInt32 i = 0; i < nBitsForBlockMap; i++)
     696     4204140 :                 nCountBlocks += TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
     697          23 :             returnErrorIf(nCountBlocks != m_n1024BlocksPresent);
     698             :         }
     699             :     }
     700        4245 :     return TRUE;
     701             : }
     702             : 
     703             : /************************************************************************/
     704             : /*                                 Open()                               */
     705             : /************************************************************************/
     706             : 
     707        4371 : bool FileGDBTable::Open(const char *pszFilename, bool bUpdate,
     708             :                         const char *pszLayerName)
     709             : {
     710        4371 :     const bool errorRetValue = false;
     711        4371 :     CPLAssert(m_fpTable == nullptr);
     712             : 
     713        4371 :     m_bUpdate = bUpdate;
     714             : 
     715        4371 :     m_osFilename = pszFilename;
     716        8742 :     CPLString m_osFilenameWithLayerName(m_osFilename);
     717        4371 :     if (pszLayerName)
     718         834 :         m_osFilenameWithLayerName += CPLSPrintf(" (layer %s)", pszLayerName);
     719             : 
     720        4371 :     m_fpTable = VSIFOpenL(pszFilename, m_bUpdate ? "r+b" : "rb");
     721        4371 :     if (m_fpTable == nullptr)
     722             :     {
     723           2 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s: %s",
     724           2 :                  m_osFilenameWithLayerName.c_str(), VSIStrerror(errno));
     725           2 :         return false;
     726             :     }
     727             : 
     728             :     // Read .gdtable file header
     729             :     GByte abyHeader[40];
     730        4369 :     returnErrorIf(VSIFReadL(abyHeader, 40, 1, m_fpTable) != 1);
     731        4369 :     m_nValidRecordCount = GetInt32(abyHeader + 4, 0);
     732        4369 :     returnErrorIf(m_nValidRecordCount < 0);
     733             : 
     734        4368 :     m_nHeaderBufferMaxSize = GetInt32(abyHeader + 8, 0);
     735             : 
     736        8736 :     CPLString osTableXName;
     737        7215 :     if (m_bUpdate || (m_nValidRecordCount > 0 &&
     738        2847 :                       !CPLTestBool(CPLGetConfigOption(
     739             :                           "OPENFILEGDB_IGNORE_GDBTABLX", "false"))))
     740             :     {
     741             :         osTableXName = CPLFormFilename(CPLGetPath(pszFilename),
     742        4253 :                                        CPLGetBasename(pszFilename), "gdbtablx");
     743        4253 :         m_fpTableX = VSIFOpenL(osTableXName, m_bUpdate ? "r+b" : "rb");
     744        4253 :         if (m_fpTableX == nullptr)
     745             :         {
     746           0 :             if (m_bUpdate)
     747             :             {
     748           0 :                 CPLError(CE_Failure, CPLE_OpenFailed, "Cannot open %s: %s",
     749           0 :                          osTableXName.c_str(), VSIStrerror(errno));
     750           0 :                 return false;
     751             :             }
     752           0 :             const char *pszIgnoreGDBTablXAbsence = CPLGetConfigOption(
     753             :                 "OPENFILEGDB_IGNORE_GDBTABLX_ABSENCE", nullptr);
     754           0 :             if (pszIgnoreGDBTablXAbsence == nullptr)
     755             :             {
     756           0 :                 CPLError(
     757             :                     CE_Warning, CPLE_AppDefined,
     758             :                     "%s could not be found. "
     759             :                     "Trying to guess feature locations, but this might fail or "
     760             :                     "return incorrect results",
     761             :                     osTableXName.c_str());
     762             :             }
     763           0 :             else if (!CPLTestBool(pszIgnoreGDBTablXAbsence))
     764             :             {
     765           0 :                 returnErrorIf(m_fpTableX == nullptr);
     766             :             }
     767             :         }
     768        4253 :         else if (!ReadTableXHeader())
     769           8 :             return false;
     770             :     }
     771             : 
     772        4360 :     if (m_fpTableX != nullptr)
     773             :     {
     774        4245 :         if (m_nValidRecordCount > m_nTotalRecordCount)
     775             :         {
     776           6 :             if (CPLTestBool(CPLGetConfigOption(
     777             :                     "OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT", "false")))
     778             :             {
     779             :                 /* Potentially unsafe. See #5842 */
     780           0 :                 CPLDebug("OpenFileGDB",
     781             :                          "%s: nTotalRecordCount (was %d) forced to "
     782             :                          "nValidRecordCount=%d",
     783             :                          m_osFilenameWithLayerName.c_str(), m_nTotalRecordCount,
     784             :                          m_nValidRecordCount);
     785           0 :                 m_nTotalRecordCount = m_nValidRecordCount;
     786             :             }
     787             :             else
     788             :             {
     789             :                 /* By default err on the safe side */
     790           6 :                 CPLError(
     791             :                     CE_Warning, CPLE_AppDefined,
     792             :                     "File %s declares %d valid records, but %s declares "
     793             :                     "only %d total records. Using that later value for safety "
     794             :                     "(this possibly ignoring features). "
     795             :                     "You can also try setting OPENFILEGDB_IGNORE_GDBTABLX=YES "
     796             :                     "to "
     797             :                     "completely ignore the .gdbtablx file (but possibly "
     798             :                     "retrieving "
     799             :                     "deleted features), or set "
     800             :                     "OPENFILEGDB_USE_GDBTABLE_RECORD_COUNT=YES "
     801             :                     "(but that setting can potentially cause crashes)",
     802             :                     m_osFilenameWithLayerName.c_str(), m_nValidRecordCount,
     803             :                     osTableXName.c_str(), m_nTotalRecordCount);
     804           6 :                 m_nValidRecordCount = m_nTotalRecordCount;
     805             :             }
     806             :         }
     807             : 
     808             : #ifdef DEBUG_VERBOSE
     809             :         else if (m_nTotalRecordCount != m_nValidRecordCount)
     810             :         {
     811             :             CPLDebug("OpenFileGDB",
     812             :                      "%s: nTotalRecordCount=%d nValidRecordCount=%d",
     813             :                      pszFilename, m_nTotalRecordCount, m_nValidRecordCount);
     814             :         }
     815             : #endif
     816             :     }
     817             : 
     818        4360 :     m_nOffsetFieldDesc = GetUInt64(abyHeader + 32, 0);
     819             : 
     820             : #ifdef DEBUG_VERBOSE
     821             :     if (m_nOffsetFieldDesc != 40)
     822             :     {
     823             :         CPLDebug("OpenFileGDB", "%s: nOffsetFieldDesc=" CPL_FRMT_GUIB,
     824             :                  pszFilename, m_nOffsetFieldDesc);
     825             :     }
     826             : #endif
     827             : 
     828        4360 :     if (m_bUpdate)
     829             :     {
     830        1444 :         VSIFSeekL(m_fpTable, 0, SEEK_END);
     831        1444 :         m_nFileSize = VSIFTellL(m_fpTable);
     832             :     }
     833             : 
     834             :     // Skip to field description section
     835        4360 :     VSIFSeekL(m_fpTable, m_nOffsetFieldDesc, SEEK_SET);
     836        4360 :     returnErrorIf(VSIFReadL(abyHeader, 14, 1, m_fpTable) != 1);
     837        4359 :     m_nFieldDescLength = GetUInt32(abyHeader, 0);
     838             : 
     839        4359 :     const auto nVersion = GetUInt32(abyHeader + 4, 0);
     840             :     // nVersion == 6 is used in table arcgis_pro_32_types.gdb/a0000000b.gdbtable (big_int)
     841             :     // Not sure why...
     842        4359 :     if (m_bUpdate && nVersion != 4 && nVersion != 6)  // FileGDB v10
     843             :     {
     844           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     845             :                  "Version %u of the FileGeodatabase format is not supported "
     846             :                  "for update.",
     847             :                  nVersion);
     848           0 :         return false;
     849             :     }
     850        4359 :     m_bIsV9 = (nVersion == 3);
     851             : 
     852        4359 :     returnErrorIf(m_nOffsetFieldDesc >
     853             :                   std::numeric_limits<GUIntBig>::max() - m_nFieldDescLength);
     854             : 
     855        4359 :     returnErrorIf(m_nFieldDescLength > 10 * 1024 * 1024 ||
     856             :                   m_nFieldDescLength < 10);
     857        4357 :     GByte byTableGeomType = abyHeader[8];
     858        4357 :     if (IS_VALID_LAYER_GEOM_TYPE(byTableGeomType))
     859        4357 :         m_eTableGeomType =
     860        4357 :             static_cast<FileGDBTableGeometryType>(byTableGeomType);
     861             :     else
     862           0 :         CPLDebug("OpenFileGDB", "Unknown table geometry type: %d",
     863             :                  byTableGeomType);
     864        4357 :     m_bStringsAreUTF8 = (abyHeader[9] & 0x1) != 0;
     865        4357 :     const GByte byTableGeomTypeFlags = abyHeader[11];
     866        4357 :     m_bGeomTypeHasM = (byTableGeomTypeFlags & (1 << 6)) != 0;
     867        4357 :     m_bGeomTypeHasZ = (byTableGeomTypeFlags & (1 << 7)) != 0;
     868             : 
     869             :     GUInt16 iField, nFields;
     870        4357 :     nFields = GetUInt16(abyHeader + 12, 0);
     871             : 
     872             :     /* No interest in guessing a trivial file */
     873        4357 :     returnErrorIf(m_fpTableX == nullptr && nFields == 0);
     874             : 
     875        4357 :     GUInt32 nRemaining = m_nFieldDescLength - 10;
     876        4357 :     m_nRowBufferMaxSize = nRemaining;
     877             :     try
     878             :     {
     879        4357 :         m_abyBuffer.resize(m_nRowBufferMaxSize + ZEROES_AFTER_END_OF_BUFFER);
     880             :     }
     881           0 :     catch (const std::exception &e)
     882             :     {
     883           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
     884           0 :         returnError();
     885             :     }
     886        4357 :     returnErrorIf(VSIFReadL(m_abyBuffer.data(), nRemaining, 1, m_fpTable) != 1);
     887             : 
     888        4356 :     GByte *pabyIter = m_abyBuffer.data();
     889       49170 :     for (iField = 0; iField < nFields; iField++)
     890             :     {
     891       44838 :         returnErrorIf(nRemaining < 1);
     892       44826 :         GByte nCarCount = pabyIter[0];
     893             : 
     894       44826 :         pabyIter++;
     895       44826 :         nRemaining--;
     896       44826 :         returnErrorIf(nCarCount > nRemaining / 2);
     897       44823 :         const std::string osName(ReadUTF16String(pabyIter, nCarCount));
     898       44823 :         pabyIter += 2 * nCarCount;
     899       44823 :         nRemaining -= 2 * nCarCount;
     900             : 
     901       44823 :         returnErrorIf(nRemaining < 1);
     902       44823 :         nCarCount = pabyIter[0];
     903       44823 :         pabyIter++;
     904       44823 :         nRemaining--;
     905       44823 :         returnErrorIf(nCarCount > nRemaining / 2);
     906       44820 :         const std::string osAlias(ReadUTF16String(pabyIter, nCarCount));
     907       44820 :         pabyIter += 2 * nCarCount;
     908       44820 :         nRemaining -= 2 * nCarCount;
     909             : 
     910       44820 :         returnErrorIf(nRemaining < 1);
     911       44820 :         GByte byFieldType = pabyIter[0];
     912       44820 :         pabyIter++;
     913       44820 :         nRemaining--;
     914             : 
     915       44820 :         if (byFieldType > FGFT_DATETIME_WITH_OFFSET)
     916             :         {
     917           3 :             CPLDebug("OpenFileGDB", "Unhandled field type : %d", byFieldType);
     918           3 :             returnError();
     919             :         }
     920             : 
     921       44817 :         FileGDBFieldType eType = static_cast<FileGDBFieldType>(byFieldType);
     922       44817 :         if (eType != FGFT_GEOMETRY && eType != FGFT_RASTER)
     923             :         {
     924       42806 :             GByte flags = 0;
     925       42806 :             int nMaxWidth = 0;
     926       42806 :             GUInt32 defaultValueLength = 0;
     927             : 
     928       42806 :             switch (eType)
     929             :             {
     930       11517 :                 case FGFT_STRING:
     931             :                 {
     932       11520 :                     returnErrorIf(nRemaining < 6);
     933       11517 :                     nMaxWidth = GetInt32(pabyIter, 0);
     934       11517 :                     returnErrorIf(nMaxWidth < 0);
     935       11516 :                     flags = pabyIter[4];
     936       11516 :                     pabyIter += 5;
     937       11516 :                     nRemaining -= 5;
     938       11516 :                     GByte *pabyIterBefore = pabyIter;
     939       11516 :                     returnErrorIf(!ReadVarUInt32(
     940             :                         pabyIter, pabyIter + nRemaining, defaultValueLength));
     941       11516 :                     nRemaining -=
     942       11516 :                         static_cast<GUInt32>(pabyIter - pabyIterBefore);
     943       11516 :                     break;
     944             :                 }
     945             : 
     946       16170 :                 case FGFT_OBJECTID:
     947             :                 case FGFT_BINARY:
     948             :                 case FGFT_GUID:
     949             :                 case FGFT_GLOBALID:
     950             :                 case FGFT_XML:
     951       16170 :                     returnErrorIf(nRemaining < 2);
     952       16170 :                     flags = pabyIter[1];
     953       16170 :                     pabyIter += 2;
     954       16170 :                     nRemaining -= 2;
     955       16170 :                     break;
     956             : 
     957       15119 :                 default:
     958       15119 :                     returnErrorIf(nRemaining < 3);
     959       15119 :                     flags = pabyIter[1];
     960       15119 :                     defaultValueLength = pabyIter[2];
     961       15119 :                     pabyIter += 3;
     962       15119 :                     nRemaining -= 3;
     963       15119 :                     break;
     964             :             }
     965             : 
     966             :             OGRField sDefault;
     967       42805 :             OGR_RawField_SetUnset(&sDefault);
     968       42805 :             if ((flags & 4) != 0)
     969             :             {
     970             :                 /* Default value */
     971             :                 /* Found on PreNIS.gdb/a0000000d.gdbtable */
     972       38092 :                 returnErrorIf(nRemaining < defaultValueLength);
     973       38090 :                 if (defaultValueLength)
     974             :                 {
     975          95 :                     if (eType == FGFT_STRING)
     976             :                     {
     977          15 :                         if (m_bStringsAreUTF8)
     978             :                         {
     979          13 :                             sDefault.String = static_cast<char *>(
     980          13 :                                 CPLMalloc(defaultValueLength + 1));
     981          13 :                             memcpy(sDefault.String, pabyIter,
     982             :                                    defaultValueLength);
     983          13 :                             sDefault.String[defaultValueLength] = 0;
     984             :                         }
     985             :                         else
     986             :                         {
     987           2 :                             m_osTempString = ReadUTF16String(
     988           2 :                                 pabyIter, defaultValueLength / 2);
     989           2 :                             sDefault.String = CPLStrdup(m_osTempString.c_str());
     990             :                         }
     991             :                     }
     992          80 :                     else if (eType == FGFT_INT16 && defaultValueLength == 2)
     993             :                     {
     994           7 :                         sDefault.Integer = GetInt16(pabyIter, 0);
     995           7 :                         sDefault.Set.nMarker2 = 0;
     996           7 :                         sDefault.Set.nMarker3 = 0;
     997             :                     }
     998          73 :                     else if (eType == FGFT_INT32 && defaultValueLength == 4)
     999             :                     {
    1000          19 :                         sDefault.Integer = GetInt32(pabyIter, 0);
    1001          19 :                         sDefault.Set.nMarker2 = 0;
    1002          19 :                         sDefault.Set.nMarker3 = 0;
    1003             :                     }
    1004          54 :                     else if (eType == FGFT_FLOAT32 && defaultValueLength == 4)
    1005             :                     {
    1006           7 :                         sDefault.Real = GetFloat32(pabyIter, 0);
    1007             :                     }
    1008          47 :                     else if (eType == FGFT_FLOAT64 && defaultValueLength == 8)
    1009             :                     {
    1010          12 :                         sDefault.Real = GetFloat64(pabyIter, 0);
    1011             :                     }
    1012          35 :                     else if ((eType == FGFT_DATETIME || eType == FGFT_DATE) &&
    1013          18 :                              defaultValueLength == 8)
    1014             :                     {
    1015          18 :                         const double dfVal = GetFloat64(pabyIter, 0);
    1016          18 :                         FileGDBDoubleDateToOGRDate(dfVal, true, &sDefault);
    1017             :                     }
    1018          17 :                     else if (eType == FGFT_TIME && defaultValueLength == 8)
    1019             :                     {
    1020           6 :                         const double dfVal = GetFloat64(pabyIter, 0);
    1021           6 :                         FileGDBDoubleTimeToOGRTime(dfVal, &sDefault);
    1022             :                     }
    1023          11 :                     else if (eType == FGFT_INT64 && defaultValueLength == 8)
    1024             :                     {
    1025           4 :                         sDefault.Integer64 = GetInt64(pabyIter, 0);
    1026           4 :                         sDefault.Set.nMarker3 = 0;
    1027             :                     }
    1028           7 :                     else if (eType == FGFT_DATETIME_WITH_OFFSET &&
    1029           7 :                              defaultValueLength ==
    1030             :                                  sizeof(double) + sizeof(int16_t))
    1031             :                     {
    1032           7 :                         const double dfVal = GetFloat64(pabyIter, 0);
    1033             :                         const int16_t nUTCOffset =
    1034           7 :                             GetInt16(pabyIter + sizeof(double), 0);
    1035           7 :                         FileGDBDateTimeWithOffsetToOGRDate(dfVal, nUTCOffset,
    1036             :                                                            &sDefault);
    1037             :                     }
    1038             :                 }
    1039             : 
    1040       38090 :                 pabyIter += defaultValueLength;
    1041       38090 :                 nRemaining -= defaultValueLength;
    1042             :             }
    1043             : 
    1044       42803 :             if (eType == FGFT_OBJECTID)
    1045             :             {
    1046        4331 :                 returnErrorIf(flags != 2);
    1047        4331 :                 returnErrorIf(m_iObjectIdField >= 0);
    1048        4331 :                 m_iObjectIdField = static_cast<int>(m_apoFields.size());
    1049             :             }
    1050             : 
    1051       85606 :             auto poField = std::make_unique<FileGDBField>(this);
    1052       42803 :             poField->m_osName = osName;
    1053       42803 :             poField->m_osAlias = osAlias;
    1054       42803 :             poField->m_eType = eType;
    1055       42803 :             poField->m_bNullable = (flags & 1) != 0;
    1056       42803 :             poField->m_nMaxWidth = nMaxWidth;
    1057       42803 :             poField->m_sDefault = sDefault;
    1058       85606 :             m_apoFields.emplace_back(std::move(poField));
    1059             :         }
    1060             :         else
    1061             :         {
    1062             : 
    1063        2011 :             FileGDBRasterField *poRasterField = nullptr;
    1064             :             FileGDBGeomField *poField;
    1065        2011 :             if (eType == FGFT_GEOMETRY)
    1066             :             {
    1067        2008 :                 returnErrorIf(m_iGeomField >= 0);
    1068        2008 :                 poField = new FileGDBGeomField(this);
    1069             :             }
    1070             :             else
    1071             :             {
    1072           3 :                 poRasterField = new FileGDBRasterField(this);
    1073           3 :                 poField = poRasterField;
    1074             :             }
    1075             : 
    1076        2011 :             poField->m_osName = osName;
    1077        2011 :             poField->m_osAlias = osAlias;
    1078        2011 :             poField->m_eType = eType;
    1079        2011 :             if (eType == FGFT_GEOMETRY)
    1080        2008 :                 m_iGeomField = static_cast<int>(m_apoFields.size());
    1081             :             m_apoFields.emplace_back(
    1082        2011 :                 std::unique_ptr<FileGDBGeomField>(poField));
    1083             : 
    1084        2011 :             returnErrorIf(nRemaining < 2);
    1085        2011 :             GByte flags = pabyIter[1];
    1086        2011 :             poField->m_bNullable = (flags & 1) != 0;
    1087        2011 :             pabyIter += 2;
    1088        2011 :             nRemaining -= 2;
    1089             : 
    1090        2011 :             if (eType == FGFT_RASTER)
    1091             :             {
    1092           3 :                 returnErrorIf(nRemaining < 1);
    1093           3 :                 nCarCount = pabyIter[0];
    1094           3 :                 pabyIter++;
    1095           3 :                 nRemaining--;
    1096           3 :                 returnErrorIf(nRemaining <
    1097             :                               static_cast<GUInt32>(2 * nCarCount + 1));
    1098             :                 poRasterField->m_osRasterColumnName =
    1099           3 :                     ReadUTF16String(pabyIter, nCarCount);
    1100           3 :                 pabyIter += 2 * nCarCount;
    1101           3 :                 nRemaining -= 2 * nCarCount;
    1102             :             }
    1103             : 
    1104        2011 :             returnErrorIf(nRemaining < 2);
    1105        2011 :             GUInt16 nLengthWKT = GetUInt16(pabyIter, 0);
    1106        2011 :             pabyIter += sizeof(nLengthWKT);
    1107        2011 :             nRemaining -= sizeof(nLengthWKT);
    1108             : 
    1109        2011 :             returnErrorIf(nRemaining < static_cast<GUInt32>(1 + nLengthWKT));
    1110        2011 :             poField->m_osWKT = ReadUTF16String(pabyIter, nLengthWKT / 2);
    1111        2011 :             pabyIter += nLengthWKT;
    1112        2011 :             nRemaining -= nLengthWKT;
    1113             : 
    1114        2011 :             GByte abyGeomFlags = pabyIter[0];
    1115        2011 :             pabyIter++;
    1116        2011 :             nRemaining--;
    1117        2011 :             poField->m_bHasMOriginScaleTolerance = (abyGeomFlags & 2) != 0;
    1118        2011 :             poField->m_bHasZOriginScaleTolerance = (abyGeomFlags & 4) != 0;
    1119             : 
    1120        2011 :             if (eType == FGFT_GEOMETRY || abyGeomFlags > 0)
    1121             :             {
    1122        2010 :                 returnErrorIf(nRemaining <
    1123             :                               static_cast<GUInt32>(
    1124             :                                   sizeof(double) *
    1125             :                                   (4 + ((eType == FGFT_GEOMETRY) ? 4 : 0) +
    1126             :                                    (poField->m_bHasMOriginScaleTolerance +
    1127             :                                     poField->m_bHasZOriginScaleTolerance) *
    1128             :                                        3)));
    1129             : 
    1130             : #define READ_DOUBLE(field)                                                     \
    1131             :     do                                                                         \
    1132             :     {                                                                          \
    1133             :         field = GetFloat64(pabyIter, 0);                                       \
    1134             :         pabyIter += sizeof(double);                                            \
    1135             :         nRemaining -= sizeof(double);                                          \
    1136             :     } while (false)
    1137             : 
    1138        2010 :                 READ_DOUBLE(poField->m_dfXOrigin);
    1139        2010 :                 READ_DOUBLE(poField->m_dfYOrigin);
    1140        2010 :                 READ_DOUBLE(poField->m_dfXYScale);
    1141        2010 :                 returnErrorIf(poField->m_dfXYScale == 0);
    1142             : 
    1143        2010 :                 if (poField->m_bHasMOriginScaleTolerance)
    1144             :                 {
    1145        2000 :                     READ_DOUBLE(poField->m_dfMOrigin);
    1146        2000 :                     READ_DOUBLE(poField->m_dfMScale);
    1147             :                 }
    1148             : 
    1149        2010 :                 if (poField->m_bHasZOriginScaleTolerance)
    1150             :                 {
    1151        2007 :                     READ_DOUBLE(poField->m_dfZOrigin);
    1152        2007 :                     READ_DOUBLE(poField->m_dfZScale);
    1153             :                 }
    1154             : 
    1155        2010 :                 READ_DOUBLE(poField->m_dfXYTolerance);
    1156             : 
    1157        2010 :                 if (poField->m_bHasMOriginScaleTolerance)
    1158             :                 {
    1159        2000 :                     READ_DOUBLE(poField->m_dfMTolerance);
    1160             : #ifdef DEBUG_VERBOSE
    1161             :                     CPLDebug("OpenFileGDB",
    1162             :                              "MOrigin = %g, MScale = %g, MTolerance = %g",
    1163             :                              poField->m_dfMOrigin, poField->m_dfMScale,
    1164             :                              poField->m_dfMTolerance);
    1165             : #endif
    1166             :                 }
    1167             : 
    1168        2010 :                 if (poField->m_bHasZOriginScaleTolerance)
    1169             :                 {
    1170        2007 :                     READ_DOUBLE(poField->m_dfZTolerance);
    1171             :                 }
    1172             :             }
    1173             : 
    1174        2011 :             if (eType == FGFT_RASTER)
    1175             :             {
    1176           3 :                 returnErrorIf(nRemaining < 1);
    1177           3 :                 if (*pabyIter == 0)
    1178           0 :                     poRasterField->m_eRasterType =
    1179             :                         FileGDBRasterField::Type::EXTERNAL;
    1180           3 :                 else if (*pabyIter == 1)
    1181           3 :                     poRasterField->m_eRasterType =
    1182             :                         FileGDBRasterField::Type::MANAGED;
    1183           0 :                 else if (*pabyIter == 2)
    1184           0 :                     poRasterField->m_eRasterType =
    1185             :                         FileGDBRasterField::Type::INLINE;
    1186             :                 else
    1187             :                 {
    1188           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    1189           0 :                              "Unknown raster field type %d", *pabyIter);
    1190             :                 }
    1191           3 :                 pabyIter += 1;
    1192           3 :                 nRemaining -= 1;
    1193             :             }
    1194             :             else
    1195             :             {
    1196        2008 :                 returnErrorIf(nRemaining < 4 * sizeof(double));
    1197        2008 :                 m_nGeomFieldBBoxSubOffset =
    1198        2008 :                     static_cast<uint32_t>(pabyIter - m_abyBuffer.data()) + 14;
    1199        2008 :                 READ_DOUBLE(poField->m_dfXMin);
    1200        2008 :                 READ_DOUBLE(poField->m_dfYMin);
    1201        2008 :                 READ_DOUBLE(poField->m_dfXMax);
    1202        2008 :                 READ_DOUBLE(poField->m_dfYMax);
    1203             : 
    1204             : #ifdef PARANOID_CHECK
    1205             :                 const auto nCurrentPos = VSIFTellL(m_fpTable);
    1206             :                 VSIFSeekL(m_fpTable,
    1207             :                           m_nOffsetFieldDesc + m_nGeomFieldBBoxSubOffset,
    1208             :                           SEEK_SET);
    1209             :                 double dfXMinFromFile;
    1210             :                 VSIFReadL(&dfXMinFromFile, 1, sizeof(dfXMinFromFile),
    1211             :                           m_fpTable);
    1212             :                 fprintf(stderr, "%f %f\n", dfXMinFromFile, /*ok*/
    1213             :                         poField->m_dfXMin);
    1214             :                 double dfYMinFromFile;
    1215             :                 VSIFReadL(&dfYMinFromFile, 1, sizeof(dfYMinFromFile),
    1216             :                           m_fpTable);
    1217             :                 fprintf(stderr, "%f %f\n", dfYMinFromFile, /*ok*/
    1218             :                         poField->m_dfYMin);
    1219             :                 double dfXMaxFromFile;
    1220             :                 VSIFReadL(&dfXMaxFromFile, 1, sizeof(dfXMaxFromFile),
    1221             :                           m_fpTable);
    1222             :                 fprintf(stderr, "%f %f\n", dfXMaxFromFile, /*ok*/
    1223             :                         poField->m_dfXMax);
    1224             :                 double dfYMaxFromFile;
    1225             :                 VSIFReadL(&dfYMaxFromFile, 1, sizeof(dfYMaxFromFile),
    1226             :                           m_fpTable);
    1227             :                 fprintf(stderr, "%f %f\n", dfYMaxFromFile, /*ok*/
    1228             :                         poField->m_dfYMax);
    1229             :                 VSIFSeekL(m_fpTable, nCurrentPos, SEEK_SET);
    1230             : #endif
    1231        2008 :                 if (m_bGeomTypeHasZ)
    1232             :                 {
    1233         201 :                     returnErrorIf(nRemaining < 2 * sizeof(double));
    1234         201 :                     READ_DOUBLE(poField->m_dfZMin);
    1235         201 :                     READ_DOUBLE(poField->m_dfZMax);
    1236             :                 }
    1237             : 
    1238        2008 :                 if (m_bGeomTypeHasM)
    1239             :                 {
    1240          89 :                     returnErrorIf(nRemaining < 2 * sizeof(double));
    1241          89 :                     READ_DOUBLE(poField->m_dfMMin);
    1242          89 :                     READ_DOUBLE(poField->m_dfMMax);
    1243             :                 }
    1244             : 
    1245        2008 :                 returnErrorIf(nRemaining < 5);
    1246             :                 // Skip byte at zero
    1247        2008 :                 pabyIter += 1;
    1248        2008 :                 nRemaining -= 1;
    1249             : 
    1250        2008 :                 GUInt32 nGridSizeCount = GetUInt32(pabyIter, 0);
    1251        2008 :                 pabyIter += sizeof(nGridSizeCount);
    1252        2008 :                 nRemaining -= sizeof(nGridSizeCount);
    1253        2008 :                 returnErrorIf(nGridSizeCount == 0 || nGridSizeCount > 3);
    1254        2008 :                 returnErrorIf(nRemaining < nGridSizeCount * sizeof(double));
    1255        2008 :                 m_nGeomFieldSpatialIndexGridResSubOffset =
    1256        2008 :                     static_cast<uint32_t>(pabyIter - m_abyBuffer.data()) + 14;
    1257        7042 :                 for (GUInt32 i = 0; i < nGridSizeCount; i++)
    1258             :                 {
    1259             :                     double dfGridResolution;
    1260        5034 :                     READ_DOUBLE(dfGridResolution);
    1261        5034 :                     m_adfSpatialIndexGridResolution.push_back(dfGridResolution);
    1262             :                 }
    1263             :                 poField->m_adfSpatialIndexGridResolution =
    1264        2008 :                     m_adfSpatialIndexGridResolution;
    1265             :             }
    1266             :         }
    1267             : 
    1268       44814 :         m_nCountNullableFields +=
    1269       44814 :             static_cast<int>(m_apoFields.back()->m_bNullable);
    1270             :     }
    1271        4344 :     m_nNullableFieldsSizeInBytes =
    1272        4344 :         BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields);
    1273             : 
    1274             : #ifdef DEBUG_VERBOSE
    1275             :     if (nRemaining > 0)
    1276             :     {
    1277             :         CPLDebug("OpenFileGDB",
    1278             :                  "%u remaining (ignored) bytes in field header section",
    1279             :                  nRemaining);
    1280             :     }
    1281             : #endif
    1282             : 
    1283        4344 :     if (m_nValidRecordCount > 0 && m_fpTableX == nullptr)
    1284          38 :         return GuessFeatureLocations();
    1285             : 
    1286        4306 :     return true;
    1287             : }
    1288             : 
    1289             : /************************************************************************/
    1290             : /*                          SkipVarUInt()                               */
    1291             : /************************************************************************/
    1292             : 
    1293             : /* Bound check only valid if nIter <= 4 */
    1294        6991 : static int SkipVarUInt(GByte *&pabyIter, GByte *pabyEnd, int nIter = 1)
    1295             : {
    1296        6991 :     const int errorRetValue = FALSE;
    1297        6991 :     GByte *pabyLocalIter = pabyIter;
    1298        6991 :     returnErrorIf(pabyLocalIter /*+ nIter - 1*/ >= pabyEnd);
    1299       31785 :     while (nIter-- > 0)
    1300             :     {
    1301             :         while (true)
    1302             :         {
    1303      129086 :             GByte b = *pabyLocalIter;
    1304      129086 :             pabyLocalIter++;
    1305      129086 :             if ((b & 0x80) == 0)
    1306       24794 :                 break;
    1307      104292 :         }
    1308             :     }
    1309        6991 :     pabyIter = pabyLocalIter;
    1310        6991 :     return TRUE;
    1311             : }
    1312             : 
    1313             : /************************************************************************/
    1314             : /*                      ReadVarIntAndAddNoCheck()                       */
    1315             : /************************************************************************/
    1316             : 
    1317             : CPL_NOSANITIZE_UNSIGNED_INT_OVERFLOW
    1318       95941 : static void ReadVarIntAndAddNoCheck(GByte *&pabyIter, GIntBig &nOutVal)
    1319             : {
    1320             :     GUInt32 b;
    1321             : 
    1322       95941 :     b = *pabyIter;
    1323       95941 :     GUIntBig nVal = (b & 0x3F);
    1324       95941 :     bool bNegative = (b & 0x40) != 0;
    1325       95941 :     if ((b & 0x80) == 0)
    1326             :     {
    1327       37247 :         pabyIter++;
    1328       37247 :         if (bNegative)
    1329           6 :             nOutVal -= nVal;
    1330             :         else
    1331       37241 :             nOutVal += nVal;
    1332       37247 :         return;
    1333             :     }
    1334             : 
    1335       58694 :     GByte *pabyLocalIter = pabyIter + 1;
    1336       58694 :     int nShift = 6;
    1337             :     while (true)
    1338             :     {
    1339      228689 :         GUIntBig b64 = *pabyLocalIter;
    1340      228689 :         pabyLocalIter++;
    1341      228689 :         nVal |= (b64 & 0x7F) << nShift;
    1342      228689 :         if ((b64 & 0x80) == 0)
    1343             :         {
    1344       58694 :             pabyIter = pabyLocalIter;
    1345       58694 :             if (bNegative)
    1346       17245 :                 nOutVal -= nVal;
    1347             :             else
    1348       41449 :                 nOutVal += nVal;
    1349       58694 :             return;
    1350             :         }
    1351      169995 :         nShift += 7;
    1352             :         // To avoid undefined behavior later when doing << nShift
    1353      169995 :         if (nShift >= static_cast<int>(sizeof(GIntBig)) * 8)
    1354             :         {
    1355           0 :             pabyIter = pabyLocalIter;
    1356           0 :             nOutVal = nVal;
    1357           0 :             return;
    1358             :         }
    1359      169995 :     }
    1360             : }
    1361             : 
    1362             : /************************************************************************/
    1363             : /*                       GetOffsetInTableForRow()                       */
    1364             : /************************************************************************/
    1365             : 
    1366             : vsi_l_offset
    1367      136697 : FileGDBTable::GetOffsetInTableForRow(int iRow, vsi_l_offset *pnOffsetInTableX)
    1368             : {
    1369      136697 :     const int errorRetValue = 0;
    1370      136697 :     if (pnOffsetInTableX)
    1371        2697 :         *pnOffsetInTableX = 0;
    1372      136697 :     returnErrorIf(iRow < 0 || iRow >= m_nTotalRecordCount);
    1373             : 
    1374      136697 :     m_bIsDeleted = FALSE;
    1375      136697 :     if (m_fpTableX == nullptr)
    1376             :     {
    1377         155 :         m_bIsDeleted = IS_DELETED(m_anFeatureOffsets[iRow]);
    1378         155 :         return GET_OFFSET(m_anFeatureOffsets[iRow]);
    1379             :     }
    1380             : 
    1381             :     vsi_l_offset nOffsetInTableX;
    1382      136542 :     if (!m_abyTablXBlockMap.empty())
    1383             :     {
    1384       29356 :         GUInt32 nCountBlocksBefore = 0;
    1385       29356 :         int iBlock = iRow / 1024;
    1386             : 
    1387             :         // Check if the block is not empty
    1388       29356 :         if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0)
    1389          11 :             return 0;
    1390             : 
    1391             :         // In case of sequential reading, optimization to avoid recomputing
    1392             :         // the number of blocks since the beginning of the map
    1393       29345 :         if (iBlock >= m_nCountBlocksBeforeIBlockIdx)
    1394             :         {
    1395       29338 :             nCountBlocksBefore = m_nCountBlocksBeforeIBlockValue;
    1396     4233460 :             for (int i = m_nCountBlocksBeforeIBlockIdx; i < iBlock; i++)
    1397     4204120 :                 nCountBlocksBefore +=
    1398     4204120 :                     TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
    1399             :         }
    1400             :         else
    1401             :         {
    1402           7 :             nCountBlocksBefore = 0;
    1403          28 :             for (int i = 0; i < iBlock; i++)
    1404          21 :                 nCountBlocksBefore +=
    1405          21 :                     TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
    1406             :         }
    1407       29345 :         m_nCountBlocksBeforeIBlockIdx = iBlock;
    1408       29345 :         m_nCountBlocksBeforeIBlockValue = nCountBlocksBefore;
    1409       29345 :         const int iCorrectedRow = nCountBlocksBefore * 1024 + (iRow % 1024);
    1410       29345 :         nOffsetInTableX =
    1411       29345 :             16 + static_cast<vsi_l_offset>(m_nTablxOffsetSize) * iCorrectedRow;
    1412             :     }
    1413             :     else
    1414             :     {
    1415      107186 :         nOffsetInTableX =
    1416      107186 :             16 + static_cast<vsi_l_offset>(m_nTablxOffsetSize) * iRow;
    1417             :     }
    1418             : 
    1419      136531 :     if (pnOffsetInTableX)
    1420        2697 :         *pnOffsetInTableX = nOffsetInTableX;
    1421      136531 :     VSIFSeekL(m_fpTableX, nOffsetInTableX, SEEK_SET);
    1422             : 
    1423             :     GByte abyBuffer[6];
    1424      136531 :     m_bError = VSIFReadL(abyBuffer, m_nTablxOffsetSize, 1, m_fpTableX) != 1;
    1425      136531 :     returnErrorIf(m_bError);
    1426             : 
    1427      136531 :     const vsi_l_offset nOffset = ReadFeatureOffset(abyBuffer);
    1428             : 
    1429             : #ifdef DEBUG_VERBOSE
    1430             :     const auto nOffsetHeaderEnd = m_nOffsetFieldDesc + m_nFieldDescLength;
    1431             :     if (iRow == 0 && nOffset != 0 && nOffset != nOffsetHeaderEnd &&
    1432             :         nOffset != nOffsetHeaderEnd + 4)
    1433             :         CPLDebug("OpenFileGDB",
    1434             :                  "%s: first feature offset = " CPL_FRMT_GUIB
    1435             :                  ". Expected " CPL_FRMT_GUIB,
    1436             :                  m_osFilename.c_str(), nOffset, nOffsetHeaderEnd);
    1437             : #endif
    1438             : 
    1439      136531 :     return nOffset;
    1440             : }
    1441             : 
    1442             : /************************************************************************/
    1443             : /*                        ReadFeatureOffset()                           */
    1444             : /************************************************************************/
    1445             : 
    1446      184759 : uint64_t FileGDBTable::ReadFeatureOffset(const GByte *pabyBuffer)
    1447             : {
    1448      184759 :     uint64_t nOffset = 0;
    1449      184759 :     memcpy(&nOffset, pabyBuffer, m_nTablxOffsetSize);
    1450      184759 :     CPL_LSBPTR64(&nOffset);
    1451      184759 :     return nOffset;
    1452             : }
    1453             : 
    1454             : /************************************************************************/
    1455             : /*                      GetAndSelectNextNonEmptyRow()                   */
    1456             : /************************************************************************/
    1457             : 
    1458       33633 : int FileGDBTable::GetAndSelectNextNonEmptyRow(int iRow)
    1459             : {
    1460       33633 :     const int errorRetValue = -1;
    1461       33633 :     returnErrorAndCleanupIf(iRow < 0 || iRow >= m_nTotalRecordCount,
    1462             :                             m_nCurRow = -1);
    1463             : 
    1464       78343 :     while (iRow < m_nTotalRecordCount)
    1465             :     {
    1466       78341 :         if (!m_abyTablXBlockMap.empty() && (iRow % 1024) == 0)
    1467             :         {
    1468          49 :             int iBlock = iRow / 1024;
    1469          49 :             if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0)
    1470             :             {
    1471          22 :                 int nBlocks = DIV_ROUND_UP(m_nTotalRecordCount, 1024);
    1472     4204070 :                 do
    1473             :                 {
    1474     4204090 :                     iBlock++;
    1475     8408190 :                 } while (iBlock < nBlocks &&
    1476     4204090 :                          TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0);
    1477             : 
    1478          22 :                 iRow = iBlock * 1024;
    1479          22 :                 if (iRow >= m_nTotalRecordCount)
    1480           0 :                     return -1;
    1481             :             }
    1482             :         }
    1483             : 
    1484       78341 :         if (SelectRow(iRow))
    1485       33631 :             return iRow;
    1486       44710 :         if (HasGotError())
    1487           0 :             return -1;
    1488       44710 :         iRow++;
    1489             :     }
    1490             : 
    1491           2 :     return -1;
    1492             : }
    1493             : 
    1494             : /************************************************************************/
    1495             : /*                            SelectRow()                               */
    1496             : /************************************************************************/
    1497             : 
    1498      135218 : int FileGDBTable::SelectRow(int iRow)
    1499             : {
    1500      135218 :     const int errorRetValue = FALSE;
    1501      135218 :     returnErrorAndCleanupIf(iRow < 0 || iRow >= m_nTotalRecordCount,
    1502             :                             m_nCurRow = -1);
    1503             : 
    1504      135218 :     if (m_nCurRow != iRow)
    1505             :     {
    1506      133904 :         vsi_l_offset nOffsetTable = GetOffsetInTableForRow(iRow);
    1507      133904 :         if (nOffsetTable == 0)
    1508             :         {
    1509       53825 :             m_nCurRow = -1;
    1510       53829 :             return FALSE;
    1511             :         }
    1512             : 
    1513       80079 :         VSIFSeekL(m_fpTable, nOffsetTable, SEEK_SET);
    1514             :         GByte abyBuffer[4];
    1515       80079 :         returnErrorAndCleanupIf(VSIFReadL(abyBuffer, 4, 1, m_fpTable) != 1,
    1516             :                                 m_nCurRow = -1);
    1517             : 
    1518       80079 :         m_nRowBlobLength = GetUInt32(abyBuffer, 0);
    1519       80079 :         if (m_bIsDeleted)
    1520             :         {
    1521           0 :             m_nRowBlobLength =
    1522           0 :                 static_cast<GUInt32>(-static_cast<int>(m_nRowBlobLength));
    1523             :         }
    1524             : 
    1525       80079 :         if (m_nRowBlobLength > 0)
    1526             :         {
    1527             :             /* CPLDebug("OpenFileGDB", "nRowBlobLength = %u", nRowBlobLength);
    1528             :              */
    1529       79923 :             returnErrorAndCleanupIf(
    1530             :                 m_nRowBlobLength <
    1531             :                         static_cast<GUInt32>(m_nNullableFieldsSizeInBytes) ||
    1532             :                     m_nRowBlobLength > INT_MAX - ZEROES_AFTER_END_OF_BUFFER,
    1533             :                 m_nCurRow = -1);
    1534             : 
    1535       79923 :             if (m_nRowBlobLength > m_nHeaderBufferMaxSize)
    1536             :             {
    1537           4 :                 if (CPLTestBool(CPLGetConfigOption(
    1538             :                         "OGR_OPENFILEGDB_ERROR_ON_INCONSISTENT_BUFFER_MAX_SIZE",
    1539             :                         "NO")))
    1540             :                 {
    1541           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1542             :                              "Invalid row length (%u) on feature %u compared "
    1543             :                              "to the maximum size in the header (%u)",
    1544             :                              m_nRowBlobLength, iRow + 1,
    1545             :                              m_nHeaderBufferMaxSize);
    1546           0 :                     m_nCurRow = -1;
    1547           0 :                     return errorRetValue;
    1548             :                 }
    1549             :                 else
    1550             :                 {
    1551           4 :                     CPLDebug("OpenFileGDB",
    1552             :                              "Invalid row length (%u) on feature %u compared "
    1553             :                              "to the maximum size in the header (%u)",
    1554             :                              m_nRowBlobLength, iRow + 1,
    1555             :                              m_nHeaderBufferMaxSize);
    1556             :                 }
    1557             :             }
    1558             : 
    1559       79923 :             if (m_nRowBlobLength > m_nRowBufferMaxSize)
    1560             :             {
    1561             :                 /* For suspicious row blob length, check if we don't go beyond
    1562             :                  * file size */
    1563        1409 :                 if (m_nRowBlobLength > 100 * 1024 * 1024)
    1564             :                 {
    1565           1 :                     if (m_nFileSize == 0)
    1566             :                     {
    1567           1 :                         VSIFSeekL(m_fpTable, 0, SEEK_END);
    1568           1 :                         m_nFileSize = VSIFTellL(m_fpTable);
    1569           1 :                         VSIFSeekL(m_fpTable, nOffsetTable + 4, SEEK_SET);
    1570             :                     }
    1571           1 :                     if (nOffsetTable + 4 + m_nRowBlobLength > m_nFileSize)
    1572             :                     {
    1573           1 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1574             :                                  "Invalid row length (%u) on feature %u",
    1575             :                                  m_nRowBlobLength, iRow + 1);
    1576           1 :                         m_nCurRow = -1;
    1577           1 :                         return errorRetValue;
    1578             :                     }
    1579             :                 }
    1580        1408 :                 m_nRowBufferMaxSize = m_nRowBlobLength;
    1581             :             }
    1582             : 
    1583       79922 :             if (m_abyBuffer.size() <
    1584       79922 :                 m_nRowBlobLength + ZEROES_AFTER_END_OF_BUFFER)
    1585             :             {
    1586             :                 try
    1587             :                 {
    1588        3707 :                     m_abyBuffer.resize(m_nRowBlobLength +
    1589             :                                        ZEROES_AFTER_END_OF_BUFFER);
    1590             :                 }
    1591           0 :                 catch (const std::exception &e)
    1592             :                 {
    1593           0 :                     CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1594           0 :                     returnErrorAndCleanupIf(true, m_nCurRow = -1);
    1595             :                 }
    1596             :             }
    1597             : 
    1598       79922 :             returnErrorAndCleanupIf(VSIFReadL(m_abyBuffer.data(),
    1599             :                                               m_nRowBlobLength, 1,
    1600             :                                               m_fpTable) != 1,
    1601             :                                     m_nCurRow = -1);
    1602             :             /* Protection for 4 ReadVarUInt64NoCheck */
    1603       79919 :             CPL_STATIC_ASSERT(ZEROES_AFTER_END_OF_BUFFER == 4);
    1604       79919 :             m_abyBuffer[m_nRowBlobLength] = 0;
    1605       79919 :             m_abyBuffer[m_nRowBlobLength + 1] = 0;
    1606       79919 :             m_abyBuffer[m_nRowBlobLength + 2] = 0;
    1607       79919 :             m_abyBuffer[m_nRowBlobLength + 3] = 0;
    1608             :         }
    1609             : 
    1610       80075 :         m_nCurRow = iRow;
    1611       80075 :         m_nLastCol = -1;
    1612       80075 :         m_pabyIterVals = m_abyBuffer.data() + m_nNullableFieldsSizeInBytes;
    1613       80075 :         m_iAccNullable = 0;
    1614       80075 :         m_bError = FALSE;
    1615       80075 :         m_nChSaved = -1;
    1616             :     }
    1617             : 
    1618       81389 :     return TRUE;
    1619             : }
    1620             : 
    1621             : /************************************************************************/
    1622             : /*                      FileGDBDoubleDateToOGRDate()                    */
    1623             : /************************************************************************/
    1624             : 
    1625        7678 : int FileGDBDoubleDateToOGRDate(double dfVal, bool bHighPrecision,
    1626             :                                OGRField *psField)
    1627             : {
    1628             :     // 25569: Number of days between 1899/12/30 00:00:00 and 1970/01/01 00:00:00
    1629        7678 :     double dfSeconds = (dfVal - 25569.0) * 3600.0 * 24.0;
    1630       23034 :     if (CPLIsNan(dfSeconds) ||
    1631             :         dfSeconds <
    1632       15356 :             static_cast<double>(std::numeric_limits<GIntBig>::min()) + 1000 ||
    1633             :         dfSeconds >
    1634        7678 :             static_cast<double>(std::numeric_limits<GIntBig>::max()) - 1000)
    1635             :     {
    1636           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1637             :                  "FileGDBDoubleDateToOGRDate: Invalid days: %lf", dfVal);
    1638           0 :         dfSeconds = 0.0;
    1639             :     }
    1640        7678 :     if (!bHighPrecision)
    1641        7624 :         dfSeconds = std::floor(dfSeconds + 0.5);
    1642          54 :     else if (fmod(dfSeconds, 1.0) > 1 - 1e-4)
    1643          18 :         dfSeconds = std::floor(dfSeconds + 0.5);
    1644             : 
    1645             :     struct tm brokendowntime;
    1646        7678 :     CPLUnixTimeToYMDHMS(static_cast<GIntBig>(dfSeconds), &brokendowntime);
    1647             : 
    1648        7678 :     psField->Date.Year = static_cast<GInt16>(brokendowntime.tm_year + 1900);
    1649        7678 :     psField->Date.Month = static_cast<GByte>(brokendowntime.tm_mon + 1);
    1650        7678 :     psField->Date.Day = static_cast<GByte>(brokendowntime.tm_mday);
    1651        7678 :     psField->Date.Hour = static_cast<GByte>(brokendowntime.tm_hour);
    1652        7678 :     psField->Date.Minute = static_cast<GByte>(brokendowntime.tm_min);
    1653        7678 :     double dfSec = brokendowntime.tm_sec;
    1654        7678 :     if (bHighPrecision)
    1655             :     {
    1656          54 :         dfSec += fmod(dfSeconds, 1.0);
    1657             :     }
    1658        7678 :     psField->Date.Second = static_cast<float>(dfSec);
    1659        7678 :     psField->Date.TZFlag = 0;
    1660        7678 :     psField->Date.Reserved = 0;
    1661             : 
    1662        7678 :     return TRUE;
    1663             : }
    1664             : 
    1665             : /************************************************************************/
    1666             : /*                      FileGDBDoubleTimeToOGRTime()                    */
    1667             : /************************************************************************/
    1668             : 
    1669          28 : int FileGDBDoubleTimeToOGRTime(double dfVal, OGRField *psField)
    1670             : {
    1671          28 :     double dfSeconds = dfVal * 3600.0 * 24.0;
    1672          28 :     if (CPLIsNan(dfSeconds) || dfSeconds < 0 || dfSeconds > 86400)
    1673             :     {
    1674           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1675             :                  "FileGDBDoubleTimeToOGRTime: Invalid time: %lf", dfVal);
    1676           0 :         dfSeconds = 0.0;
    1677             :     }
    1678             : 
    1679          28 :     psField->Date.Year = 0;
    1680          28 :     psField->Date.Month = 0;
    1681          28 :     psField->Date.Day = 0;
    1682          28 :     psField->Date.Hour = static_cast<GByte>(dfSeconds / 3600);
    1683          28 :     psField->Date.Minute =
    1684          28 :         static_cast<GByte>((static_cast<int>(dfSeconds) % 3600) / 60);
    1685          28 :     psField->Date.Second = static_cast<float>(fmod(dfSeconds, 60));
    1686          28 :     psField->Date.TZFlag = 0;
    1687          28 :     psField->Date.Reserved = 0;
    1688             : 
    1689          28 :     return TRUE;
    1690             : }
    1691             : 
    1692             : /************************************************************************/
    1693             : /*                  FileGDBDateTimeWithOffsetToOGRDate()                */
    1694             : /************************************************************************/
    1695             : 
    1696          31 : int FileGDBDateTimeWithOffsetToOGRDate(double dfVal, int16_t nUTCOffset,
    1697             :                                        OGRField *psField)
    1698             : {
    1699          31 :     int ret = FileGDBDoubleDateToOGRDate(dfVal, true, psField);
    1700          31 :     if (nUTCOffset >= -14 * 60 && nUTCOffset <= 14 * 60)
    1701             :     {
    1702          31 :         psField->Date.TZFlag = static_cast<GByte>(100 + nUTCOffset / 15);
    1703             :     }
    1704             :     else
    1705           0 :         ret = FALSE;
    1706          31 :     return ret;
    1707             : }
    1708             : 
    1709             : /************************************************************************/
    1710             : /*                         GetAllFieldValues()                          */
    1711             : /************************************************************************/
    1712             : 
    1713          68 : std::vector<OGRField> FileGDBTable::GetAllFieldValues()
    1714             : {
    1715             :     std::vector<OGRField> asFields(m_apoFields.size(),
    1716          68 :                                    FileGDBField::UNSET_FIELD);
    1717         851 :     for (int i = 0; i < static_cast<int>(m_apoFields.size()); ++i)
    1718             :     {
    1719         783 :         const OGRField *psField = GetFieldValue(i);
    1720         482 :         if (psField && !OGR_RawField_IsNull(psField) &&
    1721        2010 :             !OGR_RawField_IsUnset(psField) &&
    1722         745 :             (m_apoFields[i]->GetType() == FGFT_STRING ||
    1723         484 :              m_apoFields[i]->GetType() == FGFT_XML ||
    1724         404 :              m_apoFields[i]->GetType() == FGFT_GLOBALID ||
    1725         183 :              m_apoFields[i]->GetType() == FGFT_GUID))
    1726             :         {
    1727         337 :             asFields[i].String = CPLStrdup(psField->String);
    1728             :         }
    1729         145 :         else if (psField && !OGR_RawField_IsNull(psField) &&
    1730         864 :                  !OGR_RawField_IsUnset(psField) &&
    1731         273 :                  (m_apoFields[i]->GetType() == FGFT_BINARY ||
    1732         128 :                   m_apoFields[i]->GetType() == FGFT_GEOMETRY))
    1733             :         {
    1734          34 :             asFields[i].Binary.paData =
    1735          17 :                 static_cast<GByte *>(CPLMalloc(psField->Binary.nCount));
    1736          17 :             asFields[i].Binary.nCount = psField->Binary.nCount;
    1737          17 :             memcpy(asFields[i].Binary.paData, psField->Binary.paData,
    1738          17 :                    asFields[i].Binary.nCount);
    1739             :         }
    1740         429 :         else if (psField && !(m_apoFields[i]->GetType() == FGFT_RASTER))
    1741             :         {
    1742         128 :             asFields[i] = *psField;
    1743             :         }
    1744             :     }
    1745          68 :     return asFields;
    1746             : }
    1747             : 
    1748             : /************************************************************************/
    1749             : /*                        FreeAllFieldValues()                          */
    1750             : /************************************************************************/
    1751             : 
    1752          68 : void FileGDBTable::FreeAllFieldValues(std::vector<OGRField> &asFields)
    1753             : {
    1754         851 :     for (int i = 0; i < static_cast<int>(m_apoFields.size()); ++i)
    1755             :     {
    1756         783 :         if (!OGR_RawField_IsNull(&asFields[i]) &&
    1757        1528 :             !OGR_RawField_IsUnset(&asFields[i]) &&
    1758         745 :             (m_apoFields[i]->GetType() == FGFT_STRING ||
    1759         484 :              m_apoFields[i]->GetType() == FGFT_XML ||
    1760         404 :              m_apoFields[i]->GetType() == FGFT_GLOBALID ||
    1761         183 :              m_apoFields[i]->GetType() == FGFT_GUID))
    1762             :         {
    1763         337 :             CPLFree(asFields[i].String);
    1764         337 :             asFields[i].String = nullptr;
    1765             :         }
    1766         446 :         else if (!OGR_RawField_IsNull(&asFields[i]) &&
    1767         719 :                  !OGR_RawField_IsUnset(&asFields[i]) &&
    1768         273 :                  (m_apoFields[i]->GetType() == FGFT_BINARY ||
    1769         128 :                   m_apoFields[i]->GetType() == FGFT_GEOMETRY))
    1770             :         {
    1771          17 :             CPLFree(asFields[i].Binary.paData);
    1772          17 :             asFields[i].Binary.paData = nullptr;
    1773             :         }
    1774             :     }
    1775          68 : }
    1776             : 
    1777             : /************************************************************************/
    1778             : /*                          GetFieldValue()                             */
    1779             : /************************************************************************/
    1780             : 
    1781      225578 : const OGRField *FileGDBTable::GetFieldValue(int iCol)
    1782             : {
    1783      225578 :     OGRField *errorRetValue = nullptr;
    1784             : 
    1785      225578 :     returnErrorIf(m_nCurRow < 0);
    1786      225578 :     returnErrorIf(static_cast<GUInt32>(iCol) >= m_apoFields.size());
    1787      225578 :     returnErrorIf(m_bError);
    1788             : 
    1789      225578 :     GByte *pabyEnd = m_abyBuffer.data() + m_nRowBlobLength;
    1790             : 
    1791             :     /* In case a string was previously read */
    1792      225578 :     if (m_nChSaved >= 0)
    1793             :     {
    1794       56002 :         *m_pabyIterVals = static_cast<GByte>(m_nChSaved);
    1795       56002 :         m_nChSaved = -1;
    1796             :     }
    1797             : 
    1798      225578 :     if (iCol <= m_nLastCol)
    1799             :     {
    1800        9853 :         m_nLastCol = -1;
    1801        9853 :         m_pabyIterVals = m_abyBuffer.data() + m_nNullableFieldsSizeInBytes;
    1802        9853 :         m_iAccNullable = 0;
    1803             :     }
    1804             : 
    1805             :     // Skip previous fields
    1806      448059 :     for (int j = m_nLastCol + 1; j < iCol; j++)
    1807             :     {
    1808      222481 :         if (m_apoFields[j]->m_bNullable)
    1809             :         {
    1810      122781 :             int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
    1811      122781 :             m_iAccNullable++;
    1812      122781 :             if (bIsNull)
    1813       43124 :                 continue;
    1814             :         }
    1815             : 
    1816      179357 :         GUInt32 nLength = 0;
    1817      179357 :         CPL_IGNORE_RET_VAL(nLength);
    1818      179357 :         switch (m_apoFields[j]->m_eType)
    1819             :         {
    1820           0 :             case FGFT_UNDEFINED:
    1821           0 :                 CPLAssert(false);
    1822             :                 break;
    1823             : 
    1824       61864 :             case FGFT_OBJECTID:
    1825       61864 :                 break;
    1826             : 
    1827       66591 :             case FGFT_STRING:
    1828             :             case FGFT_XML:
    1829             :             case FGFT_GEOMETRY:
    1830             :             case FGFT_BINARY:
    1831             :             {
    1832       66591 :                 if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
    1833             :                 {
    1834           0 :                     m_bError = TRUE;
    1835           0 :                     returnError();
    1836             :                 }
    1837       66591 :                 break;
    1838             :             }
    1839             : 
    1840           0 :             case FGFT_RASTER:
    1841             :             {
    1842             :                 const FileGDBRasterField *rasterField =
    1843           0 :                     cpl::down_cast<const FileGDBRasterField *>(
    1844           0 :                         m_apoFields[j].get());
    1845           0 :                 if (rasterField->GetRasterType() ==
    1846             :                     FileGDBRasterField::Type::MANAGED)
    1847           0 :                     nLength = sizeof(GInt32);
    1848             :                 else
    1849             :                 {
    1850           0 :                     if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
    1851             :                     {
    1852           0 :                         m_bError = TRUE;
    1853           0 :                         returnError();
    1854             :                     }
    1855             :                 }
    1856           0 :                 break;
    1857             :             }
    1858             : 
    1859          12 :             case FGFT_INT16:
    1860          12 :                 nLength = sizeof(GInt16);
    1861          12 :                 break;
    1862       13476 :             case FGFT_INT32:
    1863       13476 :                 nLength = sizeof(GInt32);
    1864       13476 :                 break;
    1865           8 :             case FGFT_FLOAT32:
    1866           8 :                 nLength = sizeof(float);
    1867           8 :                 break;
    1868         198 :             case FGFT_FLOAT64:
    1869         198 :                 nLength = sizeof(double);
    1870         198 :                 break;
    1871          27 :             case FGFT_DATETIME:
    1872             :             case FGFT_DATE:
    1873             :             case FGFT_TIME:
    1874          27 :                 nLength = sizeof(double);
    1875          27 :                 break;
    1876       37172 :             case FGFT_GUID:
    1877             :             case FGFT_GLOBALID:
    1878       37172 :                 nLength = UUID_SIZE_IN_BYTES;
    1879       37172 :                 break;
    1880           0 :             case FGFT_INT64:
    1881           0 :                 nLength = sizeof(int64_t);
    1882           0 :                 break;
    1883           9 :             case FGFT_DATETIME_WITH_OFFSET:
    1884           9 :                 nLength += sizeof(double) + sizeof(int16_t);
    1885           9 :                 break;
    1886             :         }
    1887             : 
    1888      179357 :         if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
    1889             :         {
    1890           0 :             m_bError = TRUE;
    1891           0 :             returnError();
    1892             :         }
    1893      179357 :         m_pabyIterVals += nLength;
    1894             :     }
    1895             : 
    1896      225578 :     m_nLastCol = iCol;
    1897             : 
    1898      225578 :     if (m_apoFields[iCol]->m_bNullable)
    1899             :     {
    1900      198389 :         int bIsNull = TEST_BIT(m_abyBuffer.data(), m_iAccNullable);
    1901      198389 :         m_iAccNullable++;
    1902      198389 :         if (bIsNull)
    1903             :         {
    1904       26776 :             return nullptr;
    1905             :         }
    1906             :     }
    1907             : 
    1908      198802 :     switch (m_apoFields[iCol]->m_eType)
    1909             :     {
    1910           0 :         case FGFT_UNDEFINED:
    1911           0 :             CPLAssert(false);
    1912             :             break;
    1913             : 
    1914          68 :         case FGFT_OBJECTID:
    1915          68 :             return nullptr;
    1916             : 
    1917       62344 :         case FGFT_STRING:
    1918             :         case FGFT_XML:
    1919             :         {
    1920             :             GUInt32 nLength;
    1921       62344 :             if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
    1922             :             {
    1923           0 :                 m_bError = TRUE;
    1924          68 :                 returnError();
    1925             :             }
    1926       62344 :             if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
    1927             :             {
    1928          68 :                 m_bError = TRUE;
    1929          68 :                 returnError();
    1930             :             }
    1931             : 
    1932       62276 :             if (m_bStringsAreUTF8 || m_apoFields[iCol]->m_eType != FGFT_STRING)
    1933             :             {
    1934             :                 /* eCurFieldType = OFTString; */
    1935       62274 :                 m_sCurField.String = reinterpret_cast<char *>(m_pabyIterVals);
    1936       62274 :                 m_pabyIterVals += nLength;
    1937             : 
    1938             :                 /* This is a trick to avoid a alloc()+copy(). We null-terminate
    1939             :                  */
    1940             :                 /* after the string, and save the pointer and value to restore
    1941             :                  */
    1942       62274 :                 m_nChSaved = *m_pabyIterVals;
    1943       62274 :                 *m_pabyIterVals = '\0';
    1944             :             }
    1945             :             else
    1946             :             {
    1947           2 :                 m_osTempString = ReadUTF16String(m_pabyIterVals, nLength / 2);
    1948           2 :                 m_sCurField.String = &m_osTempString[0];
    1949           2 :                 m_pabyIterVals += nLength;
    1950             :             }
    1951             : 
    1952             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow,
    1953             :              * sCurField.String); */
    1954             : 
    1955       62276 :             break;
    1956             :         }
    1957             : 
    1958        7589 :         case FGFT_INT16:
    1959             :         {
    1960        7589 :             if (m_pabyIterVals + sizeof(GInt16) > pabyEnd)
    1961             :             {
    1962           0 :                 m_bError = TRUE;
    1963           0 :                 returnError();
    1964             :             }
    1965             : 
    1966             :             /* eCurFieldType = OFTInteger; */
    1967        7589 :             m_sCurField.Integer = GetInt16(m_pabyIterVals, 0);
    1968             : 
    1969        7589 :             m_pabyIterVals += sizeof(GInt16);
    1970             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow,
    1971             :              * sCurField.Integer); */
    1972             : 
    1973        7589 :             break;
    1974             :         }
    1975             : 
    1976       19751 :         case FGFT_INT32:
    1977             :         {
    1978       19751 :             if (m_pabyIterVals + sizeof(GInt32) > pabyEnd)
    1979             :             {
    1980           0 :                 m_bError = TRUE;
    1981           0 :                 returnError();
    1982             :             }
    1983             : 
    1984             :             /* eCurFieldType = OFTInteger; */
    1985       19751 :             m_sCurField.Integer = GetInt32(m_pabyIterVals, 0);
    1986             : 
    1987       19751 :             m_pabyIterVals += sizeof(GInt32);
    1988             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d", iCol, nCurRow,
    1989             :              * sCurField.Integer); */
    1990             : 
    1991       19751 :             break;
    1992             :         }
    1993             : 
    1994        7589 :         case FGFT_FLOAT32:
    1995             :         {
    1996        7589 :             if (m_pabyIterVals + sizeof(float) > pabyEnd)
    1997             :             {
    1998           0 :                 m_bError = TRUE;
    1999           0 :                 returnError();
    2000             :             }
    2001             : 
    2002             :             /* eCurFieldType = OFTReal; */
    2003        7589 :             m_sCurField.Real = GetFloat32(m_pabyIterVals, 0);
    2004             : 
    2005        7589 :             m_pabyIterVals += sizeof(float);
    2006             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow,
    2007             :              * sCurField.Real); */
    2008             : 
    2009        7589 :             break;
    2010             :         }
    2011             : 
    2012       26553 :         case FGFT_FLOAT64:
    2013             :         {
    2014       26553 :             if (m_pabyIterVals + sizeof(double) > pabyEnd)
    2015             :             {
    2016           0 :                 m_bError = TRUE;
    2017           0 :                 returnError();
    2018             :             }
    2019             : 
    2020             :             /* eCurFieldType = OFTReal; */
    2021       26553 :             m_sCurField.Real = GetFloat64(m_pabyIterVals, 0);
    2022             : 
    2023       26553 :             m_pabyIterVals += sizeof(double);
    2024             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: %f", iCol, nCurRow,
    2025             :              * sCurField.Real); */
    2026             : 
    2027       26553 :             break;
    2028             :         }
    2029             : 
    2030        7626 :         case FGFT_DATETIME:
    2031             :         case FGFT_DATE:
    2032             :         {
    2033        7626 :             if (m_pabyIterVals + sizeof(double) > pabyEnd)
    2034             :             {
    2035           0 :                 m_bError = TRUE;
    2036           0 :                 returnError();
    2037             :             }
    2038             : 
    2039             :             /* Number of days since 1899/12/30 00:00:00 */
    2040        7626 :             const double dfVal = GetFloat64(m_pabyIterVals, 0);
    2041             : 
    2042        7626 :             if (m_apoFields[iCol]->m_bReadAsDouble)
    2043             :             {
    2044           5 :                 m_sCurField.Real = dfVal;
    2045             :             }
    2046             :             else
    2047             :             {
    2048        7621 :                 FileGDBDoubleDateToOGRDate(
    2049        7621 :                     dfVal, m_apoFields[iCol]->IsHighPrecision(), &m_sCurField);
    2050             :                 /* eCurFieldType = OFTDateTime; */
    2051             :             }
    2052             : 
    2053        7626 :             m_pabyIterVals += sizeof(double);
    2054             : 
    2055        7626 :             break;
    2056             :         }
    2057             : 
    2058       49705 :         case FGFT_GEOMETRY:
    2059             :         case FGFT_BINARY:
    2060             :         {
    2061             :             GUInt32 nLength;
    2062       49705 :             if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
    2063             :             {
    2064           0 :                 m_bError = TRUE;
    2065           0 :                 returnError();
    2066             :             }
    2067       49705 :             if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
    2068             :             {
    2069           0 :                 m_bError = TRUE;
    2070           0 :                 returnError();
    2071             :             }
    2072             : 
    2073             :             /* eCurFieldType = OFTBinary; */
    2074       49705 :             m_sCurField.Binary.nCount = nLength;
    2075       49705 :             m_sCurField.Binary.paData = const_cast<GByte *>(m_pabyIterVals);
    2076             : 
    2077       49705 :             m_pabyIterVals += nLength;
    2078             : 
    2079             :             /* Null terminate binary in case it is used as a string */
    2080       49705 :             m_nChSaved = *m_pabyIterVals;
    2081       49705 :             *m_pabyIterVals = '\0';
    2082             : 
    2083             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: %d bytes", iCol,
    2084             :              * nCurRow, snLength); */
    2085             : 
    2086       49705 :             break;
    2087             :         }
    2088             : 
    2089           0 :         case FGFT_RASTER:
    2090             :         {
    2091             :             const FileGDBRasterField *rasterField =
    2092           0 :                 cpl::down_cast<const FileGDBRasterField *>(
    2093           0 :                     m_apoFields[iCol].get());
    2094           0 :             if (rasterField->GetRasterType() ==
    2095             :                 FileGDBRasterField::Type::MANAGED)
    2096             :             {
    2097           0 :                 if (m_pabyIterVals + sizeof(GInt32) > pabyEnd)
    2098             :                 {
    2099           0 :                     m_bError = TRUE;
    2100           0 :                     returnError();
    2101             :                 }
    2102             : 
    2103           0 :                 const GInt32 nVal = GetInt32(m_pabyIterVals, 0);
    2104             : 
    2105             :                 /* eCurFieldType = OFTIntger; */
    2106           0 :                 m_sCurField.Integer = nVal;
    2107             : 
    2108           0 :                 m_pabyIterVals += sizeof(GInt32);
    2109             :             }
    2110             :             else
    2111             :             {
    2112             :                 GUInt32 nLength;
    2113           0 :                 if (!ReadVarUInt32(m_pabyIterVals, pabyEnd, nLength))
    2114             :                 {
    2115           0 :                     m_bError = TRUE;
    2116           0 :                     returnError();
    2117             :                 }
    2118           0 :                 if (nLength > static_cast<GUInt32>(pabyEnd - m_pabyIterVals))
    2119             :                 {
    2120           0 :                     m_bError = TRUE;
    2121           0 :                     returnError();
    2122             :                 }
    2123             : 
    2124           0 :                 if (rasterField->GetRasterType() ==
    2125             :                     FileGDBRasterField::Type::EXTERNAL)
    2126             :                 {
    2127             :                     // coverity[tainted_data,tainted_data_argument]
    2128             :                     m_osCacheRasterFieldPath =
    2129           0 :                         ReadUTF16String(m_pabyIterVals, nLength / 2);
    2130           0 :                     m_sCurField.String = &m_osCacheRasterFieldPath[0];
    2131           0 :                     m_pabyIterVals += nLength;
    2132             :                 }
    2133             :                 else
    2134             :                 {
    2135             :                     /* eCurFieldType = OFTBinary; */
    2136           0 :                     m_sCurField.Binary.nCount = nLength;
    2137           0 :                     m_sCurField.Binary.paData =
    2138           0 :                         const_cast<GByte *>(m_pabyIterVals);
    2139             : 
    2140           0 :                     m_pabyIterVals += nLength;
    2141             : 
    2142             :                     /* Null terminate binary in case it is used as a string */
    2143           0 :                     m_nChSaved = *m_pabyIterVals;
    2144           0 :                     *m_pabyIterVals = '\0';
    2145             :                 }
    2146             :             }
    2147           0 :             break;
    2148             :         }
    2149             : 
    2150       17518 :         case FGFT_GUID:
    2151             :         case FGFT_GLOBALID:
    2152             :         {
    2153       17518 :             if (m_pabyIterVals + UUID_SIZE_IN_BYTES > pabyEnd)
    2154             :             {
    2155           0 :                 m_bError = TRUE;
    2156           0 :                 returnError();
    2157             :             }
    2158             : 
    2159             :             /* eCurFieldType = OFTString; */
    2160       17518 :             m_sCurField.String = m_achGUIDBuffer;
    2161             :             /*78563412BC9AF0DE1234567890ABCDEF -->
    2162             :              * {12345678-9ABC-DEF0-1234-567890ABCDEF} */
    2163       17518 :             snprintf(m_achGUIDBuffer, sizeof(m_achGUIDBuffer),
    2164             :                      "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%"
    2165             :                      "02X%02X%02X%02X}",
    2166       17518 :                      m_pabyIterVals[3], m_pabyIterVals[2], m_pabyIterVals[1],
    2167       17518 :                      m_pabyIterVals[0], m_pabyIterVals[5], m_pabyIterVals[4],
    2168       17518 :                      m_pabyIterVals[7], m_pabyIterVals[6], m_pabyIterVals[8],
    2169       17518 :                      m_pabyIterVals[9], m_pabyIterVals[10], m_pabyIterVals[11],
    2170       17518 :                      m_pabyIterVals[12], m_pabyIterVals[13], m_pabyIterVals[14],
    2171       17518 :                      m_pabyIterVals[15]);
    2172             : 
    2173       17518 :             m_pabyIterVals += UUID_SIZE_IN_BYTES;
    2174             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: %s", iCol, nCurRow,
    2175             :              * sCurField.String); */
    2176             : 
    2177       17518 :             break;
    2178             :         }
    2179             : 
    2180           9 :         case FGFT_INT64:
    2181             :         {
    2182           9 :             if (m_pabyIterVals + sizeof(int64_t) > pabyEnd)
    2183             :             {
    2184           0 :                 m_bError = TRUE;
    2185           0 :                 returnError();
    2186             :             }
    2187             : 
    2188             :             /* eCurFieldType = OFTInteger; */
    2189           9 :             m_sCurField.Integer64 = GetInt64(m_pabyIterVals, 0);
    2190             : 
    2191           9 :             m_pabyIterVals += sizeof(int64_t);
    2192             :             /* CPLDebug("OpenFileGDB", "Field %d, row %d: " CPL_FRMT_GIB, iCol, nCurRow,
    2193             :              * sCurField.Integer64); */
    2194             : 
    2195           9 :             break;
    2196             :         }
    2197             : 
    2198          23 :         case FGFT_TIME:
    2199             :         {
    2200          23 :             if (m_pabyIterVals + sizeof(double) > pabyEnd)
    2201             :             {
    2202           0 :                 m_bError = TRUE;
    2203           0 :                 returnError();
    2204             :             }
    2205             : 
    2206             :             /* Fraction of day */
    2207          23 :             const double dfVal = GetFloat64(m_pabyIterVals, 0);
    2208             : 
    2209          23 :             if (m_apoFields[iCol]->m_bReadAsDouble)
    2210             :             {
    2211           3 :                 m_sCurField.Real = dfVal;
    2212             :             }
    2213             :             else
    2214             :             {
    2215          20 :                 FileGDBDoubleTimeToOGRTime(dfVal, &m_sCurField);
    2216             :                 /* eCurFieldType = OFTTime; */
    2217             :             }
    2218             : 
    2219          23 :             m_pabyIterVals += sizeof(double);
    2220             : 
    2221          23 :             break;
    2222             :         }
    2223             : 
    2224          27 :         case FGFT_DATETIME_WITH_OFFSET:
    2225             :         {
    2226          27 :             if (m_pabyIterVals + sizeof(double) + sizeof(int16_t) > pabyEnd)
    2227             :             {
    2228           0 :                 m_bError = TRUE;
    2229           0 :                 returnError();
    2230             :             }
    2231             : 
    2232             :             /* Number of days since 1899/12/30 00:00:00 */
    2233          27 :             const double dfVal = GetFloat64(m_pabyIterVals, 0);
    2234          27 :             m_pabyIterVals += sizeof(double);
    2235          27 :             const int16_t nUTCOffset = GetInt16(m_pabyIterVals, 0);
    2236          27 :             m_pabyIterVals += sizeof(int16_t);
    2237             : 
    2238          27 :             if (m_apoFields[iCol]->m_bReadAsDouble)
    2239             :             {
    2240           3 :                 m_sCurField.Real = dfVal - nUTCOffset * 60.0 / 86400.0;
    2241             :             }
    2242             :             else
    2243             :             {
    2244          24 :                 FileGDBDateTimeWithOffsetToOGRDate(dfVal, nUTCOffset,
    2245             :                                                    &m_sCurField);
    2246             :                 /* eCurFieldType = OFTDateTime; */
    2247             :             }
    2248             : 
    2249          27 :             break;
    2250             :         }
    2251             :     }
    2252             : 
    2253      221033 :     if (iCol == static_cast<int>(m_apoFields.size()) - 1 &&
    2254       22367 :         m_pabyIterVals < pabyEnd)
    2255             :     {
    2256           1 :         CPLDebug("OpenFileGDB", "%d bytes remaining at end of record %d",
    2257           1 :                  static_cast<int>(pabyEnd - m_pabyIterVals), m_nCurRow);
    2258             :     }
    2259             : 
    2260      198666 :     return &m_sCurField;
    2261             : }
    2262             : 
    2263             : /************************************************************************/
    2264             : /*                           GetIndexCount()                            */
    2265             : /************************************************************************/
    2266             : 
    2267        4410 : int FileGDBTable::GetIndexCount()
    2268             : {
    2269        4410 :     const int errorRetValue = 0;
    2270        4410 :     if (m_bHasReadGDBIndexes)
    2271        2904 :         return static_cast<int>(m_apoIndexes.size());
    2272             : 
    2273        1506 :     m_bHasReadGDBIndexes = TRUE;
    2274             : 
    2275             :     const char *pszIndexesName =
    2276        1506 :         CPLFormFilename(CPLGetPath(m_osFilename.c_str()),
    2277             :                         CPLGetBasename(m_osFilename.c_str()), "gdbindexes");
    2278        1506 :     VSILFILE *fpIndexes = VSIFOpenL(pszIndexesName, "rb");
    2279             :     VSIStatBufL sStat;
    2280        1506 :     if (fpIndexes == nullptr)
    2281             :     {
    2282        1297 :         if (VSIStatExL(pszIndexesName, &sStat, VSI_STAT_EXISTS_FLAG) == 0)
    2283           0 :             returnError();
    2284             :         else
    2285        1297 :             return 0;
    2286             :     }
    2287             : 
    2288         209 :     VSIFSeekL(fpIndexes, 0, SEEK_END);
    2289         209 :     vsi_l_offset nFileSize = VSIFTellL(fpIndexes);
    2290         209 :     returnErrorAndCleanupIf(nFileSize > 1024 * 1024, VSIFCloseL(fpIndexes));
    2291             : 
    2292             :     GByte *pabyIdx = static_cast<GByte *>(
    2293         209 :         VSI_MALLOC_VERBOSE(static_cast<size_t>(nFileSize)));
    2294         209 :     returnErrorAndCleanupIf(pabyIdx == nullptr, VSIFCloseL(fpIndexes));
    2295             : 
    2296         209 :     VSIFSeekL(fpIndexes, 0, SEEK_SET);
    2297             :     int nRead = static_cast<int>(
    2298         209 :         VSIFReadL(pabyIdx, static_cast<size_t>(nFileSize), 1, fpIndexes));
    2299         209 :     VSIFCloseL(fpIndexes);
    2300         209 :     returnErrorAndCleanupIf(nRead != 1, VSIFree(pabyIdx));
    2301             : 
    2302         209 :     GByte *pabyCur = pabyIdx;
    2303         209 :     GByte *pabyEnd = pabyIdx + nFileSize;
    2304         209 :     returnErrorAndCleanupIf(pabyEnd - pabyCur < 4, VSIFree(pabyIdx));
    2305         209 :     GUInt32 nIndexCount = GetUInt32(pabyCur, 0);
    2306         209 :     pabyCur += 4;
    2307             : 
    2308             :     // FileGDB v9 indexes structure not handled yet. Start with 13 98 85 03
    2309         209 :     if (nIndexCount == 0x03859813)
    2310             :     {
    2311          44 :         VSIFree(pabyIdx);
    2312             : 
    2313             :         // Hard code detection of blk_key_index on raster layers
    2314          44 :         const int iBlockKeyFieldIdx = GetFieldIdx("block_key");
    2315          44 :         if (iBlockKeyFieldIdx >= 0)
    2316             :         {
    2317             :             std::string osAtxFilename =
    2318           2 :                 CPLResetExtension(m_osFilename.c_str(), "blk_key_index.atx");
    2319           2 :             if (VSIStatExL(osAtxFilename.c_str(), &sStat,
    2320           2 :                            VSI_STAT_EXISTS_FLAG) == 0)
    2321             :             {
    2322           2 :                 auto poIndex = std::make_unique<FileGDBIndex>();
    2323           2 :                 poIndex->m_osIndexName = "blk_key_index";
    2324           2 :                 poIndex->m_osExpression = "block_key";
    2325           2 :                 m_apoFields[iBlockKeyFieldIdx]->m_poIndex = poIndex.get();
    2326           2 :                 m_apoIndexes.push_back(std::move(poIndex));
    2327           2 :                 return 1;
    2328             :             }
    2329             :         }
    2330             : 
    2331          42 :         CPLDebug("OpenFileGDB", ".gdbindexes v9 not handled yet");
    2332          42 :         return 0;
    2333             :     }
    2334         165 :     returnErrorAndCleanupIf(nIndexCount >=
    2335             :                                 static_cast<size_t>(GetFieldCount() + 1) * 10,
    2336             :                             VSIFree(pabyIdx));
    2337             : 
    2338             :     GUInt32 i;
    2339         752 :     for (i = 0; i < nIndexCount; i++)
    2340             :     {
    2341         628 :         returnErrorAndCleanupIf(static_cast<GUInt32>(pabyEnd - pabyCur) <
    2342             :                                     sizeof(GUInt32),
    2343             :                                 VSIFree(pabyIdx));
    2344         608 :         GUInt32 nIdxNameCarCount = GetUInt32(pabyCur, 0);
    2345         608 :         pabyCur += sizeof(GUInt32);
    2346         608 :         returnErrorAndCleanupIf(nIdxNameCarCount > 1024, VSIFree(pabyIdx));
    2347         603 :         returnErrorAndCleanupIf(static_cast<GUInt32>(pabyEnd - pabyCur) <
    2348             :                                     2 * nIdxNameCarCount,
    2349             :                                 VSIFree(pabyIdx));
    2350             :         const std::string osIndexName(
    2351         598 :             ReadUTF16String(pabyCur, nIdxNameCarCount));
    2352         598 :         pabyCur += 2 * nIdxNameCarCount;
    2353             : 
    2354             :         // Skip magic fields
    2355         598 :         pabyCur += 2 + 4 + 2 + 4;
    2356             : 
    2357         598 :         returnErrorAndCleanupIf(static_cast<GUInt32>(pabyEnd - pabyCur) <
    2358             :                                     sizeof(GUInt32),
    2359             :                                 VSIFree(pabyIdx));
    2360         598 :         GUInt32 nColNameCarCount = GetUInt32(pabyCur, 0);
    2361         598 :         pabyCur += sizeof(GUInt32);
    2362         598 :         returnErrorAndCleanupIf(nColNameCarCount > 1024, VSIFree(pabyIdx));
    2363         593 :         returnErrorAndCleanupIf(static_cast<GUInt32>(pabyEnd - pabyCur) <
    2364             :                                     2 * nColNameCarCount,
    2365             :                                 VSIFree(pabyIdx));
    2366             :         const std::string osExpression(
    2367        1176 :             ReadUTF16String(pabyCur, nColNameCarCount));
    2368         588 :         pabyCur += 2 * nColNameCarCount;
    2369             : 
    2370             :         // Skip magic field
    2371         588 :         pabyCur += 2;
    2372             : 
    2373        1176 :         auto poIndex = std::make_unique<FileGDBIndex>();
    2374         588 :         poIndex->m_osIndexName = osIndexName;
    2375         588 :         poIndex->m_osExpression = osExpression;
    2376             : 
    2377        1176 :         if (m_iObjectIdField < 0 ||
    2378         588 :             osExpression != m_apoFields[m_iObjectIdField]->GetName())
    2379             :         {
    2380         858 :             const auto osFieldName = poIndex->GetFieldName();
    2381         429 :             int nFieldIdx = GetFieldIdx(osFieldName);
    2382         429 :             if (nFieldIdx < 0)
    2383             :             {
    2384           0 :                 CPLDebug("OpenFileGDB",
    2385             :                          "Index defined for field %s that does not exist",
    2386             :                          osFieldName.c_str());
    2387             :             }
    2388             :             else
    2389             :             {
    2390         429 :                 if (m_apoFields[nFieldIdx]->m_poIndex != nullptr)
    2391             :                 {
    2392           0 :                     CPLDebug("OpenFileGDB",
    2393             :                              "There is already one index defined for field %s",
    2394             :                              osFieldName.c_str());
    2395             :                 }
    2396             :                 else
    2397             :                 {
    2398         429 :                     m_apoFields[nFieldIdx]->m_poIndex = poIndex.get();
    2399             :                 }
    2400             :             }
    2401             :         }
    2402             : 
    2403         588 :         m_apoIndexes.push_back(std::move(poIndex));
    2404             :     }
    2405             : 
    2406         144 :     VSIFree(pabyIdx);
    2407             : 
    2408         144 :     return static_cast<int>(m_apoIndexes.size());
    2409             : }
    2410             : 
    2411             : /************************************************************************/
    2412             : /*                           HasSpatialIndex()                          */
    2413             : /************************************************************************/
    2414             : 
    2415         886 : bool FileGDBTable::HasSpatialIndex()
    2416             : {
    2417         886 :     if (m_nHasSpatialIndex < 0)
    2418             :     {
    2419             :         const char *pszSpxName =
    2420         497 :             CPLFormFilename(CPLGetPath(m_osFilename.c_str()),
    2421             :                             CPLGetBasename(m_osFilename.c_str()), "spx");
    2422             :         VSIStatBufL sStat;
    2423         497 :         m_nHasSpatialIndex =
    2424         497 :             (VSIStatExL(pszSpxName, &sStat, VSI_STAT_EXISTS_FLAG) == 0);
    2425             :     }
    2426         886 :     return m_nHasSpatialIndex != FALSE;
    2427             : }
    2428             : 
    2429             : /************************************************************************/
    2430             : /*                       InstallFilterEnvelope()                        */
    2431             : /************************************************************************/
    2432             : 
    2433             : #define MAX_GUINTBIG std::numeric_limits<GUIntBig>::max()
    2434             : 
    2435        3001 : void FileGDBTable::InstallFilterEnvelope(const OGREnvelope *psFilterEnvelope)
    2436             : {
    2437        3001 :     if (psFilterEnvelope != nullptr)
    2438             :     {
    2439        1452 :         CPLAssert(m_iGeomField >= 0);
    2440             :         FileGDBGeomField *poGeomField =
    2441        1452 :             cpl::down_cast<FileGDBGeomField *>(GetField(m_iGeomField));
    2442             : 
    2443             :         /* We store the bounding box as unscaled coordinates, so that BBOX */
    2444             :         /* intersection is done with integer comparisons */
    2445        1452 :         if (psFilterEnvelope->MinX >= poGeomField->m_dfXOrigin)
    2446        1452 :             m_nFilterXMin = static_cast<GUIntBig>(
    2447        1452 :                 0.5 + (psFilterEnvelope->MinX - poGeomField->m_dfXOrigin) *
    2448        1452 :                           poGeomField->m_dfXYScale);
    2449             :         else
    2450           0 :             m_nFilterXMin = 0;
    2451        2904 :         if (psFilterEnvelope->MaxX - poGeomField->m_dfXOrigin <
    2452        1452 :             static_cast<double>(MAX_GUINTBIG) / poGeomField->m_dfXYScale)
    2453        1452 :             m_nFilterXMax = static_cast<GUIntBig>(
    2454        1452 :                 0.5 + (psFilterEnvelope->MaxX - poGeomField->m_dfXOrigin) *
    2455        1452 :                           poGeomField->m_dfXYScale);
    2456             :         else
    2457           0 :             m_nFilterXMax = MAX_GUINTBIG;
    2458        1452 :         if (psFilterEnvelope->MinY >= poGeomField->m_dfYOrigin)
    2459        1452 :             m_nFilterYMin = static_cast<GUIntBig>(
    2460        1452 :                 0.5 + (psFilterEnvelope->MinY - poGeomField->m_dfYOrigin) *
    2461        1452 :                           poGeomField->m_dfXYScale);
    2462             :         else
    2463           0 :             m_nFilterYMin = 0;
    2464        2904 :         if (psFilterEnvelope->MaxY - poGeomField->m_dfYOrigin <
    2465        1452 :             static_cast<double>(MAX_GUINTBIG) / poGeomField->m_dfXYScale)
    2466        1452 :             m_nFilterYMax = static_cast<GUIntBig>(
    2467        1452 :                 0.5 + (psFilterEnvelope->MaxY - poGeomField->m_dfYOrigin) *
    2468        1452 :                           poGeomField->m_dfXYScale);
    2469             :         else
    2470           0 :             m_nFilterYMax = MAX_GUINTBIG;
    2471             :     }
    2472             :     else
    2473             :     {
    2474        1549 :         m_nFilterXMin = 0;
    2475        1549 :         m_nFilterXMax = 0;
    2476        1549 :         m_nFilterYMin = 0;
    2477        1549 :         m_nFilterYMax = 0;
    2478             :     }
    2479        3001 : }
    2480             : 
    2481             : /************************************************************************/
    2482             : /*                  GetMinMaxProjYForSpatialIndex()                     */
    2483             : /************************************************************************/
    2484             : 
    2485             : // ESRI software seems to have an extremely weird behavior regarding spatial
    2486             : // indexing of geometries.
    2487             : // When a projected CRS is associated with a layer, the northing of geometries
    2488             : // is clamped, using the returned (dfYMin,dfYMax) values of this method.
    2489             : // When creating the .spx file, if the maximum Y of a geometry is > dfYMax, then
    2490             : // the geometry must be shifted along the Y axis so that its maximum value is
    2491             : // dfYMax
    2492         447 : void FileGDBTable::GetMinMaxProjYForSpatialIndex(double &dfYMin,
    2493             :                                                  double &dfYMax) const
    2494             : {
    2495         447 :     dfYMin = -std::numeric_limits<double>::max();
    2496         447 :     dfYMax = std::numeric_limits<double>::max();
    2497         447 :     const auto poGeomField = GetGeomField();
    2498         447 :     if (poGeomField == nullptr)
    2499         443 :         return;
    2500         447 :     const auto &osWKT = poGeomField->GetWKT();
    2501         447 :     OGRSpatialReference oSRS;
    2502         585 :     if (osWKT.empty() || osWKT[0] == '{' ||
    2503         138 :         oSRS.importFromWkt(osWKT.c_str()) != OGRERR_NONE)
    2504         309 :         return;
    2505         138 :     if (!oSRS.IsProjected())
    2506         134 :         return;
    2507           4 :     const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
    2508           4 :     if (pszProjection == nullptr)
    2509           0 :         return;
    2510             :     double dfMinLat;
    2511             :     double dfMaxLat;
    2512             :     // Determined through experimentation, e.g with the following script
    2513             : #if 0
    2514             :     from osgeo import gdal, ogr, osr
    2515             :     import struct
    2516             :     gdal.RmdirRecursive('test.gdb')
    2517             :     ds = ogr.GetDriverByName('FileGDB').CreateDataSource('test.gdb')
    2518             :     srs = osr.SpatialReference()
    2519             : #srs.SetFromUserInput(                                                         \
    2520             :     "+proj=tmerc +lon_0=-40 +lat_0=60 +k=0.9 +x_0=00000 +y_0=00000 +datum=WGS84")
    2521             :     srs.SetFromUserInput("+proj=merc +lon_0=-40 +lat_0=60 +k=0.9 +x_0=00000 +y_0=00000 +datum=WGS84")
    2522             :     srs_lonlat = srs.CloneGeogCS()
    2523             :     srs_lonlat.SetAxisMappingStrategy(osr.OAMS_TRADITIONAL_GIS_ORDER)
    2524             :     ct = osr.CoordinateTransformation(srs, srs_lonlat)
    2525             :     grid_step = 0.1
    2526             :     lyr = ds.CreateLayer('test', geom_type=ogr.wkbPoint, srs=srs)
    2527             : #Add 2 dummy features to set the grid_step to what we want
    2528             :     f = ogr.Feature(lyr.GetLayerDefn())
    2529             :     f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(0 0)'))
    2530             :     lyr.CreateFeature(f)
    2531             :     f = ogr.Feature(lyr.GetLayerDefn())
    2532             :     y = (2**0.5) * grid_step
    2533             :     f.SetGeometry(ogr.CreateGeometryFromWkt('POINT(%f %f)' % (y, y)))
    2534             :     lyr.CreateFeature(f)
    2535             :     ds = None
    2536             :     ds = ogr.GetDriverByName('FileGDB').Open('test.gdb', update=1)
    2537             :     lyr = ds.GetLayer(0)
    2538             :     lyr.DeleteFeature(1)
    2539             :     lyr.DeleteFeature(2)
    2540             :     y = 1e9 # some big value
    2541             :     f = ogr.Feature(lyr.GetLayerDefn())
    2542             :     g = ogr.CreateGeometryFromWkt('POINT(0 %f)' % y)
    2543             :     f.SetGeometry(g)
    2544             :     lyr.CreateFeature(f)
    2545             :     f = ogr.Feature(lyr.GetLayerDefn())
    2546             :     g = ogr.CreateGeometryFromWkt('POINT(0 %f)' % -y)
    2547             :     f.SetGeometry(g)
    2548             :     lyr.CreateFeature(f)
    2549             :     ds = None
    2550             :     f = open('test.gdb/a00000009.spx', 'rb')
    2551             :     f.seek(4)
    2552             :     n = ord(f.read(1))
    2553             :     for x in range(n):
    2554             :         f.seek(1372 + x * 8, 0)
    2555             :         v = struct.unpack('Q', f.read(8))[0]
    2556             :         x = (v >> 31 & 0x7fffffff - (1 << 29))
    2557             :         y = (v & 0x7fffffff) - (1 << 29)
    2558             :         print(x, y)  # the y value will be clamped
    2559             :         print(ct.TransformPoint(srs.GetProjParm( osr.SRS_PP_FALSE_EASTING, 0.0 ), y * grid_step))
    2560             : #endif
    2561             : 
    2562           4 :     if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
    2563             :     {
    2564           4 :         dfMinLat = -90;
    2565           4 :         dfMaxLat = 90;
    2566             :     }
    2567           0 :     else if (EQUAL(pszProjection, SRS_PT_MERCATOR_2SP) ||
    2568           0 :              EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
    2569             :     {
    2570           0 :         dfMinLat = -89.9;
    2571           0 :         dfMaxLat = 89.9;
    2572             :     }
    2573             :     else
    2574             :     {
    2575             :         // TODO? add other projection methods
    2576           0 :         return;
    2577             :     }
    2578             : 
    2579             :     auto poSRSLongLat =
    2580           4 :         std::unique_ptr<OGRSpatialReference>(oSRS.CloneGeogCS());
    2581             :     auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    2582           4 :         OGRCreateCoordinateTransformation(poSRSLongLat.get(), &oSRS));
    2583           4 :     if (!poCT)
    2584           0 :         return;
    2585             :     {
    2586           4 :         double x = 0;
    2587           4 :         double y = dfMinLat;
    2588           4 :         if (poCT->Transform(1, &x, &y))
    2589           4 :             dfYMin = y;
    2590             :     }
    2591             :     {
    2592           4 :         double x = 0;
    2593           4 :         double y = dfMaxLat;
    2594           4 :         if (poCT->Transform(1, &x, &y))
    2595           4 :             dfYMax = y;
    2596             :     }
    2597             : }
    2598             : 
    2599             : /************************************************************************/
    2600             : /*                         GetFeatureExtent()                           */
    2601             : /************************************************************************/
    2602             : 
    2603         154 : int FileGDBTable::GetFeatureExtent(const OGRField *psField,
    2604             :                                    OGREnvelope *psOutFeatureEnvelope)
    2605             : {
    2606         154 :     const int errorRetValue = FALSE;
    2607         154 :     GByte *pabyCur = psField->Binary.paData;
    2608         154 :     GByte *pabyEnd = pabyCur + psField->Binary.nCount;
    2609             :     GUInt32 nGeomType;
    2610         154 :     int nToSkip = 0;
    2611             : 
    2612         154 :     CPLAssert(m_iGeomField >= 0);
    2613             :     FileGDBGeomField *poGeomField =
    2614         154 :         cpl::down_cast<FileGDBGeomField *>(GetField(m_iGeomField));
    2615             : 
    2616         154 :     ReadVarUInt32NoCheck(pabyCur, nGeomType);
    2617             : 
    2618         154 :     switch ((nGeomType & 0xff))
    2619             :     {
    2620           0 :         case SHPT_NULL:
    2621           0 :             return FALSE;
    2622             : 
    2623          17 :         case SHPT_POINTZ:
    2624             :         case SHPT_POINTZM:
    2625             :         case SHPT_POINT:
    2626             :         case SHPT_POINTM:
    2627             :         case SHPT_GENERALPOINT:
    2628             :         {
    2629             :             GUIntBig x, y;
    2630          17 :             ReadVarUInt64NoCheck(pabyCur, x);
    2631          17 :             x = CPLUnsanitizedAdd<GUIntBig>(x, -1);
    2632          17 :             ReadVarUInt64NoCheck(pabyCur, y);
    2633          17 :             y = CPLUnsanitizedAdd<GUIntBig>(y, -1);
    2634          17 :             psOutFeatureEnvelope->MinX =
    2635          17 :                 x / poGeomField->m_dfXYScale + poGeomField->m_dfXOrigin;
    2636          17 :             psOutFeatureEnvelope->MinY =
    2637          17 :                 y / poGeomField->m_dfXYScale + poGeomField->m_dfYOrigin;
    2638          17 :             psOutFeatureEnvelope->MaxX = psOutFeatureEnvelope->MinX;
    2639          17 :             psOutFeatureEnvelope->MaxY = psOutFeatureEnvelope->MinY;
    2640          17 :             return TRUE;
    2641             :         }
    2642             : 
    2643           1 :         case SHPT_MULTIPOINTZM:
    2644             :         case SHPT_MULTIPOINTZ:
    2645             :         case SHPT_MULTIPOINT:
    2646             :         case SHPT_MULTIPOINTM:
    2647             :         {
    2648           1 :             break;
    2649             :         }
    2650             : 
    2651         123 :         case SHPT_ARC:
    2652             :         case SHPT_ARCZ:
    2653             :         case SHPT_ARCZM:
    2654             :         case SHPT_ARCM:
    2655             :         case SHPT_POLYGON:
    2656             :         case SHPT_POLYGONZ:
    2657             :         case SHPT_POLYGONZM:
    2658             :         case SHPT_POLYGONM:
    2659             :         {
    2660         123 :             nToSkip = 1;
    2661         123 :             break;
    2662             :         }
    2663          12 :         case SHPT_GENERALPOLYLINE:
    2664             :         case SHPT_GENERALPOLYGON:
    2665             :         {
    2666          12 :             nToSkip = 1 + ((nGeomType & EXT_SHAPE_CURVE_FLAG) ? 1 : 0);
    2667          12 :             break;
    2668             :         }
    2669             : 
    2670           1 :         case SHPT_GENERALMULTIPATCH:
    2671             :         case SHPT_MULTIPATCHM:
    2672             :         case SHPT_MULTIPATCH:
    2673             :         {
    2674           1 :             nToSkip = 2;
    2675           1 :             break;
    2676             :         }
    2677             : 
    2678           0 :         default:
    2679           0 :             return FALSE;
    2680             :     }
    2681             : 
    2682             :     GUInt32 nPoints;
    2683         137 :     ReadVarUInt32NoCheck(pabyCur, nPoints);
    2684         137 :     if (nPoints == 0)
    2685          16 :         return TRUE;
    2686         121 :     returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip));
    2687             : 
    2688             :     GUIntBig vxmin, vymin, vdx, vdy;
    2689             : 
    2690         121 :     returnErrorIf(pabyCur >= pabyEnd);
    2691         121 :     ReadVarUInt64NoCheck(pabyCur, vxmin);
    2692         121 :     ReadVarUInt64NoCheck(pabyCur, vymin);
    2693         121 :     ReadVarUInt64NoCheck(pabyCur, vdx);
    2694         121 :     ReadVarUInt64NoCheck(pabyCur, vdy);
    2695             : 
    2696         121 :     psOutFeatureEnvelope->MinX =
    2697         121 :         vxmin / poGeomField->m_dfXYScale + poGeomField->m_dfXOrigin;
    2698         121 :     psOutFeatureEnvelope->MinY =
    2699         121 :         vymin / poGeomField->m_dfXYScale + poGeomField->m_dfYOrigin;
    2700         121 :     psOutFeatureEnvelope->MaxX =
    2701         121 :         CPLUnsanitizedAdd<GUIntBig>(vxmin, vdx) / poGeomField->m_dfXYScale +
    2702         121 :         poGeomField->m_dfXOrigin;
    2703         121 :     psOutFeatureEnvelope->MaxY =
    2704         121 :         CPLUnsanitizedAdd<GUIntBig>(vymin, vdy) / poGeomField->m_dfXYScale +
    2705         121 :         poGeomField->m_dfYOrigin;
    2706             : 
    2707         121 :     return TRUE;
    2708             : }
    2709             : 
    2710             : /************************************************************************/
    2711             : /*                 DoesGeometryIntersectsFilterEnvelope()               */
    2712             : /************************************************************************/
    2713             : 
    2714       15075 : int FileGDBTable::DoesGeometryIntersectsFilterEnvelope(const OGRField *psField)
    2715             : {
    2716       15075 :     const int errorRetValue = TRUE;
    2717       15075 :     GByte *pabyCur = psField->Binary.paData;
    2718       15075 :     GByte *pabyEnd = pabyCur + psField->Binary.nCount;
    2719             :     GUInt32 nGeomType;
    2720       15075 :     int nToSkip = 0;
    2721             : 
    2722       15075 :     ReadVarUInt32NoCheck(pabyCur, nGeomType);
    2723             : 
    2724       15075 :     switch ((nGeomType & 0xff))
    2725             :     {
    2726           0 :         case SHPT_NULL:
    2727           0 :             return TRUE;
    2728             : 
    2729       14688 :         case SHPT_POINTZ:
    2730             :         case SHPT_POINTZM:
    2731             :         case SHPT_POINT:
    2732             :         case SHPT_POINTM:
    2733             :         case SHPT_GENERALPOINT:
    2734             :         {
    2735             :             GUIntBig x, y;
    2736       14688 :             ReadVarUInt64NoCheck(pabyCur, x);
    2737       14688 :             if (x == 0)  // POINT EMPTY
    2738           0 :                 return FALSE;
    2739       14688 :             x--;
    2740       14688 :             if (x < m_nFilterXMin || x > m_nFilterXMax)
    2741       11011 :                 return FALSE;
    2742        3677 :             ReadVarUInt64NoCheck(pabyCur, y);
    2743        3677 :             y--;
    2744        3677 :             return y >= m_nFilterYMin && y <= m_nFilterYMax;
    2745             :         }
    2746             : 
    2747           8 :         case SHPT_MULTIPOINTZM:
    2748             :         case SHPT_MULTIPOINTZ:
    2749             :         case SHPT_MULTIPOINT:
    2750             :         case SHPT_MULTIPOINTM:
    2751             :         {
    2752           8 :             break;
    2753             :         }
    2754             : 
    2755         364 :         case SHPT_ARC:
    2756             :         case SHPT_ARCZ:
    2757             :         case SHPT_ARCZM:
    2758             :         case SHPT_ARCM:
    2759             :         case SHPT_POLYGON:
    2760             :         case SHPT_POLYGONZ:
    2761             :         case SHPT_POLYGONZM:
    2762             :         case SHPT_POLYGONM:
    2763             :         {
    2764         364 :             nToSkip = 1;
    2765         364 :             break;
    2766             :         }
    2767             : 
    2768           0 :         case SHPT_GENERALPOLYLINE:
    2769             :         case SHPT_GENERALPOLYGON:
    2770             :         {
    2771           0 :             nToSkip = 1 + ((nGeomType & EXT_SHAPE_CURVE_FLAG) ? 1 : 0);
    2772           0 :             break;
    2773             :         }
    2774             : 
    2775          15 :         case SHPT_GENERALMULTIPATCH:
    2776             :         case SHPT_MULTIPATCHM:
    2777             :         case SHPT_MULTIPATCH:
    2778             :         {
    2779          15 :             nToSkip = 2;
    2780          15 :             break;
    2781             :         }
    2782             : 
    2783           0 :         default:
    2784           0 :             return TRUE;
    2785             :     }
    2786             : 
    2787             :     GUInt32 nPoints;
    2788         387 :     ReadVarUInt32NoCheck(pabyCur, nPoints);
    2789         387 :     if (nPoints == 0)
    2790           0 :         return TRUE;
    2791         387 :     returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, nToSkip));
    2792             : 
    2793             :     GUIntBig vxmin, vymin, vdx, vdy;
    2794             : 
    2795         387 :     returnErrorIf(pabyCur >= pabyEnd);
    2796         387 :     ReadVarUInt64NoCheck(pabyCur, vxmin);
    2797         387 :     if (vxmin > m_nFilterXMax)
    2798          78 :         return FALSE;
    2799         309 :     ReadVarUInt64NoCheck(pabyCur, vymin);
    2800         309 :     if (vymin > m_nFilterYMax)
    2801          36 :         return FALSE;
    2802         273 :     ReadVarUInt64NoCheck(pabyCur, vdx);
    2803         273 :     if (CPLUnsanitizedAdd<GUIntBig>(vxmin, vdx) < m_nFilterXMin)
    2804           0 :         return FALSE;
    2805         273 :     ReadVarUInt64NoCheck(pabyCur, vdy);
    2806         273 :     return CPLUnsanitizedAdd<GUIntBig>(vymin, vdy) >= m_nFilterYMin;
    2807             : }
    2808             : 
    2809             : /************************************************************************/
    2810             : /*                      FileGDBField::UNSET_FIELD                       */
    2811             : /************************************************************************/
    2812             : 
    2813        1242 : static OGRField GetUnsetField()
    2814             : {
    2815             :     OGRField sUnsetField;
    2816        1242 :     OGR_RawField_SetUnset(&sUnsetField);
    2817        1242 :     return sUnsetField;
    2818             : }
    2819             : 
    2820             : const OGRField FileGDBField::UNSET_FIELD = GetUnsetField();
    2821             : 
    2822             : /************************************************************************/
    2823             : /*                           FileGDBField()                             */
    2824             : /************************************************************************/
    2825             : 
    2826       44814 : FileGDBField::FileGDBField(FileGDBTable *poParentIn) : m_poParent(poParentIn)
    2827             : {
    2828       44814 :     OGR_RawField_SetUnset(&m_sDefault);
    2829       44814 : }
    2830             : 
    2831             : /************************************************************************/
    2832             : /*                           FileGDBField()                             */
    2833             : /************************************************************************/
    2834             : 
    2835       12938 : FileGDBField::FileGDBField(const std::string &osName,
    2836             :                            const std::string &osAlias, FileGDBFieldType eType,
    2837             :                            bool bNullable, int nMaxWidth,
    2838       12938 :                            const OGRField &sDefault)
    2839             :     : m_osName(osName), m_osAlias(osAlias), m_eType(eType),
    2840       12938 :       m_bNullable(bNullable), m_nMaxWidth(nMaxWidth)
    2841             : {
    2842       12954 :     if (m_eType == FGFT_STRING && !OGR_RawField_IsUnset(&sDefault) &&
    2843          16 :         !OGR_RawField_IsNull(&sDefault))
    2844             :     {
    2845          16 :         m_sDefault.String = CPLStrdup(sDefault.String);
    2846             :     }
    2847             :     else
    2848             :     {
    2849       12922 :         m_sDefault = sDefault;
    2850             :     }
    2851       12938 : }
    2852             : 
    2853             : /************************************************************************/
    2854             : /*                          ~FileGDBField()                             */
    2855             : /************************************************************************/
    2856             : 
    2857      113076 : FileGDBField::~FileGDBField()
    2858             : {
    2859       57783 :     if (m_eType == FGFT_STRING && !OGR_RawField_IsUnset(&m_sDefault) &&
    2860          31 :         !OGR_RawField_IsNull(&m_sDefault))
    2861          31 :         CPLFree(m_sDefault.String);
    2862      113076 : }
    2863             : 
    2864             : /************************************************************************/
    2865             : /*                            HasIndex()                                */
    2866             : /************************************************************************/
    2867             : 
    2868        1128 : int FileGDBField::HasIndex()
    2869             : {
    2870        1128 :     m_poParent->GetIndexCount();
    2871        1128 :     return m_poIndex != nullptr;
    2872             : }
    2873             : 
    2874             : /************************************************************************/
    2875             : /*                            GetIndex()                                */
    2876             : /************************************************************************/
    2877             : 
    2878         562 : FileGDBIndex *FileGDBField::GetIndex()
    2879             : {
    2880         562 :     m_poParent->GetIndexCount();
    2881         562 :     return m_poIndex;
    2882             : }
    2883             : 
    2884             : /************************************************************************/
    2885             : /*                              getESRI_NAN()                           */
    2886             : /************************************************************************/
    2887             : 
    2888        1242 : static double getESRI_NAN()
    2889             : {
    2890             :     // Use exact same quiet NaN value as generated by the ESRI SDK, just
    2891             :     // for the purpose of ensuring binary identical output for some tests.
    2892             :     // I doubt this matter much which NaN is generated for usage.
    2893             :     // The reason is that std::numeric_limits<double>::quiet_NaN() on my
    2894             :     // platform has not the least significant bit set.
    2895        1242 :     constexpr uint64_t nNAN = (static_cast<uint64_t>(0x7FF80000U) << 32) | 1;
    2896             :     double v;
    2897        1242 :     memcpy(&v, &nNAN, sizeof(v));
    2898        1242 :     return v;
    2899             : }
    2900             : 
    2901             : const double FileGDBGeomField::ESRI_NAN = getESRI_NAN();
    2902             : 
    2903             : /************************************************************************/
    2904             : /*                           FileGDBGeomField()                         */
    2905             : /************************************************************************/
    2906             : 
    2907        2011 : FileGDBGeomField::FileGDBGeomField(FileGDBTable *poParentIn)
    2908        2011 :     : FileGDBField(poParentIn)
    2909             : {
    2910        2011 : }
    2911             : 
    2912             : /************************************************************************/
    2913             : /*                           FileGDBGeomField()                         */
    2914             : /************************************************************************/
    2915             : 
    2916         417 : FileGDBGeomField::FileGDBGeomField(
    2917             :     const std::string &osName, const std::string &osAlias, bool bNullable,
    2918             :     const std::string &osWKT, double dfXOrigin, double dfYOrigin,
    2919             :     double dfXYScale, double dfXYTolerance,
    2920         417 :     const std::vector<double> &adfSpatialIndexGridResolution)
    2921             :     : FileGDBField(osName, osAlias, FGFT_GEOMETRY, bNullable, 0,
    2922             :                    FileGDBField::UNSET_FIELD),
    2923             :       m_osWKT(osWKT), m_dfXOrigin(dfXOrigin), m_dfYOrigin(dfYOrigin),
    2924             :       m_dfXYScale(dfXYScale), m_dfXYTolerance(dfXYTolerance),
    2925         417 :       m_adfSpatialIndexGridResolution(adfSpatialIndexGridResolution)
    2926             : {
    2927         417 : }
    2928             : 
    2929             : /************************************************************************/
    2930             : /*                                SetXYMinMax()                         */
    2931             : /************************************************************************/
    2932             : 
    2933        3867 : void FileGDBGeomField::SetXYMinMax(double dfXMin, double dfYMin, double dfXMax,
    2934             :                                    double dfYMax)
    2935             : {
    2936        3867 :     m_dfXMin = dfXMin;
    2937        3867 :     m_dfYMin = dfYMin;
    2938        3867 :     m_dfXMax = dfXMax;
    2939        3867 :     m_dfYMax = dfYMax;
    2940        3867 : }
    2941             : 
    2942             : /************************************************************************/
    2943             : /*                                SetZMinMax()                          */
    2944             : /************************************************************************/
    2945             : 
    2946        3865 : void FileGDBGeomField::SetZMinMax(double dfZMin, double dfZMax)
    2947             : {
    2948        3865 :     m_dfZMin = dfZMin;
    2949        3865 :     m_dfZMax = dfZMax;
    2950        3865 : }
    2951             : 
    2952             : /************************************************************************/
    2953             : /*                                SetMMinMax()                          */
    2954             : /************************************************************************/
    2955             : 
    2956           0 : void FileGDBGeomField::SetMMinMax(double dfMMin, double dfMMax)
    2957             : {
    2958           0 :     m_dfMMin = dfMMin;
    2959           0 :     m_dfMMax = dfMMax;
    2960           0 : }
    2961             : 
    2962             : /************************************************************************/
    2963             : /*                      SetZOriginScaleTolerance()                      */
    2964             : /************************************************************************/
    2965             : 
    2966         417 : void FileGDBGeomField::SetZOriginScaleTolerance(double dfZOrigin,
    2967             :                                                 double dfZScale,
    2968             :                                                 double dfZTolerance)
    2969             : {
    2970         417 :     m_bHasZOriginScaleTolerance = TRUE;
    2971         417 :     m_dfZOrigin = dfZOrigin;
    2972         417 :     m_dfZScale = dfZScale;
    2973         417 :     m_dfZTolerance = dfZTolerance;
    2974         417 : }
    2975             : 
    2976             : /************************************************************************/
    2977             : /*                      SetMOriginScaleTolerance()                      */
    2978             : /************************************************************************/
    2979             : 
    2980         417 : void FileGDBGeomField::SetMOriginScaleTolerance(double dfMOrigin,
    2981             :                                                 double dfMScale,
    2982             :                                                 double dfMTolerance)
    2983             : {
    2984         417 :     m_bHasMOriginScaleTolerance = TRUE;
    2985         417 :     m_dfMOrigin = dfMOrigin;
    2986         417 :     m_dfMScale = dfMScale;
    2987         417 :     m_dfMTolerance = dfMTolerance;
    2988         417 : }
    2989             : 
    2990             : /************************************************************************/
    2991             : /*                      FileGDBOGRGeometryConverterImpl                 */
    2992             : /************************************************************************/
    2993             : 
    2994             : class FileGDBOGRGeometryConverterImpl final : public FileGDBOGRGeometryConverter
    2995             : {
    2996             :     const FileGDBGeomField *poGeomField;
    2997             :     GUInt32 *panPointCount = nullptr;
    2998             :     GUInt32 nPointCountMax = 0;
    2999             : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
    3000             :     int bUseOrganize = 0;
    3001             : #endif
    3002             : 
    3003             :     bool ReadPartDefs(GByte *&pabyCur, GByte *pabyEnd, GUInt32 &nPoints,
    3004             :                       GUInt32 &nParts, GUInt32 &nCurves, bool bHasCurveDesc,
    3005             :                       bool bIsMultiPatch);
    3006             :     template <class XYSetter>
    3007             :     int ReadXYArray(XYSetter &setter, GByte *&pabyCur, GByte *pabyEnd,
    3008             :                     GUInt32 nPoints, GIntBig &dx, GIntBig &dy);
    3009             :     template <class ZSetter>
    3010             :     int ReadZArray(ZSetter &setter, GByte *&pabyCur, GByte *pabyEnd,
    3011             :                    GUInt32 nPoints, GIntBig &dz);
    3012             :     template <class MSetter>
    3013             :     int ReadMArray(MSetter &setter, GByte *&pabyCur, GByte *pabyEnd,
    3014             :                    GUInt32 nPoints, GIntBig &dm);
    3015             : 
    3016             :     OGRGeometry *CreateCurveGeometry(GUInt32 nBaseShapeType, GUInt32 nParts,
    3017             :                                      GUInt32 nPoints, GUInt32 nCurves,
    3018             :                                      bool bHasZ, bool bHasM, GByte *&pabyCur,
    3019             :                                      GByte *pabyEnd);
    3020             : 
    3021             :     FileGDBOGRGeometryConverterImpl(const FileGDBOGRGeometryConverterImpl &) =
    3022             :         delete;
    3023             :     FileGDBOGRGeometryConverterImpl &
    3024             :     operator=(const FileGDBOGRGeometryConverterImpl &) = delete;
    3025             : 
    3026             :   public:
    3027             :     explicit FileGDBOGRGeometryConverterImpl(
    3028             :         const FileGDBGeomField *poGeomField);
    3029             :     virtual ~FileGDBOGRGeometryConverterImpl();
    3030             : 
    3031             :     virtual OGRGeometry *GetAsGeometry(const OGRField *psField) override;
    3032             : };
    3033             : 
    3034             : /************************************************************************/
    3035             : /*                  FileGDBOGRGeometryConverterImpl()                   */
    3036             : /************************************************************************/
    3037             : 
    3038         867 : FileGDBOGRGeometryConverterImpl::FileGDBOGRGeometryConverterImpl(
    3039         867 :     const FileGDBGeomField *poGeomFieldIn)
    3040         867 :     : poGeomField(poGeomFieldIn)
    3041             : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
    3042             :       ,
    3043             :       bUseOrganize(CPLGetConfigOption("OGR_ORGANIZE_POLYGONS", NULL) != NULL)
    3044             : #endif
    3045             : {
    3046         867 : }
    3047             : 
    3048             : /************************************************************************/
    3049             : /*                 ~FileGDBOGRGeometryConverter()                       */
    3050             : /************************************************************************/
    3051             : 
    3052        1734 : FileGDBOGRGeometryConverterImpl::~FileGDBOGRGeometryConverterImpl()
    3053             : {
    3054         867 :     CPLFree(panPointCount);
    3055        1734 : }
    3056             : 
    3057             : /************************************************************************/
    3058             : /*                          ReadPartDefs()                              */
    3059             : /************************************************************************/
    3060             : 
    3061        5092 : bool FileGDBOGRGeometryConverterImpl::ReadPartDefs(
    3062             :     GByte *&pabyCur, GByte *pabyEnd, GUInt32 &nPoints, GUInt32 &nParts,
    3063             :     GUInt32 &nCurves, bool bHasCurveDesc, bool bIsMultiPatch)
    3064             : {
    3065        5092 :     const bool errorRetValue = false;
    3066        5092 :     returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints));
    3067        5092 :     if (nPoints == 0)
    3068             :     {
    3069          10 :         nParts = 0;
    3070          10 :         nCurves = 0;
    3071          10 :         return true;
    3072             :     }
    3073        5082 :     returnErrorIf(nPoints > static_cast<GUInt32>(pabyEnd - pabyCur));
    3074        5082 :     if (bIsMultiPatch)
    3075         555 :         returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd));
    3076        5082 :     returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nParts));
    3077        5082 :     returnErrorIf(nParts > static_cast<GUInt32>(pabyEnd - pabyCur));
    3078        5082 :     returnErrorIf(nParts > static_cast<GUInt32>(INT_MAX) / sizeof(GUInt32));
    3079        5082 :     if (bHasCurveDesc)
    3080             :     {
    3081          69 :         returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nCurves));
    3082          69 :         returnErrorIf(nCurves > static_cast<GUInt32>(pabyEnd - pabyCur));
    3083             :     }
    3084             :     else
    3085        5013 :         nCurves = 0;
    3086        5082 :     if (nParts == 0)
    3087           0 :         return true;
    3088             :     GUInt32 i;
    3089        5082 :     returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4));
    3090        5082 :     if (nParts > nPointCountMax)
    3091             :     {
    3092             :         GUInt32 *panPointCountNew = static_cast<GUInt32 *>(
    3093         277 :             VSI_REALLOC_VERBOSE(panPointCount, nParts * sizeof(GUInt32)));
    3094         277 :         returnErrorIf(panPointCountNew == nullptr);
    3095         277 :         panPointCount = panPointCountNew;
    3096         277 :         nPointCountMax = nParts;
    3097             :     }
    3098        5082 :     GUIntBig nSumNPartsM1 = 0;
    3099        8524 :     for (i = 0; i < nParts - 1; i++)
    3100             :     {
    3101             :         GUInt32 nTmp;
    3102        3442 :         returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nTmp));
    3103        3442 :         returnErrorIf(nTmp > static_cast<GUInt32>(pabyEnd - pabyCur));
    3104        3442 :         panPointCount[i] = nTmp;
    3105        3442 :         nSumNPartsM1 += nTmp;
    3106             :     }
    3107        5082 :     returnErrorIf(nSumNPartsM1 > nPoints);
    3108        5082 :     panPointCount[nParts - 1] = static_cast<GUInt32>(nPoints - nSumNPartsM1);
    3109             : 
    3110        5082 :     return true;
    3111             : }
    3112             : 
    3113             : /************************************************************************/
    3114             : /*                         XYLineStringSetter                           */
    3115             : /************************************************************************/
    3116             : 
    3117             : class FileGDBOGRLineString : public OGRLineString
    3118             : {
    3119             :   public:
    3120        2239 :     FileGDBOGRLineString()
    3121        2239 :     {
    3122        2239 :     }
    3123             : 
    3124        2239 :     OGRRawPoint *GetPoints() const
    3125             :     {
    3126        2239 :         return paoPoints;
    3127             :     }
    3128             : };
    3129             : 
    3130             : class FileGDBOGRLinearRing : public OGRLinearRing
    3131             : {
    3132             :   public:
    3133        3425 :     FileGDBOGRLinearRing()
    3134        3425 :     {
    3135        3425 :     }
    3136             : 
    3137        3425 :     OGRRawPoint *GetPoints() const
    3138             :     {
    3139        3425 :         return paoPoints;
    3140             :     }
    3141             : };
    3142             : 
    3143             : class XYLineStringSetter
    3144             : {
    3145             :     OGRRawPoint *paoPoints;
    3146             : 
    3147             :   public:
    3148        5664 :     explicit XYLineStringSetter(OGRRawPoint *paoPointsIn)
    3149        5664 :         : paoPoints(paoPointsIn)
    3150             :     {
    3151        5664 :     }
    3152             : 
    3153       24220 :     void set(int i, double dfX, double dfY)
    3154             :     {
    3155       24220 :         paoPoints[i].x = dfX;
    3156       24220 :         paoPoints[i].y = dfY;
    3157       24220 :     }
    3158             : };
    3159             : 
    3160             : /************************************************************************/
    3161             : /*                         XYMultiPointSetter                           */
    3162             : /************************************************************************/
    3163             : 
    3164             : class XYMultiPointSetter
    3165             : {
    3166             :     OGRMultiPoint *poMPoint;
    3167             : 
    3168             :   public:
    3169         846 :     explicit XYMultiPointSetter(OGRMultiPoint *poMPointIn)
    3170         846 :         : poMPoint(poMPointIn)
    3171             :     {
    3172         846 :     }
    3173             : 
    3174        1698 :     void set(int i, double dfX, double dfY)
    3175             :     {
    3176             :         (void)i;
    3177        1698 :         poMPoint->addGeometryDirectly(new OGRPoint(dfX, dfY));
    3178        1698 :     }
    3179             : };
    3180             : 
    3181             : /************************************************************************/
    3182             : /*                             XYArraySetter                            */
    3183             : /************************************************************************/
    3184             : 
    3185             : class XYArraySetter
    3186             : {
    3187             :     double *padfX;
    3188             :     double *padfY;
    3189             : 
    3190             :   public:
    3191         555 :     XYArraySetter(double *padfXIn, double *padfYIn)
    3192         555 :         : padfX(padfXIn), padfY(padfYIn)
    3193             :     {
    3194         555 :     }
    3195             : 
    3196       11627 :     void set(int i, double dfX, double dfY)
    3197             :     {
    3198       11627 :         padfX[i] = dfX;
    3199       11627 :         padfY[i] = dfY;
    3200       11627 :     }
    3201             : };
    3202             : 
    3203             : /************************************************************************/
    3204             : /*                          ReadXYArray()                               */
    3205             : /************************************************************************/
    3206             : 
    3207             : template <class XYSetter>
    3208        7134 : int FileGDBOGRGeometryConverterImpl::ReadXYArray(XYSetter &setter,
    3209             :                                                  GByte *&pabyCur,
    3210             :                                                  GByte *pabyEnd,
    3211             :                                                  GUInt32 nPoints, GIntBig &dx,
    3212             :                                                  GIntBig &dy)
    3213             : {
    3214        7134 :     const int errorRetValue = FALSE;
    3215        7134 :     GIntBig dxLocal = dx;
    3216        7134 :     GIntBig dyLocal = dy;
    3217             : 
    3218       44993 :     for (GUInt32 i = 0; i < nPoints; i++)
    3219             :     {
    3220       37859 :         returnErrorIf(pabyCur /*+ 1*/ >= pabyEnd);
    3221             : 
    3222       37859 :         ReadVarIntAndAddNoCheck(pabyCur, dxLocal);
    3223       37859 :         ReadVarIntAndAddNoCheck(pabyCur, dyLocal);
    3224             : 
    3225       37859 :         double dfX =
    3226       37859 :             dxLocal / poGeomField->GetXYScale() + poGeomField->GetXOrigin();
    3227       37859 :         double dfY =
    3228       37859 :             dyLocal / poGeomField->GetXYScale() + poGeomField->GetYOrigin();
    3229       37859 :         setter.set(i, dfX, dfY);
    3230             :     }
    3231             : 
    3232        7134 :     dx = dxLocal;
    3233        7134 :     dy = dyLocal;
    3234        7134 :     return TRUE;
    3235             : }
    3236             : 
    3237             : /************************************************************************/
    3238             : /*                          ZLineStringSetter                           */
    3239             : /************************************************************************/
    3240             : 
    3241             : class ZLineStringSetter
    3242             : {
    3243             :     OGRLineString *poLS;
    3244             : 
    3245             :   public:
    3246        1998 :     explicit ZLineStringSetter(OGRLineString *poLSIn) : poLS(poLSIn)
    3247             :     {
    3248        1998 :     }
    3249             : 
    3250        6663 :     void set(int i, double dfZ)
    3251             :     {
    3252        6663 :         poLS->setZ(i, dfZ);
    3253        6663 :     }
    3254             : };
    3255             : 
    3256             : /************************************************************************/
    3257             : /*                         ZMultiPointSetter                           */
    3258             : /************************************************************************/
    3259             : 
    3260             : class ZMultiPointSetter
    3261             : {
    3262             :     OGRMultiPoint *poMPoint;
    3263             : 
    3264             :   public:
    3265         422 :     explicit ZMultiPointSetter(OGRMultiPoint *poMPointIn) : poMPoint(poMPointIn)
    3266             :     {
    3267         422 :     }
    3268             : 
    3269         848 :     void set(int i, double dfZ)
    3270             :     {
    3271         848 :         poMPoint->getGeometryRef(i)->setZ(dfZ);
    3272         848 :     }
    3273             : };
    3274             : 
    3275             : /************************************************************************/
    3276             : /*                             FileGDBArraySetter                            */
    3277             : /************************************************************************/
    3278             : 
    3279             : class FileGDBArraySetter
    3280             : {
    3281             :     double *padfValues;
    3282             : 
    3283             :   public:
    3284         555 :     explicit FileGDBArraySetter(double *padfValuesIn) : padfValues(padfValuesIn)
    3285             :     {
    3286         555 :     }
    3287             : 
    3288       11627 :     void set(int i, double dfValue)
    3289             :     {
    3290       11627 :         padfValues[i] = dfValue;
    3291       11627 :     }
    3292             : };
    3293             : 
    3294             : /************************************************************************/
    3295             : /*                          ReadZArray()                                */
    3296             : /************************************************************************/
    3297             : 
    3298             : template <class ZSetter>
    3299        2991 : int FileGDBOGRGeometryConverterImpl::ReadZArray(ZSetter &setter,
    3300             :                                                 GByte *&pabyCur, GByte *pabyEnd,
    3301             :                                                 GUInt32 nPoints, GIntBig &dz)
    3302             : {
    3303        2991 :     const int errorRetValue = FALSE;
    3304        2991 :     const double dfZScale = SanitizeScale(poGeomField->GetZScale());
    3305       22203 :     for (GUInt32 i = 0; i < nPoints; i++)
    3306             :     {
    3307       19212 :         returnErrorIf(pabyCur >= pabyEnd);
    3308       19212 :         ReadVarIntAndAddNoCheck(pabyCur, dz);
    3309             : 
    3310       19212 :         double dfZ = dz / dfZScale + poGeomField->GetZOrigin();
    3311       19212 :         setter.set(i, dfZ);
    3312             :     }
    3313        2991 :     return TRUE;
    3314             : }
    3315             : 
    3316             : /************************************************************************/
    3317             : /*                          MLineStringSetter                           */
    3318             : /************************************************************************/
    3319             : 
    3320             : class MLineStringSetter
    3321             : {
    3322             :     OGRLineString *poLS;
    3323             : 
    3324             :   public:
    3325         235 :     explicit MLineStringSetter(OGRLineString *poLSIn) : poLS(poLSIn)
    3326             :     {
    3327         235 :     }
    3328             : 
    3329         833 :     void set(int i, double dfM)
    3330             :     {
    3331         833 :         poLS->setM(i, dfM);
    3332         833 :     }
    3333             : };
    3334             : 
    3335             : /************************************************************************/
    3336             : /*                         MMultiPointSetter                           */
    3337             : /************************************************************************/
    3338             : 
    3339             : class MMultiPointSetter
    3340             : {
    3341             :     OGRMultiPoint *poMPoint;
    3342             : 
    3343             :   public:
    3344          54 :     explicit MMultiPointSetter(OGRMultiPoint *poMPointIn) : poMPoint(poMPointIn)
    3345             :     {
    3346          54 :     }
    3347             : 
    3348         114 :     void set(int i, double dfM)
    3349             :     {
    3350         114 :         poMPoint->getGeometryRef(i)->setM(dfM);
    3351         114 :     }
    3352             : };
    3353             : 
    3354             : /************************************************************************/
    3355             : /*                          ReadMArray()                                */
    3356             : /************************************************************************/
    3357             : 
    3358             : template <class MSetter>
    3359         303 : int FileGDBOGRGeometryConverterImpl::ReadMArray(MSetter &setter,
    3360             :                                                 GByte *&pabyCur, GByte *pabyEnd,
    3361             :                                                 GUInt32 nPoints, GIntBig &dm)
    3362             : {
    3363         303 :     const int errorRetValue = FALSE;
    3364         303 :     const double dfMScale = SanitizeScale(poGeomField->GetMScale());
    3365        1314 :     for (GUInt32 i = 0; i < nPoints; i++)
    3366             :     {
    3367        1011 :         returnErrorIf(pabyCur >= pabyEnd);
    3368        1011 :         ReadVarIntAndAddNoCheck(pabyCur, dm);
    3369             : 
    3370        1011 :         double dfM = dm / dfMScale + poGeomField->GetMOrigin();
    3371        1011 :         setter.set(i, dfM);
    3372             :     }
    3373         303 :     return TRUE;
    3374             : }
    3375             : 
    3376             : /************************************************************************/
    3377             : /*                          CreateCurveGeometry()                       */
    3378             : /************************************************************************/
    3379             : 
    3380             : class XYBufferSetter
    3381             : {
    3382             :     GByte *pabyBuffer;
    3383             : 
    3384             :   public:
    3385          69 :     explicit XYBufferSetter(GByte *pabyBufferIn) : pabyBuffer(pabyBufferIn)
    3386             :     {
    3387          69 :     }
    3388             : 
    3389         314 :     void set(int i, double dfX, double dfY)
    3390             :     {
    3391         314 :         CPL_LSBPTR64(&dfX);
    3392         314 :         memcpy(pabyBuffer + 16 * i, &dfX, 8);
    3393         314 :         CPL_LSBPTR64(&dfY);
    3394         314 :         memcpy(pabyBuffer + 16 * i + 8, &dfY, 8);
    3395         314 :     }
    3396             : };
    3397             : 
    3398             : class ZOrMBufferSetter
    3399             : {
    3400             :     GByte *pabyBuffer;
    3401             : 
    3402             :   public:
    3403          30 :     explicit ZOrMBufferSetter(GByte *pabyBufferIn) : pabyBuffer(pabyBufferIn)
    3404             :     {
    3405          30 :     }
    3406             : 
    3407         138 :     void set(int i, double dfValue)
    3408             :     {
    3409         138 :         CPL_LSBPTR64(&dfValue);
    3410         138 :         memcpy(pabyBuffer + 8 * i, &dfValue, 8);
    3411         138 :     }
    3412             : };
    3413             : 
    3414             : /* We first create an extended shape buffer from the compressed stream */
    3415             : /* and finally use OGRCreateFromShapeBin() to make a geometry from it */
    3416             : 
    3417          69 : OGRGeometry *FileGDBOGRGeometryConverterImpl::CreateCurveGeometry(
    3418             :     GUInt32 nBaseShapeType, GUInt32 nParts, GUInt32 nPoints, GUInt32 nCurves,
    3419             :     bool bHasZ, bool bHasM, GByte *&pabyCur, GByte *pabyEnd)
    3420             : {
    3421          69 :     OGRGeometry *errorRetValue = nullptr;
    3422             :     GUInt32 i;
    3423          69 :     const int nDims = 2 + (bHasZ ? 1 : 0) + (bHasM ? 1 : 0);
    3424          69 :     GIntBig nMaxSize64 = 44 + 4 * static_cast<GUIntBig>(nParts) +
    3425          69 :                          8 * nDims * static_cast<GUIntBig>(nPoints);
    3426          69 :     nMaxSize64 += 4;  // nCurves
    3427          69 :     nMaxSize64 +=
    3428          69 :         static_cast<GUIntBig>(nCurves) * (4 + /* start index */
    3429             :                                           4 + /* curve type */
    3430             :                                           44 /* size of ellipse struct */);
    3431          69 :     nMaxSize64 +=
    3432          69 :         ((bHasZ ? 1 : 0) + (bHasM ? 1 : 0)) * 16;  // space for bounding boxes
    3433          69 :     if (nMaxSize64 >= INT_MAX)
    3434             :     {
    3435           0 :         returnError();
    3436             :     }
    3437          69 :     const int nMaxSize = static_cast<int>(nMaxSize64);
    3438             :     GByte *pabyExtShapeBuffer =
    3439          69 :         static_cast<GByte *>(VSI_MALLOC_VERBOSE(nMaxSize));
    3440          69 :     if (pabyExtShapeBuffer == nullptr)
    3441             :     {
    3442           0 :         VSIFree(pabyExtShapeBuffer);
    3443           0 :         returnError();
    3444             :     }
    3445          69 :     GUInt32 nShapeType = nBaseShapeType | EXT_SHAPE_CURVE_FLAG;
    3446          69 :     if (bHasZ)
    3447          16 :         nShapeType |= EXT_SHAPE_Z_FLAG;
    3448          69 :     if (bHasM)
    3449          16 :         nShapeType |= EXT_SHAPE_M_FLAG;
    3450             :     GUInt32 nTmp;
    3451          69 :     nTmp = CPL_LSBWORD32(nShapeType);
    3452          69 :     GByte *pabyShapeTypePtr = pabyExtShapeBuffer;
    3453          69 :     memcpy(pabyExtShapeBuffer, &nTmp, 4);
    3454          69 :     memset(pabyExtShapeBuffer + 4, 0, 32); /* bbox: unused */
    3455          69 :     nTmp = CPL_LSBWORD32(nParts);
    3456          69 :     memcpy(pabyExtShapeBuffer + 36, &nTmp, 4);
    3457          69 :     nTmp = CPL_LSBWORD32(nPoints);
    3458          69 :     memcpy(pabyExtShapeBuffer + 40, &nTmp, 4);
    3459          69 :     GUInt32 nIdx = 0;
    3460         160 :     for (i = 0; i < nParts; i++)
    3461             :     {
    3462          91 :         nTmp = CPL_LSBWORD32(nIdx);
    3463          91 :         nIdx += panPointCount[i];
    3464          91 :         memcpy(pabyExtShapeBuffer + 44 + 4 * i, &nTmp, 4);
    3465             :     }
    3466          69 :     int nOffset = 44 + 4 * nParts;
    3467          69 :     GIntBig dx = 0;
    3468          69 :     GIntBig dy = 0;
    3469          69 :     XYBufferSetter arraySetter(pabyExtShapeBuffer + nOffset);
    3470          69 :     if (!ReadXYArray<XYBufferSetter>(arraySetter, pabyCur, pabyEnd, nPoints, dx,
    3471             :                                      dy))
    3472             :     {
    3473           0 :         VSIFree(pabyExtShapeBuffer);
    3474           0 :         returnError();
    3475             :     }
    3476          69 :     nOffset += 16 * nPoints;
    3477             : 
    3478          69 :     if (bHasZ)
    3479             :     {
    3480          16 :         memset(pabyExtShapeBuffer + nOffset, 0, 16); /* bbox: unused */
    3481          16 :         nOffset += 16;
    3482          16 :         GIntBig dz = 0;
    3483          16 :         ZOrMBufferSetter arrayzSetter(pabyExtShapeBuffer + nOffset);
    3484          16 :         if (!ReadZArray<ZOrMBufferSetter>(arrayzSetter, pabyCur, pabyEnd,
    3485             :                                           nPoints, dz))
    3486             :         {
    3487           0 :             VSIFree(pabyExtShapeBuffer);
    3488           0 :             returnError();
    3489             :         }
    3490          16 :         nOffset += 8 * nPoints;
    3491             :     }
    3492             : 
    3493          69 :     if (bHasM)
    3494             :     {
    3495             :         // It seems that absence of M is marked with a single byte
    3496             :         // with value 66.
    3497          16 :         if (*pabyCur == 66)
    3498             :         {
    3499           2 :             pabyCur++;
    3500             : #if 1
    3501             :             // In other code paths of this file, we drop the M component when
    3502             :             // it is at null. The disabled code path would fill it with NaN
    3503             :             // instead.
    3504           2 :             nShapeType &= ~EXT_SHAPE_M_FLAG;
    3505           2 :             nTmp = CPL_LSBWORD32(nShapeType);
    3506           2 :             memcpy(pabyShapeTypePtr, &nTmp, 4);
    3507             : #else
    3508             :             memset(pabyExtShapeBuffer + nOffset, 0, 16); /* bbox: unused */
    3509             :             nOffset += 16;
    3510             :             const double myNan = std::numeric_limits<double>::quiet_NaN();
    3511             :             for (i = 0; i < nPoints; i++)
    3512             :             {
    3513             :                 memcpy(pabyExtShapeBuffer + nOffset + 8 * i, &myNan, 8);
    3514             :                 CPL_LSBPTR64(pabyExtShapeBuffer + nOffset + 8 * i);
    3515             :             }
    3516             :             nOffset += 8 * nPoints;
    3517             : #endif
    3518             :         }
    3519             :         else
    3520             :         {
    3521          14 :             memset(pabyExtShapeBuffer + nOffset, 0, 16); /* bbox: unused */
    3522          14 :             nOffset += 16;
    3523          14 :             ZOrMBufferSetter arraymSetter(pabyExtShapeBuffer + nOffset);
    3524          14 :             GIntBig dm = 0;
    3525          14 :             if (!ReadMArray<ZOrMBufferSetter>(arraymSetter, pabyCur, pabyEnd,
    3526             :                                               nPoints, dm))
    3527             :             {
    3528           0 :                 VSIFree(pabyExtShapeBuffer);
    3529           0 :                 returnError();
    3530             :             }
    3531          14 :             nOffset += 8 * nPoints;
    3532             :         }
    3533             :     }
    3534             : 
    3535          69 :     nTmp = CPL_LSBWORD32(nCurves);
    3536          69 :     memcpy(pabyExtShapeBuffer + nOffset, &nTmp, 4);
    3537          69 :     nOffset += 4;
    3538         191 :     for (i = 0; i < nCurves; i++)
    3539             :     {
    3540             :         // start index
    3541         122 :         returnErrorAndCleanupIf(!ReadVarUInt32(pabyCur, pabyEnd, nTmp),
    3542             :                                 VSIFree(pabyExtShapeBuffer));
    3543         122 :         CPL_LSBPTR32(&nTmp);
    3544         122 :         memcpy(pabyExtShapeBuffer + nOffset, &nTmp, 4);
    3545         122 :         nOffset += 4;
    3546             : 
    3547             :         GUInt32 nCurveType;
    3548         122 :         returnErrorAndCleanupIf(!ReadVarUInt32(pabyCur, pabyEnd, nCurveType),
    3549             :                                 VSIFree(pabyExtShapeBuffer));
    3550         122 :         nTmp = CPL_LSBWORD32(nCurveType);
    3551         122 :         memcpy(pabyExtShapeBuffer + nOffset, &nTmp, 4);
    3552         122 :         nOffset += 4;
    3553             : 
    3554         122 :         int nStructureSize = 0;
    3555         122 :         if (nCurveType == EXT_SHAPE_SEGMENT_ARC)
    3556          96 :             nStructureSize = 2 * 8 + 4;
    3557          26 :         else if (nCurveType == EXT_SHAPE_SEGMENT_BEZIER)
    3558          19 :             nStructureSize = 4 * 8;
    3559           7 :         else if (nCurveType == EXT_SHAPE_SEGMENT_ELLIPSE)
    3560           7 :             nStructureSize = 5 * 8 + 4;
    3561         122 :         if (nStructureSize == 0 || pabyCur + nStructureSize > pabyEnd)
    3562             :         {
    3563           0 :             VSIFree(pabyExtShapeBuffer);
    3564           0 :             returnError();
    3565             :         }
    3566         122 :         memcpy(pabyExtShapeBuffer + nOffset, pabyCur, nStructureSize);
    3567         122 :         pabyCur += nStructureSize;
    3568         122 :         nOffset += nStructureSize;
    3569             :     }
    3570          69 :     CPLAssert(nOffset <= nMaxSize);
    3571             : 
    3572          69 :     OGRGeometry *poRet = nullptr;
    3573          69 :     OGRCreateFromShapeBin(pabyExtShapeBuffer, &poRet, nOffset);
    3574          69 :     VSIFree(pabyExtShapeBuffer);
    3575          69 :     return poRet;
    3576             : }
    3577             : 
    3578             : /************************************************************************/
    3579             : /*                          GetAsGeometry()                             */
    3580             : /************************************************************************/
    3581             : 
    3582             : OGRGeometry *
    3583       14856 : FileGDBOGRGeometryConverterImpl::GetAsGeometry(const OGRField *psField)
    3584             : {
    3585       14856 :     OGRGeometry *errorRetValue = nullptr;
    3586       14856 :     GByte *pabyCur = psField->Binary.paData;
    3587       14856 :     GByte *pabyEnd = pabyCur + psField->Binary.nCount;
    3588             :     GUInt32 nGeomType, i, nPoints, nParts, nCurves;
    3589             :     GUIntBig x, y, z;
    3590             :     GIntBig dx, dy, dz;
    3591             : 
    3592       14856 :     ReadVarUInt32NoCheck(pabyCur, nGeomType);
    3593             : 
    3594       14856 :     bool bHasZ = (nGeomType & EXT_SHAPE_Z_FLAG) != 0;
    3595       14856 :     bool bHasM = (nGeomType & EXT_SHAPE_M_FLAG) != 0;
    3596       14856 :     switch ((nGeomType & 0xff))
    3597             :     {
    3598           0 :         case SHPT_NULL:
    3599           0 :             return nullptr;
    3600             : 
    3601         455 :         case SHPT_POINTZ:
    3602             :         case SHPT_POINTZM:
    3603         455 :             bHasZ = true; /* go on */
    3604             :             [[fallthrough]];
    3605        8918 :         case SHPT_POINT:
    3606             :         case SHPT_POINTM:
    3607             :         case SHPT_GENERALPOINT:
    3608             :         {
    3609        8918 :             if (nGeomType == SHPT_POINTM || nGeomType == SHPT_POINTZM)
    3610          57 :                 bHasM = true;
    3611             : 
    3612        8918 :             ReadVarUInt64NoCheck(pabyCur, x);
    3613        8918 :             ReadVarUInt64NoCheck(pabyCur, y);
    3614             : 
    3615       17828 :             const double dfX = x == 0 ? std::numeric_limits<double>::quiet_NaN()
    3616        8910 :                                       : (x - 1U) / poGeomField->GetXYScale() +
    3617        8910 :                                             poGeomField->GetXOrigin();
    3618       17828 :             const double dfY = y == 0 ? std::numeric_limits<double>::quiet_NaN()
    3619        8910 :                                       : (y - 1U) / poGeomField->GetXYScale() +
    3620        8910 :                                             poGeomField->GetYOrigin();
    3621        8918 :             if (bHasZ)
    3622             :             {
    3623         455 :                 ReadVarUInt64NoCheck(pabyCur, z);
    3624         455 :                 const double dfZScale = SanitizeScale(poGeomField->GetZScale());
    3625             :                 const double dfZ =
    3626         906 :                     z == 0 ? std::numeric_limits<double>::quiet_NaN()
    3627         451 :                            : (z - 1U) / dfZScale + poGeomField->GetZOrigin();
    3628         455 :                 if (bHasM)
    3629             :                 {
    3630          30 :                     GUIntBig m = 0;
    3631          30 :                     ReadVarUInt64NoCheck(pabyCur, m);
    3632             :                     const double dfMScale =
    3633          30 :                         SanitizeScale(poGeomField->GetMScale());
    3634             :                     const double dfM =
    3635          30 :                         m == 0
    3636          58 :                             ? std::numeric_limits<double>::quiet_NaN()
    3637          28 :                             : (m - 1U) / dfMScale + poGeomField->GetMOrigin();
    3638          30 :                     return new OGRPoint(dfX, dfY, dfZ, dfM);
    3639             :                 }
    3640         425 :                 return new OGRPoint(dfX, dfY, dfZ);
    3641             :             }
    3642        8463 :             else if (bHasM)
    3643             :             {
    3644          27 :                 OGRPoint *poPoint = new OGRPoint(dfX, dfY);
    3645          27 :                 GUIntBig m = 0;
    3646          27 :                 ReadVarUInt64NoCheck(pabyCur, m);
    3647          27 :                 const double dfMScale = SanitizeScale(poGeomField->GetMScale());
    3648             :                 const double dfM =
    3649          52 :                     m == 0 ? std::numeric_limits<double>::quiet_NaN()
    3650          25 :                            : (m - 1U) / dfMScale + poGeomField->GetMOrigin();
    3651          27 :                 poPoint->setM(dfM);
    3652          27 :                 return poPoint;
    3653             :             }
    3654             :             else
    3655             :             {
    3656        8436 :                 return new OGRPoint(dfX, dfY);
    3657             :             }
    3658             :             break;
    3659             :         }
    3660             : 
    3661         422 :         case SHPT_MULTIPOINTZM:
    3662             :         case SHPT_MULTIPOINTZ:
    3663         422 :             bHasZ = true; /* go on */
    3664             :             [[fallthrough]];
    3665         846 :         case SHPT_MULTIPOINT:
    3666             :         case SHPT_MULTIPOINTM:
    3667             :         {
    3668         846 :             if (nGeomType == SHPT_MULTIPOINTM || nGeomType == SHPT_MULTIPOINTZM)
    3669          54 :                 bHasM = true;
    3670             : 
    3671         846 :             returnErrorIf(!ReadVarUInt32(pabyCur, pabyEnd, nPoints));
    3672         846 :             if (nPoints == 0)
    3673             :             {
    3674           0 :                 OGRMultiPoint *poMP = new OGRMultiPoint();
    3675           0 :                 if (bHasZ)
    3676           0 :                     poMP->set3D(TRUE);
    3677           0 :                 if (bHasM)
    3678           0 :                     poMP->setMeasured(TRUE);
    3679           0 :                 return poMP;
    3680             :             }
    3681             : 
    3682         846 :             returnErrorIf(!SkipVarUInt(pabyCur, pabyEnd, 4));
    3683             : 
    3684         846 :             dx = dy = dz = 0;
    3685             : 
    3686         846 :             OGRMultiPoint *poMP = new OGRMultiPoint();
    3687         846 :             XYMultiPointSetter mpSetter(poMP);
    3688         846 :             if (!ReadXYArray<XYMultiPointSetter>(mpSetter, pabyCur, pabyEnd,
    3689             :                                                  nPoints, dx, dy))
    3690             :             {
    3691           0 :                 delete poMP;
    3692           0 :                 returnError();
    3693             :             }
    3694             : 
    3695         846 :             if (bHasZ)
    3696             :             {
    3697         422 :                 poMP->setCoordinateDimension(3);
    3698         422 :                 ZMultiPointSetter mpzSetter(poMP);
    3699         422 :                 if (!ReadZArray<ZMultiPointSetter>(mpzSetter, pabyCur, pabyEnd,
    3700             :                                                    nPoints, dz))
    3701             :                 {
    3702           0 :                     delete poMP;
    3703           0 :                     returnError();
    3704             :                 }
    3705             :             }
    3706             : 
    3707             :             // It seems that absence of M is marked with a single byte
    3708             :             // with value 66. Be more tolerant and only try to parse the M
    3709             :             // array is there are at least as many remaining bytes as
    3710             :             // expected points
    3711         846 :             if (bHasM && pabyCur + nPoints <= pabyEnd)
    3712             :             {
    3713          54 :                 poMP->setMeasured(TRUE);
    3714          54 :                 GIntBig dm = 0;
    3715          54 :                 MMultiPointSetter mpmSetter(poMP);
    3716          54 :                 if (!ReadMArray<MMultiPointSetter>(mpmSetter, pabyCur, pabyEnd,
    3717             :                                                    nPoints, dm))
    3718             :                 {
    3719           0 :                     delete poMP;
    3720           0 :                     returnError();
    3721             :                 }
    3722             :             }
    3723             : 
    3724         846 :             return poMP;
    3725             :             // cppcheck-suppress duplicateBreak
    3726             :             break;
    3727             :         }
    3728             : 
    3729         977 :         case SHPT_ARCZ:
    3730             :         case SHPT_ARCZM:
    3731         977 :             bHasZ = true; /* go on */
    3732             :             [[fallthrough]];
    3733        1996 :         case SHPT_ARC:
    3734             :         case SHPT_ARCM:
    3735             :         case SHPT_GENERALPOLYLINE:
    3736             :         {
    3737        1996 :             if (nGeomType == SHPT_ARCM || nGeomType == SHPT_ARCZM)
    3738         111 :                 bHasM = true;
    3739             : 
    3740        1996 :             returnErrorIf(
    3741             :                 !ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, nCurves,
    3742             :                               (nGeomType & EXT_SHAPE_CURVE_FLAG) != 0, false));
    3743             : 
    3744        1996 :             if (nPoints == 0 || nParts == 0)
    3745             :             {
    3746           4 :                 OGRLineString *poLS = new OGRLineString();
    3747           4 :                 if (bHasZ)
    3748           2 :                     poLS->set3D(TRUE);
    3749           4 :                 if (bHasM)
    3750           2 :                     poLS->setMeasured(TRUE);
    3751           4 :                 return poLS;
    3752             :             }
    3753             : 
    3754        1992 :             if (nCurves)
    3755             :             {
    3756          29 :                 GByte *pabyCurBackup = pabyCur;
    3757          29 :                 OGRGeometry *poRet = CreateCurveGeometry(
    3758             :                     SHPT_GENERALPOLYLINE, nParts, nPoints, nCurves, bHasZ,
    3759             :                     bHasM, pabyCur, pabyEnd);
    3760          29 :                 if (poRet)
    3761          29 :                     return poRet;
    3762             :                 // In case something went wrong, go on without curves
    3763           0 :                 pabyCur = pabyCurBackup;
    3764             :             }
    3765             : 
    3766        1963 :             OGRMultiLineString *poMLS = nullptr;
    3767        1963 :             FileGDBOGRLineString *poLS = nullptr;
    3768        1963 :             if (nParts > 1)
    3769             :             {
    3770         276 :                 poMLS = new OGRMultiLineString();
    3771         276 :                 if (bHasZ)
    3772         136 :                     poMLS->set3D(TRUE);
    3773         276 :                 if (bHasM)
    3774           6 :                     poMLS->setMeasured(TRUE);
    3775             :             }
    3776             : 
    3777        1963 :             dx = dy = dz = 0;
    3778        4202 :             for (i = 0; i < nParts; i++)
    3779             :             {
    3780        2239 :                 poLS = new FileGDBOGRLineString();
    3781        2239 :                 poLS->setNumPoints(panPointCount[i], FALSE);
    3782        2239 :                 if (nParts > 1)
    3783         552 :                     poMLS->addGeometryDirectly(poLS);
    3784             : 
    3785        2239 :                 XYLineStringSetter lsSetter(poLS->GetPoints());
    3786        2239 :                 if (!ReadXYArray<XYLineStringSetter>(lsSetter, pabyCur, pabyEnd,
    3787        2239 :                                                      panPointCount[i], dx, dy))
    3788             :                 {
    3789           0 :                     if (nParts > 1)
    3790           0 :                         delete poMLS;
    3791             :                     else
    3792           0 :                         delete poLS;
    3793           0 :                     returnError();
    3794             :                 }
    3795             :             }
    3796             : 
    3797        1963 :             if (bHasZ)
    3798             :             {
    3799        2086 :                 for (i = 0; i < nParts; i++)
    3800             :                 {
    3801        1111 :                     if (nParts > 1)
    3802         272 :                         poLS = cpl::down_cast<FileGDBOGRLineString *>(
    3803             :                             poMLS->getGeometryRef(i));
    3804             : 
    3805        1111 :                     ZLineStringSetter lszSetter(poLS);
    3806        1111 :                     if (!ReadZArray<ZLineStringSetter>(
    3807        1111 :                             lszSetter, pabyCur, pabyEnd, panPointCount[i], dz))
    3808             :                     {
    3809           0 :                         if (nParts > 1)
    3810           0 :                             delete poMLS;
    3811             :                         else
    3812           0 :                             delete poLS;
    3813           0 :                         returnError();
    3814             :                     }
    3815             :                 }
    3816             :             }
    3817             : 
    3818        1963 :             if (bHasM)
    3819             :             {
    3820         109 :                 GIntBig dm = 0;
    3821         223 :                 for (i = 0; i < nParts; i++)
    3822             :                 {
    3823         115 :                     if (nParts > 1)
    3824          12 :                         poLS = cpl::down_cast<FileGDBOGRLineString *>(
    3825             :                             poMLS->getGeometryRef(i));
    3826             : 
    3827             :                     // It seems that absence of M is marked with a single byte
    3828             :                     // with value 66. Be more tolerant and only try to parse the
    3829             :                     // M array is there are at least as many remaining bytes as
    3830             :                     // expected points
    3831         115 :                     if (pabyCur + panPointCount[i] > pabyEnd)
    3832             :                     {
    3833           1 :                         if (nParts > 1)
    3834           0 :                             poMLS->setMeasured(FALSE);
    3835           1 :                         break;
    3836             :                     }
    3837             : 
    3838         114 :                     MLineStringSetter lsmSetter(poLS);
    3839         114 :                     if (!ReadMArray<MLineStringSetter>(
    3840         114 :                             lsmSetter, pabyCur, pabyEnd, panPointCount[i], dm))
    3841             :                     {
    3842           0 :                         if (nParts > 1)
    3843           0 :                             delete poMLS;
    3844             :                         else
    3845           0 :                             delete poLS;
    3846           0 :                         returnError();
    3847             :                     }
    3848             :                 }
    3849             :             }
    3850             : 
    3851        1963 :             if (poMLS)
    3852         276 :                 return poMLS;
    3853             :             else
    3854        1687 :                 return poLS;
    3855             : 
    3856             :             break;
    3857             :         }
    3858             : 
    3859         883 :         case SHPT_POLYGONZ:
    3860             :         case SHPT_POLYGONZM:
    3861         883 :             bHasZ = true; /* go on */
    3862             :             [[fallthrough]];
    3863        2541 :         case SHPT_POLYGON:
    3864             :         case SHPT_POLYGONM:
    3865             :         case SHPT_GENERALPOLYGON:
    3866             :         {
    3867        2541 :             if (nGeomType == SHPT_POLYGONM || nGeomType == SHPT_POLYGONZM)
    3868         119 :                 bHasM = true;
    3869             : 
    3870        2541 :             returnErrorIf(
    3871             :                 !ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts, nCurves,
    3872             :                               (nGeomType & EXT_SHAPE_CURVE_FLAG) != 0, false));
    3873             : 
    3874        2541 :             if (nPoints == 0 || nParts == 0)
    3875             :             {
    3876           6 :                 OGRPolygon *poPoly = new OGRPolygon();
    3877           6 :                 if (bHasZ)
    3878           2 :                     poPoly->set3D(TRUE);
    3879           6 :                 if (bHasM)
    3880           2 :                     poPoly->setMeasured(TRUE);
    3881           6 :                 return poPoly;
    3882             :             }
    3883             : 
    3884        2535 :             if (nCurves)
    3885             :             {
    3886          40 :                 GByte *pabyCurBackup = pabyCur;
    3887          40 :                 OGRGeometry *poRet = CreateCurveGeometry(
    3888             :                     SHPT_GENERALPOLYGON, nParts, nPoints, nCurves, bHasZ, bHasM,
    3889             :                     pabyCur, pabyEnd);
    3890          40 :                 if (poRet)
    3891          40 :                     return poRet;
    3892             :                 // In case something went wrong, go on without curves
    3893           0 :                 pabyCur = pabyCurBackup;
    3894             :             }
    3895             : 
    3896        2495 :             OGRLinearRing **papoRings = new OGRLinearRing *[nParts];
    3897             : 
    3898        2495 :             dx = dy = dz = 0;
    3899        5920 :             for (i = 0; i < nParts; i++)
    3900             :             {
    3901        3425 :                 FileGDBOGRLinearRing *poRing = new FileGDBOGRLinearRing();
    3902        3425 :                 papoRings[i] = poRing;
    3903        3425 :                 poRing->setNumPoints(panPointCount[i], FALSE);
    3904             : 
    3905        3425 :                 XYLineStringSetter lsSetter(poRing->GetPoints());
    3906        3425 :                 if (!ReadXYArray<XYLineStringSetter>(lsSetter, pabyCur, pabyEnd,
    3907        3425 :                                                      panPointCount[i], dx, dy))
    3908             :                 {
    3909             :                     while (true)
    3910             :                     {
    3911           0 :                         delete papoRings[i];
    3912           0 :                         if (i == 0)
    3913           0 :                             break;
    3914           0 :                         i--;
    3915             :                     }
    3916           0 :                     delete[] papoRings;
    3917             :                     // For some reason things that papoRings is leaking
    3918             :                     // cppcheck-suppress memleak
    3919           0 :                     returnError();
    3920             :                 }
    3921             :             }
    3922             : 
    3923        2495 :             if (bHasZ)
    3924             :             {
    3925        1768 :                 for (i = 0; i < nParts; i++)
    3926             :                 {
    3927         887 :                     papoRings[i]->setCoordinateDimension(3);
    3928             : 
    3929         887 :                     ZLineStringSetter lszSetter(papoRings[i]);
    3930         887 :                     if (!ReadZArray<ZLineStringSetter>(
    3931         887 :                             lszSetter, pabyCur, pabyEnd, panPointCount[i], dz))
    3932             :                     {
    3933           0 :                         for (i = 0; i < nParts; i++)
    3934           0 :                             delete papoRings[i];
    3935           0 :                         delete[] papoRings;
    3936           0 :                         returnError();
    3937             :                     }
    3938             :                 }
    3939             :             }
    3940             : 
    3941        2495 :             if (bHasM)
    3942             :             {
    3943         117 :                 GIntBig dm = 0;
    3944         238 :                 for (i = 0; i < nParts; i++)
    3945             :                 {
    3946             :                     // It seems that absence of M is marked with a single byte
    3947             :                     // with value 66. Be more tolerant and only try to parse the
    3948             :                     // M array is there are at least as many remaining bytes as
    3949             :                     // expected points
    3950         123 :                     if (pabyCur + panPointCount[i] > pabyEnd)
    3951             :                     {
    3952           2 :                         while (i != 0)
    3953             :                         {
    3954           0 :                             --i;
    3955           0 :                             papoRings[i]->setMeasured(FALSE);
    3956             :                         }
    3957           2 :                         break;
    3958             :                     }
    3959             : 
    3960         121 :                     papoRings[i]->setMeasured(TRUE);
    3961             : 
    3962         121 :                     MLineStringSetter lsmSetter(papoRings[i]);
    3963         121 :                     if (!ReadMArray<MLineStringSetter>(
    3964         121 :                             lsmSetter, pabyCur, pabyEnd, panPointCount[i], dm))
    3965             :                     {
    3966           0 :                         for (i = 0; i < nParts; i++)
    3967           0 :                             delete papoRings[i];
    3968           0 :                         delete[] papoRings;
    3969           0 :                         returnError();
    3970             :                     }
    3971             :                 }
    3972             :             }
    3973             : 
    3974        2495 :             OGRGeometry *poRet = nullptr;
    3975        2495 :             if (nParts == 1)
    3976             :             {
    3977        2025 :                 OGRPolygon *poPoly = new OGRPolygon();
    3978        2025 :                 poRet = poPoly;
    3979        2025 :                 poPoly->addRingDirectly(papoRings[0]);
    3980             :             }
    3981             :             else
    3982             :             // Note: ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING is *not* defined
    3983             : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
    3984             :                 if (bUseOrganize || !(papoRings[0]->isClockwise()))
    3985             : #endif
    3986             :             {
    3987             :                 /* Slow method: we do a rather expensive topological analysis of
    3988             :                  * the rings to figure out which ones are inner rings from outer
    3989             :                  * rings, and to which outer ring an inner ring belongs too.
    3990             :                  * In most cases, inner rings are CCW oriented and follow
    3991             :                  * immediately the outer ring in which they are included,
    3992             :                  * (that situation is the commented code in the below else
    3993             :                  * branch).
    3994             :                  * In nearly all cases, inner rings are CCW and outer rings
    3995             :                  * are CW oriented, so we could call organizePolygons() with
    3996             :                  * the relatively lightweight METHOD=ONLY_CCW strategy (which
    3997             :                  * is what the shapefile drivers does at time of writing).
    3998             :                  * Unfortunately in https://github.com/OSGeo/gdal/issues/1369,
    3999             :                  * we found likely broken datasets where a polygon with inner
    4000             :                  * rings has its exterior ring with wrong orientation, hence
    4001             :                  * we use the slowest but bullet-proof method.
    4002             :                  */
    4003         470 :                 OGRPolygon **papoPolygons = new OGRPolygon *[nParts];
    4004        1870 :                 for (i = 0; i < nParts; i++)
    4005             :                 {
    4006        1400 :                     papoPolygons[i] = new OGRPolygon();
    4007        1400 :                     papoPolygons[i]->addRingDirectly(papoRings[i]);
    4008             :                 }
    4009         470 :                 delete[] papoRings;
    4010         470 :                 papoRings = nullptr;
    4011         470 :                 poRet = OGRGeometryFactory::organizePolygons(
    4012             :                     reinterpret_cast<OGRGeometry **>(papoPolygons), nParts,
    4013             :                     nullptr, nullptr);
    4014         470 :                 delete[] papoPolygons;
    4015             :             }
    4016             : #ifdef ASSUME_INNER_RINGS_IMMEDIATELY_AFTER_OUTER_RING
    4017             :             else
    4018             :             {
    4019             :                 /* Inner rings are CCW oriented and follow immediately the outer
    4020             :                  */
    4021             :                 /* ring (that is CW oriented) in which they are included */
    4022             :                 OGRMultiPolygon *poMulti = NULL;
    4023             :                 OGRPolygon *poCur = new OGRPolygon();
    4024             :                 poRet = poCur;
    4025             :                 /* We have already checked that the first ring is CW */
    4026             :                 poCur->addRingDirectly(papoRings[0]);
    4027             :                 OGREnvelope sEnvelope;
    4028             :                 papoRings[0]->getEnvelope(&sEnvelope);
    4029             :                 for (i = 1; i < nParts; i++)
    4030             :                 {
    4031             :                     int bIsCW = papoRings[i]->isClockwise();
    4032             :                     if (bIsCW)
    4033             :                     {
    4034             :                         if (poMulti == NULL)
    4035             :                         {
    4036             :                             poMulti = new OGRMultiPolygon();
    4037             :                             poRet = poMulti;
    4038             :                             poMulti->addGeometryDirectly(poCur);
    4039             :                         }
    4040             :                         poCur = new OGRPolygon();
    4041             :                         poMulti->addGeometryDirectly(poCur);
    4042             :                         poCur->addRingDirectly(papoRings[i]);
    4043             :                         papoRings[i]->getEnvelope(&sEnvelope);
    4044             :                     }
    4045             :                     else
    4046             :                     {
    4047             :                         poCur->addRingDirectly(papoRings[i]);
    4048             :                         OGRPoint oPoint;
    4049             :                         papoRings[i]->getPoint(0, &oPoint);
    4050             :                         CPLAssert(oPoint.getX() >= sEnvelope.MinX &&
    4051             :                                   oPoint.getX() <= sEnvelope.MaxX &&
    4052             :                                   oPoint.getY() >= sEnvelope.MinY &&
    4053             :                                   oPoint.getY() <= sEnvelope.MaxY);
    4054             :                     }
    4055             :                 }
    4056             :             }
    4057             : #endif
    4058             : 
    4059        2495 :             delete[] papoRings;
    4060        2495 :             return poRet;
    4061             :             // cppcheck-suppress duplicateBreak
    4062             :             break;
    4063             :         }
    4064             : 
    4065         277 :         case SHPT_MULTIPATCHM:
    4066             :         case SHPT_MULTIPATCH:
    4067         277 :             bHasZ = true; /* go on */
    4068             :             [[fallthrough]];
    4069         555 :         case SHPT_GENERALMULTIPATCH:
    4070             :         {
    4071         555 :             returnErrorIf(!ReadPartDefs(pabyCur, pabyEnd, nPoints, nParts,
    4072             :                                         nCurves, false, true));
    4073             : 
    4074         555 :             if (nPoints == 0 || nParts == 0)
    4075             :             {
    4076           0 :                 OGRPolygon *poPoly = new OGRPolygon();
    4077           0 :                 if (bHasZ)
    4078           0 :                     poPoly->setCoordinateDimension(3);
    4079           0 :                 return poPoly;
    4080             :             }
    4081             :             int *panPartType =
    4082         555 :                 static_cast<int *>(VSI_MALLOC_VERBOSE(sizeof(int) * nParts));
    4083             :             int *panPartStart =
    4084         555 :                 static_cast<int *>(VSI_MALLOC_VERBOSE(sizeof(int) * nParts));
    4085             :             double *padfXYZ = static_cast<double *>(
    4086         555 :                 VSI_MALLOC_VERBOSE(3 * sizeof(double) * nPoints));
    4087         555 :             double *padfX = padfXYZ;
    4088         555 :             double *padfY = padfXYZ ? padfXYZ + nPoints : nullptr;
    4089         555 :             double *padfZ = padfXYZ ? padfXYZ + 2 * nPoints : nullptr;
    4090         555 :             if (panPartType == nullptr || panPartStart == nullptr ||
    4091             :                 padfXYZ == nullptr)
    4092             :             {
    4093           0 :                 VSIFree(panPartType);
    4094           0 :                 VSIFree(panPartStart);
    4095           0 :                 VSIFree(padfXYZ);
    4096           0 :                 returnError();
    4097             :             }
    4098        3324 :             for (i = 0; i < nParts; i++)
    4099             :             {
    4100             :                 GUInt32 nPartType;
    4101        2769 :                 if (!ReadVarUInt32(pabyCur, pabyEnd, nPartType))
    4102             :                 {
    4103           0 :                     VSIFree(panPartType);
    4104           0 :                     VSIFree(panPartStart);
    4105           0 :                     VSIFree(padfXYZ);
    4106           0 :                     returnError();
    4107             :                 }
    4108        2769 :                 panPartType[i] = static_cast<int>(nPartType);
    4109             :             }
    4110         555 :             dx = dy = dz = 0;
    4111             : 
    4112         555 :             XYArraySetter arraySetter(padfX, padfY);
    4113         555 :             if (!ReadXYArray<XYArraySetter>(arraySetter, pabyCur, pabyEnd,
    4114             :                                             nPoints, dx, dy))
    4115             :             {
    4116           0 :                 VSIFree(panPartType);
    4117           0 :                 VSIFree(panPartStart);
    4118           0 :                 VSIFree(padfXYZ);
    4119           0 :                 returnError();
    4120             :             }
    4121             : 
    4122         555 :             if (bHasZ)
    4123             :             {
    4124         555 :                 FileGDBArraySetter arrayzSetter(padfZ);
    4125         555 :                 if (!ReadZArray<FileGDBArraySetter>(arrayzSetter, pabyCur,
    4126             :                                                     pabyEnd, nPoints, dz))
    4127             :                 {
    4128           0 :                     VSIFree(panPartType);
    4129           0 :                     VSIFree(panPartStart);
    4130           0 :                     VSIFree(padfXYZ);
    4131           0 :                     returnError();
    4132             :                 }
    4133             :             }
    4134             :             else
    4135             :             {
    4136           0 :                 memset(padfZ, 0, nPoints * sizeof(double));
    4137             :             }
    4138             : 
    4139         555 :             panPartStart[0] = 0;
    4140        2769 :             for (i = 1; i < nParts; ++i)
    4141        2214 :                 panPartStart[i] = panPartStart[i - 1] + panPointCount[i - 1];
    4142             :             // (CID 1404102)
    4143             :             // coverity[overrun-buffer-arg]
    4144         555 :             OGRGeometry *poRet = OGRCreateFromMultiPatch(
    4145             :                 static_cast<int>(nParts), panPartStart, panPartType,
    4146             :                 static_cast<int>(nPoints), padfX, padfY, padfZ);
    4147             : 
    4148         555 :             VSIFree(panPartType);
    4149         555 :             VSIFree(panPartStart);
    4150         555 :             VSIFree(padfXYZ);
    4151             : 
    4152         555 :             return poRet;
    4153             :             // cppcheck-suppress duplicateBreak
    4154             :             break;
    4155             :         }
    4156             : 
    4157           0 :         default:
    4158           0 :             CPLDebug("OpenFileGDB", "Unhandled geometry type = %d",
    4159             :                      static_cast<int>(nGeomType));
    4160           0 :             break;
    4161             :             /*
    4162             :             #define SHPT_GENERALMULTIPOINT  53
    4163             :             */
    4164             :     }
    4165           0 :     return nullptr;
    4166             : }
    4167             : 
    4168             : /************************************************************************/
    4169             : /*                           BuildConverter()                           */
    4170             : /************************************************************************/
    4171             : 
    4172             : FileGDBOGRGeometryConverter *
    4173         867 : FileGDBOGRGeometryConverter::BuildConverter(const FileGDBGeomField *poGeomField)
    4174             : {
    4175         867 :     return new FileGDBOGRGeometryConverterImpl(poGeomField);
    4176             : }
    4177             : 
    4178             : /************************************************************************/
    4179             : /*                      GetGeometryTypeFromESRI()                       */
    4180             : /************************************************************************/
    4181             : 
    4182             : static const struct
    4183             : {
    4184             :     const char *pszStr;
    4185             :     OGRwkbGeometryType eType;
    4186             : } AssocESRIGeomTypeToOGRGeomType[] = {
    4187             :     {"esriGeometryPoint", wkbPoint},
    4188             :     {"esriGeometryMultipoint", wkbMultiPoint},
    4189             :     {"esriGeometryLine", wkbMultiLineString},
    4190             :     {"esriGeometryPolyline", wkbMultiLineString},
    4191             :     {"esriGeometryPolygon", wkbMultiPolygon},
    4192             :     {"esriGeometryMultiPatch", wkbUnknown}};
    4193             : 
    4194             : OGRwkbGeometryType
    4195        2642 : FileGDBOGRGeometryConverter::GetGeometryTypeFromESRI(const char *pszESRIType)
    4196             : {
    4197        9245 :     for (size_t i = 0; i < sizeof(AssocESRIGeomTypeToOGRGeomType) /
    4198             :                                sizeof(AssocESRIGeomTypeToOGRGeomType[0]);
    4199             :          i++)
    4200             :     {
    4201        9245 :         if (strcmp(pszESRIType, AssocESRIGeomTypeToOGRGeomType[i].pszStr) == 0)
    4202        2642 :             return AssocESRIGeomTypeToOGRGeomType[i].eType;
    4203             :     }
    4204           0 :     CPLDebug("OpenFileGDB", "Unhandled geometry type : %s", pszESRIType);
    4205           0 :     return wkbUnknown;
    4206             : }
    4207             : 
    4208             : } /* namespace OpenFileGDB */

Generated by: LCOV version 1.14