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

Generated by: LCOV version 1.14