LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_mapfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 829 1118 74.2 %
Date: 2026-01-16 04:37:55 Functions: 54 61 88.5 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_mapfile.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABMAPFile class used to handle
       7             :  *           reading/writing of the .MAP files at the MapInfo object level
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999-2002, Daniel Morissette
      12             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  **********************************************************************/
      16             : 
      17             : #include "cpl_port.h"
      18             : #include "mitab.h"
      19             : 
      20             : #include <cassert>
      21             : #include <cstddef>
      22             : #include <algorithm>
      23             : #include <utility>
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_error.h"
      27             : #include "cpl_vsi.h"
      28             : #include "mitab_priv.h"
      29             : #include "ogr_feature.h"
      30             : 
      31             : /*=====================================================================
      32             :  *                      class TABMAPFile
      33             :  *====================================================================*/
      34             : 
      35             : /**********************************************************************
      36             :  *                   TABMAPFile::TABMAPFile()
      37             :  *
      38             :  * Constructor.
      39             :  **********************************************************************/
      40        1427 : TABMAPFile::TABMAPFile(const char *pszEncoding)
      41             :     : m_nMinTABVersion(300), m_pszFname(nullptr), m_fp(nullptr),
      42             :       m_eAccessMode(TABRead), m_poHeader(nullptr), m_poSpIndex(nullptr),
      43             :       // See bug 1732: Optimized spatial index produces broken files because of
      44             :       // the way CoordBlocks are split. For now we have to force using the quick
      45             :       // (old) spatial index mode by default until bug 1732 is fixed.
      46             :       m_bQuickSpatialIndexMode(TRUE), m_poIdIndex(nullptr),
      47             :       m_poCurObjBlock(nullptr), m_nCurObjPtr(-1), m_nCurObjType(TAB_GEOM_UNSET),
      48             :       m_nCurObjId(-1), m_poCurCoordBlock(nullptr), m_poToolDefTable(nullptr),
      49             :       m_XMinFilter(0), m_YMinFilter(0), m_XMaxFilter(0), m_YMaxFilter(0),
      50             :       m_bUpdated(FALSE), m_bLastOpWasRead(FALSE), m_bLastOpWasWrite(FALSE),
      51        1427 :       m_poSpIndexLeaf(nullptr), m_osEncoding(pszEncoding)
      52             : {
      53        1427 :     m_sMinFilter.x = 0;
      54        1427 :     m_sMinFilter.y = 0;
      55        1427 :     m_sMaxFilter.x = 0;
      56        1427 :     m_sMaxFilter.y = 0;
      57             : 
      58        1427 :     m_oBlockManager.SetName("MAP");
      59        1427 : }
      60             : 
      61             : /**********************************************************************
      62             :  *                   TABMAPFile::~TABMAPFile()
      63             :  *
      64             :  * Destructor.
      65             :  **********************************************************************/
      66        1427 : TABMAPFile::~TABMAPFile()
      67             : {
      68        1427 :     Close();
      69        1427 : }
      70             : 
      71             : /**********************************************************************
      72             :  *                   TABMAPFile::Open()
      73             :  *
      74             :  * Compatibility layer with new interface.
      75             :  * Return 0 on success, -1 in case of failure.
      76             :  **********************************************************************/
      77             : 
      78           0 : int TABMAPFile::Open(const char *pszFname, const char *pszAccess,
      79             :                      GBool bNoErrorMsg, int nBlockSizeForCreate)
      80             : {
      81             :     // cppcheck-suppress nullPointer
      82           0 :     if (STARTS_WITH_CI(pszAccess, "r"))
      83           0 :         return Open(pszFname, TABRead, bNoErrorMsg, nBlockSizeForCreate);
      84           0 :     else if (STARTS_WITH_CI(pszAccess, "w"))
      85           0 :         return Open(pszFname, TABWrite, bNoErrorMsg, nBlockSizeForCreate);
      86             :     else
      87             :     {
      88           0 :         CPLError(CE_Failure, CPLE_FileIO,
      89             :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
      90           0 :         return -1;
      91             :     }
      92             : }
      93             : 
      94             : /**********************************************************************
      95             :  *                   TABMAPFile::Open()
      96             :  *
      97             :  * Open a .MAP file, and initialize the structures to be ready to read
      98             :  * objects from it.
      99             :  *
     100             :  * Since .MAP and .ID files are optional, you can set bNoErrorMsg=TRUE to
     101             :  * disable the error message and receive an return value of 1 if file
     102             :  * cannot be opened.
     103             :  * In this case, only the methods MoveToObjId() and GetCurObjType() can
     104             :  * be used.  They will behave as if the .ID file contained only null
     105             :  * references, so all object will look like they have NONE geometries.
     106             :  *
     107             :  * Returns 0 on success, 1 when the .map file does not exist, -1 on error.
     108             :  **********************************************************************/
     109        1454 : int TABMAPFile::Open(const char *pszFname, TABAccess eAccess,
     110             :                      GBool bNoErrorMsg /* = FALSE */,
     111             :                      int nBlockSizeForCreate /* = 512 */)
     112             : {
     113        1454 :     CPLErrorReset();
     114             : 
     115        1454 :     VSILFILE *fp = nullptr;
     116        1454 :     TABRawBinBlock *poBlock = nullptr;
     117             : 
     118        1454 :     if (m_fp)
     119             :     {
     120           0 :         CPLError(CE_Failure, CPLE_FileIO,
     121             :                  "Open() failed: object already contains an open file");
     122           0 :         return -1;
     123             :     }
     124             : 
     125        1454 :     m_nMinTABVersion = 300;
     126        1454 :     m_fp = nullptr;
     127        1454 :     m_poHeader = nullptr;
     128        1454 :     m_poIdIndex = nullptr;
     129        1454 :     m_poSpIndex = nullptr;
     130        1454 :     m_poToolDefTable = nullptr;
     131        1454 :     m_eAccessMode = eAccess;
     132        1454 :     m_bUpdated = FALSE;
     133        1454 :     m_bLastOpWasRead = FALSE;
     134        1454 :     m_bLastOpWasWrite = FALSE;
     135             : 
     136        1454 :     if (m_eAccessMode == TABWrite &&
     137         127 :         (nBlockSizeForCreate < TAB_MIN_BLOCK_SIZE ||
     138         126 :          nBlockSizeForCreate > TAB_MAX_BLOCK_SIZE ||
     139         126 :          (nBlockSizeForCreate % TAB_MIN_BLOCK_SIZE) != 0))
     140             :     {
     141           1 :         CPLError(CE_Failure, CPLE_NotSupported,
     142             :                  "Open() failed: invalid block size: %d", nBlockSizeForCreate);
     143           1 :         return -1;
     144             :     }
     145             : 
     146             :     /*-----------------------------------------------------------------
     147             :      * Open file
     148             :      *----------------------------------------------------------------*/
     149        1453 :     const char *pszAccess = (eAccess == TABRead)    ? "rb"
     150             :                             : (eAccess == TABWrite) ? "wb+"
     151             :                                                     : "rb+";
     152        1453 :     fp = VSIFOpenL(pszFname, pszAccess);
     153             : 
     154        1453 :     m_oBlockManager.Reset();
     155             : 
     156        1453 :     if (fp != nullptr &&
     157        1450 :         (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite))
     158             :     {
     159             :         /*-----------------------------------------------------------------
     160             :          * Read access: try to read header block
     161             :          * First try with a 512 bytes block to check the .map version.
     162             :          * If it is version 500 or more then read again a 1024 bytes block
     163             :          *----------------------------------------------------------------*/
     164        1324 :         poBlock = TABCreateMAPBlockFromFile(fp, 0, 512, TRUE, m_eAccessMode);
     165             : 
     166        2648 :         if (poBlock && poBlock->GetBlockClass() == TABMAP_HEADER_BLOCK &&
     167        1324 :             cpl::down_cast<TABMAPHeaderBlock *>(poBlock)->m_nMAPVersionNumber >=
     168             :                 500)
     169             :         {
     170             :             // Version 500 or higher.  Read 1024 bytes block instead of 512
     171        1324 :             delete poBlock;
     172             :             poBlock =
     173        1324 :                 TABCreateMAPBlockFromFile(fp, 0, 1024, TRUE, m_eAccessMode);
     174             :         }
     175             : 
     176        2648 :         if (poBlock == nullptr ||
     177        1324 :             poBlock->GetBlockClass() != TABMAP_HEADER_BLOCK)
     178             :         {
     179           0 :             if (poBlock)
     180           0 :                 delete poBlock;
     181           0 :             poBlock = nullptr;
     182           0 :             VSIFCloseL(fp);
     183           0 :             CPLError(
     184             :                 CE_Failure, CPLE_FileIO,
     185             :                 "Open() failed: %s does not appear to be a valid .MAP file",
     186             :                 pszFname);
     187           0 :             return -1;
     188             :         }
     189        2648 :         m_oBlockManager.SetBlockSize(
     190        1324 :             cpl::down_cast<TABMAPHeaderBlock *>(poBlock)->m_nRegularBlockSize);
     191             :     }
     192         129 :     else if (fp != nullptr && m_eAccessMode == TABWrite)
     193             :     {
     194             :         /*-----------------------------------------------------------------
     195             :          * Write access: create a new header block
     196             :          * .MAP files of Version 500 and up appear to have a 1024 bytes
     197             :          * header.  The last 512 bytes are usually all zeros.
     198             :          *----------------------------------------------------------------*/
     199         126 :         m_poHeader = new TABMAPHeaderBlock(m_eAccessMode);
     200         126 :         poBlock = m_poHeader;
     201         126 :         poBlock->InitNewBlock(fp, nBlockSizeForCreate, 0);
     202             : 
     203         126 :         m_oBlockManager.SetBlockSize(m_poHeader->m_nRegularBlockSize);
     204         126 :         if (m_poHeader->m_nRegularBlockSize == 512)
     205         125 :             m_oBlockManager.SetLastPtr(512);
     206             :         else
     207           1 :             m_oBlockManager.SetLastPtr(0);
     208             : 
     209         126 :         m_bUpdated = TRUE;
     210             :     }
     211           3 :     else if (bNoErrorMsg)
     212             :     {
     213             :         /*-----------------------------------------------------------------
     214             :          * .MAP does not exist... produce no error message, but set
     215             :          * the class members so that MoveToObjId() and GetCurObjType()
     216             :          * can be used to return only NONE geometries.
     217             :          *----------------------------------------------------------------*/
     218           3 :         m_fp = nullptr;
     219           3 :         m_nCurObjType = TAB_GEOM_NONE;
     220             : 
     221             :         /* Create a false header block that will return default
     222             :          * values for projection and coordsys conversion stuff...
     223             :          */
     224           3 :         m_poHeader = new TABMAPHeaderBlock(m_eAccessMode);
     225           3 :         m_poHeader->InitNewBlock(nullptr, 512, 0);
     226             : 
     227           3 :         return 1;
     228             :     }
     229             :     else
     230             :     {
     231           0 :         CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s", pszFname);
     232           0 :         return -1;
     233             :     }
     234             : 
     235             :     /*-----------------------------------------------------------------
     236             :      * File appears to be valid... set the various class members
     237             :      *----------------------------------------------------------------*/
     238        1450 :     m_fp = fp;
     239        1450 :     m_poHeader = cpl::down_cast<TABMAPHeaderBlock *>(poBlock);
     240        1450 :     m_pszFname = CPLStrdup(pszFname);
     241             : 
     242             :     /*-----------------------------------------------------------------
     243             :      * Create a TABMAPObjectBlock, in READ mode only or in UPDATE mode
     244             :      * if there's an object
     245             :      *
     246             :      * In WRITE mode, the object block will be created only when needed.
     247             :      * We do not create the object block in the open() call because
     248             :      * files that contained only "NONE" geometries ended up with empty
     249             :      * object and spatial index blocks.
     250             :      *----------------------------------------------------------------*/
     251             : 
     252        1450 :     if (m_eAccessMode == TABRead ||
     253        1201 :         (m_eAccessMode == TABReadWrite && m_poHeader->m_nFirstIndexBlock != 0))
     254             :     {
     255        1297 :         m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
     256        1297 :         m_poCurObjBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize);
     257             :     }
     258             :     else
     259             :     {
     260         153 :         m_poCurObjBlock = nullptr;
     261             :     }
     262             : 
     263             :     /*-----------------------------------------------------------------
     264             :      * Open associated .ID (object id index) file
     265             :      *----------------------------------------------------------------*/
     266        1450 :     m_poIdIndex = new TABIDFile;
     267        1450 :     if (m_poIdIndex->Open(pszFname, m_eAccessMode) != 0)
     268             :     {
     269             :         // Failed... an error has already been reported
     270           0 :         Close();
     271           0 :         return -1;
     272             :     }
     273             : 
     274             :     /*-----------------------------------------------------------------
     275             :      * Default Coord filter is the MBR of the whole file
     276             :      * This is currently unused but could eventually be used to handle
     277             :      * spatial filters more efficiently.
     278             :      *----------------------------------------------------------------*/
     279        1450 :     if (m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite)
     280             :     {
     281        1324 :         ResetCoordFilter();
     282             :     }
     283             : 
     284             :     /*-----------------------------------------------------------------
     285             :      * We could scan a file through its quad tree index... but we don't!
     286             :      *
     287             :      * In read mode, we just ignore the spatial index.
     288             :      *
     289             :      * In write mode the index is created and maintained as new object
     290             :      * blocks are added inside CommitObjBlock().
     291             :      *----------------------------------------------------------------*/
     292        1450 :     m_poSpIndex = nullptr;
     293             : 
     294        1450 :     if (m_eAccessMode == TABReadWrite)
     295             :     {
     296             :         /* We don't allow quick mode in read/write mode */
     297        1075 :         m_bQuickSpatialIndexMode = FALSE;
     298             : 
     299        1075 :         if (m_poHeader->m_nFirstIndexBlock != 0)
     300             :         {
     301        1048 :             poBlock = GetIndexObjectBlock(m_poHeader->m_nFirstIndexBlock);
     302        2096 :             if (poBlock == nullptr ||
     303        1048 :                 (poBlock->GetBlockType() != TABMAP_INDEX_BLOCK &&
     304           4 :                  poBlock->GetBlockType() != TABMAP_OBJECT_BLOCK))
     305             :             {
     306           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     307             :                          "Cannot find first index block at offset %d",
     308           0 :                          m_poHeader->m_nFirstIndexBlock);
     309           0 :                 delete poBlock;
     310             :             }
     311        1048 :             else if (poBlock->GetBlockType() == TABMAP_INDEX_BLOCK)
     312             :             {
     313        1044 :                 m_poSpIndex = cpl::down_cast<TABMAPIndexBlock *>(poBlock);
     314        1044 :                 m_poSpIndex->SetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin,
     315        1044 :                                     m_poHeader->m_nXMax, m_poHeader->m_nYMax);
     316             :             }
     317             :             else /* if( poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK ) */
     318             :             {
     319             :                 /* This can happen if the file created by MapInfo contains just
     320             :                  */
     321             :                 /* a few objects */
     322           4 :                 delete poBlock;
     323             :             }
     324             :         }
     325             :     }
     326             : 
     327             :     /*-----------------------------------------------------------------
     328             :      * Initialization of the Drawing Tools table will be done automatically
     329             :      * as Read/Write calls are done later.
     330             :      *----------------------------------------------------------------*/
     331        1450 :     m_poToolDefTable = nullptr;
     332             : 
     333        1450 :     if (m_eAccessMode == TABReadWrite)
     334             :     {
     335        1075 :         InitDrawingTools();
     336             :     }
     337             : 
     338        1450 :     if (m_eAccessMode == TABReadWrite)
     339             :     {
     340             :         VSIStatBufL sStatBuf;
     341        1075 :         if (VSIStatL(m_pszFname, &sStatBuf) != 0)
     342             :         {
     343           0 :             Close();
     344           0 :             return -1;
     345             :         }
     346        1075 :         m_oBlockManager.SetLastPtr(static_cast<int>(
     347        1075 :             ((sStatBuf.st_size - 1) / m_poHeader->m_nRegularBlockSize) *
     348        1075 :             m_poHeader->m_nRegularBlockSize));
     349             : 
     350             :         /* Read chain of garbage blocks */
     351        1075 :         if (m_poHeader->m_nFirstGarbageBlock != 0)
     352             :         {
     353           0 :             int nCurGarbBlock = m_poHeader->m_nFirstGarbageBlock;
     354           0 :             m_oBlockManager.PushGarbageBlockAsLast(nCurGarbBlock);
     355             :             while (true)
     356             :             {
     357           0 :                 GUInt16 nBlockType = 0;
     358           0 :                 int nNextGarbBlockPtr = 0;
     359           0 :                 if (VSIFSeekL(fp, static_cast<vsi_l_offset>(nCurGarbBlock),
     360           0 :                               SEEK_SET) != 0 ||
     361           0 :                     VSIFReadL(&nBlockType, sizeof(nBlockType), 1, fp) != 1 ||
     362           0 :                     VSIFReadL(&nNextGarbBlockPtr, sizeof(nNextGarbBlockPtr), 1,
     363             :                               fp) != 1)
     364             :                 {
     365           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     366             :                              "Cannot read garbage block at offset %d",
     367             :                              nCurGarbBlock);
     368           0 :                     break;
     369             :                 }
     370           0 :                 if (nBlockType != TABMAP_GARB_BLOCK)
     371             :                 {
     372           0 :                     CPLError(CE_Failure, CPLE_AppDefined,
     373             :                              "Got block type (%d) instead of %d at offset %d",
     374             :                              nBlockType, TABMAP_GARB_BLOCK, nCurGarbBlock);
     375             :                 }
     376           0 :                 if (nNextGarbBlockPtr == 0)
     377           0 :                     break;
     378           0 :                 nCurGarbBlock = nNextGarbBlockPtr;
     379           0 :                 m_oBlockManager.PushGarbageBlockAsLast(nCurGarbBlock);
     380           0 :             }
     381             :         }
     382             :     }
     383             : 
     384             :     /*-----------------------------------------------------------------
     385             :      * Make sure all previous calls succeeded.
     386             :      *----------------------------------------------------------------*/
     387        1450 :     if (CPLGetLastErrorType() == CE_Failure)
     388             :     {
     389             :         // Open Failed... an error has already been reported
     390           0 :         Close();
     391           0 :         return -1;
     392             :     }
     393             : 
     394        1450 :     return 0;
     395             : }
     396             : 
     397             : /**********************************************************************
     398             :  *                   TABMAPFile::Close()
     399             :  *
     400             :  * Close current file, and release all memory used.
     401             :  *
     402             :  * Returns 0 on success, -1 on error.
     403             :  **********************************************************************/
     404        2881 : int TABMAPFile::Close()
     405             : {
     406             :     // Check if file is opened... it is possible to have a fake header
     407             :     // without an actual file attached to it.
     408        2881 :     if (m_fp == nullptr && m_poHeader == nullptr)
     409        1428 :         return 0;
     410             : 
     411             :     /*----------------------------------------------------------------
     412             :      * Write access: commit latest changes to the file.
     413             :      *---------------------------------------------------------------*/
     414        1453 :     if (m_eAccessMode != TABRead)
     415             :     {
     416        1202 :         SyncToDisk();
     417             :     }
     418             : 
     419             :     // Delete all structures
     420        1453 :     if (m_poHeader)
     421        1453 :         delete m_poHeader;
     422        1453 :     m_poHeader = nullptr;
     423             : 
     424        1453 :     if (m_poIdIndex)
     425             :     {
     426        1450 :         m_poIdIndex->Close();
     427        1450 :         delete m_poIdIndex;
     428        1450 :         m_poIdIndex = nullptr;
     429             :     }
     430             : 
     431        1453 :     if (m_poCurObjBlock)
     432             :     {
     433        1391 :         delete m_poCurObjBlock;
     434        1391 :         m_poCurObjBlock = nullptr;
     435        1391 :         m_nCurObjPtr = -1;
     436        1391 :         m_nCurObjType = TAB_GEOM_UNSET;
     437        1391 :         m_nCurObjId = -1;
     438             :     }
     439             : 
     440        1453 :     if (m_poCurCoordBlock)
     441             :     {
     442          62 :         delete m_poCurCoordBlock;
     443          62 :         m_poCurCoordBlock = nullptr;
     444             :     }
     445             : 
     446        1453 :     if (m_poSpIndex)
     447             :     {
     448        1146 :         delete m_poSpIndex;
     449        1146 :         m_poSpIndex = nullptr;
     450        1146 :         m_poSpIndexLeaf = nullptr;
     451             :     }
     452             : 
     453        1453 :     if (m_poToolDefTable)
     454             :     {
     455        1332 :         delete m_poToolDefTable;
     456        1332 :         m_poToolDefTable = nullptr;
     457             :     }
     458             : 
     459             :     // Close file
     460        1453 :     if (m_fp)
     461        1450 :         VSIFCloseL(m_fp);
     462        1453 :     m_fp = nullptr;
     463             : 
     464        1453 :     CPLFree(m_pszFname);
     465        1453 :     m_pszFname = nullptr;
     466             : 
     467        1453 :     return 0;
     468             : }
     469             : 
     470             : /************************************************************************/
     471             : /*                         GetFileSize()                                */
     472             : /************************************************************************/
     473             : 
     474           0 : GUInt32 TABMAPFile::GetFileSize()
     475             : {
     476           0 :     if (!m_fp)
     477           0 :         return 0;
     478           0 :     vsi_l_offset nCurPos = VSIFTellL(m_fp);
     479           0 :     VSIFSeekL(m_fp, 0, SEEK_END);
     480           0 :     vsi_l_offset nSize = VSIFTellL(m_fp);
     481           0 :     VSIFSeekL(m_fp, nCurPos, SEEK_SET);
     482           0 :     return nSize > UINT_MAX ? UINT_MAX : static_cast<GUInt32>(nSize);
     483             : }
     484             : 
     485             : /************************************************************************/
     486             : /*                            SyncToDisk()                             */
     487             : /************************************************************************/
     488             : 
     489        1325 : int TABMAPFile::SyncToDisk()
     490             : {
     491        1325 :     if (m_eAccessMode == TABRead)
     492             :     {
     493           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     494             :                  "SyncToDisk() can be used only with Write access.");
     495           0 :         return -1;
     496             :     }
     497             : 
     498        1325 :     if (!m_bUpdated)
     499         120 :         return 0;
     500             : 
     501             :     // Start by committing current object and coord blocks
     502             :     // Nothing happens if none has been created yet.
     503        1205 :     if (CommitObjAndCoordBlocks(FALSE) != 0)
     504           0 :         return -1;
     505             : 
     506             :     // Write the drawing tools definitions now.
     507        1205 :     if (CommitDrawingTools() != 0)
     508           0 :         return -1;
     509             : 
     510             :     // Commit spatial index blocks
     511        1205 :     if (CommitSpatialIndex() != 0)
     512           0 :         return -1;
     513             : 
     514             :     // Update header fields and commit
     515        1205 :     if (m_poHeader)
     516             :     {
     517             :         // OK, with V450 files, objects are not limited to 32k nodes
     518             :         // any more, and this means that m_nMaxCoordBufSize can become
     519             :         // huge, and actually more huge than can be held in memory.
     520             :         // MapInfo counts m_nMaxCoordBufSize=0 for V450 objects, but
     521             :         // until this is cleanly implemented, we will just prevent
     522             :         // m_nMaxCoordBufSizefrom going beyond 512k in V450 files.
     523        1205 :         if (m_nMinTABVersion >= 450)
     524             :         {
     525           0 :             m_poHeader->m_nMaxCoordBufSize =
     526           0 :                 std::min(m_poHeader->m_nMaxCoordBufSize, 512 * 1024);
     527             :         }
     528             : 
     529             :         // Write Ref to beginning of the chain of garbage blocks
     530        2410 :         m_poHeader->m_nFirstGarbageBlock =
     531        1205 :             m_oBlockManager.GetFirstGarbageBlock();
     532             : 
     533        1205 :         if (m_poHeader->CommitToFile() != 0)
     534           0 :             return -1;
     535             :     }
     536             : 
     537             :     // Check for overflow of internal coordinates and produce a warning
     538             :     // if that happened...
     539        1205 :     if (m_poHeader && m_poHeader->m_bIntBoundsOverflow)
     540             :     {
     541           0 :         double dBoundsMinX = 0.0;
     542           0 :         double dBoundsMinY = 0.0;
     543           0 :         double dBoundsMaxX = 0.0;
     544           0 :         double dBoundsMaxY = 0.0;
     545           0 :         Int2Coordsys(-1000000000, -1000000000, dBoundsMinX, dBoundsMinY);
     546           0 :         Int2Coordsys(1000000000, 1000000000, dBoundsMaxX, dBoundsMaxY);
     547             : 
     548           0 :         CPLError(CE_Warning,
     549             :                  static_cast<CPLErrorNum>(TAB_WarningBoundsOverflow),
     550             :                  "Some objects were written outside of the file's "
     551             :                  "predefined bounds.\n"
     552             :                  "These objects may have invalid coordinates when the file "
     553             :                  "is reopened.\n"
     554             :                  "Predefined bounds: (%.15g,%.15g)-(%.15g,%.15g)\n",
     555             :                  dBoundsMinX, dBoundsMinY, dBoundsMaxX, dBoundsMaxY);
     556             :     }
     557             : 
     558        1205 :     if (m_poIdIndex != nullptr && m_poIdIndex->SyncToDisk() != 0)
     559           0 :         return -1;
     560             : 
     561        1205 :     m_bUpdated = FALSE;
     562             : 
     563        1205 :     return 0;
     564             : }
     565             : 
     566             : /**********************************************************************
     567             :  *                   TABMAPFile::ReOpenReadWrite()
     568             :  **********************************************************************/
     569          27 : int TABMAPFile::ReOpenReadWrite()
     570             : {
     571          27 :     char *pszFname = m_pszFname;
     572          27 :     m_pszFname = nullptr;
     573          27 :     Close();
     574          27 :     if (Open(pszFname, TABReadWrite) < 0)
     575             :     {
     576           0 :         CPLFree(pszFname);
     577           0 :         return -1;
     578             :     }
     579          27 :     CPLFree(pszFname);
     580          27 :     return 0;
     581             : }
     582             : 
     583             : /**********************************************************************
     584             :  *                   TABMAPFile::SetQuickSpatialIndexMode()
     585             :  *
     586             :  * Select "quick spatial index mode".
     587             :  *
     588             :  * The default behavior of MITAB is to generate an optimized spatial index,
     589             :  * but this results in slower write speed.
     590             :  *
     591             :  * Applications that want faster write speed and do not care
     592             :  * about the performance of spatial queries on the resulting file can
     593             :  * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
     594             :  * spatial index (actually emulating the type of spatial index produced
     595             :  * by MITAB before version 1.6.0). In this mode writing files can be
     596             :  * about 5 times faster, but spatial queries can be up to 30 times slower.
     597             :  *
     598             :  * Returns 0 on success, -1 on error.
     599             :  **********************************************************************/
     600           0 : int TABMAPFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode /*=TRUE*/)
     601             : {
     602           0 :     if (m_eAccessMode != TABWrite)
     603             :     {
     604           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     605             :                  "SetQuickSpatialIndexMode() failed: file not opened for write "
     606             :                  "access.");
     607           0 :         return -1;
     608             :     }
     609             : 
     610           0 :     if (m_poCurObjBlock != nullptr || m_poSpIndex != nullptr)
     611             :     {
     612           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     613             :                  "SetQuickSpatialIndexMode() must be called before writing the "
     614             :                  "first object.");
     615           0 :         return -1;
     616             :     }
     617             : 
     618           0 :     m_bQuickSpatialIndexMode = bQuickSpatialIndexMode;
     619             : 
     620           0 :     return 0;
     621             : }
     622             : 
     623             : /************************************************************************/
     624             : /*                             PushBlock()                              */
     625             : /*                                                                      */
     626             : /*      Install a new block (object or spatial) as being current -      */
     627             : /*      whatever that means.  This method is only intended to ever      */
     628             : /*      be called from LoadNextMatchingObjectBlock().                   */
     629             : /************************************************************************/
     630             : 
     631       47316 : TABRawBinBlock *TABMAPFile::PushBlock(int nFileOffset)
     632             : 
     633             : {
     634       47316 :     TABRawBinBlock *poBlock = GetIndexObjectBlock(nFileOffset);
     635       47316 :     if (poBlock == nullptr)
     636           0 :         return nullptr;
     637             : 
     638       47316 :     if (poBlock->GetBlockType() == TABMAP_INDEX_BLOCK)
     639             :     {
     640             :         auto poIndex = std::unique_ptr<TABMAPIndexBlock>(
     641       60010 :             cpl::down_cast<TABMAPIndexBlock *>(poBlock));
     642             : 
     643       30005 :         if (m_poSpIndexLeaf == nullptr)
     644             :         {
     645           5 :             delete m_poSpIndex;
     646           5 :             m_poSpIndex = poIndex.release();
     647           5 :             m_poSpIndexLeaf = m_poSpIndex;
     648             :         }
     649             :         else
     650             :         {
     651       30000 :             CPLAssert(
     652             :                 m_poSpIndexLeaf->GetEntry(m_poSpIndexLeaf->GetCurChildIndex())
     653             :                     ->nBlockPtr == nFileOffset);
     654             : 
     655       30000 :             m_poSpIndexLeaf->SetCurChild(std::move(poIndex),
     656       30000 :                                          m_poSpIndexLeaf->GetCurChildIndex());
     657       30000 :             m_poSpIndexLeaf = m_poSpIndexLeaf->GetCurChild();
     658             :         }
     659             :     }
     660             :     else
     661             :     {
     662       17311 :         CPLAssert(poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK);
     663             : 
     664       17311 :         if (m_poCurObjBlock != nullptr)
     665       17311 :             delete m_poCurObjBlock;
     666             : 
     667       17311 :         m_poCurObjBlock = cpl::down_cast<TABMAPObjectBlock *>(poBlock);
     668             : 
     669       17311 :         m_nCurObjPtr = nFileOffset;
     670       17311 :         m_nCurObjType = TAB_GEOM_NONE;
     671       17311 :         m_nCurObjId = -1;
     672             :     }
     673             : 
     674       47316 :     return poBlock;
     675             : }
     676             : 
     677             : /************************************************************************/
     678             : /*                    LoadNextMatchingObjectBlock()                     */
     679             : /*                                                                      */
     680             : /*      Advance through the spatial indices till the next object        */
     681             : /*      block is loaded that matching the spatial query extents.        */
     682             : /************************************************************************/
     683             : 
     684       27435 : int TABMAPFile::LoadNextMatchingObjectBlock(int bFirstObject)
     685             : 
     686             : {
     687             :     // If we are just starting, verify the stack is empty.
     688       27435 :     if (bFirstObject)
     689             :     {
     690       10624 :         CPLAssert(m_poSpIndexLeaf == nullptr);
     691             : 
     692             :         /* m_nFirstIndexBlock set to 0 means that there is no feature */
     693       10624 :         if (m_poHeader->m_nFirstIndexBlock == 0)
     694           0 :             return FALSE;
     695             : 
     696       10624 :         if (m_poSpIndex != nullptr)
     697             :         {
     698       10619 :             m_poSpIndex->UnsetCurChild();
     699       10619 :             m_poSpIndexLeaf = m_poSpIndex;
     700             :         }
     701             :         else
     702             :         {
     703           5 :             if (PushBlock(m_poHeader->m_nFirstIndexBlock) == nullptr)
     704           0 :                 return FALSE;
     705             : 
     706           5 :             if (m_poSpIndex == nullptr)
     707             :             {
     708           0 :                 CPLAssert(m_poCurObjBlock != nullptr);
     709           0 :                 return TRUE;
     710             :             }
     711             :         }
     712             :     }
     713             : 
     714      596382 :     while (m_poSpIndexLeaf != nullptr)
     715             :     {
     716      586258 :         int iEntry = m_poSpIndexLeaf->GetCurChildIndex();
     717             : 
     718      586258 :         if (iEntry >= m_poSpIndexLeaf->GetNumEntries() - 1)
     719             :         {
     720       40124 :             TABMAPIndexBlock *poParent = m_poSpIndexLeaf->GetParentRef();
     721       40124 :             if (m_poSpIndexLeaf == m_poSpIndex)
     722       10124 :                 m_poSpIndex->UnsetCurChild();
     723       40124 :             m_poSpIndexLeaf = poParent;
     724             : 
     725       40124 :             if (poParent != nullptr)
     726             :             {
     727       30000 :                 poParent->SetCurChild(nullptr, poParent->GetCurChildIndex());
     728             :             }
     729       40124 :             continue;
     730             :         }
     731             : 
     732      546134 :         m_poSpIndexLeaf->SetCurChild(nullptr, ++iEntry);
     733             : 
     734      546134 :         TABMAPIndexEntry *psEntry = m_poSpIndexLeaf->GetEntry(iEntry);
     735      546134 :         if (!psEntry)
     736             :         {
     737           0 :             CPLAssert(false);
     738             :             continue;
     739             :         }
     740      546134 :         if (psEntry->XMax < m_XMinFilter || psEntry->YMax < m_YMinFilter ||
     741      214171 :             psEntry->XMin > m_XMaxFilter || psEntry->YMin > m_YMaxFilter)
     742      498823 :             continue;
     743             : 
     744       47311 :         TABRawBinBlock *poBlock = PushBlock(psEntry->nBlockPtr);
     745       47311 :         if (poBlock == nullptr)
     746           0 :             return FALSE;
     747       47311 :         else if (poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK)
     748       17311 :             return TRUE;
     749             :         else
     750             :         {
     751             :             /* continue processing new index block */
     752             :         }
     753             :     }
     754             : 
     755       10124 :     return false;
     756             : }
     757             : 
     758             : /************************************************************************/
     759             : /*                            ResetReading()                            */
     760             : /*                                                                      */
     761             : /*      Ensure that any resources related to a spatial traversal of     */
     762             : /*      the file are recovered, and the state reinitialized to the      */
     763             : /*      initial conditions.                                             */
     764             : /************************************************************************/
     765             : 
     766       33830 : void TABMAPFile::ResetReading()
     767             : 
     768             : {
     769       33830 :     if (m_bLastOpWasWrite)
     770         235 :         CommitObjAndCoordBlocks(FALSE);
     771             : 
     772       33830 :     if (m_poSpIndex)
     773             :     {
     774       33334 :         m_poSpIndex->UnsetCurChild();
     775             :     }
     776       33830 :     m_poSpIndexLeaf = nullptr;
     777             : 
     778       33830 :     m_bLastOpWasWrite = FALSE;
     779       33830 :     m_bLastOpWasRead = FALSE;
     780       33830 : }
     781             : 
     782             : /************************************************************************/
     783             : /*                          GetNextFeatureId()                          */
     784             : /*                                                                      */
     785             : /*      Fetch the next feature id based on a traversal of the           */
     786             : /*      spatial index.                                                  */
     787             : /************************************************************************/
     788             : 
     789      430364 : int TABMAPFile::GetNextFeatureId(int nPrevId)
     790             : 
     791             : {
     792      430364 :     if (m_bLastOpWasWrite)
     793             :     {
     794           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     795             :                  "GetNextFeatureId() cannot be called after write operation");
     796           0 :         return -1;
     797             :     }
     798      430364 :     if (m_eAccessMode == TABWrite)
     799             :     {
     800           0 :         if (ReOpenReadWrite() < 0)
     801           0 :             return -1;
     802             :     }
     803      430364 :     m_bLastOpWasRead = TRUE;
     804             : 
     805             :     /* -------------------------------------------------------------------- */
     806             :     /*      m_fp is NULL when all geometry are NONE and/or there's          */
     807             :     /*          no .map file and/or there's no spatial indexes              */
     808             :     /* -------------------------------------------------------------------- */
     809      430364 :     if (m_fp == nullptr)
     810           0 :         return -1;
     811             : 
     812      430364 :     if (nPrevId == 0)
     813       10624 :         nPrevId = -1;
     814             : 
     815             :     /* -------------------------------------------------------------------- */
     816             :     /*      This should always be true if we are being called properly.     */
     817             :     /* -------------------------------------------------------------------- */
     818      430364 :     if (nPrevId != -1 && m_nCurObjId != nPrevId)
     819             :     {
     820           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     821             :                  "TABMAPFile::GetNextFeatureId(%d) called out of sequence.",
     822             :                  nPrevId);
     823           0 :         return -1;
     824             :     }
     825             : 
     826      430364 :     CPLAssert(nPrevId == -1 || m_poCurObjBlock != nullptr);
     827             : 
     828             :     /* -------------------------------------------------------------------- */
     829             :     /*      Ensure things are initialized properly if this is a request     */
     830             :     /*      for the first feature.                                          */
     831             :     /* -------------------------------------------------------------------- */
     832      430364 :     if (nPrevId == -1)
     833             :     {
     834       10624 :         m_nCurObjId = -1;
     835             :     }
     836             : 
     837             :     /* -------------------------------------------------------------------- */
     838             :     /*      Try to advance to the next object in the current object         */
     839             :     /*      block.                                                          */
     840             :     /* -------------------------------------------------------------------- */
     841      430364 :     if (nPrevId == -1 || m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1)
     842             :     {
     843             :         // If not, try to advance to the next object block, and get
     844             :         // first object from it.  Note that some object blocks actually
     845             :         // have no objects, so we may have to advance to additional
     846             :         // object blocks till we find a non-empty one.
     847       27399 :         GBool bFirstCall = (nPrevId == -1);
     848          36 :         do
     849             :         {
     850       27435 :             if (!LoadNextMatchingObjectBlock(bFirstCall))
     851       10124 :                 return -1;
     852             : 
     853       17311 :             bFirstCall = FALSE;
     854       17311 :         } while (m_poCurObjBlock->AdvanceToNextObject(m_poHeader) == -1);
     855             :     }
     856             : 
     857      420240 :     m_nCurObjType = m_poCurObjBlock->GetCurObjectType();
     858      420240 :     m_nCurObjId = m_poCurObjBlock->GetCurObjectId();
     859      420240 :     m_nCurObjPtr = m_poCurObjBlock->GetStartAddress() +
     860      420240 :                    m_poCurObjBlock->GetCurObjectOffset();
     861             : 
     862      420240 :     CPLAssert(m_nCurObjId != -1);
     863             : 
     864      420240 :     return m_nCurObjId;
     865             : }
     866             : 
     867             : /**********************************************************************
     868             :  *                   TABMAPFile::Int2Coordsys()
     869             :  *
     870             :  * Convert from long integer (internal) to coordinates system units
     871             :  * as defined in the file's coordsys clause.
     872             :  *
     873             :  * Note that the false easting/northing and the conversion factor from
     874             :  * datum to coordsys units are not included in the calculation.
     875             :  *
     876             :  * Returns 0 on success, -1 on error.
     877             :  **********************************************************************/
     878      676160 : int TABMAPFile::Int2Coordsys(GInt32 nX, GInt32 nY, double &dX, double &dY)
     879             : {
     880      676160 :     if (m_poHeader == nullptr)
     881           0 :         return -1;
     882             : 
     883      676160 :     return m_poHeader->Int2Coordsys(nX, nY, dX, dY);
     884             : }
     885             : 
     886             : /**********************************************************************
     887             :  *                   TABMAPFile::Coordsys2Int()
     888             :  *
     889             :  * Convert from coordinates system units as defined in the file's
     890             :  * coordsys clause to long integer (internal) coordinates.
     891             :  *
     892             :  * Note that the false easting/northing and the conversion factor from
     893             :  * datum to coordsys units are not included in the calculation.
     894             :  *
     895             :  * Returns 0 on success, -1 on error.
     896             :  **********************************************************************/
     897      118529 : int TABMAPFile::Coordsys2Int(double dX, double dY, GInt32 &nX, GInt32 &nY,
     898             :                              GBool bIgnoreOverflow /*=FALSE*/)
     899             : {
     900      118529 :     if (m_poHeader == nullptr)
     901           0 :         return -1;
     902             : 
     903      118529 :     return m_poHeader->Coordsys2Int(dX, dY, nX, nY, bIgnoreOverflow);
     904             : }
     905             : 
     906             : /**********************************************************************
     907             :  *                   TABMAPFile::Int2CoordsysDist()
     908             :  *
     909             :  * Convert a pair of X,Y size (or distance) values from long integer
     910             :  * (internal) to coordinates system units as defined in the file's coordsys
     911             :  * clause.
     912             :  *
     913             :  * The difference with Int2Coordsys() is that this function only applies
     914             :  * the scaling factor: it does not apply the displacement.
     915             :  *
     916             :  * Since the calculations on the X and Y values are independent, either
     917             :  * one can be omitted (i.e. passed as 0)
     918             :  *
     919             :  * Returns 0 on success, -1 on error.
     920             :  **********************************************************************/
     921          14 : int TABMAPFile::Int2CoordsysDist(GInt32 nX, GInt32 nY, double &dX, double &dY)
     922             : {
     923          14 :     if (m_poHeader == nullptr)
     924           0 :         return -1;
     925             : 
     926          14 :     return m_poHeader->Int2CoordsysDist(nX, nY, dX, dY);
     927             : }
     928             : 
     929             : /**********************************************************************
     930             :  *                   TABMAPFile::Coordsys2IntDist()
     931             :  *
     932             :  * Convert a pair of X,Y size (or distance) values from coordinates
     933             :  * system units as defined in the file's coordsys clause to long
     934             :  * integer (internal) coordinate units.
     935             :  *
     936             :  * The difference with Int2Coordsys() is that this function only applies
     937             :  * the scaling factor: it does not apply the displacement.
     938             :  *
     939             :  * Since the calculations on the X and Y values are independent, either
     940             :  * one can be omitted (i.e. passed as 0)
     941             :  *
     942             :  * Returns 0 on success, -1 on error.
     943             :  **********************************************************************/
     944           4 : int TABMAPFile::Coordsys2IntDist(double dX, double dY, GInt32 &nX, GInt32 &nY)
     945             : {
     946           4 :     if (m_poHeader == nullptr)
     947           0 :         return -1;
     948             : 
     949           4 :     return m_poHeader->Coordsys2IntDist(dX, dY, nX, nY);
     950             : }
     951             : 
     952             : /**********************************************************************
     953             :  *                   TABMAPFile::SetCoordsysBounds()
     954             :  *
     955             :  * Set projection coordinates bounds of the newly created dataset.
     956             :  *
     957             :  * This function must be called after creating a new dataset and before any
     958             :  * feature can be written to it.
     959             :  *
     960             :  * Returns 0 on success, -1 on error.
     961             :  **********************************************************************/
     962         128 : int TABMAPFile::SetCoordsysBounds(double dXMin, double dYMin, double dXMax,
     963             :                                   double dYMax)
     964             : {
     965         128 :     if (m_poHeader == nullptr)
     966           0 :         return -1;
     967             : 
     968             :     const int nStatus =
     969         128 :         m_poHeader->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
     970             : 
     971         128 :     if (nStatus == 0)
     972         128 :         ResetCoordFilter();
     973             : 
     974         128 :     return nStatus;
     975             : }
     976             : 
     977             : /**********************************************************************
     978             :  *                   TABMAPFile::GetMaxObjId()
     979             :  *
     980             :  * Return the value of the biggest valid object id.
     981             :  *
     982             :  * Note that object ids are positive and start at 1.
     983             :  *
     984             :  * Returns a value >= 0 on success, -1 on error.
     985             :  **********************************************************************/
     986           0 : GInt32 TABMAPFile::GetMaxObjId()
     987             : {
     988           0 :     if (m_poIdIndex)
     989           0 :         return m_poIdIndex->GetMaxObjId();
     990             : 
     991           0 :     return -1;
     992             : }
     993             : 
     994             : /**********************************************************************
     995             :  *                   TABMAPFile::MoveToObjId()
     996             :  *
     997             :  * Get ready to work with the object with the specified id.  The object
     998             :  * data pointer (inside m_poCurObjBlock) will be moved to the first byte
     999             :  * of data for this map object.
    1000             :  *
    1001             :  * The object type and id (i.e. table row number) will be accessible
    1002             :  * using GetCurObjType() and GetCurObjId().
    1003             :  *
    1004             :  * Note that object ids are positive and start at 1.
    1005             :  *
    1006             :  * Returns 0 on success, -1 on error.
    1007             :  **********************************************************************/
    1008      726331 : int TABMAPFile::MoveToObjId(int nObjId)
    1009             : {
    1010      726331 :     if (m_bLastOpWasWrite)
    1011             :     {
    1012           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1013             :                  "MoveToObjId() cannot be called after write operation");
    1014           0 :         return -1;
    1015             :     }
    1016      726331 :     if (m_eAccessMode == TABWrite)
    1017             :     {
    1018          27 :         if (ReOpenReadWrite() < 0)
    1019           0 :             return -1;
    1020             :     }
    1021      726331 :     m_bLastOpWasRead = TRUE;
    1022             : 
    1023             :     /*-----------------------------------------------------------------
    1024             :      * In non creation mode, since the .MAP/.ID are optional, if the
    1025             :      * file is not opened then we can still act as if one existed and
    1026             :      * make any object id look like a TAB_GEOM_NONE
    1027             :      *----------------------------------------------------------------*/
    1028      726331 :     if (m_fp == nullptr && m_eAccessMode != TABWrite)
    1029             :     {
    1030          22 :         CPLAssert(m_poIdIndex == nullptr && m_poCurObjBlock == nullptr);
    1031          22 :         m_nCurObjPtr = 0;
    1032          22 :         m_nCurObjId = nObjId;
    1033          22 :         m_nCurObjType = TAB_GEOM_NONE;
    1034             : 
    1035          22 :         return 0;
    1036             :     }
    1037             : 
    1038      726309 :     if (m_poIdIndex == nullptr)
    1039             :     {
    1040           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1041             :                  "MoveToObjId(): file not opened!");
    1042           0 :         m_nCurObjPtr = -1;
    1043           0 :         m_nCurObjId = -1;
    1044           0 :         m_nCurObjType = TAB_GEOM_UNSET;
    1045           0 :         return -1;
    1046             :     }
    1047             : 
    1048             :     /*-----------------------------------------------------------------
    1049             :      * Move map object pointer to the right location.  Fetch location
    1050             :      * from the index file, unless we are already pointing at it.
    1051             :      *----------------------------------------------------------------*/
    1052             :     int nFileOffset =
    1053      726309 :         m_nCurObjId == nObjId ? m_nCurObjPtr : m_poIdIndex->GetObjPtr(nObjId);
    1054             : 
    1055      726309 :     if (nFileOffset != 0 && m_poCurObjBlock == nullptr)
    1056             :     {
    1057           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1058             :                  "MoveToObjId(): no current object block!");
    1059           0 :         m_nCurObjPtr = -1;
    1060           0 :         m_nCurObjId = -1;
    1061           0 :         m_nCurObjType = TAB_GEOM_UNSET;
    1062           0 :         return -1;
    1063             :     }
    1064             : 
    1065      726309 :     if (nFileOffset == 0)
    1066             :     {
    1067             :         /*---------------------------------------------------------
    1068             :          * Object with no geometry... this is a valid case.
    1069             :          *--------------------------------------------------------*/
    1070        1287 :         m_nCurObjPtr = 0;
    1071        1287 :         m_nCurObjId = nObjId;
    1072        1287 :         m_nCurObjType = TAB_GEOM_NONE;
    1073             :     }
    1074      725022 :     else if (m_poCurObjBlock->GotoByteInFile(nFileOffset, TRUE) == 0)
    1075             :     {
    1076             :         /*-------------------------------------------------------------
    1077             :          * OK, it worked, read the object type and row id.
    1078             :          *------------------------------------------------------------*/
    1079      725022 :         m_nCurObjPtr = nFileOffset;
    1080             : 
    1081      725022 :         const GByte byVal = m_poCurObjBlock->ReadByte();
    1082      725022 :         if (IsValidObjType(byVal))
    1083             :         {
    1084      725022 :             m_nCurObjType = static_cast<TABGeomType>(byVal);
    1085             :         }
    1086             :         else
    1087             :         {
    1088           0 :             CPLError(
    1089             :                 CE_Warning,
    1090             :                 static_cast<CPLErrorNum>(TAB_WarningFeatureTypeNotSupported),
    1091             :                 "Unsupported object type %d (0x%2.2x).  Feature will be "
    1092             :                 "returned with NONE geometry.",
    1093             :                 byVal, byVal);
    1094           0 :             m_nCurObjType = TAB_GEOM_NONE;
    1095             :         }
    1096      725022 :         m_nCurObjId = m_poCurObjBlock->ReadInt32();
    1097             : 
    1098             :         // Do a consistency check...
    1099      725022 :         if (m_nCurObjId != nObjId)
    1100             :         {
    1101           0 :             if (m_nCurObjId == (nObjId | 0x40000000))
    1102             :             {
    1103           0 :                 CPLError(CE_Failure, CPLE_FileIO,
    1104             :                          "Object %d is marked as deleted in the .MAP file but "
    1105             :                          "not in the .ID file."
    1106             :                          "File may be corrupt.",
    1107             :                          nObjId);
    1108             :             }
    1109             :             else
    1110             :             {
    1111           0 :                 CPLError(
    1112             :                     CE_Failure, CPLE_FileIO,
    1113             :                     "Object ID from the .ID file (%d) differs from the value "
    1114             :                     "in the .MAP file (%d).  File may be corrupt.",
    1115             :                     nObjId, m_nCurObjId);
    1116             :             }
    1117           0 :             m_nCurObjPtr = -1;
    1118           0 :             m_nCurObjId = -1;
    1119           0 :             m_nCurObjType = TAB_GEOM_UNSET;
    1120           0 :             return -1;
    1121             :         }
    1122             :     }
    1123             :     else
    1124             :     {
    1125             :         /*---------------------------------------------------------
    1126             :          * Failed positioning input file... CPLError has been called.
    1127             :          *--------------------------------------------------------*/
    1128           0 :         m_nCurObjPtr = -1;
    1129           0 :         m_nCurObjId = -1;
    1130           0 :         m_nCurObjType = TAB_GEOM_UNSET;
    1131           0 :         return -1;
    1132             :     }
    1133             : 
    1134      726309 :     return 0;
    1135             : }
    1136             : 
    1137             : /**********************************************************************
    1138             :  *                   TABMAPFile::MarkAsDeleted()
    1139             :  *
    1140             :  * Returns 0 on success, -1 on error.
    1141             :  **********************************************************************/
    1142         715 : int TABMAPFile::MarkAsDeleted()
    1143             : {
    1144         715 :     if (m_eAccessMode == TABRead)
    1145           0 :         return -1;
    1146             : 
    1147         715 :     if (m_nCurObjPtr <= 0)
    1148           2 :         return 0;
    1149             : 
    1150         713 :     int ret = 0;
    1151         713 :     if (m_nCurObjType != TAB_GEOM_NONE)
    1152             :     {
    1153             :         /* Goto offset for object id */
    1154        1426 :         if (m_poCurObjBlock == nullptr ||
    1155         713 :             m_poCurObjBlock->GotoByteInFile(m_nCurObjPtr + 1, TRUE) != 0)
    1156           0 :             return -1;
    1157             : 
    1158             :         /* Mark object as deleted */
    1159         713 :         m_poCurObjBlock->WriteInt32(m_nCurObjId | 0x40000000);
    1160             : 
    1161         713 :         if (m_poCurObjBlock->CommitToFile() != 0)
    1162           0 :             ret = -1;
    1163             :     }
    1164             : 
    1165             :     /* Update index entry to reflect delete state as well */
    1166         713 :     if (m_poIdIndex->SetObjPtr(m_nCurObjId, 0) != 0)
    1167           0 :         ret = -1;
    1168             : 
    1169         713 :     m_nCurObjPtr = -1;
    1170         713 :     m_nCurObjId = -1;
    1171         713 :     m_nCurObjType = TAB_GEOM_UNSET;
    1172         713 :     m_bUpdated = TRUE;
    1173             : 
    1174         713 :     return ret;
    1175             : }
    1176             : 
    1177             : /**********************************************************************
    1178             :  *                   TABMAPFile::UpdateMapHeaderInfo()
    1179             :  *
    1180             :  * Update .map header information (counter of objects by type and minimum
    1181             :  * required version) in light of a new object to be written to the file.
    1182             :  *
    1183             :  * Called only by PrepareNewObj() and by the TABCollection class.
    1184             :  **********************************************************************/
    1185       15067 : void TABMAPFile::UpdateMapHeaderInfo(TABGeomType nObjType)
    1186             : {
    1187             :     /*-----------------------------------------------------------------
    1188             :      * Update count of objects by type in the header block
    1189             :      *----------------------------------------------------------------*/
    1190       15067 :     if (nObjType == TAB_GEOM_SYMBOL || nObjType == TAB_GEOM_FONTSYMBOL ||
    1191         376 :         nObjType == TAB_GEOM_CUSTOMSYMBOL || nObjType == TAB_GEOM_MULTIPOINT ||
    1192         376 :         nObjType == TAB_GEOM_V800_MULTIPOINT || nObjType == TAB_GEOM_SYMBOL_C ||
    1193         376 :         nObjType == TAB_GEOM_FONTSYMBOL_C ||
    1194         376 :         nObjType == TAB_GEOM_CUSTOMSYMBOL_C ||
    1195         376 :         nObjType == TAB_GEOM_MULTIPOINT_C ||
    1196             :         nObjType == TAB_GEOM_V800_MULTIPOINT_C)
    1197             :     {
    1198       14691 :         m_poHeader->m_numPointObjects++;
    1199             :     }
    1200         376 :     else if (nObjType == TAB_GEOM_LINE || nObjType == TAB_GEOM_PLINE ||
    1201         350 :              nObjType == TAB_GEOM_MULTIPLINE ||
    1202         350 :              nObjType == TAB_GEOM_V450_MULTIPLINE ||
    1203         350 :              nObjType == TAB_GEOM_V800_MULTIPLINE || nObjType == TAB_GEOM_ARC ||
    1204         350 :              nObjType == TAB_GEOM_LINE_C || nObjType == TAB_GEOM_PLINE_C ||
    1205         138 :              nObjType == TAB_GEOM_MULTIPLINE_C ||
    1206         138 :              nObjType == TAB_GEOM_V450_MULTIPLINE_C ||
    1207         138 :              nObjType == TAB_GEOM_V800_MULTIPLINE_C ||
    1208             :              nObjType == TAB_GEOM_ARC_C)
    1209             :     {
    1210         238 :         m_poHeader->m_numLineObjects++;
    1211             :     }
    1212         138 :     else if (nObjType == TAB_GEOM_REGION || nObjType == TAB_GEOM_V450_REGION ||
    1213         104 :              nObjType == TAB_GEOM_V800_REGION || nObjType == TAB_GEOM_RECT ||
    1214         104 :              nObjType == TAB_GEOM_ROUNDRECT || nObjType == TAB_GEOM_ELLIPSE ||
    1215           4 :              nObjType == TAB_GEOM_REGION_C ||
    1216           4 :              nObjType == TAB_GEOM_V450_REGION_C ||
    1217           4 :              nObjType == TAB_GEOM_V800_REGION_C ||
    1218           4 :              nObjType == TAB_GEOM_RECT_C || nObjType == TAB_GEOM_ROUNDRECT_C ||
    1219             :              nObjType == TAB_GEOM_ELLIPSE_C)
    1220             :     {
    1221         134 :         m_poHeader->m_numRegionObjects++;
    1222             :     }
    1223           4 :     else if (nObjType == TAB_GEOM_TEXT || nObjType == TAB_GEOM_TEXT_C)
    1224             :     {
    1225           4 :         m_poHeader->m_numTextObjects++;
    1226             :     }
    1227             : 
    1228             :     /*-----------------------------------------------------------------
    1229             :      * Check for minimum TAB file version number
    1230             :      *----------------------------------------------------------------*/
    1231       15067 :     int nVersion = TAB_GEOM_GET_VERSION(nObjType);
    1232             : 
    1233       15067 :     if (nVersion > m_nMinTABVersion)
    1234             :     {
    1235           0 :         m_nMinTABVersion = nVersion;
    1236             :     }
    1237       15067 : }
    1238             : 
    1239             : /**********************************************************************
    1240             :  *                   TABMAPFile::PrepareNewObj()
    1241             :  *
    1242             :  * Get ready to write a new object described by poObjHdr (using the
    1243             :  * poObjHdr's m_nId (featureId), m_nType and IntMBR members which must
    1244             :  * have been set by the caller).
    1245             :  *
    1246             :  * Depending on whether "quick spatial index mode" is selected, we either:
    1247             :  *
    1248             :  * 1- Walk through the spatial index to find the best place to insert the
    1249             :  * new object, update the spatial index references, and prepare the object
    1250             :  * data block to be ready to write the object to it.
    1251             :  * ... or ...
    1252             :  * 2- prepare the current object data block to be ready to write the
    1253             :  * object to it. If the object block is full then it is inserted in the
    1254             :  * spatial index and committed to disk, and a new obj block is created.
    1255             :  *
    1256             :  * m_poCurObjBlock will be set to be ready to receive the new object, and
    1257             :  * a new block will be created if necessary (in which case the current
    1258             :  * block contents will be committed to disk, etc.)  The actual ObjHdr
    1259             :  * data won't be written to m_poCurObjBlock until CommitNewObj() is called.
    1260             :  *
    1261             :  * If this object type uses coordinate blocks, then the coordinate block
    1262             :  * will be prepared to receive coordinates.
    1263             :  *
    1264             :  * This function will also take care of updating the .ID index entry for
    1265             :  * the new object.
    1266             :  *
    1267             :  * Note that object ids are positive and start at 1.
    1268             :  *
    1269             :  * Returns 0 on success, -1 on error.
    1270             :  **********************************************************************/
    1271       15127 : int TABMAPFile::PrepareNewObj(TABMAPObjHdr *poObjHdr)
    1272             : {
    1273       15127 :     m_nCurObjPtr = -1;
    1274       15127 :     m_nCurObjId = -1;
    1275       15127 :     m_nCurObjType = TAB_GEOM_UNSET;
    1276             : 
    1277       15127 :     if (m_eAccessMode == TABRead || m_poIdIndex == nullptr ||
    1278       15127 :         m_poHeader == nullptr)
    1279             :     {
    1280           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1281             :                  "PrepareNewObj() failed: file not opened for write access.");
    1282           0 :         return -1;
    1283             :     }
    1284             : 
    1285       15127 :     if (m_bLastOpWasRead)
    1286             :     {
    1287         114 :         m_bLastOpWasRead = FALSE;
    1288         114 :         if (m_poSpIndex)
    1289             :         {
    1290         112 :             m_poSpIndex->UnsetCurChild();
    1291             :         }
    1292             :     }
    1293             : 
    1294             :     /*-----------------------------------------------------------------
    1295             :      * For objects with no geometry, we just update the .ID file and return
    1296             :      *----------------------------------------------------------------*/
    1297       15127 :     if (poObjHdr->m_nType == TAB_GEOM_NONE)
    1298             :     {
    1299          60 :         m_nCurObjType = poObjHdr->m_nType;
    1300          60 :         m_nCurObjId = poObjHdr->m_nId;
    1301          60 :         m_nCurObjPtr = 0;
    1302          60 :         m_poIdIndex->SetObjPtr(m_nCurObjId, 0);
    1303             : 
    1304          60 :         return 0;
    1305             :     }
    1306             : 
    1307             :     /*-----------------------------------------------------------------
    1308             :      * Update count of objects by type in the header block and minimum
    1309             :      * required version.
    1310             :      *----------------------------------------------------------------*/
    1311       15067 :     UpdateMapHeaderInfo(poObjHdr->m_nType);
    1312             : 
    1313             :     /*-----------------------------------------------------------------
    1314             :      * Depending on the selected spatial index mode, we will either insert
    1315             :      * new objects via the spatial index (slower write but results in optimal
    1316             :      * spatial index) or directly in the current ObjBlock (faster write
    1317             :      * but non-optimal spatial index)
    1318             :      *----------------------------------------------------------------*/
    1319       15067 :     if (!m_bQuickSpatialIndexMode)
    1320             :     {
    1321       11568 :         if (PrepareNewObjViaSpatialIndex(poObjHdr) != 0)
    1322           0 :             return -1; /* Error already reported */
    1323             :     }
    1324             :     else
    1325             :     {
    1326        3499 :         if (PrepareNewObjViaObjBlock(poObjHdr) != 0)
    1327           0 :             return -1; /* Error already reported */
    1328             :     }
    1329             : 
    1330             :     /*-----------------------------------------------------------------
    1331             :      * Prepare ObjBlock for this new object.
    1332             :      * Real data won't be written to the object block until CommitNewObj()
    1333             :      * is called.
    1334             :      *----------------------------------------------------------------*/
    1335       15067 :     m_nCurObjPtr = m_poCurObjBlock->PrepareNewObject(poObjHdr);
    1336       15067 :     if (m_nCurObjPtr < 0)
    1337             :     {
    1338           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1339             :                  "Failed writing object header for feature id %d",
    1340             :                  poObjHdr->m_nId);
    1341           0 :         return -1;
    1342             :     }
    1343             : 
    1344       15067 :     m_nCurObjType = poObjHdr->m_nType;
    1345       15067 :     m_nCurObjId = poObjHdr->m_nId;
    1346             : 
    1347             :     /*-----------------------------------------------------------------
    1348             :      * Update .ID Index
    1349             :      *----------------------------------------------------------------*/
    1350       15067 :     m_poIdIndex->SetObjPtr(m_nCurObjId, m_nCurObjPtr);
    1351             : 
    1352             :     /*-----------------------------------------------------------------
    1353             :      * Prepare Coords block...
    1354             :      * create a new TABMAPCoordBlock if it was not done yet.
    1355             :      *----------------------------------------------------------------*/
    1356       15067 :     PrepareCoordBlock(m_nCurObjType, m_poCurObjBlock, &m_poCurCoordBlock);
    1357             : 
    1358       15067 :     if (CPLGetLastErrorType() == CE_Failure)
    1359           0 :         return -1;
    1360             : 
    1361       15067 :     m_bUpdated = TRUE;
    1362       15067 :     m_bLastOpWasWrite = TRUE;
    1363             : 
    1364       15067 :     return 0;
    1365             : }
    1366             : 
    1367             : /**********************************************************************
    1368             :  *                   TABMAPFile::PrepareNewObjViaSpatialIndex()
    1369             :  *
    1370             :  * Used by TABMAPFile::PrepareNewObj() to walk through the spatial index
    1371             :  * to find the best place to insert the new object, update the spatial
    1372             :  * index references, and prepare the object data block to be ready to
    1373             :  * write the object to it.
    1374             :  *
    1375             :  * This method is used when "quick spatial index mode" is NOT selected,
    1376             :  * i.e. when we want to produce a file with an optimal spatial index
    1377             :  *
    1378             :  * Returns 0 on success, -1 on error.
    1379             :  **********************************************************************/
    1380       11568 : int TABMAPFile::PrepareNewObjViaSpatialIndex(TABMAPObjHdr *poObjHdr)
    1381             : {
    1382       11568 :     GInt32 nObjBlockForInsert = -1;
    1383             : 
    1384             :     /*-----------------------------------------------------------------
    1385             :      * Create spatial index if we don't have one yet.
    1386             :      * We do not create the index and object data blocks in the open()
    1387             :      * call because files that contained only "NONE" geometries ended up
    1388             :      * with empty object and spatial index blocks.
    1389             :      *----------------------------------------------------------------*/
    1390       11568 :     if (m_poSpIndex == nullptr)
    1391             :     {
    1392             :         // Spatial Index not created yet...
    1393           6 :         m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode);
    1394             : 
    1395           6 :         m_poSpIndex->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    1396             :                                   m_oBlockManager.AllocNewBlock("INDEX"));
    1397           6 :         m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager);
    1398             : 
    1399           6 :         if (m_eAccessMode == TABReadWrite &&
    1400           6 :             m_poHeader->m_nFirstIndexBlock != 0)
    1401             :         {
    1402             :             /* This can happen if the file created by MapInfo contains just */
    1403             :             /* a few objects */
    1404             :             TABRawBinBlock *poBlock =
    1405           3 :                 GetIndexObjectBlock(m_poHeader->m_nFirstIndexBlock);
    1406           3 :             CPLAssert(poBlock != nullptr &&
    1407             :                       poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK);
    1408           3 :             delete poBlock;
    1409             : 
    1410           6 :             if (m_poSpIndex->AddEntry(m_poHeader->m_nXMin, m_poHeader->m_nYMin,
    1411           3 :                                       m_poHeader->m_nXMax, m_poHeader->m_nYMax,
    1412           3 :                                       m_poHeader->m_nFirstIndexBlock) != 0)
    1413           0 :                 return -1;
    1414             : 
    1415           3 :             delete m_poCurObjBlock;
    1416           3 :             m_poCurObjBlock = nullptr;
    1417           3 :             delete m_poCurCoordBlock;
    1418           3 :             m_poCurCoordBlock = nullptr;
    1419             :         }
    1420             : 
    1421           6 :         m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr();
    1422             : 
    1423             :         /* We'll also need to create an object data block (later) */
    1424             :         // nObjBlockForInsert = -1;
    1425             : 
    1426           6 :         CPLAssert(m_poCurObjBlock == nullptr);
    1427             :     }
    1428             :     else
    1429             :     /*-----------------------------------------------------------------
    1430             :      * Search the spatial index to find the best place to insert this
    1431             :      * new object.
    1432             :      *----------------------------------------------------------------*/
    1433             :     {
    1434       11562 :         nObjBlockForInsert = m_poSpIndex->ChooseLeafForInsert(
    1435             :             poObjHdr->m_nMinX, poObjHdr->m_nMinY, poObjHdr->m_nMaxX,
    1436             :             poObjHdr->m_nMaxY);
    1437       11562 :         if (nObjBlockForInsert == -1)
    1438             :         {
    1439             :             /* ChooseLeafForInsert() should not fail unless file is corrupt*/
    1440           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
    1441             :                      "ChooseLeafForInsert() Failed?!?!");
    1442           0 :             return -1;
    1443             :         }
    1444             :     }
    1445             : 
    1446       11568 :     if (nObjBlockForInsert == -1)
    1447             :     {
    1448             :         /*-------------------------------------------------------------
    1449             :          * Create a new object data block from scratch
    1450             :          *------------------------------------------------------------*/
    1451           6 :         m_poCurObjBlock = new TABMAPObjectBlock(TABReadWrite);
    1452             : 
    1453           6 :         int nBlockOffset = m_oBlockManager.AllocNewBlock("OBJECT");
    1454             : 
    1455           6 :         m_poCurObjBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    1456             :                                       nBlockOffset);
    1457             : 
    1458             :         /*-------------------------------------------------------------
    1459             :          * Insert new object block in index, based on MBR of poObjHdr
    1460             :          *------------------------------------------------------------*/
    1461           6 :         if (m_poSpIndex->AddEntry(poObjHdr->m_nMinX, poObjHdr->m_nMinY,
    1462             :                                   poObjHdr->m_nMaxX, poObjHdr->m_nMaxY,
    1463          12 :                                   m_poCurObjBlock->GetStartAddress()) != 0)
    1464           0 :             return -1;
    1465             : 
    1466           6 :         m_poCurObjBlock->SetMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY,
    1467             :                                 poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
    1468             : 
    1469           6 :         const int nNextDepth = m_poSpIndex->GetCurMaxDepth() + 1;
    1470           6 :         m_poHeader->m_nMaxSpIndexDepth = static_cast<GByte>(std::max(
    1471           6 :             static_cast<int>(m_poHeader->m_nMaxSpIndexDepth), nNextDepth));
    1472             :     }
    1473             :     else
    1474             :     {
    1475             :         /*-------------------------------------------------------------
    1476             :          * Load existing object and Coord blocks, unless we've already
    1477             :          * got the right object block in memory
    1478             :          *------------------------------------------------------------*/
    1479       23124 :         if (m_poCurObjBlock &&
    1480       11562 :             m_poCurObjBlock->GetStartAddress() != nObjBlockForInsert)
    1481             :         {
    1482             :             /* Got a block in memory but it is not the right one, flush it */
    1483       10719 :             if (CommitObjAndCoordBlocks(TRUE) != 0)
    1484           0 :                 return -1;
    1485             :         }
    1486             : 
    1487       11562 :         if (m_poCurObjBlock == nullptr)
    1488             :         {
    1489       10719 :             if (LoadObjAndCoordBlocks(nObjBlockForInsert) != 0)
    1490           0 :                 return -1;
    1491             :         }
    1492             : 
    1493             :         /* If we have compressed objects, we don't want to change the center  */
    1494       11562 :         m_poCurObjBlock->LockCenter();
    1495             : 
    1496             :         // Check if the ObjBlock know its MBR. If not (new block, or the current
    1497             :         // block was the good one but retrieved without the index), get the
    1498             :         // value from the index and set it.
    1499             :         GInt32 nMinX, nMinY, nMaxX, nMaxY;
    1500       11562 :         m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1501       11562 :         if (nMinX > nMaxX)
    1502             :         {
    1503       10723 :             m_poSpIndex->GetCurLeafEntryMBR(m_poCurObjBlock->GetStartAddress(),
    1504             :                                             nMinX, nMinY, nMaxX, nMaxY);
    1505       10723 :             m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1506             :         }
    1507             :     }
    1508             : 
    1509             :     /*-----------------------------------------------------------------
    1510             :      * Fetch new object size, make sure there is enough room in obj.
    1511             :      * block for new object, update spatial index and split if necessary.
    1512             :      *----------------------------------------------------------------*/
    1513       11568 :     int nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
    1514             : 
    1515             :     /*-----------------------------------------------------------------
    1516             :      * But first check if we can recover space from this block in case
    1517             :      * there are deleted objects in it.
    1518             :      *----------------------------------------------------------------*/
    1519       11568 :     if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize)
    1520             :     {
    1521         531 :         std::vector<std::unique_ptr<TABMAPObjHdr>> apoSrcObjHdrs;
    1522         531 :         int nObjectSpace = 0;
    1523             : 
    1524             :         /* First pass to enumerate valid objects and compute their accumulated
    1525             :            required size. */
    1526         531 :         m_poCurObjBlock->Rewind();
    1527             :         while (auto poExistingObjHdr =
    1528       11846 :                    TABMAPObjHdr::ReadNextObj(m_poCurObjBlock, m_poHeader))
    1529             :         {
    1530       11315 :             nObjectSpace +=
    1531       11315 :                 m_poHeader->GetMapObjectSize(poExistingObjHdr->m_nType);
    1532       11315 :             apoSrcObjHdrs.emplace_back(poExistingObjHdr);
    1533       11315 :         }
    1534             : 
    1535             :         /* Check that there's really some place that can be recovered */
    1536        1062 :         if (nObjectSpace < m_poHeader->m_nRegularBlockSize - 20 -
    1537         531 :                                m_poCurObjBlock->GetNumUnusedBytes())
    1538             :         {
    1539             : #ifdef DEBUG_VERBOSE
    1540             :             CPLDebug("MITAB",
    1541             :                      "Compacting block at offset %d, %d objects valid, "
    1542             :                      "recovering %d bytes",
    1543             :                      m_poCurObjBlock->GetStartAddress(),
    1544             :                      static_cast<int>(apoSrcObjHdrs.size()),
    1545             :                      (m_poHeader->m_nRegularBlockSize - 20 -
    1546             :                       m_poCurObjBlock->GetNumUnusedBytes()) -
    1547             :                          nObjectSpace);
    1548             : #endif
    1549         271 :             m_poCurObjBlock->ClearObjects();
    1550             : 
    1551        2765 :             for (auto &poSrcObjHdrs : apoSrcObjHdrs)
    1552             :             {
    1553             :                 /*-----------------------------------------------------------------
    1554             :                  * Prepare and Write ObjHdr to this ObjBlock
    1555             :                  *----------------------------------------------------------------*/
    1556             :                 int nObjPtr =
    1557        2494 :                     m_poCurObjBlock->PrepareNewObject(poSrcObjHdrs.get());
    1558        4988 :                 if (nObjPtr < 0 ||
    1559        2494 :                     m_poCurObjBlock->CommitNewObject(poSrcObjHdrs.get()) != 0)
    1560             :                 {
    1561           0 :                     CPLError(CE_Failure, CPLE_FileIO,
    1562             :                              "Failed writing object header for feature id %d",
    1563           0 :                              poSrcObjHdrs->m_nId);
    1564           0 :                     return -1;
    1565             :                 }
    1566             : 
    1567             :                 /*-----------------------------------------------------------------
    1568             :                  * Update .ID Index
    1569             :                  *----------------------------------------------------------------*/
    1570        2494 :                 m_poIdIndex->SetObjPtr(poSrcObjHdrs->m_nId, nObjPtr);
    1571             :             }
    1572             :         }
    1573             :     }
    1574             : 
    1575       11568 :     if (m_poCurObjBlock->GetNumUnusedBytes() >= nObjSize)
    1576             :     {
    1577             :         /*-------------------------------------------------------------
    1578             :          * New object fits in current block, just update the spatial index
    1579             :          *------------------------------------------------------------*/
    1580             :         GInt32 nMinX, nMinY, nMaxX, nMaxY;
    1581       11308 :         m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1582             : 
    1583             :         /* Need to calculate the enlarged MBR that includes new object */
    1584       11308 :         nMinX = std::min(nMinX, poObjHdr->m_nMinX);
    1585       11308 :         nMinY = std::min(nMinY, poObjHdr->m_nMinY);
    1586       11308 :         nMaxX = std::max(nMaxX, poObjHdr->m_nMaxX);
    1587       11308 :         nMaxY = std::max(nMaxY, poObjHdr->m_nMaxY);
    1588             : 
    1589       11308 :         m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1590             : 
    1591       11308 :         if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(),
    1592       11308 :                                          nMinX, nMinY, nMaxX, nMaxY) != 0)
    1593           0 :             return -1;
    1594             :     }
    1595             :     else
    1596             :     {
    1597             :         /*-------------------------------------------------------------
    1598             :          * OK, the new object won't fit in the current block, need to split
    1599             :          * and update index.
    1600             :          * Split() does its job so that the current obj block will remain
    1601             :          * the best candidate to receive the new object. It also flushes
    1602             :          * everything to disk and will update m_poCurCoordBlock to point to
    1603             :          * the last coord block in the chain, ready to accept new data
    1604             :          *------------------------------------------------------------*/
    1605             :         auto poNewObjBlock = std::unique_ptr<TABMAPObjectBlock>(
    1606         260 :             SplitObjBlock(poObjHdr, nObjSize));
    1607             : 
    1608         260 :         if (poNewObjBlock == nullptr)
    1609           0 :             return -1; /* Split failed, error already reported. */
    1610             : 
    1611             :         /*-------------------------------------------------------------
    1612             :          * Update index with info about m_poCurObjectBlock *first*
    1613             :          * This is important since UpdateLeafEntry() needs the chain of
    1614             :          * index nodes preloaded by ChooseLeafEntry() in order to do its job
    1615             :          *------------------------------------------------------------*/
    1616         260 :         GInt32 nMinX = 0;
    1617         260 :         GInt32 nMinY = 0;
    1618         260 :         GInt32 nMaxX = 0;
    1619         260 :         GInt32 nMaxY = 0;
    1620         260 :         m_poCurObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1621         260 :         CPLAssert(nMinX <= nMaxX);
    1622             : 
    1623             :         /* Need to calculate the enlarged MBR that includes new object */
    1624         260 :         nMinX = std::min(nMinX, poObjHdr->m_nMinX);
    1625         260 :         nMinY = std::min(nMinY, poObjHdr->m_nMinY);
    1626         260 :         nMaxX = std::max(nMaxX, poObjHdr->m_nMaxX);
    1627         260 :         nMaxY = std::max(nMaxY, poObjHdr->m_nMaxY);
    1628             : 
    1629         260 :         m_poCurObjBlock->SetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1630             : 
    1631         260 :         if (m_poSpIndex->UpdateLeafEntry(m_poCurObjBlock->GetStartAddress(),
    1632         260 :                                          nMinX, nMinY, nMaxX, nMaxY) != 0)
    1633           0 :             return -1;
    1634             : 
    1635             :         /*-------------------------------------------------------------
    1636             :          * Add new obj block to index
    1637             :          *------------------------------------------------------------*/
    1638         260 :         poNewObjBlock->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1639         260 :         CPLAssert(nMinX <= nMaxX);
    1640             : 
    1641         260 :         if (m_poSpIndex->AddEntry(nMinX, nMinY, nMaxX, nMaxY,
    1642         520 :                                   poNewObjBlock->GetStartAddress()) != 0)
    1643           0 :             return -1;
    1644         260 :         const int nNextDepth = m_poSpIndex->GetCurMaxDepth() + 1;
    1645         260 :         m_poHeader->m_nMaxSpIndexDepth = static_cast<GByte>(std::max(
    1646         260 :             static_cast<int>(m_poHeader->m_nMaxSpIndexDepth), nNextDepth));
    1647             : 
    1648             :         /*-------------------------------------------------------------
    1649             :          * Implicitly delete second object block, no need to commit to file
    1650             :          *first since it is already been committed to disk by Split()
    1651             :          *------------------------------------------------------------*/
    1652             :     }
    1653             : 
    1654       11568 :     return 0;
    1655             : }
    1656             : 
    1657             : /**********************************************************************
    1658             :  *                   TABMAPFile::PrepareNewObjViaObjBlock()
    1659             :  *
    1660             :  * Used by TABMAPFile::PrepareNewObj() to prepare the current object
    1661             :  * data block to be ready to write the object to it. If the object block
    1662             :  * is full then it is inserted in the spatial index and committed to disk,
    1663             :  * and a new obj block is created.
    1664             :  *
    1665             :  * This method is used when "quick spatial index mode" is selected,
    1666             :  * i.e. faster write, but non-optimal spatial index.
    1667             :  *
    1668             :  * Returns 0 on success, -1 on error.
    1669             :  **********************************************************************/
    1670        3499 : int TABMAPFile::PrepareNewObjViaObjBlock(TABMAPObjHdr *poObjHdr)
    1671             : {
    1672             :     /*-------------------------------------------------------------
    1673             :      * We will need an object block... check if it exists and
    1674             :      * create it if it has not been created yet (first time for this file).
    1675             :      * We do not create the object block in the open() call because
    1676             :      * files that contained only "NONE" geometries ended up with empty
    1677             :      * object and spatial index blocks.
    1678             :      * Note: A coord block will be created only if needed later.
    1679             :      *------------------------------------------------------------*/
    1680        3499 :     if (m_poCurObjBlock == nullptr)
    1681             :     {
    1682          91 :         m_poCurObjBlock = new TABMAPObjectBlock(m_eAccessMode);
    1683             : 
    1684          91 :         int nBlockOffset = m_oBlockManager.AllocNewBlock("OBJECT");
    1685             : 
    1686          91 :         m_poCurObjBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    1687             :                                       nBlockOffset);
    1688             : 
    1689             :         // The reference to the first object block should
    1690             :         // actually go through the index blocks... this will be
    1691             :         // updated when file is closed.
    1692          91 :         m_poHeader->m_nFirstIndexBlock = nBlockOffset;
    1693             :     }
    1694             : 
    1695             :     /*-----------------------------------------------------------------
    1696             :      * Fetch new object size, make sure there is enough room in obj.
    1697             :      * block for new object, and save/create a new one if necessary.
    1698             :      *----------------------------------------------------------------*/
    1699        3499 :     const int nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
    1700        3499 :     if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize)
    1701             :     {
    1702             :         /*-------------------------------------------------------------
    1703             :          * OK, the new object won't fit in the current block. Add the
    1704             :          * current block to the spatial index, commit it to disk and init
    1705             :          * a new block
    1706             :          *------------------------------------------------------------*/
    1707          91 :         CommitObjAndCoordBlocks(FALSE);
    1708             : 
    1709          91 :         if (m_poCurObjBlock->InitNewBlock(
    1710          91 :                 m_fp, m_poHeader->m_nRegularBlockSize,
    1711          91 :                 m_oBlockManager.AllocNewBlock("OBJECT")) != 0)
    1712           0 :             return -1; /* Error already reported */
    1713             : 
    1714             :         /*-------------------------------------------------------------
    1715             :          * Coord block has been committed to disk but not deleted.
    1716             :          * Delete it to require the creation of a new coord block chain
    1717             :          * as needed.
    1718             :          *-------------------------------------------------------------*/
    1719          91 :         if (m_poCurCoordBlock)
    1720             :         {
    1721           2 :             delete m_poCurCoordBlock;
    1722           2 :             m_poCurCoordBlock = nullptr;
    1723             :         }
    1724             :     }
    1725             : 
    1726        3499 :     return 0;
    1727             : }
    1728             : 
    1729             : /**********************************************************************
    1730             :  *                   TABMAPFile::CommitNewObj()
    1731             :  *
    1732             :  * Commit object header data to the ObjBlock. Should be called after
    1733             :  * PrepareNewObj, once all members of the ObjHdr have been set.
    1734             :  *
    1735             :  * Returns 0 on success, -1 on error.
    1736             :  **********************************************************************/
    1737       15127 : int TABMAPFile::CommitNewObj(TABMAPObjHdr *poObjHdr)
    1738             : {
    1739             :     // Nothing to do for NONE objects
    1740       15127 :     if (poObjHdr->m_nType == TAB_GEOM_NONE)
    1741             :     {
    1742          60 :         return 0;
    1743             :     }
    1744             : 
    1745             :     /* Update this now so that PrepareCoordBlock() doesn't try to old an older
    1746             :      */
    1747             :     /* block */
    1748       15067 :     if (m_poCurCoordBlock != nullptr)
    1749         372 :         m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock->GetStartAddress());
    1750             : 
    1751             :     /* So that GetExtent() is up-to-date */
    1752       15067 :     if (m_poSpIndex != nullptr)
    1753             :     {
    1754       14614 :         m_poSpIndex->GetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin,
    1755       14614 :                             m_poHeader->m_nXMax, m_poHeader->m_nYMax);
    1756             :     }
    1757             : 
    1758       15067 :     return m_poCurObjBlock->CommitNewObject(poObjHdr);
    1759             : }
    1760             : 
    1761             : /**********************************************************************
    1762             :  *                   TABMAPFile::CommitObjAndCoordBlocks()
    1763             :  *
    1764             :  * Commit the TABMAPObjBlock and TABMAPCoordBlock to disk.
    1765             :  *
    1766             :  * The objects are deleted from memory if bDeleteObjects==TRUE.
    1767             :  *
    1768             :  * Returns 0 on success, -1 on error.
    1769             :  **********************************************************************/
    1770       12250 : int TABMAPFile::CommitObjAndCoordBlocks(GBool bDeleteObjects /*=FALSE*/)
    1771             : {
    1772       12250 :     int nStatus = 0;
    1773             : 
    1774             :     /*-----------------------------------------------------------------
    1775             :      * First check that a objBlock has been created.  It is possible to have
    1776             :      * no object block in files that contain only "NONE" geometries.
    1777             :      *----------------------------------------------------------------*/
    1778       12250 :     if (m_poCurObjBlock == nullptr)
    1779          36 :         return 0;
    1780             : 
    1781       12214 :     if (m_eAccessMode == TABRead)
    1782             :     {
    1783           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1784             :                  "CommitObjAndCoordBlocks() failed: file not opened for write "
    1785             :                  "access.");
    1786           0 :         return -1;
    1787             :     }
    1788             : 
    1789       12214 :     if (!m_bLastOpWasWrite)
    1790             :     {
    1791        1126 :         if (bDeleteObjects)
    1792             :         {
    1793        1088 :             delete m_poCurCoordBlock;
    1794        1088 :             m_poCurCoordBlock = nullptr;
    1795        1088 :             delete m_poCurObjBlock;
    1796        1088 :             m_poCurObjBlock = nullptr;
    1797             :         }
    1798        1126 :         return 0;
    1799             :     }
    1800       11088 :     m_bLastOpWasWrite = FALSE;
    1801             : 
    1802             :     /*-----------------------------------------------------------------
    1803             :      * We need to flush the coord block if there was one
    1804             :      * since a list of coord blocks can belong to only one obj. block
    1805             :      *----------------------------------------------------------------*/
    1806       11088 :     if (m_poCurCoordBlock)
    1807             :     {
    1808             :         // Update the m_nMaxCoordBufSize member in the header block
    1809             :         //
    1810         210 :         int nTotalCoordSize = m_poCurCoordBlock->GetNumBlocksInChain() *
    1811         210 :                               m_poHeader->m_nRegularBlockSize;
    1812         210 :         if (nTotalCoordSize > m_poHeader->m_nMaxCoordBufSize)
    1813          39 :             m_poHeader->m_nMaxCoordBufSize = nTotalCoordSize;
    1814             : 
    1815             :         // Update the references to this coord block in the MAPObjBlock
    1816             :         //
    1817         210 :         m_poCurObjBlock->AddCoordBlockRef(m_poCurCoordBlock->GetStartAddress());
    1818         210 :         nStatus = m_poCurCoordBlock->CommitToFile();
    1819             : 
    1820         210 :         if (bDeleteObjects)
    1821             :         {
    1822          64 :             delete m_poCurCoordBlock;
    1823          64 :             m_poCurCoordBlock = nullptr;
    1824             :         }
    1825             :     }
    1826             : 
    1827             :     /*-----------------------------------------------------------------
    1828             :      * Commit the obj block
    1829             :      *----------------------------------------------------------------*/
    1830       11088 :     if (nStatus == 0)
    1831             :     {
    1832       11088 :         nStatus = m_poCurObjBlock->CommitToFile();
    1833             :     }
    1834             : 
    1835             :     /*-----------------------------------------------------------------
    1836             :      * Update the spatial index ** only in "quick spatial index" mode **
    1837             :      * In the (default) optimized spatial index mode, the spatial index
    1838             :      * is already maintained up to date as part of inserting the objects in
    1839             :      * PrepareNewObj().
    1840             :      *
    1841             :      * Spatial index will be created here if it was not done yet.
    1842             :      *----------------------------------------------------------------*/
    1843       11088 :     if (nStatus == 0 && m_bQuickSpatialIndexMode)
    1844             :     {
    1845         210 :         if (m_poSpIndex == nullptr)
    1846             :         {
    1847             :             // Spatial Index not created yet...
    1848          91 :             m_poSpIndex = new TABMAPIndexBlock(m_eAccessMode);
    1849             : 
    1850          91 :             m_poSpIndex->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    1851             :                                       m_oBlockManager.AllocNewBlock("INDEX"));
    1852          91 :             m_poSpIndex->SetMAPBlockManagerRef(&m_oBlockManager);
    1853             : 
    1854          91 :             m_poHeader->m_nFirstIndexBlock = m_poSpIndex->GetNodeBlockPtr();
    1855             :         }
    1856             : 
    1857             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    1858         210 :         m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
    1859         210 :         nStatus = m_poSpIndex->AddEntry(nXMin, nYMin, nXMax, nYMax,
    1860         210 :                                         m_poCurObjBlock->GetStartAddress());
    1861             : 
    1862         210 :         const int nNextDepth = m_poSpIndex->GetCurMaxDepth() + 1;
    1863         210 :         m_poHeader->m_nMaxSpIndexDepth = static_cast<GByte>(std::max(
    1864         210 :             static_cast<int>(m_poHeader->m_nMaxSpIndexDepth), nNextDepth));
    1865             :     }
    1866             : 
    1867             :     /*-----------------------------------------------------------------
    1868             :      * Delete obj block only if requested
    1869             :      *----------------------------------------------------------------*/
    1870       11088 :     if (bDeleteObjects)
    1871             :     {
    1872        9631 :         delete m_poCurObjBlock;
    1873        9631 :         m_poCurObjBlock = nullptr;
    1874             :     }
    1875             : 
    1876       11088 :     return nStatus;
    1877             : }
    1878             : 
    1879             : /**********************************************************************
    1880             :  *                   TABMAPFile::LoadObjAndCoordBlocks()
    1881             :  *
    1882             :  * Load the TABMAPObjBlock at specified address and corresponding
    1883             :  * TABMAPCoordBlock, ready to write new objects to them.
    1884             :  *
    1885             :  * It is assumed that pre-existing m_poCurObjBlock and m_poCurCoordBlock
    1886             :  * have been flushed to disk already using CommitObjAndCoordBlocks()
    1887             :  *
    1888             :  * Returns 0 on success, -1 on error.
    1889             :  **********************************************************************/
    1890       10719 : int TABMAPFile::LoadObjAndCoordBlocks(GInt32 nBlockPtr)
    1891             : {
    1892             :     /*-----------------------------------------------------------------
    1893             :      * In Write mode, if an object block is already in memory then flush it
    1894             :      *----------------------------------------------------------------*/
    1895       10719 :     if (m_eAccessMode != TABRead && m_poCurObjBlock != nullptr)
    1896             :     {
    1897           0 :         int nStatus = CommitObjAndCoordBlocks(TRUE);
    1898           0 :         if (nStatus != 0)
    1899           0 :             return nStatus;
    1900             :     }
    1901             : 
    1902             :     /*-----------------------------------------------------------------
    1903             :      * Load Obj Block
    1904             :      *----------------------------------------------------------------*/
    1905       21438 :     TABRawBinBlock *poBlock = TABCreateMAPBlockFromFile(
    1906       10719 :         m_fp, nBlockPtr, m_poHeader->m_nRegularBlockSize, TRUE, TABReadWrite);
    1907       10719 :     if (poBlock != nullptr && poBlock->GetBlockClass() == TABMAP_OBJECT_BLOCK)
    1908             :     {
    1909       10719 :         m_poCurObjBlock = cpl::down_cast<TABMAPObjectBlock *>(poBlock);
    1910       10719 :         poBlock = nullptr;
    1911             :     }
    1912             :     else
    1913             :     {
    1914           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1915             :                  "LoadObjAndCoordBlocks() failed for object block at %d.",
    1916             :                  nBlockPtr);
    1917           0 :         return -1;
    1918             :     }
    1919             : 
    1920             :     /*-----------------------------------------------------------------
    1921             :      * Load the last coord block in the chain
    1922             :      *----------------------------------------------------------------*/
    1923       10719 :     if (m_poCurObjBlock->GetLastCoordBlockAddress() == 0)
    1924             :     {
    1925       10573 :         m_poCurCoordBlock = nullptr;
    1926       10573 :         return 0;
    1927             :     }
    1928             : 
    1929         146 :     poBlock = TABCreateMAPBlockFromFile(
    1930         146 :         m_fp, m_poCurObjBlock->GetLastCoordBlockAddress(),
    1931         146 :         m_poHeader->m_nRegularBlockSize, TRUE, TABReadWrite);
    1932         146 :     if (poBlock != nullptr && poBlock->GetBlockClass() == TABMAP_COORD_BLOCK)
    1933             :     {
    1934         146 :         m_poCurCoordBlock = cpl::down_cast<TABMAPCoordBlock *>(poBlock);
    1935         146 :         m_poCurCoordBlock->SetMAPBlockManagerRef(&m_oBlockManager);
    1936         146 :         poBlock = nullptr;
    1937             :     }
    1938             :     else
    1939             :     {
    1940           0 :         CPLError(CE_Failure, CPLE_FileIO,
    1941             :                  "LoadObjAndCoordBlocks() failed for coord block at %d.",
    1942           0 :                  m_poCurObjBlock->GetLastCoordBlockAddress());
    1943           0 :         return -1;
    1944             :     }
    1945             : 
    1946         146 :     return 0;
    1947             : }
    1948             : 
    1949             : /**********************************************************************
    1950             :  *                   TABMAPFile::SplitObjBlock()
    1951             :  *
    1952             :  * Split m_poCurObjBlock using Guttman algorithm.
    1953             :  *
    1954             :  * SplitObjBlock() doe its job so that the current obj block will remain
    1955             :  * the best candidate to receive the new object to add. It also flushes
    1956             :  * everything to disk and will update m_poCurCoordBlock to point to the
    1957             :  * last coord block in the chain, ready to accept new data
    1958             :  *
    1959             :  * Updates to the spatial index are left to the caller.
    1960             :  *
    1961             :  * Returns the TABMAPObjBlock of the second block for use by the caller
    1962             :  * in updating the spatial index, or NULL in case of error.
    1963             :  **********************************************************************/
    1964         260 : TABMAPObjectBlock *TABMAPFile::SplitObjBlock(TABMAPObjHdr *poObjHdrToAdd,
    1965             :                                              int nSizeOfObjToAdd)
    1966             : {
    1967         520 :     std::vector<std::unique_ptr<TABMAPObjHdr>> apoSrcObjHdrs;
    1968             : 
    1969             :     /*-----------------------------------------------------------------
    1970             :      * Read all object headers
    1971             :      *----------------------------------------------------------------*/
    1972         260 :     m_poCurObjBlock->Rewind();
    1973             :     while (auto poObjHdr =
    1974        9081 :                TABMAPObjHdr::ReadNextObj(m_poCurObjBlock, m_poHeader))
    1975             :     {
    1976        8821 :         apoSrcObjHdrs.emplace_back(poObjHdr);
    1977        8821 :     }
    1978             :     /* PickSeedsForSplit (reasonably) assumes at least 2 nodes */
    1979         260 :     CPLAssert(apoSrcObjHdrs.size() > 1);
    1980             : 
    1981             :     /*-----------------------------------------------------------------
    1982             :      * Reset current obj and coord block
    1983             :      *----------------------------------------------------------------*/
    1984         260 :     GInt32 nFirstSrcCoordBlock = m_poCurObjBlock->GetFirstCoordBlockAddress();
    1985             : 
    1986         260 :     m_poCurObjBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    1987         260 :                                   m_poCurObjBlock->GetStartAddress());
    1988             : 
    1989         520 :     std::unique_ptr<TABMAPCoordBlock> poSrcCoordBlock(m_poCurCoordBlock);
    1990         260 :     m_poCurCoordBlock = nullptr;
    1991             : 
    1992             :     /*-----------------------------------------------------------------
    1993             :      * Create new obj and coord block
    1994             :      *----------------------------------------------------------------*/
    1995         520 :     auto poNewObjBlock = std::make_unique<TABMAPObjectBlock>(m_eAccessMode);
    1996         260 :     poNewObjBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    1997             :                                 m_oBlockManager.AllocNewBlock("OBJECT"));
    1998             : 
    1999             :     /* Use existing center of other block in case we have compressed objects
    2000             :        and freeze it */
    2001         260 :     poNewObjBlock->SetCenterFromOtherBlock(m_poCurObjBlock);
    2002             : 
    2003             :     /* Coord block will be alloc'd automatically*/
    2004         260 :     TABMAPCoordBlock *poNewCoordBlock = nullptr;
    2005             : 
    2006             :     /*-----------------------------------------------------------------
    2007             :      * Pick Seeds for each block
    2008             :      *----------------------------------------------------------------*/
    2009         520 :     std::vector<TABMAPIndexEntry> asSrcEntries;
    2010         260 :     asSrcEntries.reserve(apoSrcObjHdrs.size());
    2011        9081 :     for (const auto &poSrcObjHdrs : apoSrcObjHdrs)
    2012             :     {
    2013             :         TABMAPIndexEntry sEntry;
    2014        8821 :         sEntry.nBlockPtr = 0;
    2015        8821 :         sEntry.XMin = poSrcObjHdrs->m_nMinX;
    2016        8821 :         sEntry.YMin = poSrcObjHdrs->m_nMinY;
    2017        8821 :         sEntry.XMax = poSrcObjHdrs->m_nMaxX;
    2018        8821 :         sEntry.YMax = poSrcObjHdrs->m_nMaxY;
    2019        8821 :         asSrcEntries.emplace_back(sEntry);
    2020             :     }
    2021             : 
    2022             :     int nSeed1, nSeed2;
    2023         260 :     TABMAPIndexBlock::PickSeedsForSplit(
    2024         260 :         asSrcEntries.data(), static_cast<int>(asSrcEntries.size()), -1,
    2025             :         poObjHdrToAdd->m_nMinX, poObjHdrToAdd->m_nMinY, poObjHdrToAdd->m_nMaxX,
    2026             :         poObjHdrToAdd->m_nMaxY, nSeed1, nSeed2);
    2027             : 
    2028             :     /*-----------------------------------------------------------------
    2029             :      * Assign the seeds to their respective block
    2030             :      *----------------------------------------------------------------*/
    2031             :     // Insert nSeed1 in this block
    2032         260 :     if (MoveObjToBlock(apoSrcObjHdrs[nSeed1].get(), poSrcCoordBlock.get(),
    2033         260 :                        m_poCurObjBlock, &m_poCurCoordBlock) <= 0)
    2034             :     {
    2035           0 :         return nullptr;
    2036             :     }
    2037             : 
    2038             :     // Move nSeed2 to 2nd block
    2039         260 :     if (MoveObjToBlock(apoSrcObjHdrs[nSeed2].get(), poSrcCoordBlock.get(),
    2040         260 :                        poNewObjBlock.get(), &poNewCoordBlock) <= 0)
    2041             :     {
    2042           0 :         return nullptr;
    2043             :     }
    2044             : 
    2045             :     /*-----------------------------------------------------------------
    2046             :      * Go through the rest of the entries and assign them to one
    2047             :      * of the 2 blocks
    2048             :      *
    2049             :      * Criteria is minimal area difference.
    2050             :      * Resolve ties by adding the entry to the block with smaller total
    2051             :      * area, then to the one with fewer entries, then to either.
    2052             :      *----------------------------------------------------------------*/
    2053        9081 :     for (int iEntry = 0; iEntry < static_cast<int>(apoSrcObjHdrs.size());
    2054             :          iEntry++)
    2055             :     {
    2056        8821 :         if (iEntry == nSeed1 || iEntry == nSeed2)
    2057         520 :             continue;
    2058             : 
    2059        8301 :         TABMAPObjHdr *poObjHdr = apoSrcObjHdrs[iEntry].get();
    2060             : 
    2061        8301 :         int nObjSize = m_poHeader->GetMapObjectSize(poObjHdr->m_nType);
    2062             : 
    2063             :         // If one of the two blocks is almost full then all remaining
    2064             :         // entries should go to the other block
    2065        8301 :         if (m_poCurObjBlock->GetNumUnusedBytes() < nObjSize + nSizeOfObjToAdd)
    2066             :         {
    2067           0 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock.get(),
    2068           0 :                                poNewObjBlock.get(), &poNewCoordBlock) <= 0)
    2069           0 :                 return nullptr;
    2070           0 :             continue;
    2071             :         }
    2072        8301 :         else if (poNewObjBlock->GetNumUnusedBytes() <
    2073        8301 :                  nObjSize + nSizeOfObjToAdd)
    2074             :         {
    2075           0 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock.get(), m_poCurObjBlock,
    2076           0 :                                &m_poCurCoordBlock) <= 0)
    2077           0 :                 return nullptr;
    2078           0 :             continue;
    2079             :         }
    2080             : 
    2081             :         // Decide which of the two blocks to put this entry in
    2082             :         GInt32 nXMin, nYMin, nXMax, nYMax;
    2083        8301 :         m_poCurObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
    2084        8301 :         CPLAssert(nXMin <= nXMax);
    2085        8301 :         double dAreaDiff1 = TABMAPIndexBlock::ComputeAreaDiff(
    2086             :             nXMin, nYMin, nXMax, nYMax, poObjHdr->m_nMinX, poObjHdr->m_nMinY,
    2087             :             poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
    2088             : 
    2089        8301 :         poNewObjBlock->GetMBR(nXMin, nYMin, nXMax, nYMax);
    2090        8301 :         CPLAssert(nXMin <= nXMax);
    2091        8301 :         double dAreaDiff2 = TABMAPIndexBlock::ComputeAreaDiff(
    2092             :             nXMin, nYMin, nXMax, nYMax, poObjHdr->m_nMinX, poObjHdr->m_nMinY,
    2093             :             poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
    2094             : 
    2095        8301 :         if (dAreaDiff1 < dAreaDiff2)
    2096             :         {
    2097             :             // This entry stays in this block
    2098        3434 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock.get(), m_poCurObjBlock,
    2099        3434 :                                &m_poCurCoordBlock) <= 0)
    2100           0 :                 return nullptr;
    2101             :         }
    2102             :         else
    2103             :         {
    2104             :             // This entry goes to new block
    2105        4867 :             if (MoveObjToBlock(poObjHdr, poSrcCoordBlock.get(),
    2106        4867 :                                poNewObjBlock.get(), &poNewCoordBlock) <= 0)
    2107           0 :                 return nullptr;
    2108             :         }
    2109             :     }
    2110             : 
    2111             :     /*-----------------------------------------------------------------
    2112             :      * Delete second coord block if one was created
    2113             :      * Refs to coord block were kept up to date by MoveObjToBlock()
    2114             :      * We just need to commit to file and delete the object now.
    2115             :      *----------------------------------------------------------------*/
    2116         260 :     if (poNewCoordBlock)
    2117             :     {
    2118          16 :         if (poNewCoordBlock->CommitToFile() != 0)
    2119             :         {
    2120           0 :             return nullptr;
    2121             :         }
    2122          16 :         delete poNewCoordBlock;
    2123             :     }
    2124             : 
    2125             :     /*-----------------------------------------------------------------
    2126             :      * Release unused coord. data blocks
    2127             :      *----------------------------------------------------------------*/
    2128         260 :     if (poSrcCoordBlock)
    2129             :     {
    2130          16 :         if (poSrcCoordBlock->GetStartAddress() != nFirstSrcCoordBlock)
    2131             :         {
    2132           0 :             if (poSrcCoordBlock->GotoByteInFile(nFirstSrcCoordBlock, TRUE) != 0)
    2133             :             {
    2134           0 :                 return nullptr;
    2135             :             }
    2136             :         }
    2137             : 
    2138          16 :         int nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock();
    2139          32 :         while (poSrcCoordBlock != nullptr)
    2140             :         {
    2141             :             // Mark this block as deleted
    2142          16 :             if (poSrcCoordBlock->CommitAsDeleted(
    2143          16 :                     m_oBlockManager.GetFirstGarbageBlock()) != 0)
    2144             :             {
    2145           0 :                 return nullptr;
    2146             :             }
    2147          16 :             m_oBlockManager.PushGarbageBlockAsFirst(
    2148          16 :                 poSrcCoordBlock->GetStartAddress());
    2149             : 
    2150             :             // Advance to next
    2151          16 :             if (nNextCoordBlock > 0)
    2152             :             {
    2153           0 :                 if (poSrcCoordBlock->GotoByteInFile(nNextCoordBlock, TRUE) != 0)
    2154           0 :                     return nullptr;
    2155             : 
    2156           0 :                 nNextCoordBlock = poSrcCoordBlock->GetNextCoordBlock();
    2157             :             }
    2158             :             else
    2159             :             {
    2160             :                 // end of chain
    2161          16 :                 poSrcCoordBlock.reset();
    2162             :             }
    2163             :         }
    2164             :     }
    2165             : 
    2166         260 :     if (poNewObjBlock->CommitToFile() != 0)
    2167           0 :         return nullptr;
    2168             : 
    2169         260 :     return poNewObjBlock.release();
    2170             : }
    2171             : 
    2172             : /**********************************************************************
    2173             :  *                   TABMAPFile::MoveObjToBlock()
    2174             :  *
    2175             :  * Moves an object and its coord data to a new ObjBlock. Used when
    2176             :  * splitting Obj Blocks.
    2177             :  *
    2178             :  * May update the value of ppoCoordBlock if a new coord block had to
    2179             :  * be created.
    2180             :  *
    2181             :  * Returns the address where new object is stored on success, -1 on error.
    2182             :  **********************************************************************/
    2183        8821 : int TABMAPFile::MoveObjToBlock(TABMAPObjHdr *poObjHdr,
    2184             :                                TABMAPCoordBlock *poSrcCoordBlock,
    2185             :                                TABMAPObjectBlock *poDstObjBlock,
    2186             :                                TABMAPCoordBlock **ppoDstCoordBlock)
    2187             : {
    2188             :     /*-----------------------------------------------------------------
    2189             :      * Copy Coord data if applicable
    2190             :      * We use a temporary TABFeature object to handle the reading/writing
    2191             :      * of coord block data.
    2192             :      *----------------------------------------------------------------*/
    2193        8821 :     if (m_poHeader->MapObjectUsesCoordBlock(poObjHdr->m_nType))
    2194             :     {
    2195             :         TABMAPObjHdrWithCoord *poObjHdrCoord =
    2196         224 :             cpl::down_cast<TABMAPObjHdrWithCoord *>(poObjHdr);
    2197         224 :         OGRFeatureDefn *poDummyDefn = new OGRFeatureDefn;
    2198             :         // Ref count defaults to 0... set it to 1
    2199         224 :         poDummyDefn->Reference();
    2200             : 
    2201             :         TABFeature *poFeature =
    2202         224 :             TABFeature::CreateFromMapInfoType(poObjHdr->m_nType, poDummyDefn);
    2203             : 
    2204         224 :         if (PrepareCoordBlock(poObjHdrCoord->m_nType, poDstObjBlock,
    2205         224 :                               ppoDstCoordBlock) != 0)
    2206           0 :             return -1;
    2207             : 
    2208         224 :         GInt32 nSrcCoordPtr = poObjHdrCoord->m_nCoordBlockPtr;
    2209             : 
    2210             :         /* Copy Coord data
    2211             :          * poObjHdrCoord->m_nCoordBlockPtr will be set by WriteGeometry...
    2212             :          * We pass second arg to GotoByteInFile() to force reading from file
    2213             :          * if nSrcCoordPtr is not in current block
    2214             :          */
    2215         224 :         if (poSrcCoordBlock->GotoByteInFile(nSrcCoordPtr, TRUE) != 0 ||
    2216         224 :             poFeature->ReadGeometryFromMAPFile(this, poObjHdr,
    2217             :                                                TRUE /* bCoordDataOnly */,
    2218         448 :                                                &poSrcCoordBlock) != 0 ||
    2219         224 :             poFeature->WriteGeometryToMAPFile(this, poObjHdr,
    2220             :                                               TRUE /* bCoordDataOnly */,
    2221         224 :                                               ppoDstCoordBlock) != 0)
    2222             :         {
    2223           0 :             delete poFeature;
    2224           0 :             delete poDummyDefn;
    2225           0 :             return -1;
    2226             :         }
    2227             : 
    2228             :         // Update the references to dest coord block in the MAPObjBlock
    2229             :         // in case new block has been alloc'd since PrepareCoordBlock()
    2230             :         //
    2231         224 :         poDstObjBlock->AddCoordBlockRef((*ppoDstCoordBlock)->GetStartAddress());
    2232             :         /* Cleanup */
    2233         224 :         delete poFeature;
    2234         224 :         poDummyDefn->Release();
    2235             :     }
    2236             : 
    2237             :     /*-----------------------------------------------------------------
    2238             :      * Prepare and Write ObjHdr to this ObjBlock
    2239             :      *----------------------------------------------------------------*/
    2240        8821 :     int nObjPtr = poDstObjBlock->PrepareNewObject(poObjHdr);
    2241        8821 :     if (nObjPtr < 0 || poDstObjBlock->CommitNewObject(poObjHdr) != 0)
    2242             :     {
    2243           0 :         CPLError(CE_Failure, CPLE_FileIO,
    2244             :                  "Failed writing object header for feature id %d",
    2245             :                  poObjHdr->m_nId);
    2246           0 :         return -1;
    2247             :     }
    2248             : 
    2249             :     /*-----------------------------------------------------------------
    2250             :      * Update .ID Index
    2251             :      *----------------------------------------------------------------*/
    2252        8821 :     m_poIdIndex->SetObjPtr(poObjHdr->m_nId, nObjPtr);
    2253             : 
    2254        8821 :     return nObjPtr;
    2255             : }
    2256             : 
    2257             : /**********************************************************************
    2258             :  *                   TABMAPFile::PrepareCoordBlock()
    2259             :  *
    2260             :  * Prepare the coord block to receive an object of specified type if one
    2261             :  * is needed, and update corresponding members in ObjBlock.
    2262             :  *
    2263             :  * May update the value of ppoCoordBlock and Returns 0 on success, -1 on error.
    2264             :  **********************************************************************/
    2265       15291 : int TABMAPFile::PrepareCoordBlock(int nObjType, TABMAPObjectBlock *poObjBlock,
    2266             :                                   TABMAPCoordBlock **ppoCoordBlock)
    2267             : {
    2268             : 
    2269             :     /*-----------------------------------------------------------------
    2270             :      * Prepare Coords block...
    2271             :      * create a new TABMAPCoordBlock if it was not done yet.
    2272             :      * Note that in write mode, TABCollections require read/write access
    2273             :      * to the coord block.
    2274             :      *----------------------------------------------------------------*/
    2275       15291 :     if (m_poHeader->MapObjectUsesCoordBlock(nObjType))
    2276             :     {
    2277         577 :         if (*ppoCoordBlock == nullptr)
    2278             :         {
    2279          72 :             *ppoCoordBlock = new TABMAPCoordBlock(
    2280          72 :                 m_eAccessMode == TABWrite ? TABReadWrite : m_eAccessMode);
    2281             :             (*ppoCoordBlock)
    2282          72 :                 ->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    2283             :                                m_oBlockManager.AllocNewBlock("COORD"));
    2284          72 :             (*ppoCoordBlock)->SetMAPBlockManagerRef(&m_oBlockManager);
    2285             : 
    2286             :             // Set the references to this coord block in the MAPObjBlock
    2287          72 :             poObjBlock->AddCoordBlockRef((*ppoCoordBlock)->GetStartAddress());
    2288             :         }
    2289             :         /* If we are not at the end of the chain of coordinate blocks, then */
    2290             :         /* reload us */
    2291        1010 :         else if ((*ppoCoordBlock)->GetStartAddress() !=
    2292         505 :                  poObjBlock->GetLastCoordBlockAddress())
    2293             :         {
    2294           3 :             TABRawBinBlock *poBlock = TABCreateMAPBlockFromFile(
    2295             :                 m_fp, poObjBlock->GetLastCoordBlockAddress(),
    2296           3 :                 m_poHeader->m_nRegularBlockSize, TRUE, TABReadWrite);
    2297           6 :             if (poBlock != nullptr &&
    2298           3 :                 poBlock->GetBlockClass() == TABMAP_COORD_BLOCK)
    2299             :             {
    2300           3 :                 delete *ppoCoordBlock;
    2301           3 :                 *ppoCoordBlock = cpl::down_cast<TABMAPCoordBlock *>(poBlock);
    2302           3 :                 (*ppoCoordBlock)->SetMAPBlockManagerRef(&m_oBlockManager);
    2303             :             }
    2304             :             else
    2305             :             {
    2306           0 :                 delete poBlock;
    2307           0 :                 CPLError(
    2308             :                     CE_Failure, CPLE_FileIO,
    2309             :                     "LoadObjAndCoordBlocks() failed for coord block at %d.",
    2310             :                     poObjBlock->GetLastCoordBlockAddress());
    2311           0 :                 return -1;
    2312             :             }
    2313             :         }
    2314             : 
    2315         577 :         if ((*ppoCoordBlock)->GetNumUnusedBytes() < 4)
    2316             :         {
    2317           0 :             int nNewBlockOffset = m_oBlockManager.AllocNewBlock("COORD");
    2318           0 :             (*ppoCoordBlock)->SetNextCoordBlock(nNewBlockOffset);
    2319           0 :             CPL_IGNORE_RET_VAL((*ppoCoordBlock)->CommitToFile());
    2320             :             (*ppoCoordBlock)
    2321           0 :                 ->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    2322             :                                nNewBlockOffset);
    2323           0 :             poObjBlock->AddCoordBlockRef((*ppoCoordBlock)->GetStartAddress());
    2324             :         }
    2325             : 
    2326             :         // Make sure read/write pointer is at the end of the block
    2327         577 :         (*ppoCoordBlock)->SeekEnd();
    2328             : 
    2329         577 :         if (CPLGetLastErrorType() == CE_Failure)
    2330           0 :             return -1;
    2331             :     }
    2332             : 
    2333       15291 :     return 0;
    2334             : }
    2335             : 
    2336             : /**********************************************************************
    2337             :  *                   TABMAPFile::GetCurObjType()
    2338             :  *
    2339             :  * Return the MapInfo object type of the object that the m_poCurObjBlock
    2340             :  * is pointing to.  This value is set after a call to MoveToObjId().
    2341             :  *
    2342             :  * Returns a value >= 0 on success, -1 on error.
    2343             :  **********************************************************************/
    2344     1298170 : TABGeomType TABMAPFile::GetCurObjType()
    2345             : {
    2346     1298170 :     return m_nCurObjType;
    2347             : }
    2348             : 
    2349             : /**********************************************************************
    2350             :  *                   TABMAPFile::GetCurObjId()
    2351             :  *
    2352             :  * Return the MapInfo object id of the object that the m_poCurObjBlock
    2353             :  * is pointing to.  This value is set after a call to MoveToObjId().
    2354             :  *
    2355             :  * Returns a value >= 0 on success, -1 on error.
    2356             :  **********************************************************************/
    2357      572557 : int TABMAPFile::GetCurObjId()
    2358             : {
    2359      572557 :     return m_nCurObjId;
    2360             : }
    2361             : 
    2362             : /**********************************************************************
    2363             :  *                   TABMAPFile::GetCurObjBlock()
    2364             :  *
    2365             :  * Return the m_poCurObjBlock.  If MoveToObjId() has previously been
    2366             :  * called then m_poCurObjBlock points to the beginning of the current
    2367             :  * object data.
    2368             :  *
    2369             :  * Returns a reference to an object owned by this TABMAPFile object, or
    2370             :  * NULL on error.
    2371             :  **********************************************************************/
    2372      572557 : TABMAPObjectBlock *TABMAPFile::GetCurObjBlock()
    2373             : {
    2374      572557 :     return m_poCurObjBlock;
    2375             : }
    2376             : 
    2377             : /**********************************************************************
    2378             :  *                   TABMAPFile::GetCurCoordBlock()
    2379             :  *
    2380             :  * Return the m_poCurCoordBlock.  This function should be used after
    2381             :  * PrepareNewObj() to get the reference to the coord block that has
    2382             :  * just been initialized.
    2383             :  *
    2384             :  * Returns a reference to an object owned by this TABMAPFile object, or
    2385             :  * NULL on error.
    2386             :  **********************************************************************/
    2387         353 : TABMAPCoordBlock *TABMAPFile::GetCurCoordBlock()
    2388             : {
    2389         353 :     return m_poCurCoordBlock;
    2390             : }
    2391             : 
    2392             : /**********************************************************************
    2393             :  *                   TABMAPFile::GetCoordBlock()
    2394             :  *
    2395             :  * Return a TABMAPCoordBlock object ready to read coordinates from it.
    2396             :  * The block that contains nFileOffset will automatically be
    2397             :  * loaded, and if nFileOffset is the beginning of a new block then the
    2398             :  * pointer will be moved to the beginning of the data.
    2399             :  *
    2400             :  * The contents of the returned object is only valid until the next call
    2401             :  * to GetCoordBlock().
    2402             :  *
    2403             :  * Returns a reference to an object owned by this TABMAPFile object, or
    2404             :  * NULL on error.
    2405             :  **********************************************************************/
    2406        2063 : TABMAPCoordBlock *TABMAPFile::GetCoordBlock(int nFileOffset)
    2407             : {
    2408        2063 :     if (m_poCurCoordBlock == nullptr)
    2409             :     {
    2410          25 :         m_poCurCoordBlock = new TABMAPCoordBlock(m_eAccessMode);
    2411          25 :         m_poCurCoordBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize);
    2412          25 :         m_poCurCoordBlock->SetMAPBlockManagerRef(&m_oBlockManager);
    2413             :     }
    2414             : 
    2415             :     /*-----------------------------------------------------------------
    2416             :      * Use GotoByteInFile() to go to the requested location.  This will
    2417             :      * force loading the block if necessary and reading its header.
    2418             :      * If nFileOffset is at the beginning of the requested block, then
    2419             :      * we make sure to move the read pointer past the 8 bytes header
    2420             :      * to be ready to read coordinates data
    2421             :      *----------------------------------------------------------------*/
    2422        2063 :     if (m_poCurCoordBlock->GotoByteInFile(nFileOffset, TRUE) != 0)
    2423             :     {
    2424             :         // Failed... an error has already been reported.
    2425           0 :         return nullptr;
    2426             :     }
    2427             : 
    2428        2063 :     if (nFileOffset % m_poHeader->m_nRegularBlockSize == 0)
    2429           0 :         m_poCurCoordBlock->GotoByteInBlock(8);  // Skip Header
    2430             : 
    2431        2063 :     return m_poCurCoordBlock;
    2432             : }
    2433             : 
    2434             : /**********************************************************************
    2435             :  *                   TABMAPFile::GetHeaderBlock()
    2436             :  *
    2437             :  * Return a reference to the MAP file's header block.
    2438             :  *
    2439             :  * The returned pointer is a reference to an object owned by this TABMAPFile
    2440             :  * object and should not be deleted by the caller.
    2441             :  *
    2442             :  * Return NULL if file has not been opened yet.
    2443             :  **********************************************************************/
    2444        4256 : TABMAPHeaderBlock *TABMAPFile::GetHeaderBlock()
    2445             : {
    2446        4256 :     return m_poHeader;
    2447             : }
    2448             : 
    2449             : /**********************************************************************
    2450             :  *                   TABMAPFile::GetIDFileRef()
    2451             :  *
    2452             :  * Return a reference to the .ID file attached to this .MAP file
    2453             :  *
    2454             :  * The returned pointer is a reference to an object owned by this TABMAPFile
    2455             :  * object and should not be deleted by the caller.
    2456             :  *
    2457             :  * Return NULL if file has not been opened yet.
    2458             :  **********************************************************************/
    2459           0 : TABIDFile *TABMAPFile::GetIDFileRef()
    2460             : {
    2461           0 :     return m_poIdIndex;
    2462             : }
    2463             : 
    2464             : /**********************************************************************
    2465             :  *                   TABMAPFile::GetIndexBlock()
    2466             :  *
    2467             :  * Return a reference to the requested index or object block..
    2468             :  *
    2469             :  * Ownership of the returned block is turned over to the caller, who should
    2470             :  * delete it when no longer needed.  The type of the block can be determined
    2471             :  * with the GetBlockType() method.
    2472             :  *
    2473             :  * @param nFileOffset the offset in the map file of the spatial index
    2474             :  * block or object block to load.
    2475             :  *
    2476             :  * @return The requested TABMAPIndexBlock, TABMAPObjectBlock or NULL if the
    2477             :  * read fails for some reason.
    2478             :  **********************************************************************/
    2479       48367 : TABRawBinBlock *TABMAPFile::GetIndexObjectBlock(int nFileOffset)
    2480             : {
    2481             :     /*----------------------------------------------------------------
    2482             :      * Read from the file
    2483             :      *---------------------------------------------------------------*/
    2484             :     GByte *pabyData =
    2485       48367 :         static_cast<GByte *>(CPLMalloc(m_poHeader->m_nRegularBlockSize));
    2486             : 
    2487       48367 :     if (VSIFSeekL(m_fp, static_cast<vsi_l_offset>(nFileOffset), SEEK_SET) !=
    2488       96734 :             0 ||
    2489       96734 :         static_cast<int>(VSIFReadL(pabyData, sizeof(GByte),
    2490       48367 :                                    m_poHeader->m_nRegularBlockSize, m_fp)) !=
    2491       48367 :             m_poHeader->m_nRegularBlockSize)
    2492             :     {
    2493           0 :         CPLError(CE_Failure, CPLE_FileIO,
    2494             :                  "GetIndexBlock() failed reading %d bytes at offset %d.",
    2495           0 :                  m_poHeader->m_nRegularBlockSize, nFileOffset);
    2496           0 :         CPLFree(pabyData);
    2497           0 :         return nullptr;
    2498             :     }
    2499             : 
    2500             :     /* -------------------------------------------------------------------- */
    2501             :     /*      Create and initialize depending on the block type.              */
    2502             :     /* -------------------------------------------------------------------- */
    2503       48367 :     int nBlockType = pabyData[0];
    2504       48367 :     TABRawBinBlock *poBlock = nullptr;
    2505             : 
    2506       48367 :     if (nBlockType == TABMAP_INDEX_BLOCK)
    2507             :     {
    2508       31049 :         TABMAPIndexBlock *poIndexBlock = new TABMAPIndexBlock(m_eAccessMode);
    2509       31049 :         poBlock = poIndexBlock;
    2510       31049 :         poIndexBlock->SetMAPBlockManagerRef(&m_oBlockManager);
    2511             :     }
    2512             :     else
    2513       17318 :         poBlock = new TABMAPObjectBlock(m_eAccessMode);
    2514             : 
    2515       48367 :     poBlock->InitBlockFromData(pabyData, m_poHeader->m_nRegularBlockSize,
    2516       48367 :                                m_poHeader->m_nRegularBlockSize, FALSE, m_fp,
    2517       48367 :                                nFileOffset);
    2518             : 
    2519       48367 :     return poBlock;
    2520             : }
    2521             : 
    2522             : /**********************************************************************
    2523             :  *                   TABMAPFile::InitDrawingTools()
    2524             :  *
    2525             :  * Init the drawing tools for this file.
    2526             :  *
    2527             :  * In Read mode, this will load the drawing tools from the file.
    2528             :  *
    2529             :  * In Write mode, this function will init an empty the tool def table.
    2530             :  *
    2531             :  * Returns 0 on success, -1 on error.
    2532             :  **********************************************************************/
    2533        1332 : int TABMAPFile::InitDrawingTools()
    2534             : {
    2535        1332 :     int nStatus = 0;
    2536             : 
    2537        1332 :     if (m_poHeader == nullptr)
    2538           0 :         return -1;  // File not opened yet!
    2539             : 
    2540             :     /*-------------------------------------------------------------
    2541             :      * We want to perform this initialization only once
    2542             :      *------------------------------------------------------------*/
    2543        1332 :     if (m_poToolDefTable != nullptr)
    2544           0 :         return 0;
    2545             : 
    2546             :     /*-------------------------------------------------------------
    2547             :      * Create a new ToolDefTable... no more initialization is required
    2548             :      * unless we want to read tool blocks from file.
    2549             :      *------------------------------------------------------------*/
    2550        1332 :     m_poToolDefTable = new TABToolDefTable;
    2551             : 
    2552        1332 :     if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) &&
    2553        1241 :         m_poHeader->m_nFirstToolBlock != 0)
    2554             :     {
    2555        1214 :         TABMAPToolBlock *poBlock = new TABMAPToolBlock(TABRead);
    2556        1214 :         poBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize);
    2557             : 
    2558             :         /*-------------------------------------------------------------
    2559             :          * Use GotoByteInFile() to go to the first block's location.  This will
    2560             :          * force loading the block if necessary and reading its header.
    2561             :          * Also make sure to move the read pointer past the 8 bytes header
    2562             :          * to be ready to read drawing tools data
    2563             :          *------------------------------------------------------------*/
    2564        1214 :         if (poBlock->GotoByteInFile(m_poHeader->m_nFirstToolBlock) != 0)
    2565             :         {
    2566             :             // Failed... an error has already been reported.
    2567           0 :             delete poBlock;
    2568           0 :             return -1;
    2569             :         }
    2570             : 
    2571        1214 :         poBlock->GotoByteInBlock(8);
    2572             : 
    2573        1214 :         nStatus = m_poToolDefTable->ReadAllToolDefs(poBlock);
    2574        1214 :         delete poBlock;
    2575             :     }
    2576             : 
    2577        1332 :     return nStatus;
    2578             : }
    2579             : 
    2580             : /**********************************************************************
    2581             :  *                   TABMAPFile::CommitDrawingTools()
    2582             :  *
    2583             :  * Write the drawing tools for this file.
    2584             :  *
    2585             :  * This function applies only to write access mode.
    2586             :  *
    2587             :  * Returns 0 on success, -1 on error.
    2588             :  **********************************************************************/
    2589        1205 : int TABMAPFile::CommitDrawingTools()
    2590             : {
    2591        1205 :     int nStatus = 0;
    2592             : 
    2593        1205 :     if (m_eAccessMode == TABRead || m_poHeader == nullptr)
    2594             :     {
    2595           0 :         CPLError(
    2596             :             CE_Failure, CPLE_AssertionFailed,
    2597             :             "CommitDrawingTools() failed: file not opened for write access.");
    2598           0 :         return -1;
    2599             :     }
    2600             : 
    2601        2374 :     if (m_poToolDefTable == nullptr ||
    2602        1169 :         (m_poToolDefTable->GetNumPen() + m_poToolDefTable->GetNumBrushes() +
    2603        1169 :          m_poToolDefTable->GetNumFonts() + m_poToolDefTable->GetNumSymbols()) ==
    2604             :             0)
    2605             :     {
    2606          36 :         return 0;  // Nothing to do!
    2607             :     }
    2608             : 
    2609             :     /*-------------------------------------------------------------
    2610             :      * Create a new TABMAPToolBlock and update header fields
    2611             :      *------------------------------------------------------------*/
    2612        1169 :     TABMAPToolBlock *poBlock = new TABMAPToolBlock(m_eAccessMode);
    2613        1169 :     if (m_poHeader->m_nFirstToolBlock != 0)
    2614        1075 :         poBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    2615        1075 :                               m_poHeader->m_nFirstToolBlock);
    2616             :     else
    2617          94 :         poBlock->InitNewBlock(m_fp, m_poHeader->m_nRegularBlockSize,
    2618             :                               m_oBlockManager.AllocNewBlock("TOOL"));
    2619        1169 :     poBlock->SetMAPBlockManagerRef(&m_oBlockManager);
    2620             : 
    2621        1169 :     m_poHeader->m_nFirstToolBlock = poBlock->GetStartAddress();
    2622             : 
    2623        1169 :     m_poHeader->m_numPenDefs =
    2624        1169 :         static_cast<GByte>(m_poToolDefTable->GetNumPen());
    2625        1169 :     m_poHeader->m_numBrushDefs =
    2626        1169 :         static_cast<GByte>(m_poToolDefTable->GetNumBrushes());
    2627        1169 :     m_poHeader->m_numFontDefs =
    2628        1169 :         static_cast<GByte>(m_poToolDefTable->GetNumFonts());
    2629        1169 :     m_poHeader->m_numSymbolDefs =
    2630        1169 :         static_cast<GByte>(m_poToolDefTable->GetNumSymbols());
    2631             : 
    2632             :     /*-------------------------------------------------------------
    2633             :      * Do the actual work and delete poBlock
    2634             :      * (Note that poBlock will have already been committed to the file
    2635             :      * by WriteAllToolDefs() )
    2636             :      *------------------------------------------------------------*/
    2637        1169 :     nStatus = m_poToolDefTable->WriteAllToolDefs(poBlock);
    2638             : 
    2639        1169 :     m_poHeader->m_numMapToolBlocks =
    2640        1169 :         static_cast<GByte>(poBlock->GetNumBlocksInChain());
    2641             : 
    2642        1169 :     delete poBlock;
    2643             : 
    2644        1169 :     return nStatus;
    2645             : }
    2646             : 
    2647             : /**********************************************************************
    2648             :  *                   TABMAPFile::ReadPenDef()
    2649             :  *
    2650             :  * Fill the TABPenDef structure with the definition of the specified pen
    2651             :  * index... (1-based pen index)
    2652             :  *
    2653             :  * If nPenIndex==0 or is invalid, then the structure is cleared.
    2654             :  *
    2655             :  * Returns 0 on success, -1 on error (i.e. Pen not found).
    2656             :  **********************************************************************/
    2657        2112 : int TABMAPFile::ReadPenDef(int nPenIndex, TABPenDef *psDef)
    2658             : {
    2659        2112 :     if (m_poToolDefTable == nullptr && InitDrawingTools() != 0)
    2660           0 :         return -1;
    2661             : 
    2662        2112 :     TABPenDef *psTmp = nullptr;
    2663        4224 :     if (psDef && m_poToolDefTable &&
    2664        2112 :         (psTmp = m_poToolDefTable->GetPenDefRef(nPenIndex)) != nullptr)
    2665             :     {
    2666        2110 :         *psDef = *psTmp;
    2667             :     }
    2668           2 :     else if (psDef)
    2669             :     {
    2670             :         /* Init to MapInfo default */
    2671             :         static const TABPenDef csDefaultPen = MITAB_PEN_DEFAULT;
    2672           2 :         *psDef = csDefaultPen;
    2673           2 :         return -1;
    2674             :     }
    2675        2110 :     return 0;
    2676             : }
    2677             : 
    2678             : /**********************************************************************
    2679             :  *                   TABMAPFile::WritePenDef()
    2680             :  *
    2681             :  * Write a Pen Tool to the map file and return the pen index that has
    2682             :  * been attributed to this Pen tool definition, or -1 if something went
    2683             :  * wrong
    2684             :  *
    2685             :  * Note that the returned index is a 1-based index.  A value of 0
    2686             :  * indicates "none" in MapInfo.
    2687             : 
    2688             :  * Returns a value >= 0 on success, -1 on error
    2689             :  **********************************************************************/
    2690         376 : int TABMAPFile::WritePenDef(TABPenDef *psDef)
    2691             : {
    2692         752 :     if (psDef == nullptr ||
    2693         752 :         (m_poToolDefTable == nullptr && InitDrawingTools() != 0) ||
    2694         376 :         m_poToolDefTable == nullptr)
    2695             :     {
    2696           0 :         return -1;
    2697             :     }
    2698             : 
    2699         376 :     return m_poToolDefTable->AddPenDefRef(psDef);
    2700             : }
    2701             : 
    2702             : /**********************************************************************
    2703             :  *                   TABMAPFile::ReadBrushDef()
    2704             :  *
    2705             :  * Fill the TABBrushDef structure with the definition of the specified Brush
    2706             :  * index... (1-based Brush index)
    2707             :  *
    2708             :  * If nBrushIndex==0 or is invalid, then the structure is cleared.
    2709             :  *
    2710             :  * Returns 0 on success, -1 on error (i.e. Brush not found).
    2711             :  **********************************************************************/
    2712         562 : int TABMAPFile::ReadBrushDef(int nBrushIndex, TABBrushDef *psDef)
    2713             : {
    2714         562 :     if (m_poToolDefTable == nullptr && InitDrawingTools() != 0)
    2715           0 :         return -1;
    2716             : 
    2717         562 :     TABBrushDef *psTmp = nullptr;
    2718        1124 :     if (psDef && m_poToolDefTable &&
    2719         562 :         (psTmp = m_poToolDefTable->GetBrushDefRef(nBrushIndex)) != nullptr)
    2720             :     {
    2721         557 :         *psDef = *psTmp;
    2722             :     }
    2723           5 :     else if (psDef)
    2724             :     {
    2725             :         /* Init to MapInfo default */
    2726             :         static const TABBrushDef csDefaultBrush = MITAB_BRUSH_DEFAULT;
    2727           5 :         *psDef = csDefaultBrush;
    2728           5 :         return -1;
    2729             :     }
    2730         557 :     return 0;
    2731             : }
    2732             : 
    2733             : /**********************************************************************
    2734             :  *                   TABMAPFile::WriteBrushDef()
    2735             :  *
    2736             :  * Write a Brush Tool to the map file and return the Brush index that has
    2737             :  * been attributed to this Brush tool definition, or -1 if something went
    2738             :  * wrong
    2739             :  *
    2740             :  * Note that the returned index is a 1-based index.  A value of 0
    2741             :  * indicates "none" in MapInfo.
    2742             : 
    2743             :  * Returns a value >= 0 on success, -1 on error
    2744             :  **********************************************************************/
    2745         134 : int TABMAPFile::WriteBrushDef(TABBrushDef *psDef)
    2746             : {
    2747         268 :     if (psDef == nullptr ||
    2748         268 :         (m_poToolDefTable == nullptr && InitDrawingTools() != 0) ||
    2749         134 :         m_poToolDefTable == nullptr)
    2750             :     {
    2751           0 :         return -1;
    2752             :     }
    2753             : 
    2754         134 :     return m_poToolDefTable->AddBrushDefRef(psDef);
    2755             : }
    2756             : 
    2757             : /**********************************************************************
    2758             :  *                   TABMAPFile::ReadFontDef()
    2759             :  *
    2760             :  * Fill the TABFontDef structure with the definition of the specified Font
    2761             :  * index... (1-based Font index)
    2762             :  *
    2763             :  * If nFontIndex==0 or is invalid, then the structure is cleared.
    2764             :  *
    2765             :  * Returns 0 on success, -1 on error (i.e. Font not found).
    2766             :  **********************************************************************/
    2767          23 : int TABMAPFile::ReadFontDef(int nFontIndex, TABFontDef *psDef)
    2768             : {
    2769          23 :     if (m_poToolDefTable == nullptr && InitDrawingTools() != 0)
    2770           0 :         return -1;
    2771             : 
    2772          23 :     TABFontDef *psTmp = nullptr;
    2773          46 :     if (psDef && m_poToolDefTable &&
    2774          23 :         (psTmp = m_poToolDefTable->GetFontDefRef(nFontIndex)) != nullptr)
    2775             :     {
    2776          23 :         *psDef = *psTmp;
    2777             :     }
    2778           0 :     else if (psDef)
    2779             :     {
    2780             :         /* Init to MapInfo default */
    2781             :         static const TABFontDef csDefaultFont = MITAB_FONT_DEFAULT;
    2782           0 :         *psDef = csDefaultFont;
    2783           0 :         return -1;
    2784             :     }
    2785          23 :     return 0;
    2786             : }
    2787             : 
    2788             : /**********************************************************************
    2789             :  *                   TABMAPFile::WriteFontDef()
    2790             :  *
    2791             :  * Write a Font Tool to the map file and return the Font index that has
    2792             :  * been attributed to this Font tool definition, or -1 if something went
    2793             :  * wrong
    2794             :  *
    2795             :  * Note that the returned index is a 1-based index.  A value of 0
    2796             :  * indicates "none" in MapInfo.
    2797             : 
    2798             :  * Returns a value >= 0 on success, -1 on error
    2799             :  **********************************************************************/
    2800           8 : int TABMAPFile::WriteFontDef(TABFontDef *psDef)
    2801             : {
    2802          16 :     if (psDef == nullptr ||
    2803          16 :         (m_poToolDefTable == nullptr && InitDrawingTools() != 0) ||
    2804           8 :         m_poToolDefTable == nullptr)
    2805             :     {
    2806           0 :         return -1;
    2807             :     }
    2808             : 
    2809           8 :     return m_poToolDefTable->AddFontDefRef(psDef);
    2810             : }
    2811             : 
    2812             : /**********************************************************************
    2813             :  *                   TABMAPFile::ReadSymbolDef()
    2814             :  *
    2815             :  * Fill the TABSymbolDef structure with the definition of the specified Symbol
    2816             :  * index... (1-based Symbol index)
    2817             :  *
    2818             :  * If nSymbolIndex==0 or is invalid, then the structure is cleared.
    2819             :  *
    2820             :  * Returns 0 on success, -1 on error (i.e. Symbol not found).
    2821             :  **********************************************************************/
    2822      570293 : int TABMAPFile::ReadSymbolDef(int nSymbolIndex, TABSymbolDef *psDef)
    2823             : {
    2824      570293 :     if (m_poToolDefTable == nullptr && InitDrawingTools() != 0)
    2825           0 :         return -1;
    2826             : 
    2827      570293 :     TABSymbolDef *psTmp = nullptr;
    2828     1140590 :     if (psDef && m_poToolDefTable &&
    2829      570293 :         (psTmp = m_poToolDefTable->GetSymbolDefRef(nSymbolIndex)) != nullptr)
    2830             :     {
    2831      570293 :         *psDef = *psTmp;
    2832             :     }
    2833           0 :     else if (psDef)
    2834             :     {
    2835             :         /* Init to MapInfo default */
    2836             :         static const TABSymbolDef csDefaultSymbol = MITAB_SYMBOL_DEFAULT;
    2837           0 :         *psDef = csDefaultSymbol;
    2838           0 :         return -1;
    2839             :     }
    2840      570293 :     return 0;
    2841             : }
    2842             : 
    2843             : /**********************************************************************
    2844             :  *                   TABMAPFile::WriteSymbolDef()
    2845             :  *
    2846             :  * Write a Symbol Tool to the map file and return the Symbol index that has
    2847             :  * been attributed to this Symbol tool definition, or -1 if something went
    2848             :  * wrong
    2849             :  *
    2850             :  * Note that the returned index is a 1-based index.  A value of 0
    2851             :  * indicates "none" in MapInfo.
    2852             : 
    2853             :  * Returns a value >= 0 on success, -1 on error
    2854             :  **********************************************************************/
    2855       14689 : int TABMAPFile::WriteSymbolDef(TABSymbolDef *psDef)
    2856             : {
    2857       29378 :     if (psDef == nullptr ||
    2858       29378 :         (m_poToolDefTable == nullptr && InitDrawingTools() != 0) ||
    2859       14689 :         m_poToolDefTable == nullptr)
    2860             :     {
    2861           0 :         return -1;
    2862             :     }
    2863             : 
    2864       14689 :     return m_poToolDefTable->AddSymbolDefRef(psDef);
    2865             : }
    2866             : 
    2867      133292 : static void ORDER_MIN_MAX(double &min, double &max)
    2868             : {
    2869      133292 :     if (max < min)
    2870           1 :         std::swap(min, max);
    2871      133292 : }
    2872             : 
    2873      133292 : static void ORDER_MIN_MAX(int &min, int &max)
    2874             : {
    2875      133292 :     if (max < min)
    2876           0 :         std::swap(min, max);
    2877      133292 : }
    2878             : 
    2879             : /**********************************************************************
    2880             :  *                   TABMAPFile::SetCoordFilter()
    2881             :  *
    2882             :  * Set the MBR of the area of interest... only objects that at least
    2883             :  * overlap with that area will be returned.
    2884             :  *
    2885             :  * @param sMin minimum x/y the file's projection coord.
    2886             :  * @param sMax maximum x/y the file's projection coord.
    2887             :  **********************************************************************/
    2888       31364 : void TABMAPFile::SetCoordFilter(TABVertex sMin, TABVertex sMax)
    2889             : {
    2890       31364 :     m_sMinFilter = sMin;
    2891       31364 :     m_sMaxFilter = sMax;
    2892             : 
    2893       31364 :     Coordsys2Int(sMin.x, sMin.y, m_XMinFilter, m_YMinFilter, TRUE);
    2894       31364 :     Coordsys2Int(sMax.x, sMax.y, m_XMaxFilter, m_YMaxFilter, TRUE);
    2895             : 
    2896       31364 :     ORDER_MIN_MAX(m_XMinFilter, m_XMaxFilter);
    2897       31364 :     ORDER_MIN_MAX(m_YMinFilter, m_YMaxFilter);
    2898       31364 :     ORDER_MIN_MAX(m_sMinFilter.x, m_sMaxFilter.x);
    2899       31364 :     ORDER_MIN_MAX(m_sMinFilter.y, m_sMaxFilter.y);
    2900       31364 : }
    2901             : 
    2902             : /**********************************************************************
    2903             :  *                   TABMAPFile::ResetCoordFilter()
    2904             :  *
    2905             :  * Reset the MBR of the area of interest to be the extents as defined
    2906             :  * in the header.
    2907             :  **********************************************************************/
    2908             : 
    2909       35282 : void TABMAPFile::ResetCoordFilter()
    2910             : 
    2911             : {
    2912       35282 :     m_XMinFilter = m_poHeader->m_nXMin;
    2913       35282 :     m_YMinFilter = m_poHeader->m_nYMin;
    2914       35282 :     m_XMaxFilter = m_poHeader->m_nXMax;
    2915       35282 :     m_YMaxFilter = m_poHeader->m_nYMax;
    2916       35282 :     Int2Coordsys(m_XMinFilter, m_YMinFilter, m_sMinFilter.x, m_sMinFilter.y);
    2917       35282 :     Int2Coordsys(m_XMaxFilter, m_YMaxFilter, m_sMaxFilter.x, m_sMaxFilter.y);
    2918             : 
    2919       35282 :     ORDER_MIN_MAX(m_XMinFilter, m_XMaxFilter);
    2920       35282 :     ORDER_MIN_MAX(m_YMinFilter, m_YMaxFilter);
    2921       35282 :     ORDER_MIN_MAX(m_sMinFilter.x, m_sMaxFilter.x);
    2922       35282 :     ORDER_MIN_MAX(m_sMinFilter.y, m_sMaxFilter.y);
    2923       35282 : }
    2924             : 
    2925             : /**********************************************************************
    2926             :  *                   TABMAPFile::GetCoordFilter()
    2927             :  *
    2928             :  * Get the MBR of the area of interest, as previously set by
    2929             :  * SetCoordFilter().
    2930             :  *
    2931             :  * @param sMin vertex into which the minimum x/y values put in coordsys space.
    2932             :  * @param sMax vertex into which the maximum x/y values put in coordsys space.
    2933             :  **********************************************************************/
    2934       31399 : void TABMAPFile::GetCoordFilter(TABVertex &sMin, TABVertex &sMax) const
    2935             : {
    2936       31399 :     sMin = m_sMinFilter;
    2937       31399 :     sMax = m_sMaxFilter;
    2938       31399 : }
    2939             : 
    2940             : /**********************************************************************
    2941             :  *                   TABMAPFile::CommitSpatialIndex()
    2942             :  *
    2943             :  * Write the spatial index blocks tree for this file.
    2944             :  *
    2945             :  * This function applies only to write access mode.
    2946             :  *
    2947             :  * Returns 0 on success, -1 on error.
    2948             :  **********************************************************************/
    2949        1205 : int TABMAPFile::CommitSpatialIndex()
    2950             : {
    2951        1205 :     if (m_eAccessMode == TABRead || m_poHeader == nullptr)
    2952             :     {
    2953           0 :         CPLError(
    2954             :             CE_Failure, CPLE_AssertionFailed,
    2955             :             "CommitSpatialIndex() failed: file not opened for write access.");
    2956           0 :         return -1;
    2957             :     }
    2958             : 
    2959        1205 :     if (m_poSpIndex == nullptr)
    2960             :     {
    2961          36 :         return 0;  // Nothing to do!
    2962             :     }
    2963             : 
    2964             :     /*-------------------------------------------------------------
    2965             :      * Update header fields and commit index block
    2966             :      * (its children will be recursively committed as well)
    2967             :      *------------------------------------------------------------*/
    2968             :     // Add 1 to Spatial Index Depth to account to the MapObjectBlocks
    2969        1169 :     const int nNextDepth = m_poSpIndex->GetCurMaxDepth() + 1;
    2970        1169 :     m_poHeader->m_nMaxSpIndexDepth = static_cast<GByte>(
    2971        1169 :         std::max(static_cast<int>(m_poHeader->m_nMaxSpIndexDepth), nNextDepth));
    2972             : 
    2973        1169 :     m_poSpIndex->GetMBR(m_poHeader->m_nXMin, m_poHeader->m_nYMin,
    2974        1169 :                         m_poHeader->m_nXMax, m_poHeader->m_nYMax);
    2975             : 
    2976        1169 :     return m_poSpIndex->CommitToFile();
    2977             : }
    2978             : 
    2979             : /**********************************************************************
    2980             :  *                   TABMAPFile::GetMinTABFileVersion()
    2981             :  *
    2982             :  * Returns the minimum TAB file version number that can contain all the
    2983             :  * objects stored in this file.
    2984             :  **********************************************************************/
    2985         148 : int TABMAPFile::GetMinTABFileVersion()
    2986             : {
    2987         148 :     int nToolVersion = 0;
    2988             : 
    2989         148 :     if (m_poToolDefTable)
    2990         112 :         nToolVersion = m_poToolDefTable->GetMinVersionNumber();
    2991             : 
    2992         148 :     return std::max(nToolVersion, m_nMinTABVersion);
    2993             : }
    2994             : 
    2995          14 : const CPLString &TABMAPFile::GetEncoding() const
    2996             : {
    2997          14 :     return m_osEncoding;
    2998             : }
    2999             : 
    3000           2 : void TABMAPFile::SetEncoding(const CPLString &osEncoding)
    3001             : {
    3002           2 :     m_osEncoding = osEncoding;
    3003           2 : }
    3004             : 
    3005     1185590 : bool TABMAPFile::IsValidObjType(int nObjType)
    3006             : {
    3007     1185590 :     switch (nObjType)
    3008             :     {
    3009     1185590 :         case TAB_GEOM_NONE:
    3010             :         case TAB_GEOM_SYMBOL_C:
    3011             :         case TAB_GEOM_SYMBOL:
    3012             :         case TAB_GEOM_LINE_C:
    3013             :         case TAB_GEOM_LINE:
    3014             :         case TAB_GEOM_PLINE_C:
    3015             :         case TAB_GEOM_PLINE:
    3016             :         case TAB_GEOM_ARC_C:
    3017             :         case TAB_GEOM_ARC:
    3018             :         case TAB_GEOM_REGION_C:
    3019             :         case TAB_GEOM_REGION:
    3020             :         case TAB_GEOM_TEXT_C:
    3021             :         case TAB_GEOM_TEXT:
    3022             :         case TAB_GEOM_RECT_C:
    3023             :         case TAB_GEOM_RECT:
    3024             :         case TAB_GEOM_ROUNDRECT_C:
    3025             :         case TAB_GEOM_ROUNDRECT:
    3026             :         case TAB_GEOM_ELLIPSE_C:
    3027             :         case TAB_GEOM_ELLIPSE:
    3028             :         case TAB_GEOM_MULTIPLINE_C:
    3029             :         case TAB_GEOM_MULTIPLINE:
    3030             :         case TAB_GEOM_FONTSYMBOL_C:
    3031             :         case TAB_GEOM_FONTSYMBOL:
    3032             :         case TAB_GEOM_CUSTOMSYMBOL_C:
    3033             :         case TAB_GEOM_CUSTOMSYMBOL:
    3034             :         case TAB_GEOM_V450_REGION_C:
    3035             :         case TAB_GEOM_V450_REGION:
    3036             :         case TAB_GEOM_V450_MULTIPLINE_C:
    3037             :         case TAB_GEOM_V450_MULTIPLINE:
    3038             :         case TAB_GEOM_MULTIPOINT_C:
    3039             :         case TAB_GEOM_MULTIPOINT:
    3040             :         case TAB_GEOM_COLLECTION_C:
    3041             :         case TAB_GEOM_COLLECTION:
    3042             :         case TAB_GEOM_UNKNOWN1_C:
    3043             :         case TAB_GEOM_UNKNOWN1:
    3044             :         case TAB_GEOM_V800_REGION_C:
    3045             :         case TAB_GEOM_V800_REGION:
    3046             :         case TAB_GEOM_V800_MULTIPLINE_C:
    3047             :         case TAB_GEOM_V800_MULTIPLINE:
    3048             :         case TAB_GEOM_V800_MULTIPOINT_C:
    3049             :         case TAB_GEOM_V800_MULTIPOINT:
    3050             :         case TAB_GEOM_V800_COLLECTION_C:
    3051             :         case TAB_GEOM_V800_COLLECTION:
    3052     1185590 :             return true;
    3053             : 
    3054           0 :         default:
    3055           0 :             return false;
    3056             :     }
    3057             : }
    3058             : 
    3059             : /**********************************************************************
    3060             :  *                   TABMAPFile::Dump()
    3061             :  *
    3062             :  * Dump block contents... available only in DEBUG mode.
    3063             :  **********************************************************************/
    3064             : #ifdef DEBUG
    3065             : 
    3066           0 : void TABMAPFile::Dump(FILE *fpOut /*=NULL*/)
    3067             : {
    3068           0 :     if (fpOut == nullptr)
    3069           0 :         fpOut = stdout;
    3070             : 
    3071           0 :     fprintf(fpOut, "----- TABMAPFile::Dump() -----\n");
    3072             : 
    3073           0 :     if (m_fp == nullptr)
    3074             :     {
    3075           0 :         fprintf(fpOut, "File is not opened.\n");
    3076             :     }
    3077             :     else
    3078             :     {
    3079           0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
    3080           0 :         fprintf(fpOut, "Coordsys filter  = (%g,%g)-(%g,%g)\n", m_sMinFilter.x,
    3081             :                 m_sMinFilter.y, m_sMaxFilter.x, m_sMaxFilter.y);
    3082           0 :         fprintf(fpOut, "Int coord filter = (%d,%d)-(%d,%d)\n", m_XMinFilter,
    3083             :                 m_YMinFilter, m_XMaxFilter, m_YMaxFilter);
    3084             : 
    3085           0 :         fprintf(fpOut, "\nFile Header follows ...\n\n");
    3086           0 :         m_poHeader->Dump(fpOut);
    3087           0 :         fprintf(fpOut, "... end of file header.\n\n");
    3088             : 
    3089           0 :         fprintf(fpOut, "Associated .ID file ...\n\n");
    3090           0 :         m_poIdIndex->Dump(fpOut);
    3091           0 :         fprintf(fpOut, "... end of ID file dump.\n\n");
    3092             :     }
    3093             : 
    3094           0 :     fflush(fpOut);
    3095           0 : }
    3096             : 
    3097             : #endif  // DEBUG
    3098             : 
    3099             : /**********************************************************************
    3100             :  *                   TABMAPFile::DumpSpatialIndexToMIF()
    3101             :  *
    3102             :  * Dump the spatial index tree... available only in DEBUG mode.
    3103             :  **********************************************************************/
    3104             : #ifdef DEBUG
    3105             : 
    3106           0 : void TABMAPFile::DumpSpatialIndexToMIF(TABMAPIndexBlock *poNode, FILE *fpMIF,
    3107             :                                        FILE *fpMID, int nParentId /*=-1*/,
    3108             :                                        int nIndexInNode /*=-1*/,
    3109             :                                        int nCurDepth /*=0*/,
    3110             :                                        int nMaxDepth /*=-1*/)
    3111             : {
    3112           0 :     if (poNode == nullptr)
    3113             :     {
    3114           0 :         if (m_poHeader && m_poHeader->m_nFirstIndexBlock != 0)
    3115             :         {
    3116             :             TABRawBinBlock *poBlock =
    3117           0 :                 GetIndexObjectBlock(m_poHeader->m_nFirstIndexBlock);
    3118           0 :             if (poBlock && poBlock->GetBlockType() == TABMAP_INDEX_BLOCK)
    3119           0 :                 poNode = cpl::down_cast<TABMAPIndexBlock *>(poBlock);
    3120             :         }
    3121             : 
    3122           0 :         if (poNode == nullptr)
    3123           0 :             return;
    3124             :     }
    3125             : 
    3126             :     /*-------------------------------------------------------------
    3127             :      * Report info on current tree node
    3128             :      *------------------------------------------------------------*/
    3129           0 :     const int numEntries = poNode->GetNumEntries();
    3130           0 :     GInt32 nXMin = 0;
    3131           0 :     GInt32 nYMin = 0;
    3132           0 :     GInt32 nXMax = 0;
    3133           0 :     GInt32 nYMax = 0;
    3134             : 
    3135           0 :     poNode->RecomputeMBR();
    3136           0 :     poNode->GetMBR(nXMin, nYMin, nXMax, nYMax);
    3137             : 
    3138           0 :     double dXMin = 0.0;
    3139           0 :     double dYMin = 0.0;
    3140           0 :     double dXMax = 0.0;
    3141           0 :     double dYMax = 0.0;
    3142           0 :     Int2Coordsys(nXMin, nYMin, dXMin, dYMin);
    3143           0 :     Int2Coordsys(nXMax, nYMax, dXMax, dYMax);
    3144             : 
    3145           0 :     VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax, dYMax);
    3146           0 :     VSIFPrintf(fpMIF, "  Brush(1, 0)\n"); /* No fill */
    3147             : 
    3148           0 :     VSIFPrintf(fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n", poNode->GetStartAddress(),
    3149             :                nParentId, nIndexInNode, nCurDepth,
    3150           0 :                MITAB_AREA(nXMin, nYMin, nXMax, nYMax), nXMin, nYMin, nXMax,
    3151             :                nYMax);
    3152             : 
    3153           0 :     if (nMaxDepth != 0)
    3154             :     {
    3155             :         /*-------------------------------------------------------------
    3156             :          * Loop through all entries, dumping each of them
    3157             :          *------------------------------------------------------------*/
    3158           0 :         for (int i = 0; i < numEntries; i++)
    3159             :         {
    3160           0 :             TABMAPIndexEntry *psEntry = poNode->GetEntry(i);
    3161             : 
    3162           0 :             TABRawBinBlock *poBlock = GetIndexObjectBlock(psEntry->nBlockPtr);
    3163           0 :             if (poBlock == nullptr)
    3164           0 :                 continue;
    3165             : 
    3166           0 :             if (poBlock->GetBlockType() == TABMAP_INDEX_BLOCK)
    3167             :             {
    3168             :                 /* Index block, dump recursively */
    3169           0 :                 DumpSpatialIndexToMIF(
    3170             :                     cpl::down_cast<TABMAPIndexBlock *>(poBlock), fpMIF, fpMID,
    3171             :                     poNode->GetStartAddress(), i, nCurDepth + 1, nMaxDepth - 1);
    3172             :             }
    3173             :             else
    3174             :             {
    3175             :                 /* Object block, dump directly */
    3176           0 :                 CPLAssert(poBlock->GetBlockType() == TABMAP_OBJECT_BLOCK);
    3177             : 
    3178           0 :                 Int2Coordsys(psEntry->XMin, psEntry->YMin, dXMin, dYMin);
    3179           0 :                 Int2Coordsys(psEntry->XMax, psEntry->YMax, dXMax, dYMax);
    3180             : 
    3181           0 :                 VSIFPrintf(fpMIF, "RECT %g %g %g %g\n", dXMin, dYMin, dXMax,
    3182             :                            dYMax);
    3183           0 :                 VSIFPrintf(fpMIF, "  Brush(1, 0)\n"); /* No fill */
    3184             : 
    3185           0 :                 VSIFPrintf(
    3186             :                     fpMID, "%d,%d,%d,%d,%g,%d,%d,%d,%d\n", psEntry->nBlockPtr,
    3187             :                     poNode->GetStartAddress(), i, nCurDepth + 1,
    3188           0 :                     MITAB_AREA(psEntry->XMin, psEntry->YMin, psEntry->XMax,
    3189             :                                psEntry->YMax),
    3190             :                     psEntry->XMin, psEntry->YMin, psEntry->XMax, psEntry->YMax);
    3191             :             }
    3192             : 
    3193           0 :             delete poBlock;
    3194             :         }
    3195             :     }
    3196             : }
    3197             : 
    3198             : #endif  // DEBUG

Generated by: LCOV version 1.14