LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - filegdbtable.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1709 2010 85.0 %
Date: 2026-05-07 20:04:54 Functions: 98 99 99.0 %

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

Generated by: LCOV version 1.14