LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/openfilegdb - filegdbtable_write.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1198 1397 85.8 %
Date: 2026-05-07 20:04:54 Functions: 24 24 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  OpenGIS Simple Features Reference Implementation
       4             :  * Purpose:  Implements writing of FileGDB tables
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2022, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "cpl_port.h"
      14             : 
      15             : #include "gdal_version_full/gdal_version.h"
      16             : 
      17             : #include "filegdbtable.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <cinttypes>
      21             : #include <cmath>
      22             : #include <cwchar>
      23             : #include <errno.h>
      24             : #include <limits.h>
      25             : #include <stddef.h>
      26             : #include <stdio.h>
      27             : #include <string.h>
      28             : #include <time.h>
      29             : #include <limits>
      30             : #include <set>
      31             : #include <string>
      32             : #include <vector>
      33             : 
      34             : #include "cpl_conv.h"
      35             : #include "cpl_error.h"
      36             : #include "cpl_string.h"
      37             : #include "cpl_time.h"
      38             : #include "cpl_vsi.h"
      39             : #include "filegdbtable_priv.h"
      40             : #include "gdal_priv_templates.hpp"
      41             : #include "ogr_api.h"
      42             : #include "ogr_core.h"
      43             : #include "ogr_geometry.h"
      44             : #include "ogrpgeogeometry.h"
      45             : 
      46             : namespace OpenFileGDB
      47             : {
      48             : 
      49             : constexpr uint8_t EXT_SHAPE_SEGMENT_ARC = 1;
      50             : constexpr int TABLX_HEADER_SIZE = 16;
      51             : constexpr int TABLX_FEATURES_PER_PAGE = 1024;
      52             : 
      53             : /************************************************************************/
      54             : /*                               Create()                               */
      55             : /************************************************************************/
      56             : 
      57        2020 : bool FileGDBTable::Create(const char *pszFilename, int nTablxOffsetSize,
      58             :                           FileGDBTableGeometryType eTableGeomType,
      59             :                           bool bGeomTypeHasZ, bool bGeomTypeHasM)
      60             : {
      61        2020 :     CPLAssert(m_fpTable == nullptr);
      62             : 
      63        2020 :     m_eGDBTableVersion = GDBTableVersion::V3;
      64        2020 :     m_bUpdate = true;
      65        2020 :     m_eTableGeomType = eTableGeomType;
      66        2020 :     m_nTablxOffsetSize = nTablxOffsetSize;
      67        2020 :     m_bGeomTypeHasZ = bGeomTypeHasZ;
      68        2020 :     m_bGeomTypeHasM = bGeomTypeHasM;
      69        2020 :     m_bHasReadGDBIndexes = TRUE;
      70             : 
      71        2020 :     if (!EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "gdbtable"))
      72             :     {
      73           0 :         CPLError(CE_Failure, CPLE_AppDefined,
      74             :                  "FileGDB table extension must be gdbtable");
      75           0 :         return false;
      76             :     }
      77             : 
      78        2020 :     m_osFilename = pszFilename;
      79        2020 :     m_osFilenameWithLayerName = m_osFilename;
      80        2020 :     m_fpTable = VSIFOpenL(pszFilename, "wb+");
      81        2020 :     if (m_fpTable == nullptr)
      82             :     {
      83           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot create %s: %s",
      84           0 :                  m_osFilename.c_str(), VSIStrerror(errno));
      85           0 :         return false;
      86             :     }
      87             : 
      88             :     const std::string osTableXName =
      89        4040 :         CPLResetExtensionSafe(pszFilename, "gdbtablx");
      90        2020 :     m_fpTableX = VSIFOpenL(osTableXName.c_str(), "wb+");
      91        2020 :     if (m_fpTableX == nullptr)
      92             :     {
      93           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Cannot create %s: %s",
      94           0 :                  osTableXName.c_str(), VSIStrerror(errno));
      95           0 :         return false;
      96             :     }
      97             : 
      98        2020 :     if (!WriteHeader(m_fpTable))
      99           0 :         return false;
     100             : 
     101        2020 :     if (!WriteHeaderX(m_fpTableX))
     102           0 :         return false;
     103             : 
     104        2020 :     m_bDirtyTableXTrailer = true;
     105             : 
     106        2020 :     return true;
     107             : }
     108             : 
     109             : /************************************************************************/
     110             : /*                            SetTextUTF16()                            */
     111             : /************************************************************************/
     112             : 
     113           1 : bool FileGDBTable::SetTextUTF16()
     114             : {
     115           1 :     if (m_nOffsetFieldDesc != 0)
     116             :     {
     117           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     118             :                  "SetTextUTF16() should be called immediately after Create()");
     119           0 :         return false;
     120             :     }
     121             : 
     122           1 :     m_bStringsAreUTF8 = false;
     123           1 :     return true;
     124             : }
     125             : 
     126             : /************************************************************************/
     127             : /*                            WriteHeader()                             */
     128             : /************************************************************************/
     129             : 
     130        2065 : bool FileGDBTable::WriteHeader(VSILFILE *fpTable)
     131             : {
     132             :     // Could be useful in case we get something wrong...
     133             :     const char *pszCreator =
     134        2065 :         CPLGetConfigOption("OPENFILEGDB_CREATOR", "GDAL " GDAL_RELEASE_NAME);
     135             : 
     136        2065 :     m_nFileSize = 0;
     137        2065 :     m_bDirtyHeader = true;
     138        2065 :     m_bDirtyFieldDescriptors = true;
     139        2065 :     m_nOffsetFieldDesc = 0;
     140        2065 :     m_nFieldDescLength = 0;
     141             : 
     142        2065 :     VSIFSeekL(fpTable, 0, SEEK_SET);
     143             : 
     144             :     bool bRet =
     145        2065 :         WriteUInt32(fpTable, 3) &&  // version number
     146             :         // number of valid rows
     147        2065 :         WriteUInt32(fpTable, static_cast<uint32_t>(m_nValidRecordCount)) &&
     148        2065 :         WriteUInt32(fpTable,
     149        2065 :                     m_nHeaderBufferMaxSize) &&  // largest size of a feature
     150             :                                                 // record / field description
     151        2065 :         WriteUInt32(fpTable, 5) &&              // magic value
     152        2065 :         WriteUInt32(fpTable, 0) &&              // magic value
     153        2065 :         WriteUInt32(fpTable, 0) &&              // magic value
     154        6195 :         WriteUInt64(fpTable, m_nFileSize) &&
     155        2065 :         WriteUInt64(fpTable, m_nOffsetFieldDesc);
     156             : 
     157        2065 :     if (bRet && pszCreator[0] != '\0')
     158             :     {
     159             :         // Writing the creator is not part of the "spec", but we just use
     160             :         // the fact that there might be ghost areas in the file
     161        2065 :         bRet =
     162        4130 :             WriteUInt32(fpTable, static_cast<uint32_t>(strlen(pszCreator))) &&
     163        2065 :             VSIFWriteL(pszCreator, strlen(pszCreator), 1, fpTable) == 1;
     164             :     }
     165             : 
     166        2065 :     if (!bRet)
     167             :     {
     168           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write .gdbtable header");
     169           0 :         return false;
     170             :     }
     171             : 
     172        2065 :     m_nFileSize = VSIFTellL(fpTable);
     173        2065 :     return true;
     174             : }
     175             : 
     176             : /************************************************************************/
     177             : /*                            WriteHeaderX()                            */
     178             : /************************************************************************/
     179             : 
     180        2051 : bool FileGDBTable::WriteHeaderX(VSILFILE *fpTableX)
     181             : {
     182        2051 :     VSIFSeekL(fpTableX, 0, SEEK_SET);
     183        2051 :     if (!WriteUInt32(fpTableX, 3) ||  // version number
     184        2051 :         !WriteUInt32(fpTableX, static_cast<uint32_t>(m_n1024BlocksPresent)) ||
     185        6153 :         !WriteUInt32(fpTableX, static_cast<uint32_t>(m_nTotalRecordCount)) ||
     186        2051 :         !WriteUInt32(fpTableX, m_nTablxOffsetSize))
     187             :     {
     188           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot write .gdbtablx header");
     189           0 :         return false;
     190             :     }
     191        2051 :     return true;
     192             : }
     193             : 
     194             : /************************************************************************/
     195             : /*                                Sync()                                */
     196             : /************************************************************************/
     197             : 
     198       12607 : bool FileGDBTable::Sync(VSILFILE *fpTable, VSILFILE *fpTableX)
     199             : {
     200       12607 :     if (!m_bUpdate)
     201        4371 :         return true;
     202             : 
     203        8236 :     if (fpTable == nullptr)
     204        8152 :         fpTable = m_fpTable;
     205             : 
     206        8236 :     if (fpTableX == nullptr)
     207        8152 :         fpTableX = m_fpTableX;
     208             : 
     209        8236 :     bool bRet = true;
     210             : 
     211        8236 :     if (m_bDirtyGdbIndexesFile)
     212             :     {
     213         329 :         m_bDirtyGdbIndexesFile = false;
     214         329 :         CreateGdbIndexesFile();
     215             :     }
     216             : 
     217        8236 :     if (m_bDirtyIndices)
     218             :     {
     219        3052 :         m_bDirtyIndices = false;
     220        3052 :         RefreshIndices();
     221             :     }
     222             : 
     223        8236 :     if (m_bDirtyFieldDescriptors && fpTable)
     224         683 :         bRet = WriteFieldDescriptors(fpTable);
     225             : 
     226        8236 :     if (m_bDirtyGeomFieldBBox && fpTable)
     227             :     {
     228         162 :         VSIFSeekL(fpTable, m_nOffsetFieldDesc + m_nGeomFieldBBoxSubOffset,
     229             :                   SEEK_SET);
     230         162 :         const auto poGeomField = cpl::down_cast<const FileGDBGeomField *>(
     231         162 :             m_apoFields[m_iGeomField].get());
     232         162 :         bRet &= WriteFloat64(fpTable, poGeomField->GetXMin());
     233         162 :         bRet &= WriteFloat64(fpTable, poGeomField->GetYMin());
     234         162 :         bRet &= WriteFloat64(fpTable, poGeomField->GetXMax());
     235         162 :         bRet &= WriteFloat64(fpTable, poGeomField->GetYMax());
     236         162 :         if (m_bGeomTypeHasZ)
     237             :         {
     238          50 :             bRet &= WriteFloat64(fpTable, poGeomField->GetZMin());
     239          50 :             bRet &= WriteFloat64(fpTable, poGeomField->GetZMax());
     240             :         }
     241         162 :         m_bDirtyGeomFieldBBox = false;
     242             :     }
     243             : 
     244        8236 :     if (m_bDirtyGeomFieldSpatialIndexGridRes && fpTable)
     245             :     {
     246         127 :         VSIFSeekL(fpTable,
     247         127 :                   m_nOffsetFieldDesc + m_nGeomFieldSpatialIndexGridResSubOffset,
     248             :                   SEEK_SET);
     249         127 :         const auto poGeomField = cpl::down_cast<const FileGDBGeomField *>(
     250         127 :             m_apoFields[m_iGeomField].get());
     251             :         const auto &adfSpatialIndexGridResolution =
     252         127 :             poGeomField->GetSpatialIndexGridResolution();
     253         254 :         for (double dfSize : adfSpatialIndexGridResolution)
     254         127 :             bRet &= WriteFloat64(fpTable, dfSize);
     255         127 :         m_bDirtyGeomFieldSpatialIndexGridRes = false;
     256             :     }
     257             : 
     258        8236 :     if (m_bDirtyHeader && fpTable)
     259             :     {
     260        3731 :         VSIFSeekL(fpTable, 4, SEEK_SET);
     261        3731 :         bRet &=
     262        3731 :             WriteUInt32(fpTable, static_cast<uint32_t>(m_nValidRecordCount));
     263        3731 :         m_nHeaderBufferMaxSize =
     264        7462 :             std::max(m_nHeaderBufferMaxSize,
     265        3731 :                      std::max(m_nRowBufferMaxSize, m_nFieldDescLength));
     266        3731 :         bRet &= WriteUInt32(fpTable, m_nHeaderBufferMaxSize);
     267             : 
     268        3731 :         VSIFSeekL(fpTable, 24, SEEK_SET);
     269        3731 :         bRet &= WriteUInt64(fpTable, m_nFileSize);
     270        3731 :         bRet &= WriteUInt64(fpTable, m_nOffsetFieldDesc);
     271             : 
     272        3731 :         VSIFSeekL(fpTable, 0, SEEK_END);
     273        3731 :         CPLAssert(VSIFTellL(fpTable) == m_nFileSize);
     274        3731 :         m_bDirtyHeader = false;
     275             :     }
     276             : 
     277        8236 :     if (m_bDirtyTableXHeader && fpTableX)
     278             :     {
     279        2979 :         VSIFSeekL(fpTableX, 4, SEEK_SET);
     280        2979 :         bRet &=
     281        2979 :             WriteUInt32(fpTableX, static_cast<uint32_t>(m_n1024BlocksPresent));
     282        2979 :         bRet &=
     283        2979 :             WriteUInt32(fpTableX, static_cast<uint32_t>(m_nTotalRecordCount));
     284        2979 :         m_bDirtyTableXHeader = false;
     285             :     }
     286             : 
     287        8236 :     if (m_bDirtyTableXTrailer && fpTableX)
     288             :     {
     289        2565 :         m_nOffsetTableXTrailer =
     290        2565 :             TABLX_HEADER_SIZE +
     291        2565 :             m_nTablxOffsetSize * TABLX_FEATURES_PER_PAGE *
     292        2565 :                 static_cast<vsi_l_offset>(m_n1024BlocksPresent);
     293        2565 :         VSIFSeekL(fpTableX, m_nOffsetTableXTrailer, SEEK_SET);
     294        7695 :         const uint32_t n1024BlocksTotal = static_cast<uint32_t>(
     295        2565 :             DIV_ROUND_UP(m_nTotalRecordCount, TABLX_FEATURES_PER_PAGE));
     296        2565 :         if (!m_abyTablXBlockMap.empty())
     297             :         {
     298          31 :             CPLAssert(m_abyTablXBlockMap.size() >= (n1024BlocksTotal + 7) / 8);
     299             :         }
     300             :         // Size of the bitmap in terms of 32-bit words, rounded to a multiple
     301             :         // of 32.
     302             :         const uint32_t nBitmapInt32Words =
     303        7695 :             DIV_ROUND_UP(
     304             :                 DIV_ROUND_UP(static_cast<uint32_t>(m_abyTablXBlockMap.size()),
     305             :                              4),
     306        7695 :                 32) *
     307        2565 :             32;
     308        2565 :         m_abyTablXBlockMap.resize(nBitmapInt32Words * 4);
     309        2565 :         bRet &= WriteUInt32(fpTableX, nBitmapInt32Words);
     310        2565 :         bRet &= WriteUInt32(fpTableX, n1024BlocksTotal);
     311        2565 :         bRet &=
     312        2565 :             WriteUInt32(fpTableX, static_cast<uint32_t>(m_n1024BlocksPresent));
     313        2565 :         uint32_t nTrailingZero32BitWords = 0;
     314        2565 :         for (int i = static_cast<int>(m_abyTablXBlockMap.size() / 4) - 1;
     315        3464 :              i >= 0; --i)
     316             :         {
     317         930 :             if (m_abyTablXBlockMap[4 * i] != 0 ||
     318         901 :                 m_abyTablXBlockMap[4 * i + 1] != 0 ||
     319        2732 :                 m_abyTablXBlockMap[4 * i + 2] != 0 ||
     320         901 :                 m_abyTablXBlockMap[4 * i + 3] != 0)
     321             :             {
     322          31 :                 break;
     323             :             }
     324         899 :             nTrailingZero32BitWords++;
     325             :         }
     326        2565 :         const uint32_t nLeadingNonZero32BitWords =
     327             :             nBitmapInt32Words - nTrailingZero32BitWords;
     328        2565 :         bRet &= WriteUInt32(fpTableX, nLeadingNonZero32BitWords);
     329        2565 :         if (!m_abyTablXBlockMap.empty())
     330             :         {
     331             : #ifdef DEBUG
     332          31 :             uint32_t nCountBlocks = 0;
     333     4194430 :             for (uint32_t i = 0; i < n1024BlocksTotal; i++)
     334     4194400 :                 nCountBlocks += TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
     335          31 :             if (nCountBlocks != m_n1024BlocksPresent)
     336             :             {
     337           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     338             :                          "Sync(): nCountBlocks(=%u) != "
     339             :                          "m_n1024BlocksPresent(=%" PRIu64 ")",
     340             :                          nCountBlocks, m_n1024BlocksPresent);
     341             :             }
     342             : #endif
     343          31 :             bRet &= VSIFWriteL(m_abyTablXBlockMap.data(), 1,
     344             :                                m_abyTablXBlockMap.size(),
     345          31 :                                fpTableX) == m_abyTablXBlockMap.size();
     346             :         }
     347        2565 :         m_bDirtyTableXTrailer = false;
     348             :     }
     349             : 
     350        8236 :     if (m_bFreelistCanBeDeleted)
     351             :     {
     352           5 :         DeleteFreeList();
     353             :     }
     354             : 
     355        8236 :     if (fpTable)
     356        8173 :         VSIFFlushL(fpTable);
     357             : 
     358        8236 :     if (fpTableX)
     359        8172 :         VSIFFlushL(fpTableX);
     360             : 
     361        8236 :     return bRet;
     362             : }
     363             : 
     364             : /************************************************************************/
     365             : /*                           EncodeEnvelope()                           */
     366             : /************************************************************************/
     367             : 
     368             : #define CHECK_CAN_BE_ENCODED_ON_VARUINT(v, msg)                                \
     369             :     if (!GDALIsValueInRange<uint64_t>(v))                                      \
     370             :     {                                                                          \
     371             :         CPLError(CE_Failure, CPLE_AppDefined, msg);                            \
     372             :         return false;                                                          \
     373             :     }
     374             : 
     375             : #define CHECK_CAN_BE_ENCODED_ON_VARINT(v, oldV, msg)                           \
     376             :     if (!GDALIsValueInRange<int64_t>(v) ||                                     \
     377             :         !GDALIsValueInRange<int64_t>((v) - (oldV)))                            \
     378             :     {                                                                          \
     379             :         CPLError(CE_Failure, CPLE_AppDefined, msg);                            \
     380             :         return false;                                                          \
     381             :     }
     382             : 
     383         278 : static bool EncodeEnvelope(std::vector<GByte> &abyBuffer,
     384             :                            const FileGDBGeomField *poGeomField,
     385             :                            const OGRGeometry *poGeom)
     386             : {
     387         278 :     OGREnvelope oEnvelope;
     388         278 :     poGeom->getEnvelope(&oEnvelope);
     389             : 
     390             :     double dfVal;
     391             : 
     392         278 :     dfVal = (oEnvelope.MinX - poGeomField->GetXOrigin()) *
     393         278 :             poGeomField->GetXYScale();
     394         278 :     CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal, "Cannot encode X value");
     395         278 :     WriteVarUInt(abyBuffer, static_cast<uint64_t>(dfVal + 0.5));
     396             : 
     397         278 :     dfVal = (oEnvelope.MinY - poGeomField->GetYOrigin()) *
     398         278 :             poGeomField->GetXYScale();
     399         278 :     CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal, "Cannot encode Y value");
     400         278 :     WriteVarUInt(abyBuffer, static_cast<uint64_t>(dfVal + 0.5));
     401             : 
     402         278 :     dfVal = (oEnvelope.MaxX - oEnvelope.MinX) * poGeomField->GetXYScale();
     403         278 :     CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal, "Cannot encode X value");
     404         278 :     WriteVarUInt(abyBuffer, static_cast<uint64_t>(dfVal + 0.5));
     405             : 
     406         278 :     dfVal = (oEnvelope.MaxY - oEnvelope.MinY) * poGeomField->GetXYScale();
     407         278 :     CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal, "Cannot encode Y value");
     408         278 :     WriteVarUInt(abyBuffer, static_cast<uint64_t>(dfVal + 0.5));
     409             : 
     410         278 :     return true;
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                           EncodeGeometry()                           */
     415             : /************************************************************************/
     416             : 
     417        4094 : bool FileGDBTable::EncodeGeometry(const FileGDBGeomField *poGeomField,
     418             :                                   const OGRGeometry *poGeom)
     419             : {
     420        4094 :     m_abyGeomBuffer.clear();
     421             : 
     422        4094 :     const auto bIs3D = poGeom->Is3D();
     423        4094 :     const auto bIsMeasured = poGeom->IsMeasured();
     424             : 
     425             :     const auto WriteEndOfCurveOrSurface =
     426       36515 :         [this, bIs3D, bIsMeasured, poGeomField, poGeom](int nCurveDescrCount)
     427             :     {
     428         236 :         WriteVarUInt(m_abyGeomBuffer, static_cast<uint32_t>(m_adfX.size()));
     429         236 :         if (m_adfX.empty())
     430           8 :             return true;
     431         228 :         WriteVarUInt(m_abyGeomBuffer,
     432         228 :                      static_cast<uint32_t>(m_anNumberPointsPerPart.size()));
     433         228 :         if (nCurveDescrCount > 0)
     434          12 :             WriteVarUInt(m_abyGeomBuffer, nCurveDescrCount);
     435             : 
     436         228 :         if (!EncodeEnvelope(m_abyGeomBuffer, poGeomField, poGeom))
     437           0 :             return false;
     438             : 
     439         284 :         for (int iPart = 0;
     440         284 :              iPart < static_cast<int>(m_anNumberPointsPerPart.size()) - 1;
     441             :              ++iPart)
     442             :         {
     443          56 :             WriteVarUInt(m_abyGeomBuffer, m_anNumberPointsPerPart[iPart]);
     444             :         }
     445             : 
     446             :         {
     447         228 :             int64_t nLastX = 0;
     448         228 :             int64_t nLastY = 0;
     449        3805 :             for (size_t i = 0; i < m_adfX.size(); ++i)
     450             :             {
     451             :                 double dfVal =
     452        3577 :                     std::round((m_adfX[i] - poGeomField->GetXOrigin()) *
     453        3577 :                                poGeomField->GetXYScale());
     454        3577 :                 CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastX,
     455             :                                                "Cannot encode X value");
     456        3577 :                 const int64_t nX = static_cast<int64_t>(dfVal);
     457        3577 :                 WriteVarInt(m_abyGeomBuffer, nX - nLastX);
     458             : 
     459        3577 :                 dfVal = std::round((m_adfY[i] - poGeomField->GetYOrigin()) *
     460        3577 :                                    poGeomField->GetXYScale());
     461        3577 :                 CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastY,
     462             :                                                "Cannot encode Y value");
     463        3577 :                 const int64_t nY = static_cast<int64_t>(dfVal);
     464        3577 :                 WriteVarInt(m_abyGeomBuffer, nY - nLastY);
     465             : 
     466        3577 :                 nLastX = nX;
     467        3577 :                 nLastY = nY;
     468             :             }
     469             :         }
     470             : 
     471         228 :         if (bIs3D)
     472             :         {
     473          61 :             int64_t nLastZ = 0;
     474         295 :             for (size_t i = 0; i < m_adfZ.size(); ++i)
     475             :             {
     476             :                 double dfVal =
     477         234 :                     std::round((m_adfZ[i] - poGeomField->GetZOrigin()) *
     478         234 :                                poGeomField->GetZScale());
     479         234 :                 CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastZ,
     480             :                                                "Cannot encode Z value");
     481         234 :                 const int64_t nZ = static_cast<int64_t>(dfVal);
     482         234 :                 WriteVarInt(m_abyGeomBuffer, nZ - nLastZ);
     483             : 
     484         234 :                 nLastZ = nZ;
     485             :             }
     486             :         }
     487             : 
     488         228 :         if (bIsMeasured)
     489             :         {
     490          13 :             int64_t nLastM = 0;
     491          79 :             for (size_t i = 0; i < m_adfM.size(); ++i)
     492             :             {
     493             :                 double dfVal =
     494          66 :                     std::round((m_adfM[i] - poGeomField->GetMOrigin()) *
     495          66 :                                poGeomField->GetMScale());
     496          66 :                 CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastM,
     497             :                                                "Cannot encode M value");
     498          66 :                 const int64_t nM = static_cast<int64_t>(dfVal);
     499          66 :                 WriteVarInt(m_abyGeomBuffer, nM - nLastM);
     500             : 
     501          66 :                 nLastM = nM;
     502             :             }
     503             :         }
     504             : 
     505         228 :         if (!m_abyCurvePart.empty())
     506             :         {
     507          12 :             m_abyGeomBuffer.insert(m_abyGeomBuffer.end(),
     508             :                                    m_abyCurvePart.begin(),
     509          24 :                                    m_abyCurvePart.end());
     510             :         }
     511             : 
     512         228 :         return true;
     513        4094 :     };
     514             : 
     515             :     const auto ReserveXYZMArrays =
     516        1220 :         [this, bIs3D, bIsMeasured](const size_t nAdditionalSize)
     517             :     {
     518         252 :         size_t nNewMinSize = m_adfX.size() + nAdditionalSize;
     519         252 :         if (nNewMinSize > m_adfX.capacity())
     520             :         {
     521         111 :             size_t nNewCapacity = nNewMinSize;
     522         111 :             if (m_adfX.capacity() < std::numeric_limits<size_t>::max() / 2)
     523             :             {
     524         111 :                 nNewCapacity = std::max(nNewCapacity, 2 * m_adfX.capacity());
     525             :             }
     526         111 :             m_adfX.reserve(nNewCapacity);
     527         111 :             m_adfY.reserve(nNewCapacity);
     528         111 :             if (bIs3D)
     529          33 :                 m_adfZ.reserve(nNewCapacity);
     530         111 :             if (bIsMeasured)
     531          17 :                 m_adfM.reserve(nNewCapacity);
     532             :         }
     533         252 :     };
     534             : 
     535        4094 :     const auto eFlatType = wkbFlatten(poGeom->getGeometryType());
     536        4094 :     switch (eFlatType)
     537             :     {
     538        3806 :         case wkbPoint:
     539             :         {
     540        3806 :             if (bIs3D)
     541             :             {
     542          24 :                 if (bIsMeasured)
     543             :                 {
     544           4 :                     WriteUInt8(m_abyGeomBuffer,
     545             :                                static_cast<uint8_t>(SHPT_POINTZM));
     546             :                 }
     547             :                 else
     548             :                 {
     549          20 :                     WriteUInt8(m_abyGeomBuffer,
     550             :                                static_cast<uint8_t>(SHPT_POINTZ));
     551             :                 }
     552             :             }
     553             :             else
     554             :             {
     555        3782 :                 if (bIsMeasured)
     556             :                 {
     557           2 :                     WriteUInt8(m_abyGeomBuffer,
     558             :                                static_cast<uint8_t>(SHPT_POINTM));
     559             :                 }
     560             :                 else
     561             :                 {
     562        3780 :                     WriteUInt8(m_abyGeomBuffer,
     563             :                                static_cast<uint8_t>(SHPT_POINT));
     564             :                 }
     565             :             }
     566        3806 :             const auto poPoint = poGeom->toPoint();
     567        3806 :             if (poPoint->IsEmpty())
     568             :             {
     569           4 :                 WriteUInt8(m_abyGeomBuffer, 0);
     570           4 :                 WriteUInt8(m_abyGeomBuffer, 0);
     571           4 :                 if (bIs3D)
     572           2 :                     WriteUInt8(m_abyGeomBuffer, 0);
     573           4 :                 if (bIsMeasured)
     574           2 :                     WriteUInt8(m_abyGeomBuffer, 0);
     575             :             }
     576             :             else
     577             :             {
     578             :                 double dfVal;
     579             : 
     580        3802 :                 dfVal = (poPoint->getX() - poGeomField->GetXOrigin()) *
     581        3802 :                             poGeomField->GetXYScale() +
     582             :                         1;
     583        3802 :                 CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal, "Cannot encode value");
     584        3802 :                 WriteVarUInt(m_abyGeomBuffer,
     585        3802 :                              static_cast<uint64_t>(dfVal + 0.5));
     586             : 
     587        3802 :                 dfVal = (poPoint->getY() - poGeomField->GetYOrigin()) *
     588        3802 :                             poGeomField->GetXYScale() +
     589             :                         1;
     590        3802 :                 CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal, "Cannot encode Y value");
     591        3802 :                 WriteVarUInt(m_abyGeomBuffer,
     592        3802 :                              static_cast<uint64_t>(dfVal + 0.5));
     593             : 
     594        3802 :                 if (bIs3D)
     595             :                 {
     596          22 :                     dfVal = (poPoint->getZ() - poGeomField->GetZOrigin()) *
     597          22 :                                 poGeomField->GetZScale() +
     598             :                             1;
     599          22 :                     CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal,
     600             :                                                     "Cannot encode Z value");
     601          22 :                     WriteVarUInt(m_abyGeomBuffer,
     602          22 :                                  static_cast<uint64_t>(dfVal + 0.5));
     603             :                 }
     604             : 
     605        3802 :                 if (bIsMeasured)
     606             :                 {
     607           4 :                     dfVal = (poPoint->getM() - poGeomField->GetMOrigin()) *
     608           4 :                                 poGeomField->GetMScale() +
     609             :                             1;
     610           4 :                     CHECK_CAN_BE_ENCODED_ON_VARUINT(dfVal,
     611             :                                                     "Cannot encode M value");
     612           4 :                     WriteVarUInt(m_abyGeomBuffer,
     613           4 :                                  static_cast<uint64_t>(dfVal + 0.5));
     614             :                 }
     615             :             }
     616             : 
     617        3806 :             return true;
     618             :         }
     619             : 
     620          29 :         case wkbMultiPoint:
     621             :         {
     622          29 :             if (bIs3D)
     623             :             {
     624          14 :                 if (bIsMeasured)
     625             :                 {
     626           1 :                     WriteUInt8(m_abyGeomBuffer,
     627             :                                static_cast<uint8_t>(SHPT_MULTIPOINTZM));
     628             :                 }
     629             :                 else
     630             :                 {
     631          13 :                     WriteUInt8(m_abyGeomBuffer,
     632             :                                static_cast<uint8_t>(SHPT_MULTIPOINTZ));
     633             :                 }
     634             :             }
     635             :             else
     636             :             {
     637          15 :                 if (bIsMeasured)
     638             :                 {
     639           1 :                     WriteUInt8(m_abyGeomBuffer,
     640             :                                static_cast<uint8_t>(SHPT_MULTIPOINTM));
     641             :                 }
     642             :                 else
     643             :                 {
     644          14 :                     WriteUInt8(m_abyGeomBuffer,
     645             :                                static_cast<uint8_t>(SHPT_MULTIPOINT));
     646             :                 }
     647             :             }
     648             : 
     649          29 :             const auto poMultiPoint = poGeom->toMultiPoint();
     650          29 :             const auto nNumGeoms = poMultiPoint->getNumGeometries();
     651          29 :             WriteVarUInt(m_abyGeomBuffer, nNumGeoms);
     652          29 :             if (nNumGeoms == 0)
     653           0 :                 return true;
     654             : 
     655          29 :             if (!EncodeEnvelope(m_abyGeomBuffer, poGeomField, poGeom))
     656           0 :                 return false;
     657             : 
     658             :             {
     659          29 :                 int64_t nLastX = 0;
     660          29 :                 int64_t nLastY = 0;
     661          86 :                 for (const auto *poPoint : *poMultiPoint)
     662             :                 {
     663          57 :                     const double dfX = poPoint->getX();
     664          57 :                     const double dfY = poPoint->getY();
     665             : 
     666             :                     double dfVal =
     667          57 :                         std::round((dfX - poGeomField->GetXOrigin()) *
     668          57 :                                    poGeomField->GetXYScale());
     669          57 :                     CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastX,
     670             :                                                    "Cannot encode value");
     671          57 :                     const int64_t nX = static_cast<int64_t>(dfVal);
     672          57 :                     WriteVarInt(m_abyGeomBuffer, nX - nLastX);
     673             : 
     674          57 :                     dfVal = std::round((dfY - poGeomField->GetYOrigin()) *
     675          57 :                                        poGeomField->GetXYScale());
     676          57 :                     CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastY,
     677             :                                                    "Cannot encode Y value");
     678          57 :                     const int64_t nY = static_cast<int64_t>(dfVal);
     679          57 :                     WriteVarInt(m_abyGeomBuffer, nY - nLastY);
     680             : 
     681          57 :                     nLastX = nX;
     682          57 :                     nLastY = nY;
     683             :                 }
     684             :             }
     685             : 
     686          29 :             if (bIs3D)
     687             :             {
     688          14 :                 int64_t nLastZ = 0;
     689          42 :                 for (const auto *poPoint : *poMultiPoint)
     690             :                 {
     691          28 :                     const double dfZ = poPoint->getZ();
     692             : 
     693             :                     double dfVal =
     694          28 :                         std::round((dfZ - poGeomField->GetZOrigin()) *
     695          28 :                                    poGeomField->GetZScale());
     696          28 :                     CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastZ,
     697             :                                                    "Bad Z value");
     698          28 :                     const int64_t nZ = static_cast<int64_t>(dfVal);
     699          28 :                     WriteVarInt(m_abyGeomBuffer, nZ - nLastZ);
     700             : 
     701          28 :                     nLastZ = nZ;
     702             :                 }
     703             :             }
     704             : 
     705          29 :             if (bIsMeasured)
     706             :             {
     707           2 :                 int64_t nLastM = 0;
     708           8 :                 for (const auto *poPoint : *poMultiPoint)
     709             :                 {
     710           6 :                     const double dfM = poPoint->getM();
     711             : 
     712             :                     double dfVal =
     713           6 :                         std::round((dfM - poGeomField->GetMOrigin()) *
     714           6 :                                    poGeomField->GetMScale());
     715           6 :                     CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastM,
     716             :                                                    "Bad M value");
     717           6 :                     const int64_t nM = static_cast<int64_t>(dfVal);
     718           6 :                     WriteVarInt(m_abyGeomBuffer, nM - nLastM);
     719             : 
     720           6 :                     nLastM = nM;
     721             :                 }
     722             :             }
     723             : 
     724          29 :             return true;
     725             :         }
     726             : 
     727          74 :         case wkbLineString:
     728             :         case wkbCircularString:
     729             :         case wkbCompoundCurve:
     730             :         case wkbMultiLineString:
     731             :         case wkbMultiCurve:
     732             :         {
     733          74 :             m_abyCurvePart.clear();
     734          74 :             m_anNumberPointsPerPart.clear();
     735          74 :             m_adfX.clear();
     736          74 :             m_adfY.clear();
     737          74 :             m_adfZ.clear();
     738          74 :             m_adfM.clear();
     739             : 
     740          74 :             int nCurveDescrCount = 0;
     741             :             const auto ProcessCurve =
     742          88 :                 [this, bIs3D, bIsMeasured, &nCurveDescrCount,
     743        5122 :                  &ReserveXYZMArrays](const OGRCurve *poCurve)
     744             :             {
     745          88 :                 if (auto poCC = dynamic_cast<const OGRCompoundCurve *>(poCurve))
     746             :                 {
     747           5 :                     const size_t nSizeBefore = m_adfX.size();
     748             : 
     749           5 :                     std::size_t nTotalSize = 0;
     750          12 :                     for (const auto *poSubCurve : *poCC)
     751             :                     {
     752           7 :                         nTotalSize += poSubCurve->getNumPoints();
     753             :                     }
     754           5 :                     ReserveXYZMArrays(nTotalSize);
     755             : 
     756           5 :                     bool bFirstSubCurve = true;
     757          12 :                     for (const auto *poSubCurve : *poCC)
     758             :                     {
     759           7 :                         if (const auto poLS =
     760           7 :                                 dynamic_cast<const OGRLineString *>(poSubCurve))
     761             :                         {
     762           4 :                             const int nNumPoints = poLS->getNumPoints();
     763          11 :                             for (int i = (bFirstSubCurve ? 0 : 1);
     764          11 :                                  i < nNumPoints; ++i)
     765             :                             {
     766           7 :                                 m_adfX.push_back(poLS->getX(i));
     767           7 :                                 m_adfY.push_back(poLS->getY(i));
     768           7 :                                 if (bIs3D)
     769           1 :                                     m_adfZ.push_back(poLS->getZ(i));
     770           7 :                                 if (bIsMeasured)
     771           1 :                                     m_adfM.push_back(poLS->getM(i));
     772             :                             }
     773             :                         }
     774           3 :                         else if (const auto poCS =
     775           0 :                                      dynamic_cast<const OGRCircularString *>(
     776           3 :                                          poSubCurve))
     777             :                         {
     778           3 :                             const int nNumPoints = poCS->getNumPoints();
     779           9 :                             for (int i = 0; i < nNumPoints; i++)
     780             :                             {
     781           6 :                                 if (i > 0 || bFirstSubCurve)
     782             :                                 {
     783           6 :                                     m_adfX.push_back(poCS->getX(i));
     784           6 :                                     m_adfY.push_back(poCS->getY(i));
     785           6 :                                     if (bIs3D)
     786           2 :                                         m_adfZ.push_back(poCS->getZ(i));
     787           6 :                                     if (bIsMeasured)
     788           2 :                                         m_adfM.push_back(poCS->getM(i));
     789             :                                 }
     790           6 :                                 if (i + 1 < nNumPoints)
     791             :                                 {
     792           3 :                                     ++nCurveDescrCount;
     793           3 :                                     ++i;
     794           3 :                                     WriteVarUInt(m_abyCurvePart,
     795             :                                                  static_cast<uint32_t>(
     796           3 :                                                      m_adfX.size() - 1));
     797           3 :                                     WriteUInt8(m_abyCurvePart,
     798             :                                                EXT_SHAPE_SEGMENT_ARC);
     799           3 :                                     WriteFloat64(m_abyCurvePart, poCS->getX(i));
     800           3 :                                     WriteFloat64(m_abyCurvePart, poCS->getY(i));
     801           3 :                                     WriteUInt32(m_abyCurvePart,
     802             :                                                 (1 << 7));  // DefinedIP
     803             :                                 }
     804             :                             }
     805             :                         }
     806             :                         else
     807             :                         {
     808           0 :                             CPLAssert(false);
     809             :                         }
     810           7 :                         bFirstSubCurve = false;
     811             :                     }
     812          10 :                     m_anNumberPointsPerPart.push_back(
     813           5 :                         static_cast<uint32_t>(m_adfX.size() - nSizeBefore));
     814             :                 }
     815          83 :                 else if (const auto poLS =
     816          83 :                              dynamic_cast<const OGRLineString *>(poCurve))
     817             :                 {
     818          80 :                     const int nNumPoints = poLS->getNumPoints();
     819          80 :                     m_anNumberPointsPerPart.push_back(nNumPoints);
     820          80 :                     ReserveXYZMArrays(nNumPoints);
     821        1264 :                     for (int i = 0; i < nNumPoints; ++i)
     822             :                     {
     823        1184 :                         m_adfX.push_back(poLS->getX(i));
     824        1184 :                         m_adfY.push_back(poLS->getY(i));
     825        1184 :                         if (bIs3D)
     826          64 :                             m_adfZ.push_back(poLS->getZ(i));
     827        1184 :                         if (bIsMeasured)
     828          16 :                             m_adfM.push_back(poLS->getM(i));
     829             :                     }
     830             :                 }
     831           3 :                 else if (const auto poCS =
     832           3 :                              dynamic_cast<const OGRCircularString *>(poCurve))
     833             :                 {
     834           3 :                     const int nNumPoints = poCS->getNumPoints();
     835           3 :                     const size_t nSizeBefore = m_adfX.size();
     836           3 :                     ReserveXYZMArrays(nNumPoints);
     837           9 :                     for (int i = 0; i < nNumPoints; i++)
     838             :                     {
     839           6 :                         m_adfX.push_back(poCS->getX(i));
     840           6 :                         m_adfY.push_back(poCS->getY(i));
     841           6 :                         if (bIs3D)
     842           2 :                             m_adfZ.push_back(poCS->getZ(i));
     843           6 :                         if (bIsMeasured)
     844           2 :                             m_adfM.push_back(poCS->getM(i));
     845           6 :                         if (i + 1 < nNumPoints)
     846             :                         {
     847           3 :                             ++nCurveDescrCount;
     848           3 :                             ++i;
     849           3 :                             WriteVarUInt(
     850           3 :                                 m_abyCurvePart,
     851           3 :                                 static_cast<uint32_t>(m_adfX.size() - 1));
     852           3 :                             WriteUInt8(m_abyCurvePart, EXT_SHAPE_SEGMENT_ARC);
     853           3 :                             WriteFloat64(m_abyCurvePart, poCS->getX(i));
     854           3 :                             WriteFloat64(m_abyCurvePart, poCS->getY(i));
     855           3 :                             WriteUInt32(m_abyCurvePart, (1 << 7));  // DefinedIP
     856             :                         }
     857             :                     }
     858           6 :                     m_anNumberPointsPerPart.push_back(
     859           3 :                         static_cast<uint32_t>(m_adfX.size() - nSizeBefore));
     860             :                 }
     861             :                 else
     862             :                 {
     863           0 :                     CPLAssert(false);
     864             :                 }
     865          88 :             };
     866             : 
     867          74 :             if (eFlatType == wkbMultiLineString || eFlatType == wkbMultiCurve)
     868             :             {
     869          35 :                 const auto poMultiCurve = poGeom->toMultiCurve();
     870          84 :                 for (const auto *poCurve : *poMultiCurve)
     871             :                 {
     872          49 :                     ProcessCurve(poCurve);
     873          35 :                 }
     874             :             }
     875             :             else
     876             :             {
     877          39 :                 ProcessCurve(poGeom->toCurve());
     878             :             }
     879             : 
     880          74 :             if (nCurveDescrCount > 0)
     881             :             {
     882           5 :                 WriteVarUInt(m_abyGeomBuffer,
     883           5 :                              SHPT_GENERALPOLYLINE | (1U << 29) |  // has curves
     884           5 :                                  ((bIsMeasured ? 1U : 0U) << 30) |
     885           5 :                                  ((bIs3D ? 1U : 0U) << 31));
     886             :             }
     887          69 :             else if (bIs3D)
     888             :             {
     889          30 :                 if (bIsMeasured)
     890             :                 {
     891           3 :                     WriteUInt8(m_abyGeomBuffer,
     892             :                                static_cast<uint8_t>(SHPT_ARCZM));
     893             :                 }
     894             :                 else
     895             :                 {
     896          27 :                     WriteUInt8(m_abyGeomBuffer,
     897             :                                static_cast<uint8_t>(SHPT_ARCZ));
     898             :                 }
     899             :             }
     900             :             else
     901             :             {
     902          39 :                 if (bIsMeasured)
     903             :                 {
     904           3 :                     WriteUInt8(m_abyGeomBuffer,
     905             :                                static_cast<uint8_t>(SHPT_ARCM));
     906             :                 }
     907             :                 else
     908             :                 {
     909          36 :                     WriteUInt8(m_abyGeomBuffer, static_cast<uint8_t>(SHPT_ARC));
     910             :                 }
     911             :             }
     912             : 
     913          74 :             return WriteEndOfCurveOrSurface(nCurveDescrCount);
     914             :         }
     915             : 
     916         162 :         case wkbPolygon:
     917             :         case wkbCurvePolygon:
     918             :         case wkbMultiPolygon:
     919             :         case wkbMultiSurface:
     920             :         {
     921         162 :             m_abyCurvePart.clear();
     922         162 :             m_anNumberPointsPerPart.clear();
     923         162 :             m_adfX.clear();
     924         162 :             m_adfY.clear();
     925         162 :             m_adfZ.clear();
     926         162 :             m_adfM.clear();
     927             : 
     928         162 :             int nCurveDescrCount = 0;
     929             :             const auto ProcessSurface =
     930         175 :                 [this, bIs3D, bIsMeasured, &nCurveDescrCount,
     931       10165 :                  &ReserveXYZMArrays](const OGRSurface *poSurface)
     932             :             {
     933         175 :                 if (const auto poPolygon =
     934         175 :                         dynamic_cast<const OGRPolygon *>(poSurface))
     935             :                 {
     936         164 :                     bool bFirstRing = true;
     937             : 
     938         164 :                     std::size_t nTotalSize = 0;
     939         345 :                     for (const auto *poLS : *poPolygon)
     940             :                     {
     941         181 :                         nTotalSize += poLS->getNumPoints();
     942             :                     }
     943         164 :                     ReserveXYZMArrays(nTotalSize);
     944             : 
     945         345 :                     for (const auto *poLS : *poPolygon)
     946             :                     {
     947         181 :                         const int nNumPoints = poLS->getNumPoints();
     948         181 :                         m_anNumberPointsPerPart.push_back(nNumPoints);
     949         181 :                         const bool bIsClockwise = poLS->isClockwise();
     950         181 :                         const bool bReverseOrder = bFirstRing != bIsClockwise;
     951         181 :                         bFirstRing = false;
     952        2494 :                         for (int i = 0; i < nNumPoints; ++i)
     953             :                         {
     954        2313 :                             const int j =
     955        2313 :                                 bReverseOrder ? nNumPoints - 1 - i : i;
     956        2313 :                             m_adfX.push_back(poLS->getX(j));
     957        2313 :                             m_adfY.push_back(poLS->getY(j));
     958        2313 :                             if (bIs3D)
     959         162 :                                 m_adfZ.push_back(poLS->getZ(j));
     960        2313 :                             if (bIsMeasured)
     961          42 :                                 m_adfM.push_back(poLS->getM(j));
     962             :                         }
     963             :                     }
     964             :                 }
     965          11 :                 else if (const auto poCurvePoly =
     966          11 :                              dynamic_cast<const OGRCurvePolygon *>(poSurface))
     967             :                 {
     968          11 :                     bool bFirstRing = true;
     969          26 :                     for (const auto *poRing : *poCurvePoly)
     970             :                     {
     971          15 :                         const bool bIsClockwise = poRing->isClockwise();
     972          15 :                         const bool bReverseOrder = bFirstRing != bIsClockwise;
     973          15 :                         bFirstRing = false;
     974          15 :                         if (auto poCC =
     975          15 :                                 dynamic_cast<const OGRCompoundCurve *>(poRing))
     976             :                         {
     977           3 :                             const size_t nSizeBefore = m_adfX.size();
     978           3 :                             bool bFirstSubCurve = true;
     979           3 :                             const int nNumCurves = poCC->getNumCurves();
     980          12 :                             for (int iSubCurve = 0; iSubCurve < nNumCurves;
     981             :                                  ++iSubCurve)
     982             :                             {
     983          15 :                                 const OGRCurve *poSubCurve = poCC->getCurve(
     984           6 :                                     bReverseOrder ? nNumCurves - 1 - iSubCurve
     985             :                                                   : iSubCurve);
     986           9 :                                 if (auto poLS =
     987           0 :                                         dynamic_cast<const OGRLineString *>(
     988           9 :                                             poSubCurve))
     989             :                                 {
     990           6 :                                     const int nNumPoints = poLS->getNumPoints();
     991          18 :                                     for (int i = (bFirstSubCurve ? 0 : 1);
     992          18 :                                          i < nNumPoints; ++i)
     993             :                                     {
     994          12 :                                         const int j = bReverseOrder
     995          12 :                                                           ? nNumPoints - 1 - i
     996             :                                                           : i;
     997          12 :                                         m_adfX.push_back(poLS->getX(j));
     998          12 :                                         m_adfY.push_back(poLS->getY(j));
     999          12 :                                         if (bIs3D)
    1000           0 :                                             m_adfZ.push_back(poLS->getZ(j));
    1001          12 :                                         if (bIsMeasured)
    1002           0 :                                             m_adfM.push_back(poLS->getM(j));
    1003             :                                     }
    1004             :                                 }
    1005           0 :                                 else if (auto poCS = dynamic_cast<
    1006             :                                              const OGRCircularString *>(
    1007           3 :                                              poSubCurve))
    1008             :                                 {
    1009           3 :                                     const int nNumPoints = poCS->getNumPoints();
    1010           9 :                                     for (int i = 0; i < nNumPoints; i++)
    1011             :                                     {
    1012           6 :                                         if (i > 0 || bFirstSubCurve)
    1013             :                                         {
    1014           3 :                                             const int j =
    1015             :                                                 bReverseOrder
    1016           3 :                                                     ? nNumPoints - 1 - i
    1017             :                                                     : i;
    1018           3 :                                             m_adfX.push_back(poCS->getX(j));
    1019           3 :                                             m_adfY.push_back(poCS->getY(j));
    1020           3 :                                             if (bIs3D)
    1021           0 :                                                 m_adfZ.push_back(poCS->getZ(j));
    1022           3 :                                             if (bIsMeasured)
    1023           0 :                                                 m_adfM.push_back(poCS->getM(j));
    1024             :                                         }
    1025           6 :                                         if (i + 1 < nNumPoints)
    1026             :                                         {
    1027           3 :                                             ++nCurveDescrCount;
    1028           3 :                                             ++i;
    1029           3 :                                             const int j =
    1030             :                                                 bReverseOrder
    1031           3 :                                                     ? nNumPoints - 1 - i
    1032             :                                                     : i;
    1033           3 :                                             WriteVarUInt(
    1034           3 :                                                 m_abyCurvePart,
    1035             :                                                 static_cast<uint32_t>(
    1036           3 :                                                     m_adfX.size() - 1));
    1037           3 :                                             WriteUInt8(m_abyCurvePart,
    1038             :                                                        EXT_SHAPE_SEGMENT_ARC);
    1039           3 :                                             WriteFloat64(m_abyCurvePart,
    1040             :                                                          poCS->getX(j));
    1041           3 :                                             WriteFloat64(m_abyCurvePart,
    1042             :                                                          poCS->getY(j));
    1043           3 :                                             WriteUInt32(m_abyCurvePart,
    1044             :                                                         (1 << 7));  // DefinedIP
    1045             :                                         }
    1046             :                                     }
    1047             :                                 }
    1048             :                                 else
    1049             :                                 {
    1050           0 :                                     CPLAssert(false);
    1051             :                                 }
    1052           9 :                                 bFirstSubCurve = false;
    1053             :                             }
    1054           6 :                             m_anNumberPointsPerPart.push_back(
    1055           3 :                                 static_cast<uint32_t>(m_adfX.size() -
    1056             :                                                       nSizeBefore));
    1057             :                         }
    1058          12 :                         else if (const auto poLS =
    1059           0 :                                      dynamic_cast<const OGRLineString *>(
    1060          12 :                                          poRing))
    1061             :                         {
    1062           7 :                             const int nNumPoints = poLS->getNumPoints();
    1063           7 :                             m_anNumberPointsPerPart.push_back(nNumPoints);
    1064          38 :                             for (int i = 0; i < nNumPoints; ++i)
    1065             :                             {
    1066          31 :                                 const int j =
    1067          31 :                                     bReverseOrder ? nNumPoints - 1 - i : i;
    1068          31 :                                 m_adfX.push_back(poLS->getX(j));
    1069          31 :                                 m_adfY.push_back(poLS->getY(j));
    1070          31 :                                 if (bIs3D)
    1071           0 :                                     m_adfZ.push_back(poLS->getZ(j));
    1072          31 :                                 if (bIsMeasured)
    1073           0 :                                     m_adfM.push_back(poLS->getM(j));
    1074             :                             }
    1075             :                         }
    1076           5 :                         else if (const auto poCS =
    1077           0 :                                      dynamic_cast<const OGRCircularString *>(
    1078           5 :                                          poRing))
    1079             :                         {
    1080           5 :                             const int nNumPoints = poCS->getNumPoints();
    1081           5 :                             const size_t nSizeBefore = m_adfX.size();
    1082          20 :                             for (int i = 0; i < nNumPoints; i++)
    1083             :                             {
    1084          15 :                                 int j = bReverseOrder ? nNumPoints - 1 - i : i;
    1085          15 :                                 m_adfX.push_back(poCS->getX(j));
    1086          15 :                                 m_adfY.push_back(poCS->getY(j));
    1087          15 :                                 if (bIs3D)
    1088           3 :                                     m_adfZ.push_back(poCS->getZ(j));
    1089          15 :                                 if (bIsMeasured)
    1090           3 :                                     m_adfM.push_back(poCS->getM(j));
    1091          15 :                                 if (i + 1 < nNumPoints)
    1092             :                                 {
    1093          10 :                                     ++nCurveDescrCount;
    1094          10 :                                     ++i;
    1095          10 :                                     j = bReverseOrder ? nNumPoints - 1 - i : i;
    1096          10 :                                     WriteVarUInt(m_abyCurvePart,
    1097             :                                                  static_cast<uint32_t>(
    1098          10 :                                                      m_adfX.size() - 1));
    1099          10 :                                     WriteUInt8(m_abyCurvePart,
    1100             :                                                EXT_SHAPE_SEGMENT_ARC);
    1101          10 :                                     WriteFloat64(m_abyCurvePart, poCS->getX(j));
    1102          10 :                                     WriteFloat64(m_abyCurvePart, poCS->getY(j));
    1103          10 :                                     WriteUInt32(m_abyCurvePart,
    1104             :                                                 (1 << 7));  // DefinedIP
    1105             :                                 }
    1106             :                             }
    1107          10 :                             m_anNumberPointsPerPart.push_back(
    1108           5 :                                 static_cast<uint32_t>(m_adfX.size() -
    1109             :                                                       nSizeBefore));
    1110             :                         }
    1111             :                         else
    1112             :                         {
    1113           0 :                             CPLAssert(false);
    1114             :                         }
    1115             :                     }
    1116             :                 }
    1117             :                 else
    1118             :                 {
    1119           0 :                     CPLAssert(false);
    1120             :                 }
    1121         175 :             };
    1122             : 
    1123         162 :             if (eFlatType == wkbMultiPolygon || eFlatType == wkbMultiSurface)
    1124             :             {
    1125          88 :                 const auto poMultiSurface = poGeom->toMultiSurface();
    1126         189 :                 for (const auto *poSurface : *poMultiSurface)
    1127             :                 {
    1128         101 :                     ProcessSurface(poSurface);
    1129          88 :                 }
    1130             :             }
    1131             :             else
    1132             :             {
    1133          74 :                 ProcessSurface(poGeom->toSurface());
    1134             :             }
    1135             : 
    1136         162 :             if (nCurveDescrCount > 0)
    1137             :             {
    1138           7 :                 WriteVarUInt(m_abyGeomBuffer,
    1139           7 :                              SHPT_GENERALPOLYGON | (1U << 29) |  // has curves
    1140           7 :                                  ((bIsMeasured ? 1U : 0U) << 30) |
    1141           7 :                                  ((bIs3D ? 1U : 0U) << 31));
    1142             :             }
    1143         155 :             else if (bIs3D)
    1144             :             {
    1145          32 :                 if (bIsMeasured)
    1146             :                 {
    1147           5 :                     WriteUInt8(m_abyGeomBuffer,
    1148             :                                static_cast<uint8_t>(SHPT_POLYGONZM));
    1149             :                 }
    1150             :                 else
    1151             :                 {
    1152          27 :                     WriteUInt8(m_abyGeomBuffer,
    1153             :                                static_cast<uint8_t>(SHPT_POLYGONZ));
    1154             :                 }
    1155             :             }
    1156             :             else
    1157             :             {
    1158         123 :                 if (bIsMeasured)
    1159             :                 {
    1160           3 :                     WriteUInt8(m_abyGeomBuffer,
    1161             :                                static_cast<uint8_t>(SHPT_POLYGONM));
    1162             :                 }
    1163             :                 else
    1164             :                 {
    1165         120 :                     WriteUInt8(m_abyGeomBuffer,
    1166             :                                static_cast<uint8_t>(SHPT_POLYGON));
    1167             :                 }
    1168             :             }
    1169             : 
    1170         162 :             return WriteEndOfCurveOrSurface(nCurveDescrCount);
    1171             :         }
    1172             : 
    1173          23 :         case wkbTIN:
    1174             :         case wkbPolyhedralSurface:
    1175             :         case wkbGeometryCollection:
    1176             :         {
    1177          23 :             int nParts = 0;
    1178          46 :             std::vector<int> anPartStart;
    1179          46 :             std::vector<int> anPartType;
    1180          23 :             int nPoints = 0;
    1181          46 :             std::vector<OGRRawPoint> aoPoints;
    1182          46 :             std::vector<double> adfZ;
    1183             :             OGRErr eErr =
    1184          23 :                 OGRCreateMultiPatch(poGeom, TRUE, nParts, anPartStart,
    1185             :                                     anPartType, nPoints, aoPoints, adfZ);
    1186          23 :             if (eErr != OGRERR_NONE)
    1187           2 :                 return false;
    1188             : 
    1189          21 :             WriteUInt8(m_abyGeomBuffer, static_cast<uint8_t>(SHPT_MULTIPATCH));
    1190          21 :             WriteVarUInt(m_abyGeomBuffer, nPoints);
    1191          21 :             if (nPoints != 0)
    1192             :             {
    1193             :                 // Apparently we must write the size of the extended buffer
    1194             :                 // shape representation, even if we don't exactly follow this
    1195             :                 // format when writing to FileGDB files...
    1196          21 :                 int nShapeBufferSize =
    1197             :                     4;  // All types start with integer type number.
    1198          21 :                 nShapeBufferSize += 16 * 2;           // xy bbox.
    1199          21 :                 nShapeBufferSize += 4;                // nparts.
    1200          21 :                 nShapeBufferSize += 4;                // npoints.
    1201          21 :                 nShapeBufferSize += 4 * nParts;       // panPartStart[nparts].
    1202          21 :                 nShapeBufferSize += 4 * nParts;       // panPartType[nparts].
    1203          21 :                 nShapeBufferSize += 8 * 2 * nPoints;  // xy points.
    1204          21 :                 nShapeBufferSize += 16;               // z bbox.
    1205          21 :                 nShapeBufferSize += 8 * nPoints;      // z points.
    1206          21 :                 WriteVarUInt(m_abyGeomBuffer, nShapeBufferSize);
    1207             : 
    1208          21 :                 WriteVarUInt(m_abyGeomBuffer, nParts);
    1209             : 
    1210          21 :                 if (!EncodeEnvelope(m_abyGeomBuffer, poGeomField, poGeom))
    1211             :                 {
    1212           0 :                     return false;
    1213             :                 }
    1214             : 
    1215          62 :                 for (int i = 0; i < nParts - 1; i++)
    1216             :                 {
    1217          41 :                     WriteVarUInt(m_abyGeomBuffer,
    1218          41 :                                  anPartStart[i + 1] - anPartStart[i]);
    1219             :                 }
    1220             : 
    1221          83 :                 for (int i = 0; i < nParts; i++)
    1222             :                 {
    1223          62 :                     WriteVarUInt(m_abyGeomBuffer, anPartType[i]);
    1224             :                 }
    1225             : 
    1226             :                 {
    1227          21 :                     int64_t nLastX = 0;
    1228          21 :                     int64_t nLastY = 0;
    1229         278 :                     for (int i = 0; i < nPoints; ++i)
    1230             :                     {
    1231         257 :                         double dfVal = std::round(
    1232         257 :                             (aoPoints[i].x - poGeomField->GetXOrigin()) *
    1233         257 :                             poGeomField->GetXYScale());
    1234         257 :                         CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastX,
    1235             :                                                        "Cannot encode value");
    1236         257 :                         const int64_t nX = static_cast<int64_t>(dfVal);
    1237         257 :                         WriteVarInt(m_abyGeomBuffer, nX - nLastX);
    1238             : 
    1239         257 :                         dfVal = std::round(
    1240         257 :                             (aoPoints[i].y - poGeomField->GetYOrigin()) *
    1241         257 :                             poGeomField->GetXYScale());
    1242         257 :                         CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastY,
    1243             :                                                        "Cannot encode Y value");
    1244         257 :                         const int64_t nY = static_cast<int64_t>(dfVal);
    1245         257 :                         WriteVarInt(m_abyGeomBuffer, nY - nLastY);
    1246             : 
    1247         257 :                         nLastX = nX;
    1248         257 :                         nLastY = nY;
    1249             :                     }
    1250             :                 }
    1251             : 
    1252             :                 {
    1253          21 :                     int64_t nLastZ = 0;
    1254         278 :                     for (int i = 0; i < nPoints; ++i)
    1255             :                     {
    1256             :                         double dfVal =
    1257         257 :                             std::round((adfZ[i] - poGeomField->GetZOrigin()) *
    1258         257 :                                        poGeomField->GetZScale());
    1259         257 :                         CHECK_CAN_BE_ENCODED_ON_VARINT(dfVal, nLastZ,
    1260             :                                                        "Bad Z value");
    1261         257 :                         const int64_t nZ = static_cast<int64_t>(dfVal);
    1262         257 :                         WriteVarInt(m_abyGeomBuffer, nZ - nLastZ);
    1263             : 
    1264         257 :                         nLastZ = nZ;
    1265             :                     }
    1266             :                 }
    1267             :             }
    1268          21 :             return true;
    1269             :         }
    1270             : 
    1271           0 :         default:
    1272             :         {
    1273           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    1274             :                      "Unsupported geometry type");
    1275           0 :             return false;
    1276             :         }
    1277             :     }
    1278             : }
    1279             : 
    1280             : /************************************************************************/
    1281             : /*                           EncodeFeature()                            */
    1282             : /************************************************************************/
    1283             : 
    1284       33593 : bool FileGDBTable::EncodeFeature(const std::vector<OGRField> &asRawFields,
    1285             :                                  const OGRGeometry *poGeom, int iSkipField)
    1286             : {
    1287       33593 :     m_abyBuffer.clear();
    1288       33593 :     if (iSkipField >= 0 && m_apoFields[iSkipField]->IsNullable())
    1289          22 :         m_abyBuffer.resize(BIT_ARRAY_SIZE_IN_BYTES(m_nCountNullableFields - 1),
    1290          22 :                            0xFF);
    1291             :     else
    1292       33571 :         m_abyBuffer.resize(m_nNullableFieldsSizeInBytes, 0xFF);
    1293             : 
    1294       33593 :     if (asRawFields.size() != m_apoFields.size())
    1295             :     {
    1296           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Bad size of asRawFields");
    1297           0 :         return false;
    1298             :     }
    1299       33593 :     int iNullableField = 0;
    1300      172448 :     for (int i = 0; i < static_cast<int>(m_apoFields.size()); ++i)
    1301             :     {
    1302      138861 :         if (i == iSkipField)
    1303          26 :             continue;
    1304      138835 :         auto &poField = m_apoFields[i];
    1305      138835 :         if (poField->GetType() == FGFT_OBJECTID)
    1306             :         {
    1307             :             // Implicit field
    1308       25120 :             continue;
    1309             :         }
    1310      113715 :         if (i == m_iGeomField)
    1311             :         {
    1312        5081 :             if (poGeom == nullptr)
    1313             :             {
    1314         987 :                 if (!poField->IsNullable())
    1315             :                 {
    1316           1 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1317             :                              "Attempting to write null geometry in "
    1318             :                              "non-nullable geometry field");
    1319           1 :                     return false;
    1320             :                 }
    1321         986 :                 iNullableField++;
    1322         986 :                 continue;
    1323             :             }
    1324             : 
    1325             :             auto poGeomField =
    1326        4094 :                 cpl::down_cast<FileGDBGeomField *>(poField.get());
    1327        4094 :             if (!EncodeGeometry(poGeomField, poGeom))
    1328           2 :                 return false;
    1329        4092 :             if (!poGeom->IsEmpty())
    1330             :             {
    1331        4080 :                 OGREnvelope3D oEnvelope;
    1332        4080 :                 poGeom->getEnvelope(&oEnvelope);
    1333        4080 :                 m_bDirtyGeomFieldBBox = true;
    1334        4080 :                 if (std::isnan(poGeomField->GetXMin()))
    1335             :                 {
    1336         156 :                     poGeomField->SetXYMinMax(oEnvelope.MinX, oEnvelope.MinY,
    1337             :                                              oEnvelope.MaxX, oEnvelope.MaxY);
    1338         156 :                     poGeomField->SetZMinMax(oEnvelope.MinZ, oEnvelope.MaxZ);
    1339             :                 }
    1340             :                 else
    1341             :                 {
    1342        3924 :                     poGeomField->SetXYMinMax(
    1343        3924 :                         std::min(poGeomField->GetXMin(), oEnvelope.MinX),
    1344        3924 :                         std::min(poGeomField->GetYMin(), oEnvelope.MinY),
    1345        3924 :                         std::max(poGeomField->GetXMax(), oEnvelope.MaxX),
    1346        3924 :                         std::max(poGeomField->GetYMax(), oEnvelope.MaxY));
    1347        3924 :                     poGeomField->SetZMinMax(
    1348        3924 :                         std::min(poGeomField->GetZMin(), oEnvelope.MinZ),
    1349        7848 :                         std::max(poGeomField->GetZMax(), oEnvelope.MaxZ));
    1350             :                 }
    1351             :             }
    1352             : 
    1353        4092 :             if (m_abyGeomBuffer.size() + m_abyBuffer.size() >
    1354             :                 static_cast<size_t>(INT_MAX))
    1355             :             {
    1356           0 :                 CPLError(CE_Failure, CPLE_AppDefined, "Too large feature");
    1357           0 :                 return false;
    1358             :             }
    1359             : 
    1360        4092 :             WriteVarUInt(m_abyBuffer, m_abyGeomBuffer.size());
    1361        4092 :             m_abyBuffer.insert(m_abyBuffer.end(), m_abyGeomBuffer.begin(),
    1362        8184 :                                m_abyGeomBuffer.end());
    1363             : 
    1364        4092 :             if (poField->IsNullable())
    1365             :             {
    1366        4090 :                 m_abyBuffer[iNullableField / 8] &= ~(1 << (iNullableField % 8));
    1367        4090 :                 iNullableField++;
    1368             :             }
    1369        4092 :             continue;
    1370             :         }
    1371             : 
    1372      217268 :         if (OGR_RawField_IsNull(&asRawFields[i]) ||
    1373      108634 :             OGR_RawField_IsUnset(&asRawFields[i]))
    1374             :         {
    1375        6016 :             if (!poField->IsNullable())
    1376             :             {
    1377           3 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1378             :                          "Attempting to write null/empty field in non-nullable "
    1379             :                          "field");
    1380           3 :                 return false;
    1381             :             }
    1382        6013 :             iNullableField++;
    1383        6013 :             continue;
    1384             :         }
    1385             : 
    1386      102618 :         switch (poField->GetType())
    1387             :         {
    1388           0 :             case FGFT_UNDEFINED:
    1389             :             {
    1390           0 :                 CPLAssert(false);
    1391             :                 break;
    1392             :             }
    1393             : 
    1394        4216 :             case FGFT_INT16:
    1395             :             {
    1396        4216 :                 WriteInt16(m_abyBuffer,
    1397        4216 :                            static_cast<int16_t>(asRawFields[i].Integer));
    1398        4216 :                 break;
    1399             :             }
    1400             : 
    1401        4585 :             case FGFT_INT32:
    1402             :             {
    1403        4585 :                 WriteInt32(m_abyBuffer, asRawFields[i].Integer);
    1404        4585 :                 break;
    1405             :             }
    1406             : 
    1407         344 :             case FGFT_FLOAT32:
    1408             :             {
    1409         344 :                 WriteFloat32(m_abyBuffer,
    1410         344 :                              static_cast<float>(asRawFields[i].Real));
    1411         344 :                 break;
    1412             :             }
    1413             : 
    1414        4603 :             case FGFT_FLOAT64:
    1415             :             {
    1416        4603 :                 WriteFloat64(m_abyBuffer, asRawFields[i].Real);
    1417        4603 :                 break;
    1418             :             }
    1419             : 
    1420       57671 :             case FGFT_STRING:
    1421             :             case FGFT_XML:
    1422             :             {
    1423       57671 :                 if (m_bStringsAreUTF8 || poField->GetType() == FGFT_XML)
    1424             :                 {
    1425       57670 :                     const auto nLen = strlen(asRawFields[i].String);
    1426       57670 :                     WriteVarUInt(m_abyBuffer, nLen);
    1427       57670 :                     if (nLen > 0)
    1428             :                     {
    1429       56060 :                         if (nLen + m_abyBuffer.size() >
    1430             :                             static_cast<size_t>(INT_MAX))
    1431             :                         {
    1432           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
    1433             :                                      "Too large feature");
    1434           0 :                             return false;
    1435             :                         }
    1436           0 :                         m_abyBuffer.insert(m_abyBuffer.end(),
    1437             :                                            reinterpret_cast<const uint8_t *>(
    1438       56060 :                                                asRawFields[i].String),
    1439             :                                            reinterpret_cast<const uint8_t *>(
    1440      112120 :                                                asRawFields[i].String) +
    1441      112120 :                                                nLen);
    1442             :                     }
    1443             :                 }
    1444             :                 else
    1445             :                 {
    1446           1 :                     WriteUTF16String(m_abyBuffer, asRawFields[i].String,
    1447             :                                      NUMBER_OF_BYTES_ON_VARUINT);
    1448             :                 }
    1449       57671 :                 break;
    1450             :             }
    1451             : 
    1452         236 :             case FGFT_DATETIME:
    1453             :             case FGFT_DATE:
    1454             :             {
    1455         236 :                 WriteFloat64(m_abyBuffer,
    1456             :                              FileGDBOGRDateToDoubleDate(
    1457         236 :                                  &asRawFields[i], /* bConvertToUTC = */ true,
    1458         236 :                                  poField->IsHighPrecision()));
    1459         236 :                 break;
    1460             :             }
    1461             : 
    1462           0 :             case FGFT_OBJECTID:
    1463             :             {
    1464           0 :                 CPLAssert(false);  // not possible given above processing
    1465             :                 break;
    1466             :             }
    1467             : 
    1468           0 :             case FGFT_GEOMETRY:
    1469             :             {
    1470           0 :                 CPLAssert(false);  // not possible given above processing
    1471             :                 break;
    1472             :             }
    1473             : 
    1474         359 :             case FGFT_BINARY:
    1475             :             {
    1476         359 :                 WriteVarUInt(m_abyBuffer, asRawFields[i].Binary.nCount);
    1477         359 :                 if (asRawFields[i].Binary.nCount)
    1478             :                 {
    1479         359 :                     if (static_cast<size_t>(asRawFields[i].Binary.nCount) +
    1480         359 :                             m_abyBuffer.size() >
    1481             :                         static_cast<size_t>(INT_MAX))
    1482             :                     {
    1483           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1484             :                                  "Too large feature");
    1485           0 :                         return false;
    1486             :                     }
    1487           0 :                     m_abyBuffer.insert(m_abyBuffer.end(),
    1488         359 :                                        asRawFields[i].Binary.paData,
    1489         718 :                                        asRawFields[i].Binary.paData +
    1490         718 :                                            asRawFields[i].Binary.nCount);
    1491             :                 }
    1492         359 :                 break;
    1493             :             }
    1494             : 
    1495           0 :             case FGFT_RASTER:
    1496             :             {
    1497             :                 // Not handled for now
    1498           0 :                 CPLAssert(false);
    1499             :                 break;
    1500             :             }
    1501             : 
    1502       30564 :             case FGFT_GUID:
    1503             :             case FGFT_GLOBALID:
    1504             :             {
    1505       30564 :                 const auto nLen = strlen(asRawFields[i].String);
    1506       30564 :                 if (nLen != 38)
    1507             :                 {
    1508           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
    1509             :                              "Bad size for UUID field");
    1510           0 :                     return false;
    1511             :                 }
    1512       61128 :                 std::vector<unsigned> anVals(16);
    1513       30564 :                 sscanf(asRawFields[i].String,
    1514             :                        "{%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%"
    1515             :                        "02X%02X%02X%02X}",
    1516       30564 :                        &anVals[3], &anVals[2], &anVals[1], &anVals[0],
    1517       30564 :                        &anVals[5], &anVals[4], &anVals[7], &anVals[6],
    1518       30564 :                        &anVals[8], &anVals[9], &anVals[10], &anVals[11],
    1519       30564 :                        &anVals[12], &anVals[13], &anVals[14], &anVals[15]);
    1520      519588 :                 for (auto v : anVals)
    1521             :                 {
    1522      489024 :                     m_abyBuffer.push_back(static_cast<uint8_t>(v));
    1523             :                 }
    1524       30564 :                 break;
    1525             :             }
    1526             : 
    1527          22 :             case FGFT_INT64:
    1528             :             {
    1529          22 :                 WriteInt64(m_abyBuffer, asRawFields[i].Integer64);
    1530          22 :                 break;
    1531             :             }
    1532             : 
    1533           6 :             case FGFT_TIME:
    1534             :             {
    1535           6 :                 WriteFloat64(m_abyBuffer,
    1536           6 :                              FileGDBOGRTimeToDoubleTime(&asRawFields[i]));
    1537           6 :                 break;
    1538             :             }
    1539             : 
    1540          12 :             case FGFT_DATETIME_WITH_OFFSET:
    1541             :             {
    1542          12 :                 WriteFloat64(m_abyBuffer, FileGDBOGRDateToDoubleDate(
    1543          12 :                                               &asRawFields[i],
    1544             :                                               /* bConvertToUTC = */ false,
    1545             :                                               /* bIsHighPrecision = */ true));
    1546          12 :                 if (asRawFields[i].Date.TZFlag > 1)
    1547             :                 {
    1548          12 :                     WriteInt16(m_abyBuffer,
    1549             :                                static_cast<int16_t>(
    1550          12 :                                    (asRawFields[i].Date.TZFlag - 100) * 15));
    1551             :                 }
    1552             :                 else
    1553             :                 {
    1554           0 :                     WriteInt16(m_abyBuffer, 0);
    1555             :                 }
    1556          12 :                 break;
    1557             :             }
    1558             :         }
    1559             : 
    1560      102618 :         if (poField->IsNullable())
    1561             :         {
    1562       42568 :             m_abyBuffer[iNullableField / 8] &= ~(1 << (iNullableField % 8));
    1563       42568 :             iNullableField++;
    1564             :         }
    1565             :     }
    1566             : 
    1567       33587 :     if (m_abyBuffer.size() > static_cast<size_t>(INT_MAX))
    1568             :     {
    1569           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too large feature");
    1570           0 :         return false;
    1571             :     }
    1572             : 
    1573       33587 :     return true;
    1574             : }
    1575             : 
    1576             : /************************************************************************/
    1577             : /*                    SeekIntoTableXForNewFeature()                     */
    1578             : /************************************************************************/
    1579             : 
    1580       33503 : bool FileGDBTable::SeekIntoTableXForNewFeature(int nObjectID)
    1581             : {
    1582             :     int iCorrectedRow;
    1583       33503 :     bool bWriteEmptyPageAtEnd = false;
    1584       33503 :     const uint32_t nPageSize = TABLX_FEATURES_PER_PAGE * m_nTablxOffsetSize;
    1585       33503 :     const int nTotalRecordCount = static_cast<int>(m_nTotalRecordCount);
    1586             : 
    1587       33503 :     if (m_abyTablXBlockMap.empty())
    1588             :     {
    1589             :         // Is the OID to write in the current allocated pages, or in the next
    1590             :         // page ?
    1591       33473 :         if ((nObjectID - 1) / TABLX_FEATURES_PER_PAGE <=
    1592       33473 :             ((nTotalRecordCount == 0)
    1593       33473 :                  ? 0
    1594       31532 :                  : (1 + (nTotalRecordCount - 1) / TABLX_FEATURES_PER_PAGE)))
    1595             :         {
    1596       33451 :             iCorrectedRow = nObjectID - 1;
    1597       33451 :             const auto n1024BlocksPresentBefore = m_n1024BlocksPresent;
    1598       33451 :             m_n1024BlocksPresent =
    1599       33451 :                 DIV_ROUND_UP(std::max(nTotalRecordCount, nObjectID),
    1600             :                              TABLX_FEATURES_PER_PAGE);
    1601       33451 :             bWriteEmptyPageAtEnd =
    1602       33451 :                 m_n1024BlocksPresent > n1024BlocksPresentBefore;
    1603             :         }
    1604             :         else
    1605             :         {
    1606             :             // No, then we have a sparse table, and need to use a bitmap
    1607          22 :             m_abyTablXBlockMap.resize(
    1608          22 :                 (DIV_ROUND_UP(nObjectID, TABLX_FEATURES_PER_PAGE) + 7) / 8);
    1609          22 :             for (int i = 0;
    1610          26 :                  i < DIV_ROUND_UP(nTotalRecordCount, TABLX_FEATURES_PER_PAGE);
    1611             :                  ++i)
    1612           4 :                 m_abyTablXBlockMap[i / 8] |= (1 << (i % 8));
    1613          22 :             const int iBlock = (nObjectID - 1) / TABLX_FEATURES_PER_PAGE;
    1614          22 :             m_abyTablXBlockMap[iBlock / 8] |= (1 << (iBlock % 8));
    1615          22 :             iCorrectedRow =
    1616          22 :                 DIV_ROUND_UP(nTotalRecordCount, TABLX_FEATURES_PER_PAGE) *
    1617             :                     TABLX_FEATURES_PER_PAGE +
    1618          22 :                 ((nObjectID - 1) % TABLX_FEATURES_PER_PAGE);
    1619          22 :             m_n1024BlocksPresent++;
    1620          22 :             bWriteEmptyPageAtEnd = true;
    1621             :         }
    1622             :     }
    1623             :     else
    1624             :     {
    1625          30 :         const int iBlock = (nObjectID - 1) / TABLX_FEATURES_PER_PAGE;
    1626             : 
    1627          30 :         if (nObjectID <= nTotalRecordCount)
    1628             :         {
    1629          16 :             CPLAssert(iBlock / 8 < static_cast<int>(m_abyTablXBlockMap.size()));
    1630          16 :             if (TEST_BIT(m_abyTablXBlockMap.data(), iBlock) == 0)
    1631             :             {
    1632             :                 // This requires rewriting the gdbtablx file to insert
    1633             :                 // a new page
    1634          10 :                 GUInt32 nCountBlocksBefore = 0;
    1635          16 :                 for (int i = 0; i < iBlock; i++)
    1636           6 :                     nCountBlocksBefore +=
    1637           6 :                         TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
    1638             : 
    1639          10 :                 std::vector<GByte> abyTmp(nPageSize);
    1640          10 :                 uint64_t nOffset =
    1641          10 :                     TABLX_HEADER_SIZE + m_n1024BlocksPresent * nPageSize;
    1642          22 :                 for (int i = static_cast<int>(m_n1024BlocksPresent - 1);
    1643          22 :                      i >= static_cast<int>(nCountBlocksBefore); --i)
    1644             :                 {
    1645          12 :                     nOffset -= nPageSize;
    1646          12 :                     VSIFSeekL(m_fpTableX, nOffset, SEEK_SET);
    1647          12 :                     if (VSIFReadL(abyTmp.data(), nPageSize, 1, m_fpTableX) != 1)
    1648             :                     {
    1649           0 :                         CPLError(CE_Failure, CPLE_FileIO,
    1650             :                                  "Cannot read .gdtablx page at offset %u",
    1651             :                                  static_cast<uint32_t>(nOffset));
    1652           0 :                         return false;
    1653             :                     }
    1654          12 :                     VSIFSeekL(m_fpTableX, VSIFTellL(m_fpTableX), SEEK_SET);
    1655          12 :                     if (VSIFWriteL(abyTmp.data(), nPageSize, 1, m_fpTableX) !=
    1656             :                         1)
    1657             :                     {
    1658           0 :                         CPLError(CE_Failure, CPLE_FileIO,
    1659             :                                  "Cannot rewrite .gdtablx page of offset %u",
    1660             :                                  static_cast<uint32_t>(nOffset));
    1661           0 :                         return false;
    1662             :                     }
    1663             :                 }
    1664          10 :                 abyTmp.clear();
    1665          10 :                 abyTmp.resize(nPageSize);
    1666          10 :                 nOffset = TABLX_HEADER_SIZE +
    1667          10 :                           static_cast<uint64_t>(nCountBlocksBefore) * nPageSize;
    1668          10 :                 VSIFSeekL(m_fpTableX, nOffset, SEEK_SET);
    1669          10 :                 if (VSIFWriteL(abyTmp.data(), nPageSize, 1, m_fpTableX) != 1)
    1670             :                 {
    1671           0 :                     CPLError(CE_Failure, CPLE_FileIO,
    1672             :                              "Cannot write empty .gdtablx page of offset %u",
    1673             :                              static_cast<uint32_t>(nOffset));
    1674           0 :                     return false;
    1675             :                 }
    1676          10 :                 m_abyTablXBlockMap[iBlock / 8] |= (1 << (iBlock % 8));
    1677          10 :                 m_n1024BlocksPresent++;
    1678          10 :                 m_bDirtyTableXTrailer = true;
    1679          10 :                 m_nOffsetTableXTrailer = 0;
    1680          10 :                 m_nCountBlocksBeforeIBlockIdx = iBlock;
    1681          10 :                 m_nCountBlocksBeforeIBlockValue = nCountBlocksBefore;
    1682             :             }
    1683             :         }
    1684          28 :         else if (DIV_ROUND_UP(nObjectID, TABLX_FEATURES_PER_PAGE) >
    1685          14 :                  DIV_ROUND_UP(nTotalRecordCount, TABLX_FEATURES_PER_PAGE))
    1686             :         {
    1687           8 :             m_abyTablXBlockMap.resize(
    1688           8 :                 (DIV_ROUND_UP(nObjectID, TABLX_FEATURES_PER_PAGE) + 7) / 8);
    1689           8 :             m_abyTablXBlockMap[iBlock / 8] |= (1 << (iBlock % 8));
    1690           8 :             m_n1024BlocksPresent++;
    1691           8 :             bWriteEmptyPageAtEnd = true;
    1692             :         }
    1693             : 
    1694          30 :         GUInt32 nCountBlocksBefore = 0;
    1695             :         // In case of sequential access, optimization to avoid recomputing
    1696             :         // the number of blocks since the beginning of the map
    1697          30 :         if (iBlock >= m_nCountBlocksBeforeIBlockIdx)
    1698             :         {
    1699          30 :             nCountBlocksBefore = m_nCountBlocksBeforeIBlockValue;
    1700          70 :             for (int i = m_nCountBlocksBeforeIBlockIdx; i < iBlock; i++)
    1701          40 :                 nCountBlocksBefore +=
    1702          40 :                     TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
    1703             :         }
    1704             :         else
    1705             :         {
    1706           0 :             nCountBlocksBefore = 0;
    1707           0 :             for (int i = 0; i < iBlock; i++)
    1708           0 :                 nCountBlocksBefore +=
    1709           0 :                     TEST_BIT(m_abyTablXBlockMap.data(), i) != 0;
    1710             :         }
    1711             : 
    1712          30 :         m_nCountBlocksBeforeIBlockIdx = iBlock;
    1713          30 :         m_nCountBlocksBeforeIBlockValue = nCountBlocksBefore;
    1714          30 :         iCorrectedRow = nCountBlocksBefore * TABLX_FEATURES_PER_PAGE +
    1715          30 :                         ((nObjectID - 1) % TABLX_FEATURES_PER_PAGE);
    1716             :     }
    1717             : 
    1718       33503 :     if (bWriteEmptyPageAtEnd)
    1719             :     {
    1720        1971 :         m_bDirtyTableXTrailer = true;
    1721        1971 :         m_nOffsetTableXTrailer = 0;
    1722        1971 :         std::vector<GByte> abyTmp(nPageSize);
    1723        1971 :         uint64_t nOffset =
    1724             :             TABLX_HEADER_SIZE +
    1725        1971 :             static_cast<uint64_t>(m_n1024BlocksPresent - 1) * nPageSize;
    1726        1971 :         VSIFSeekL(m_fpTableX, nOffset, SEEK_SET);
    1727        1971 :         if (VSIFWriteL(abyTmp.data(), nPageSize, 1, m_fpTableX) != 1)
    1728             :         {
    1729           0 :             CPLError(CE_Failure, CPLE_FileIO,
    1730             :                      "Cannot write empty .gdtablx page of offset %u",
    1731             :                      static_cast<uint32_t>(nOffset));
    1732           0 :             return false;
    1733             :         }
    1734             :     }
    1735             : 
    1736       33503 :     const uint64_t nOffset =
    1737             :         TABLX_HEADER_SIZE +
    1738       33503 :         static_cast<uint64_t>(iCorrectedRow) * m_nTablxOffsetSize;
    1739       33503 :     VSIFSeekL(m_fpTableX, nOffset, SEEK_SET);
    1740             : 
    1741       33503 :     return true;
    1742             : }
    1743             : 
    1744             : /************************************************************************/
    1745             : /*                         WriteFeatureOffset()                         */
    1746             : /************************************************************************/
    1747             : 
    1748        2752 : void FileGDBTable::WriteFeatureOffset(uint64_t nFeatureOffset,
    1749             :                                       GByte *pabyBuffer)
    1750             : {
    1751        2752 :     CPL_LSBPTR64(&nFeatureOffset);
    1752        2752 :     memcpy(pabyBuffer, &nFeatureOffset, m_nTablxOffsetSize);
    1753        2752 : }
    1754             : 
    1755             : /************************************************************************/
    1756             : /*                         WriteFeatureOffset()                         */
    1757             : /************************************************************************/
    1758             : 
    1759       36179 : bool FileGDBTable::WriteFeatureOffset(uint64_t nFeatureOffset)
    1760             : {
    1761       36179 :     CPL_LSBPTR64(&nFeatureOffset);
    1762       36179 :     return VSIFWriteL(&nFeatureOffset, m_nTablxOffsetSize, 1, m_fpTableX) == 1;
    1763             : }
    1764             : 
    1765             : /************************************************************************/
    1766             : /*                           CreateFeature()                            */
    1767             : /************************************************************************/
    1768             : 
    1769       33511 : bool FileGDBTable::CreateFeature(const std::vector<OGRField> &asRawFields,
    1770             :                                  const OGRGeometry *poGeom, int *pnFID)
    1771             : {
    1772       33511 :     if (!m_bUpdate)
    1773           0 :         return false;
    1774             : 
    1775       33511 :     if (m_bDirtyFieldDescriptors && !WriteFieldDescriptors(m_fpTable))
    1776           0 :         return false;
    1777             : 
    1778             :     int nObjectID;
    1779       33511 :     if (pnFID != nullptr && *pnFID > 0)
    1780             :     {
    1781         130 :         if (*pnFID <= m_nTotalRecordCount &&
    1782          23 :             GetOffsetInTableForRow((*pnFID) - 1) != 0)
    1783             :         {
    1784           2 :             CPLError(
    1785             :                 CE_Failure, CPLE_AppDefined,
    1786             :                 "Cannot create feature of ID %d because one already exists",
    1787             :                 *pnFID);
    1788           2 :             return false;
    1789             :         }
    1790         105 :         nObjectID = *pnFID;
    1791             :     }
    1792             :     else
    1793             :     {
    1794       33404 :         if (m_nTotalRecordCount == std::numeric_limits<int>::max())
    1795             :         {
    1796           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1797             :                      "Maximum number of records per table reached");
    1798           0 :             return false;
    1799             :         }
    1800       33404 :         nObjectID = static_cast<int>(m_nTotalRecordCount + 1);
    1801             :     }
    1802             : 
    1803             :     try
    1804             :     {
    1805       33509 :         if (!EncodeFeature(asRawFields, poGeom, -1))
    1806           6 :             return false;
    1807             :     }
    1808           0 :     catch (const std::exception &e)
    1809             :     {
    1810           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1811           0 :         return false;
    1812             :     }
    1813             : 
    1814       33503 :     const uint64_t nFreeOffset = GetOffsetOfFreeAreaFromFreeList(
    1815       33503 :         static_cast<uint32_t>(sizeof(uint32_t) + m_abyBuffer.size()));
    1816       33503 :     if (nFreeOffset == OFFSET_MINUS_ONE)
    1817             :     {
    1818       30887 :         if (((m_nFileSize + m_abyBuffer.size()) >> (8 * m_nTablxOffsetSize)) !=
    1819             :             0)
    1820             :         {
    1821           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    1822             :                      "Maximum file size for m_nTablxOffsetSize = %u reached",
    1823             :                      m_nTablxOffsetSize);
    1824           0 :             return false;
    1825             :         }
    1826             :     }
    1827             : 
    1828       33503 :     if (!SeekIntoTableXForNewFeature(nObjectID))
    1829           0 :         return false;
    1830             : 
    1831       33503 :     if (nFreeOffset == OFFSET_MINUS_ONE)
    1832             :     {
    1833       30887 :         VSIFSeekL(m_fpTable, m_nFileSize, SEEK_SET);
    1834             :     }
    1835             :     else
    1836             :     {
    1837        2616 :         VSIFSeekL(m_fpTable, nFreeOffset, SEEK_SET);
    1838             :     }
    1839       33503 :     if (!WriteUInt32(m_fpTable, static_cast<uint32_t>(m_abyBuffer.size())))
    1840           0 :         return false;
    1841       67003 :     if (!m_abyBuffer.empty() &&
    1842       33500 :         VSIFWriteL(m_abyBuffer.data(), 1, m_abyBuffer.size(), m_fpTable) !=
    1843       33500 :             m_abyBuffer.size())
    1844             :     {
    1845           0 :         return false;
    1846             :     }
    1847             : 
    1848       33503 :     if (!WriteFeatureOffset(nFreeOffset == OFFSET_MINUS_ONE ? m_nFileSize
    1849             :                                                             : nFreeOffset))
    1850           0 :         return false;
    1851       33503 :     if (pnFID)
    1852        9512 :         *pnFID = nObjectID;
    1853             : 
    1854       33503 :     m_nRowBlobLength = static_cast<uint32_t>(m_abyBuffer.size());
    1855       33503 :     if (m_nRowBlobLength > m_nHeaderBufferMaxSize)
    1856             :     {
    1857        5671 :         m_nHeaderBufferMaxSize = m_nRowBlobLength;
    1858             :     }
    1859       33503 :     m_nRowBufferMaxSize = std::max(m_nRowBufferMaxSize, m_nRowBlobLength);
    1860       33503 :     if (nFreeOffset == OFFSET_MINUS_ONE)
    1861             :     {
    1862       30887 :         m_nFileSize += sizeof(uint32_t) + m_nRowBlobLength;
    1863             :     }
    1864             : 
    1865       33503 :     m_nTotalRecordCount =
    1866       33503 :         std::max(m_nTotalRecordCount, static_cast<int64_t>(nObjectID));
    1867       33503 :     m_nValidRecordCount++;
    1868             : 
    1869       33503 :     m_bDirtyHeader = true;
    1870       33503 :     m_bDirtyTableXHeader = true;
    1871             : 
    1872       33503 :     m_bDirtyIndices = true;
    1873             : 
    1874       33503 :     return true;
    1875             : }
    1876             : 
    1877             : /************************************************************************/
    1878             : /*                           UpdateFeature()                            */
    1879             : /************************************************************************/
    1880             : 
    1881          58 : bool FileGDBTable::UpdateFeature(int64_t nFID,
    1882             :                                  const std::vector<OGRField> &asRawFields,
    1883             :                                  const OGRGeometry *poGeom)
    1884             : {
    1885          58 :     if (!m_bUpdate)
    1886           0 :         return false;
    1887             : 
    1888          58 :     if (m_bDirtyFieldDescriptors && !WriteFieldDescriptors(m_fpTable))
    1889           0 :         return false;
    1890             : 
    1891          58 :     vsi_l_offset nOffsetInTableX = 0;
    1892             :     vsi_l_offset nOffsetInTable =
    1893          58 :         GetOffsetInTableForRow(nFID - 1, &nOffsetInTableX);
    1894          58 :     if (nOffsetInTable == 0)
    1895           0 :         return false;
    1896             : 
    1897             :     try
    1898             :     {
    1899          58 :         if (!EncodeFeature(asRawFields, poGeom, -1))
    1900           0 :             return false;
    1901             :     }
    1902           0 :     catch (const std::exception &e)
    1903             :     {
    1904           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1905           0 :         return false;
    1906             :     }
    1907             : 
    1908          58 :     VSIFSeekL(m_fpTable, nOffsetInTable, SEEK_SET);
    1909          58 :     uint32_t nOldFeatureSize = 0;
    1910          58 :     if (!ReadUInt32(m_fpTable, nOldFeatureSize))
    1911           0 :         return false;
    1912             : 
    1913          58 :     m_nCurRow = -1;
    1914             : 
    1915          58 :     if (m_abyBuffer.size() <= nOldFeatureSize)
    1916             :     {
    1917             :         // Can rewrite-in-place
    1918          31 :         VSIFSeekL(m_fpTable, nOffsetInTable, SEEK_SET);
    1919             : 
    1920          31 :         if (!WriteUInt32(m_fpTable, static_cast<uint32_t>(m_abyBuffer.size())))
    1921           0 :             return false;
    1922          62 :         if (!m_abyBuffer.empty() &&
    1923          31 :             VSIFWriteL(m_abyBuffer.data(), 1, m_abyBuffer.size(), m_fpTable) !=
    1924          31 :                 m_abyBuffer.size())
    1925             :         {
    1926           0 :             return false;
    1927             :         }
    1928             : 
    1929          31 :         m_nRowBlobLength = 0;
    1930          31 :         const size_t nSizeToBlank = nOldFeatureSize - m_abyBuffer.size();
    1931          31 :         if (nSizeToBlank > 0)
    1932             :         {
    1933             :             // Blank unused areas of the old feature
    1934          18 :             m_abyBuffer.clear();
    1935             :             try
    1936             :             {
    1937          18 :                 m_abyBuffer.resize(nSizeToBlank);
    1938          18 :                 CPL_IGNORE_RET_VAL(VSIFWriteL(m_abyBuffer.data(), 1,
    1939             :                                               m_abyBuffer.size(), m_fpTable));
    1940             :             }
    1941           0 :             catch (const std::exception &e)
    1942             :             {
    1943           0 :                 CPLDebug("OpenFileGDB",
    1944             :                          "Could not blank no longer part of feature: %s",
    1945           0 :                          e.what());
    1946             :             }
    1947             :         }
    1948             :     }
    1949             :     else
    1950             :     {
    1951             :         // Updated feature is larger than older one: check if there's a chunk
    1952             :         // we can reuse by examining the .freelist, and if not, append at end
    1953             :         // of .gdbtable
    1954          27 :         const uint64_t nFreeOffset = GetOffsetOfFreeAreaFromFreeList(
    1955          27 :             static_cast<uint32_t>(sizeof(uint32_t) + m_abyBuffer.size()));
    1956             : 
    1957          27 :         if (nFreeOffset == OFFSET_MINUS_ONE)
    1958             :         {
    1959          27 :             if (((m_nFileSize + m_abyBuffer.size()) >>
    1960          27 :                  (8 * m_nTablxOffsetSize)) != 0)
    1961             :             {
    1962           0 :                 CPLError(
    1963             :                     CE_Failure, CPLE_AppDefined,
    1964             :                     "Maximum file size for m_nTablxOffsetSize = %u reached",
    1965             :                     m_nTablxOffsetSize);
    1966           0 :                 return false;
    1967             :             }
    1968             : 
    1969          27 :             VSIFSeekL(m_fpTable, m_nFileSize, SEEK_SET);
    1970             :         }
    1971             :         else
    1972             :         {
    1973           0 :             VSIFSeekL(m_fpTable, nFreeOffset, SEEK_SET);
    1974             :         }
    1975             : 
    1976          27 :         if (!WriteUInt32(m_fpTable, static_cast<uint32_t>(m_abyBuffer.size())))
    1977           0 :             return false;
    1978          54 :         if (!m_abyBuffer.empty() &&
    1979          27 :             VSIFWriteL(m_abyBuffer.data(), 1, m_abyBuffer.size(), m_fpTable) !=
    1980          27 :                 m_abyBuffer.size())
    1981             :         {
    1982           0 :             return false;
    1983             :         }
    1984             : 
    1985             :         // Update offset of feature in .gdbtablx
    1986          27 :         VSIFSeekL(m_fpTableX, nOffsetInTableX, SEEK_SET);
    1987          27 :         if (!WriteFeatureOffset(nFreeOffset == OFFSET_MINUS_ONE ? m_nFileSize
    1988             :                                                                 : nFreeOffset))
    1989           0 :             return false;
    1990             : 
    1991          27 :         m_nRowBlobLength = static_cast<uint32_t>(m_abyBuffer.size());
    1992          27 :         if (m_nRowBlobLength > m_nHeaderBufferMaxSize)
    1993             :         {
    1994          18 :             m_bDirtyHeader = true;
    1995          18 :             m_nHeaderBufferMaxSize = m_nRowBlobLength;
    1996             :         }
    1997          27 :         m_nRowBufferMaxSize = std::max(m_nRowBufferMaxSize, m_nRowBlobLength);
    1998          27 :         if (nFreeOffset == OFFSET_MINUS_ONE)
    1999             :         {
    2000          27 :             m_bDirtyHeader = true;
    2001          27 :             m_nFileSize += sizeof(uint32_t) + m_nRowBlobLength;
    2002             :         }
    2003             : 
    2004          27 :         AddEntryToFreelist(nOffsetInTable, sizeof(uint32_t) + nOldFeatureSize);
    2005             : 
    2006             :         // Blank previously used area
    2007          27 :         VSIFSeekL(m_fpTable, nOffsetInTable, SEEK_SET);
    2008          27 :         const uint32_t nNegatedOldFeatureSize =
    2009          27 :             static_cast<uint32_t>(-static_cast<int>(nOldFeatureSize));
    2010          27 :         if (!WriteUInt32(m_fpTable, nNegatedOldFeatureSize))
    2011           0 :             return false;
    2012          27 :         m_abyBuffer.clear();
    2013             :         try
    2014             :         {
    2015          27 :             m_abyBuffer.resize(nOldFeatureSize);
    2016          27 :             CPL_IGNORE_RET_VAL(VSIFWriteL(m_abyBuffer.data(), 1,
    2017             :                                           m_abyBuffer.size(), m_fpTable));
    2018             :         }
    2019           0 :         catch (const std::exception &e)
    2020             :         {
    2021           0 :             CPLDebug("OpenFileGDB", "Could not blank old feature: %s",
    2022           0 :                      e.what());
    2023             :         }
    2024             :     }
    2025             : 
    2026          58 :     m_bDirtyIndices = true;
    2027             : 
    2028          58 :     return true;
    2029             : }
    2030             : 
    2031             : /************************************************************************/
    2032             : /*                           DeleteFeature()                            */
    2033             : /************************************************************************/
    2034             : 
    2035        2649 : bool FileGDBTable::DeleteFeature(int64_t nFID)
    2036             : {
    2037        2649 :     if (!m_bUpdate)
    2038           0 :         return false;
    2039             : 
    2040        2649 :     if (m_bDirtyFieldDescriptors && !WriteFieldDescriptors(m_fpTable))
    2041           0 :         return false;
    2042             : 
    2043        2649 :     vsi_l_offset nOffsetInTableX = 0;
    2044             :     vsi_l_offset nOffsetInTable =
    2045        2649 :         GetOffsetInTableForRow(nFID - 1, &nOffsetInTableX);
    2046        2649 :     if (nOffsetInTable == 0)
    2047           0 :         return false;
    2048             : 
    2049             :     // Set 0 as offset for the feature in .gdbtablx
    2050        2649 :     VSIFSeekL(m_fpTableX, nOffsetInTableX, SEEK_SET);
    2051        2649 :     if (!WriteFeatureOffset(0))
    2052           0 :         return false;
    2053             : 
    2054             :     // Negate the size of the feature in .gdbtable
    2055        2649 :     VSIFSeekL(m_fpTable, nOffsetInTable, SEEK_SET);
    2056        2649 :     uint32_t nFeatureSize = 0;
    2057        2649 :     if (!ReadUInt32(m_fpTable, nFeatureSize))
    2058           0 :         return false;
    2059        2649 :     if (nFeatureSize > static_cast<uint32_t>(INT_MAX))
    2060           0 :         return false;
    2061        2649 :     const int nDeletedFeatureSize =
    2062        2649 :         static_cast<uint32_t>(-static_cast<int32_t>(nFeatureSize));
    2063        2649 :     VSIFSeekL(m_fpTable, nOffsetInTable, SEEK_SET);
    2064        2649 :     if (!WriteUInt32(m_fpTable, nDeletedFeatureSize))
    2065           0 :         return false;
    2066             : 
    2067        2649 :     AddEntryToFreelist(nOffsetInTable, sizeof(uint32_t) + nFeatureSize);
    2068             : 
    2069             :     // Blank feature content
    2070        2649 :     m_nCurRow = -1;
    2071        2649 :     m_abyBuffer.clear();
    2072             :     try
    2073             :     {
    2074        2649 :         m_abyBuffer.resize(nFeatureSize);
    2075        2649 :         CPL_IGNORE_RET_VAL(
    2076        2649 :             VSIFWriteL(m_abyBuffer.data(), 1, m_abyBuffer.size(), m_fpTable));
    2077             :     }
    2078           0 :     catch (const std::exception &e)
    2079             :     {
    2080           0 :         CPLDebug("OpenFileGDB", "Could not blank deleted feature: %s",
    2081           0 :                  e.what());
    2082             :     }
    2083             : 
    2084        2649 :     m_nValidRecordCount--;
    2085        2649 :     m_bDirtyHeader = true;
    2086             : 
    2087        2649 :     m_bDirtyIndices = true;
    2088             : 
    2089        2649 :     return true;
    2090             : }
    2091             : 
    2092             : /************************************************************************/
    2093             : /*               WholeFileRewriter::~WholeFileRewriter()                */
    2094             : /************************************************************************/
    2095             : 
    2096          45 : FileGDBTable::WholeFileRewriter::~WholeFileRewriter()
    2097             : {
    2098          45 :     if (m_bIsInit)
    2099           6 :         Rollback();
    2100          45 : }
    2101             : 
    2102             : /************************************************************************/
    2103             : /*                      WholeFileRewriter::Begin()                      */
    2104             : /************************************************************************/
    2105             : 
    2106          45 : bool FileGDBTable::WholeFileRewriter::Begin()
    2107             : {
    2108          45 :     m_bOldDirtyIndices = m_oTable.m_bDirtyIndices;
    2109          45 :     m_oTable.RemoveIndices();
    2110          45 :     m_oTable.m_bDirtyIndices = false;
    2111          45 :     if (!m_oTable.Sync())
    2112           0 :         return false;
    2113             : 
    2114             :     // On Windows, we might have issues renaming opened files, even if trying
    2115             :     // to close them before, so updating opened files is less risky.
    2116          45 :     m_bModifyInPlace =
    2117          45 :         CPLTestBool(CPLGetConfigOption("OPENFILEGDB_MODIFY_IN_PLACE",
    2118             : #ifdef _WIN32
    2119             :                                        "YES"
    2120             : #else
    2121             :                                        "NO"
    2122             : #endif
    2123             :                                        ));
    2124             : 
    2125         135 :     m_osGdbTablx = CPLFormFilenameSafe(
    2126          90 :         CPLGetPathSafe(m_oTable.m_osFilename.c_str()).c_str(),
    2127         135 :         CPLGetBasenameSafe(m_oTable.m_osFilename.c_str()).c_str(), "gdbtablx");
    2128             : 
    2129          90 :     m_osBackupGdbTable = CPLResetExtensionSafe(m_oTable.m_osFilename.c_str(),
    2130          45 :                                                "_backup.gdbtable");
    2131             :     VSIStatBufL sStat;
    2132          45 :     if (VSIStatL(m_osBackupGdbTable.c_str(), &sStat) == 0)
    2133             :     {
    2134           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2135             :                  "Cannot create backup file %s as it already exists",
    2136             :                  m_osBackupGdbTable.c_str());
    2137           0 :         return false;
    2138             :     }
    2139             : 
    2140             :     m_osBackupGdbTablx =
    2141          45 :         CPLResetExtensionSafe(m_osGdbTablx.c_str(), "_backup.gdbtablx");
    2142             : 
    2143          45 :     if (m_bModifyInPlace)
    2144             :     {
    2145             :         // Create backups of .gdtable and .gdtablx if something wrongs happen
    2146          14 :         if (CPLCopyFile(m_osBackupGdbTable.c_str(),
    2147          28 :                         m_oTable.m_osFilename.c_str()) != 0)
    2148             :         {
    2149           0 :             VSIUnlink(m_osBackupGdbTable.c_str());
    2150           0 :             m_osBackupGdbTable.clear();
    2151           0 :             return false;
    2152             :         }
    2153             : 
    2154          14 :         if (CPLCopyFile(m_osBackupGdbTablx.c_str(), m_osGdbTablx.c_str()) != 0)
    2155             :         {
    2156           0 :             VSIUnlink(m_osBackupGdbTable.c_str());
    2157           0 :             VSIUnlink(m_osBackupGdbTablx.c_str());
    2158           0 :             m_osBackupGdbTable.clear();
    2159           0 :             m_osBackupGdbTablx.clear();
    2160           0 :             return false;
    2161             :         }
    2162             : 
    2163          14 :         m_osBackupValidFilename = m_oTable.m_osFilename + ".backup_valid";
    2164          14 :         VSILFILE *fp = VSIFOpenL(m_osBackupValidFilename.c_str(), "wb");
    2165          14 :         if (fp != nullptr)
    2166          14 :             VSIFCloseL(fp);
    2167             : 
    2168          14 :         m_fpOldGdbtable = VSIFOpenL(m_osBackupGdbTable.c_str(), "rb");
    2169          14 :         if (m_fpOldGdbtable == nullptr)
    2170             :         {
    2171           0 :             VSIUnlink(m_osBackupValidFilename.c_str());
    2172           0 :             VSIUnlink(m_osBackupGdbTable.c_str());
    2173           0 :             VSIUnlink(m_osBackupGdbTablx.c_str());
    2174           0 :             m_osBackupValidFilename.clear();
    2175           0 :             m_osBackupGdbTable.clear();
    2176           0 :             m_osBackupGdbTablx.clear();
    2177           0 :             return false;
    2178             :         }
    2179             : 
    2180          14 :         m_fpOldGdbtablx = m_oTable.m_fpTableX;
    2181          14 :         m_fpTable = m_oTable.m_fpTable;
    2182          14 :         m_fpTableX = m_oTable.m_fpTableX;
    2183             :     }
    2184             :     else
    2185             :     {
    2186          62 :         m_osTmpGdbTable = CPLResetExtensionSafe(m_oTable.m_osFilename.c_str(),
    2187          31 :                                                 "_compress.gdbtable");
    2188             :         m_osTmpGdbTablx =
    2189          31 :             CPLResetExtensionSafe(m_osGdbTablx.c_str(), "_compress.gdbtablx");
    2190             : 
    2191          31 :         m_fpOldGdbtable = m_oTable.m_fpTable;
    2192          31 :         m_fpOldGdbtablx = m_oTable.m_fpTableX;
    2193             : 
    2194          31 :         m_fpTable = VSIFOpenL(m_osTmpGdbTable.c_str(), "wb+");
    2195          31 :         if (m_fpTable == nullptr)
    2196             :         {
    2197           0 :             return false;
    2198             :         }
    2199             : 
    2200          31 :         m_fpTableX = VSIFOpenL(m_osTmpGdbTablx.c_str(), "wb+");
    2201          31 :         if (m_fpTableX == nullptr)
    2202             :         {
    2203           0 :             VSIFCloseL(m_fpTable);
    2204           0 :             m_fpTable = nullptr;
    2205           0 :             VSIUnlink(m_osTmpGdbTable.c_str());
    2206           0 :             return false;
    2207             :         }
    2208             : 
    2209          31 :         if (!m_oTable.WriteHeaderX(m_fpTableX))
    2210             :         {
    2211           0 :             VSIFCloseL(m_fpTable);
    2212           0 :             m_fpTable = nullptr;
    2213           0 :             VSIFCloseL(m_fpTableX);
    2214           0 :             m_fpTableX = nullptr;
    2215           0 :             VSIUnlink(m_osTmpGdbTable.c_str());
    2216           0 :             VSIUnlink(m_osTmpGdbTablx.c_str());
    2217           0 :             m_osTmpGdbTable.clear();
    2218           0 :             m_osTmpGdbTablx.clear();
    2219           0 :             return false;
    2220             :         }
    2221             :     }
    2222             : 
    2223          45 :     m_nOldFileSize = m_oTable.m_nFileSize;
    2224          45 :     m_nOldOffsetFieldDesc = m_oTable.m_nOffsetFieldDesc;
    2225          45 :     m_nOldFieldDescLength = m_oTable.m_nFieldDescLength;
    2226          45 :     m_bIsInit = true;
    2227             : 
    2228          45 :     if (!m_oTable.WriteHeader(m_fpTable))
    2229             :     {
    2230           0 :         Rollback();
    2231           0 :         return false;
    2232             :     }
    2233          45 :     if (m_bModifyInPlace)
    2234             :     {
    2235          14 :         VSIFTruncateL(m_fpTable, m_oTable.m_nFileSize);
    2236             :     }
    2237             : 
    2238             :     // Rewrite field descriptors
    2239          45 :     if (!m_oTable.Sync(m_fpTable, m_fpTableX))
    2240             :     {
    2241           0 :         Rollback();
    2242           0 :         return false;
    2243             :     }
    2244             : 
    2245          45 :     VSIFSeekL(m_fpTable, m_oTable.m_nFileSize, SEEK_SET);
    2246             : 
    2247          45 :     return true;
    2248             : }
    2249             : 
    2250             : /************************************************************************/
    2251             : /*                     WholeFileRewriter::Commit()                      */
    2252             : /************************************************************************/
    2253             : 
    2254          39 : bool FileGDBTable::WholeFileRewriter::Commit()
    2255             : {
    2256          39 :     m_oTable.m_bDirtyTableXTrailer = true;
    2257          39 :     m_oTable.m_bDirtyHeader = true;
    2258          39 :     if (!m_oTable.Sync(m_fpTable, m_fpTableX))
    2259             :     {
    2260           0 :         Rollback();
    2261           0 :         return false;
    2262             :     }
    2263             : 
    2264          39 :     if (m_bModifyInPlace)
    2265             :     {
    2266          12 :         VSIFCloseL(m_fpOldGdbtable);
    2267          12 :         VSIUnlink(m_osBackupValidFilename.c_str());
    2268          12 :         VSIUnlink(m_osBackupGdbTable.c_str());
    2269          12 :         VSIUnlink(m_osBackupGdbTablx.c_str());
    2270             :     }
    2271             :     else
    2272             :     {
    2273          27 :         VSIFCloseL(m_oTable.m_fpTable);
    2274          27 :         VSIFCloseL(m_oTable.m_fpTableX);
    2275          27 :         m_oTable.m_fpTable = nullptr;
    2276          27 :         m_oTable.m_fpTableX = nullptr;
    2277             : 
    2278             :         const bool bUseWIN32CodePath =
    2279          27 :             CPLTestBool(CPLGetConfigOption("OPENFILEGDB_SIMUL_WIN32",
    2280             : #ifdef _WIN32
    2281             :                                            "YES"
    2282             : #else
    2283             :                                            "NO"
    2284             : #endif
    2285             :                                            ));
    2286             : 
    2287          27 :         if (bUseWIN32CodePath)
    2288             :         {
    2289             :             // Renaming over an open file doesn't work on Windows
    2290          12 :             VSIFCloseL(m_fpTable);
    2291          12 :             VSIFCloseL(m_fpTableX);
    2292          12 :             m_fpTable = nullptr;
    2293          12 :             m_fpTableX = nullptr;
    2294             : 
    2295             :             // _wrename() on Windows doesn't honour POSIX semantics and forbids
    2296             :             // renaming over an existing file, hence create a temporary backup
    2297          12 :             if (VSIRename(m_oTable.m_osFilename.c_str(),
    2298          12 :                           m_osBackupGdbTable.c_str()) != 0)
    2299             :             {
    2300           0 :                 m_oTable.m_fpTable =
    2301           0 :                     VSIFOpenL(m_oTable.m_osFilename.c_str(), "rb+");
    2302           0 :                 m_oTable.m_fpTableX = VSIFOpenL(m_osGdbTablx.c_str(), "rb+");
    2303           0 :                 Rollback();
    2304           0 :                 return false;
    2305             :             }
    2306             : 
    2307          12 :             if (VSIRename(m_osGdbTablx.c_str(), m_osBackupGdbTablx.c_str()) !=
    2308             :                 0)
    2309             :             {
    2310           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    2311             :                          "Renaming of %s onto %s failed, but renaming of "
    2312             :                          "%s onto %s succeeded. Dataset in corrupt state",
    2313             :                          m_osGdbTablx.c_str(), m_osBackupGdbTablx.c_str(),
    2314           0 :                          m_oTable.m_osFilename.c_str(),
    2315             :                          m_osBackupGdbTable.c_str());
    2316           0 :                 Rollback();
    2317           0 :                 return false;
    2318             :             }
    2319             :         }
    2320             :         else
    2321             :         {
    2322          15 :             m_oTable.m_fpTable = m_fpTable;
    2323          15 :             m_oTable.m_fpTableX = m_fpTableX;
    2324             :         }
    2325             : 
    2326          27 :         if (VSIRename(m_osTmpGdbTable.c_str(), m_oTable.m_osFilename.c_str()) !=
    2327             :             0)
    2328             :         {
    2329           0 :             CPLError(CE_Failure, CPLE_FileIO, "Renaming of %s onto %s failed",
    2330           0 :                      m_osTmpGdbTable.c_str(), m_oTable.m_osFilename.c_str());
    2331           0 :             Rollback();
    2332           0 :             return false;
    2333             :         }
    2334             : 
    2335          27 :         if (VSIRename(m_osTmpGdbTablx.c_str(), m_osGdbTablx.c_str()) != 0)
    2336             :         {
    2337           0 :             CPLError(CE_Failure, CPLE_FileIO, "Renaming of %s onto %s failed",
    2338             :                      m_osTmpGdbTablx.c_str(), m_osGdbTablx.c_str());
    2339           0 :             Rollback();
    2340           0 :             return false;
    2341             :         }
    2342             : 
    2343          27 :         if (bUseWIN32CodePath)
    2344             :         {
    2345          24 :             m_oTable.m_fpTable =
    2346          12 :                 VSIFOpenL(m_oTable.m_osFilename.c_str(), "rb+");
    2347          12 :             m_oTable.m_fpTableX = VSIFOpenL(m_osGdbTablx.c_str(), "rb+");
    2348          12 :             VSIUnlink(m_osBackupGdbTable.c_str());
    2349          12 :             VSIUnlink(m_osBackupGdbTablx.c_str());
    2350             :         }
    2351             :     }
    2352             : 
    2353          39 :     m_oTable.DeleteFreeList();
    2354          39 :     if (m_bOldDirtyIndices)
    2355             :     {
    2356           8 :         m_oTable.m_bDirtyIndices = true;
    2357           8 :         m_oTable.Sync();
    2358             :     }
    2359             : 
    2360          39 :     m_bIsInit = false;
    2361             : 
    2362          39 :     return true;
    2363             : }
    2364             : 
    2365             : /************************************************************************/
    2366             : /*                    WholeFileRewriter::Rollback()                     */
    2367             : /************************************************************************/
    2368             : 
    2369           6 : void FileGDBTable::WholeFileRewriter::Rollback()
    2370             : {
    2371           6 :     CPLAssert(m_bIsInit);
    2372           6 :     m_bIsInit = false;
    2373             : 
    2374           6 :     if (m_bModifyInPlace)
    2375             :     {
    2376           2 :         VSIFCloseL(m_fpOldGdbtable);
    2377           2 :         m_fpOldGdbtable = nullptr;
    2378             : 
    2379             :         // Try to restore from backup files in case of failure
    2380           2 :         if (CPLCopyFile(m_oTable.m_osFilename.c_str(),
    2381           4 :                         m_osBackupGdbTable.c_str()) == 0 &&
    2382           2 :             CPLCopyFile(m_osGdbTablx.c_str(), m_osBackupGdbTablx.c_str()) == 0)
    2383             :         {
    2384           2 :             VSIUnlink(m_osBackupValidFilename.c_str());
    2385           2 :             VSIUnlink(m_osBackupGdbTable.c_str());
    2386           2 :             VSIUnlink(m_osBackupGdbTablx.c_str());
    2387             :         }
    2388             :         else
    2389             :         {
    2390           0 :             CPLError(CE_Failure, CPLE_AppDefined,
    2391             :                      "%s and %s are corrupted, and couldn't be restored from "
    2392             :                      "their backups %s and %s. You'll have to manually replace "
    2393             :                      "the former files by the latter ones.",
    2394           0 :                      m_oTable.m_osFilename.c_str(), m_osGdbTablx.c_str(),
    2395             :                      m_osBackupGdbTable.c_str(), m_osBackupGdbTablx.c_str());
    2396             :         }
    2397             :     }
    2398             :     else
    2399             :     {
    2400           4 :         VSIFCloseL(m_fpTable);
    2401           4 :         VSIFCloseL(m_fpTableX);
    2402           4 :         m_fpTable = nullptr;
    2403           4 :         m_fpTableX = nullptr;
    2404           4 :         VSIUnlink(m_osTmpGdbTable.c_str());
    2405           4 :         VSIUnlink(m_osTmpGdbTablx.c_str());
    2406             :     }
    2407             : 
    2408           6 :     m_oTable.m_nFileSize = m_nOldFileSize;
    2409           6 :     m_oTable.m_nOffsetFieldDesc = m_nOldOffsetFieldDesc;
    2410           6 :     m_oTable.m_nFieldDescLength = m_nOldFieldDescLength;
    2411             : 
    2412           6 :     m_oTable.m_bDirtyFieldDescriptors = false;
    2413           6 :     m_oTable.m_bDirtyTableXHeader = false;
    2414           6 :     m_oTable.m_bDirtyTableXTrailer = false;
    2415           6 :     m_oTable.m_bDirtyHeader = false;
    2416           6 : }
    2417             : 
    2418             : /************************************************************************/
    2419             : /*                               Repack()                               */
    2420             : /************************************************************************/
    2421             : 
    2422           6 : bool FileGDBTable::Repack(GDALProgressFunc pfnProgress, void *pProgressData)
    2423             : {
    2424           6 :     if (!m_bUpdate || !Sync())
    2425           0 :         return false;
    2426             : 
    2427           6 :     bool bRepackNeeded = false;
    2428           6 :     if (m_nOffsetFieldDesc > 40)
    2429             :     {
    2430             :         // If the field descriptor section is not at offset 40, it is possible
    2431             :         // that there's our "ghost area" there.
    2432           6 :         GByte abyBuffer[8] = {0};
    2433           6 :         VSIFSeekL(m_fpTable, 40, SEEK_SET);
    2434           6 :         VSIFReadL(abyBuffer, 1, sizeof(abyBuffer), m_fpTable);
    2435          12 :         if (!(memcmp(abyBuffer + 4, "GDAL", 4) == 0 &&
    2436           6 :               static_cast<uint64_t>(40) + sizeof(uint32_t) +
    2437           6 :                       GetUInt32(abyBuffer, 0) ==
    2438           6 :                   m_nOffsetFieldDesc))
    2439             :         {
    2440           0 :             CPLDebug("OpenFileGDB",
    2441             :                      "Repack(%s): field descriptors not at beginning of file",
    2442             :                      m_osFilename.c_str());
    2443           0 :             bRepackNeeded = true;
    2444             :         }
    2445             :     }
    2446             : 
    2447           6 :     uint64_t nExpectedOffset =
    2448           6 :         m_nOffsetFieldDesc + sizeof(uint32_t) + m_nFieldDescLength;
    2449             : 
    2450          12 :     std::vector<GByte> abyBufferOffsets;
    2451           6 :     abyBufferOffsets.resize(TABLX_FEATURES_PER_PAGE * m_nTablxOffsetSize);
    2452             : 
    2453           6 :     constexpr double RATIO_SCAN = 0.2;
    2454             : 
    2455             :     // Scan all features
    2456          12 :     for (uint32_t iPage = 0; !bRepackNeeded && iPage < m_n1024BlocksPresent;
    2457             :          ++iPage)
    2458             :     {
    2459           6 :         const vsi_l_offset nOffsetInTableX =
    2460           6 :             TABLX_HEADER_SIZE + m_nTablxOffsetSize *
    2461           6 :                                     static_cast<vsi_l_offset>(iPage) *
    2462             :                                     TABLX_FEATURES_PER_PAGE;
    2463           6 :         VSIFSeekL(m_fpTableX, nOffsetInTableX, SEEK_SET);
    2464           6 :         if (VSIFReadL(abyBufferOffsets.data(),
    2465           6 :                       m_nTablxOffsetSize * TABLX_FEATURES_PER_PAGE, 1,
    2466           6 :                       m_fpTableX) != 1)
    2467           0 :             return false;
    2468             : 
    2469           6 :         GByte *pabyBufferOffsets = abyBufferOffsets.data();
    2470        5127 :         for (int i = 0; i < TABLX_FEATURES_PER_PAGE;
    2471        5121 :              i++, pabyBufferOffsets += m_nTablxOffsetSize)
    2472             :         {
    2473        5122 :             const uint64_t nOffset = ReadFeatureOffset(pabyBufferOffsets);
    2474        5122 :             if (nOffset != 0)
    2475             :             {
    2476           9 :                 if (!bRepackNeeded && nOffset != nExpectedOffset)
    2477             :                 {
    2478           1 :                     bRepackNeeded = true;
    2479           1 :                     CPLDebug("OpenFileGDB",
    2480             :                              "Repack(%s): feature at offset " CPL_FRMT_GUIB
    2481             :                              " instead of " CPL_FRMT_GUIB ". Repack needed",
    2482             :                              m_osFilename.c_str(),
    2483             :                              static_cast<GUIntBig>(nOffset),
    2484             :                              static_cast<GUIntBig>(nExpectedOffset));
    2485           1 :                     break;
    2486             :                 }
    2487             : 
    2488             :                 // Read feature size
    2489           8 :                 VSIFSeekL(m_fpTable, nOffset, SEEK_SET);
    2490           8 :                 uint32_t nFeatureSize = 0;
    2491           8 :                 if (!ReadUInt32(m_fpTable, nFeatureSize))
    2492           0 :                     return false;
    2493             : 
    2494           8 :                 nExpectedOffset += sizeof(uint32_t);
    2495           8 :                 nExpectedOffset += nFeatureSize;
    2496             :             }
    2497             :         }
    2498             : 
    2499           6 :         bRepackNeeded =
    2500           1 :             (!pfnProgress ||
    2501           1 :              pfnProgress(RATIO_SCAN * static_cast<double>(iPage + 1) /
    2502           1 :                              m_n1024BlocksPresent,
    2503           7 :                          "", pProgressData)) &&
    2504             :             bRepackNeeded;
    2505             :     }
    2506             : 
    2507           6 :     if (!bRepackNeeded)
    2508             :     {
    2509           5 :         if (pfnProgress)
    2510           1 :             pfnProgress(1.0, "", pProgressData);
    2511             : 
    2512           5 :         if (m_nFileSize > nExpectedOffset)
    2513             :         {
    2514           1 :             CPLDebug("OpenFileGDB",
    2515             :                      "Deleted features at end of file. Truncating it");
    2516             : 
    2517           1 :             m_nFileSize = nExpectedOffset;
    2518           1 :             VSIFTruncateL(m_fpTable, m_nFileSize);
    2519           1 :             m_bDirtyHeader = true;
    2520             : 
    2521           1 :             DeleteFreeList();
    2522             : 
    2523           1 :             return Sync();
    2524             :         }
    2525             : 
    2526           4 :         CPLDebug("OpenFileGDB", "Repack(%s): file already compacted",
    2527             :                  m_osFilename.c_str());
    2528           4 :         return true;
    2529             :     }
    2530             : 
    2531           2 :     WholeFileRewriter oWholeFileRewriter(*this);
    2532           1 :     if (!oWholeFileRewriter.Begin())
    2533           0 :         return false;
    2534             : 
    2535           1 :     uint32_t nRowBufferMaxSize = 0;
    2536           1 :     m_nCurRow = -1;
    2537             : 
    2538             :     // Rewrite all features
    2539           2 :     for (uint32_t iPage = 0; iPage < m_n1024BlocksPresent; ++iPage)
    2540             :     {
    2541           1 :         const vsi_l_offset nOffsetInTableX =
    2542           1 :             TABLX_HEADER_SIZE + m_nTablxOffsetSize *
    2543           1 :                                     static_cast<vsi_l_offset>(iPage) *
    2544             :                                     TABLX_FEATURES_PER_PAGE;
    2545           1 :         VSIFSeekL(oWholeFileRewriter.m_fpOldGdbtablx, nOffsetInTableX,
    2546             :                   SEEK_SET);
    2547           1 :         if (VSIFReadL(abyBufferOffsets.data(),
    2548           1 :                       m_nTablxOffsetSize * TABLX_FEATURES_PER_PAGE, 1,
    2549           1 :                       oWholeFileRewriter.m_fpOldGdbtablx) != 1)
    2550           0 :             return false;
    2551             : 
    2552           1 :         GByte *pabyBufferOffsets = abyBufferOffsets.data();
    2553        1025 :         for (int i = 0; i < TABLX_FEATURES_PER_PAGE;
    2554        1024 :              i++, pabyBufferOffsets += m_nTablxOffsetSize)
    2555             :         {
    2556        1024 :             const uint64_t nOffset = ReadFeatureOffset(pabyBufferOffsets);
    2557        1024 :             if (nOffset != 0)
    2558             :             {
    2559             :                 // Read feature size
    2560           1 :                 VSIFSeekL(oWholeFileRewriter.m_fpOldGdbtable, nOffset,
    2561             :                           SEEK_SET);
    2562           1 :                 uint32_t nFeatureSize = 0;
    2563           1 :                 if (!ReadUInt32(oWholeFileRewriter.m_fpOldGdbtable,
    2564             :                                 nFeatureSize))
    2565           0 :                     return false;
    2566             : 
    2567             :                 // Read feature data
    2568           1 :                 if (nFeatureSize > m_abyBuffer.size())
    2569             :                 {
    2570             :                     try
    2571             :                     {
    2572           0 :                         m_abyBuffer.resize(nFeatureSize);
    2573             :                     }
    2574           0 :                     catch (const std::exception &e)
    2575             :                     {
    2576           0 :                         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    2577           0 :                         return false;
    2578             :                     }
    2579             :                 }
    2580           1 :                 if (VSIFReadL(m_abyBuffer.data(), nFeatureSize, 1,
    2581           1 :                               oWholeFileRewriter.m_fpOldGdbtable) != 1)
    2582           0 :                     return false;
    2583             : 
    2584             :                 // Update offset of updated feature
    2585           1 :                 WriteFeatureOffset(m_nFileSize, pabyBufferOffsets);
    2586             : 
    2587             :                 // Write feature size
    2588           1 :                 if (!WriteUInt32(oWholeFileRewriter.m_fpTable, nFeatureSize))
    2589           0 :                     return false;
    2590           1 :                 if (VSIFWriteL(m_abyBuffer.data(), nFeatureSize, 1,
    2591           1 :                                oWholeFileRewriter.m_fpTable) != 1)
    2592           0 :                     return false;
    2593             : 
    2594           1 :                 if (nFeatureSize > nRowBufferMaxSize)
    2595           1 :                     nRowBufferMaxSize = nFeatureSize;
    2596           1 :                 m_nFileSize += sizeof(uint32_t) + nFeatureSize;
    2597             :             }
    2598             :         }
    2599           1 :         VSIFSeekL(oWholeFileRewriter.m_fpTableX, nOffsetInTableX, SEEK_SET);
    2600           1 :         if (VSIFWriteL(abyBufferOffsets.data(),
    2601           1 :                        m_nTablxOffsetSize * TABLX_FEATURES_PER_PAGE, 1,
    2602           1 :                        oWholeFileRewriter.m_fpTableX) != 1)
    2603           0 :             return false;
    2604             : 
    2605           1 :         if (pfnProgress &&
    2606           0 :             !pfnProgress(RATIO_SCAN + (1.0 - RATIO_SCAN) *
    2607           0 :                                           static_cast<double>(iPage + 1) /
    2608           0 :                                           m_n1024BlocksPresent,
    2609             :                          "", pProgressData))
    2610             :         {
    2611           0 :             return false;
    2612             :         }
    2613             :     }
    2614             : 
    2615           1 :     m_nRowBufferMaxSize = nRowBufferMaxSize;
    2616           1 :     m_nHeaderBufferMaxSize = std::max(m_nFieldDescLength, m_nRowBufferMaxSize);
    2617             : 
    2618           1 :     return oWholeFileRewriter.Commit();
    2619             : }
    2620             : 
    2621             : /************************************************************************/
    2622             : /*                          RecomputeExtent()                           */
    2623             : /************************************************************************/
    2624             : 
    2625           2 : void FileGDBTable::RecomputeExtent()
    2626             : {
    2627           2 :     if (!m_bUpdate || m_iGeomField < 0)
    2628           0 :         return;
    2629             : 
    2630             :     // Scan all features
    2631           2 :     OGREnvelope sLayerEnvelope;
    2632           2 :     OGREnvelope sFeatureEnvelope;
    2633           6 :     for (int64_t iCurFeat = 0; iCurFeat < m_nTotalRecordCount; ++iCurFeat)
    2634             :     {
    2635           4 :         iCurFeat = GetAndSelectNextNonEmptyRow(iCurFeat);
    2636           4 :         if (iCurFeat < 0)
    2637           0 :             break;
    2638           4 :         const auto psGeomField = GetFieldValue(m_iGeomField);
    2639           4 :         if (psGeomField && GetFeatureExtent(psGeomField, &sFeatureEnvelope))
    2640             :         {
    2641           2 :             sLayerEnvelope.Merge(sFeatureEnvelope);
    2642             :         }
    2643             :     }
    2644             : 
    2645           2 :     m_bDirtyGeomFieldBBox = true;
    2646             :     auto poGeomField =
    2647           2 :         cpl::down_cast<FileGDBGeomField *>(m_apoFields[m_iGeomField].get());
    2648           2 :     if (sLayerEnvelope.IsInit())
    2649             :     {
    2650           1 :         poGeomField->SetXYMinMax(sLayerEnvelope.MinX, sLayerEnvelope.MinY,
    2651             :                                  sLayerEnvelope.MaxX, sLayerEnvelope.MaxY);
    2652             :     }
    2653             :     else
    2654             :     {
    2655           1 :         poGeomField->SetXYMinMax(
    2656             :             FileGDBGeomField::ESRI_NAN, FileGDBGeomField::ESRI_NAN,
    2657             :             FileGDBGeomField::ESRI_NAN, FileGDBGeomField::ESRI_NAN);
    2658             :     }
    2659             : }
    2660             : 
    2661             : } /* namespace OpenFileGDB */

Generated by: LCOV version 1.14