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

Generated by: LCOV version 1.14