LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_mapindexblock.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 454 517 87.8 %
Date: 2024-11-21 22:18:42 Functions: 30 31 96.8 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_mapindexblock.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABMAPIndexBlock class used to handle
       7             :  *           reading/writing of the .MAP files' index blocks
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999, 2000, 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 <cmath>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : 
      24             : #include <algorithm>
      25             : 
      26             : #include "cpl_conv.h"
      27             : #include "cpl_error.h"
      28             : #include "cpl_vsi.h"
      29             : #include "mitab_priv.h"
      30             : 
      31             : /*=====================================================================
      32             :  *                      class TABMAPIndexBlock
      33             :  *====================================================================*/
      34             : 
      35             : /**********************************************************************
      36             :  *                   TABMAPIndexBlock::TABMAPIndexBlock()
      37             :  *
      38             :  * Constructor.
      39             :  **********************************************************************/
      40       34493 : TABMAPIndexBlock::TABMAPIndexBlock(TABAccess eAccessMode /*= TABRead*/)
      41             :     : TABRawBinBlock(eAccessMode, TRUE), m_numEntries(0), m_nMinX(1000000000),
      42             :       m_nMinY(1000000000), m_nMaxX(-1000000000), m_nMaxY(-1000000000),
      43       34493 :       m_poBlockManagerRef(nullptr), m_nCurChildIndex(-1), m_poParentRef(nullptr)
      44             : {
      45       34493 :     memset(m_asEntries, 0, sizeof(m_asEntries));
      46       34493 : }
      47             : 
      48             : /**********************************************************************
      49             :  *                   TABMAPIndexBlock::~TABMAPIndexBlock()
      50             :  *
      51             :  * Destructor.
      52             :  **********************************************************************/
      53       68986 : TABMAPIndexBlock::~TABMAPIndexBlock()
      54             : {
      55       34493 :     UnsetCurChild();
      56       68986 : }
      57             : 
      58             : /**********************************************************************
      59             :  *                   TABMAPIndexBlock::UnsetCurChild()
      60             :  **********************************************************************/
      61             : 
      62       88676 : void TABMAPIndexBlock::UnsetCurChild()
      63             : {
      64       88676 :     if (m_poCurChild)
      65             :     {
      66         616 :         if (m_eAccess == TABWrite || m_eAccess == TABReadWrite)
      67         616 :             m_poCurChild->CommitToFile();
      68         616 :         m_poCurChild.reset();
      69             :     }
      70       88676 :     m_nCurChildIndex = -1;
      71       88676 : }
      72             : 
      73             : /**********************************************************************
      74             :  *                   TABMAPIndexBlock::InitBlockFromData()
      75             :  *
      76             :  * Perform some initialization on the block after its binary data has
      77             :  * been set or changed (or loaded from a file).
      78             :  *
      79             :  * Returns 0 if successful or -1 if an error happened, in which case
      80             :  * CPLError() will have been called.
      81             :  **********************************************************************/
      82       34375 : int TABMAPIndexBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
      83             :                                         int nSizeUsed,
      84             :                                         GBool bMakeCopy /* = TRUE */,
      85             :                                         VSILFILE *fpSrc /* = NULL */,
      86             :                                         int nOffset /* = 0 */)
      87             : {
      88             :     /*-----------------------------------------------------------------
      89             :      * First of all, we must call the base class' InitBlockFromData()
      90             :      *----------------------------------------------------------------*/
      91       34375 :     const int nStatus = TABRawBinBlock::InitBlockFromData(
      92             :         pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
      93       34375 :     if (nStatus != 0)
      94           0 :         return nStatus;
      95             : 
      96             :     /*-----------------------------------------------------------------
      97             :      * Validate block type
      98             :      *----------------------------------------------------------------*/
      99       34375 :     if (m_nBlockType != TABMAP_INDEX_BLOCK)
     100             :     {
     101           0 :         CPLError(CE_Failure, CPLE_FileIO,
     102             :                  "InitBlockFromData(): Invalid Block Type: got %d expected %d",
     103             :                  m_nBlockType, TABMAP_INDEX_BLOCK);
     104           0 :         CPLFree(m_pabyBuf);
     105           0 :         m_pabyBuf = nullptr;
     106           0 :         return -1;
     107             :     }
     108             : 
     109             :     /*-----------------------------------------------------------------
     110             :      * Init member variables
     111             :      *----------------------------------------------------------------*/
     112       34375 :     GotoByteInBlock(0x002);
     113       34375 :     m_numEntries = ReadInt16();
     114             : 
     115       34375 :     if (m_numEntries > 0)
     116       34375 :         ReadAllEntries();
     117             : 
     118       34375 :     return 0;
     119             : }
     120             : 
     121             : /**********************************************************************
     122             :  *                   TABMAPIndexBlock::CommitToFile()
     123             :  *
     124             :  * Commit the current state of the binary block to the file to which
     125             :  * it has been previously attached.
     126             :  *
     127             :  * This method makes sure all values are properly set in the map object
     128             :  * block header and then calls TABRawBinBlock::CommitToFile() to do
     129             :  * the actual writing to disk.
     130             :  *
     131             :  * Returns 0 if successful or -1 if an error happened, in which case
     132             :  * CPLError() will have been called.
     133             :  **********************************************************************/
     134       12392 : int TABMAPIndexBlock::CommitToFile()
     135             : {
     136       12392 :     if (m_pabyBuf == nullptr)
     137             :     {
     138           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     139             :                  "CommitToFile(): Block has not been initialized yet!");
     140           0 :         return -1;
     141             :     }
     142             : 
     143             :     /*-----------------------------------------------------------------
     144             :      * Commit child first
     145             :      *----------------------------------------------------------------*/
     146       12392 :     if (m_poCurChild)
     147             :     {
     148         619 :         if (m_poCurChild->CommitToFile() != 0)
     149           0 :             return -1;
     150             :     }
     151             : 
     152             :     /*-----------------------------------------------------------------
     153             :      * Nothing to do here if block has not been modified
     154             :      *----------------------------------------------------------------*/
     155       12392 :     if (!m_bModified)
     156       10385 :         return 0;
     157             : 
     158             :     /*-----------------------------------------------------------------
     159             :      * Make sure 4 bytes block header is up to date.
     160             :      *----------------------------------------------------------------*/
     161        2007 :     GotoByteInBlock(0x000);
     162             : 
     163        2007 :     WriteInt16(TABMAP_INDEX_BLOCK);  // Block type code
     164        2007 :     WriteInt16(static_cast<GInt16>(m_numEntries));
     165             : 
     166        2007 :     int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
     167             : 
     168             :     /*-----------------------------------------------------------------
     169             :      * Loop through all entries, writing each of them, and calling
     170             :      * CommitToFile() (recursively) on any child index entries we may
     171             :      * encounter.
     172             :      *----------------------------------------------------------------*/
     173       24802 :     for (int i = 0; nStatus == 0 && i < m_numEntries; i++)
     174             :     {
     175       22795 :         nStatus = WriteNextEntry(&(m_asEntries[i]));
     176             :     }
     177             : 
     178             :     /*-----------------------------------------------------------------
     179             :      * OK, call the base class to write the block to disk.
     180             :      *----------------------------------------------------------------*/
     181        2007 :     if (nStatus == 0)
     182             :     {
     183             : #ifdef DEBUG_VERBOSE
     184             :         CPLDebug("MITAB", "Committing INDEX block to offset %d", m_nFileOffset);
     185             : #endif
     186        2007 :         nStatus = TABRawBinBlock::CommitToFile();
     187             :     }
     188             : 
     189        2007 :     return nStatus;
     190             : }
     191             : 
     192             : /**********************************************************************
     193             :  *                   TABMAPIndexBlock::InitNewBlock()
     194             :  *
     195             :  * Initialize a newly created block so that it knows to which file it
     196             :  * is attached, its block size, etc . and then perform any specific
     197             :  * initialization for this block type, including writing a default
     198             :  * block header, etc. and leave the block ready to receive data.
     199             :  *
     200             :  * This is an alternative to calling ReadFromFile() or InitBlockFromData()
     201             :  * that puts the block in a stable state without loading any initial
     202             :  * data in it.
     203             :  *
     204             :  * Returns 0 if successful or -1 if an error happened, in which case
     205             :  * CPLError() will have been called.
     206             :  **********************************************************************/
     207         118 : int TABMAPIndexBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
     208             :                                    int nFileOffset /* = 0*/)
     209             : {
     210             :     /*-----------------------------------------------------------------
     211             :      * Start with the default initialization
     212             :      *----------------------------------------------------------------*/
     213         118 :     if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
     214           0 :         return -1;
     215             : 
     216             :     /*-----------------------------------------------------------------
     217             :      * And then set default values for the block header.
     218             :      *----------------------------------------------------------------*/
     219         118 :     m_numEntries = 0;
     220             : 
     221         118 :     m_nMinX = 1000000000;
     222         118 :     m_nMinY = 1000000000;
     223         118 :     m_nMaxX = -1000000000;
     224         118 :     m_nMaxY = -1000000000;
     225             : 
     226         118 :     if (m_eAccess != TABRead && nFileOffset != 0)
     227             :     {
     228         118 :         GotoByteInBlock(0x000);
     229             : 
     230         118 :         WriteInt16(TABMAP_INDEX_BLOCK);  // Block type code
     231         118 :         WriteInt16(0);                   // num. index entries
     232             :     }
     233             : 
     234         118 :     if (CPLGetLastErrorType() == CE_Failure)
     235           0 :         return -1;
     236             : 
     237         118 :     return 0;
     238             : }
     239             : 
     240             : /**********************************************************************
     241             :  *                   TABMAPIndexBlock::ReadNextEntry()
     242             :  *
     243             :  * Read the next index entry from the block and fill the sEntry
     244             :  * structure.
     245             :  *
     246             :  * Returns 0 if successful or -1 if we reached the end of the block.
     247             :  **********************************************************************/
     248      558293 : int TABMAPIndexBlock::ReadNextEntry(TABMAPIndexEntry *psEntry)
     249             : {
     250      558293 :     if (m_nCurPos < 4)
     251           0 :         GotoByteInBlock(0x004);
     252             : 
     253      558293 :     if (m_nCurPos > 4 + (20 * m_numEntries))
     254             :     {
     255             :         // End of BLock
     256           0 :         return -1;
     257             :     }
     258             : 
     259      558293 :     psEntry->XMin = ReadInt32();
     260      558293 :     psEntry->YMin = ReadInt32();
     261      558293 :     psEntry->XMax = ReadInt32();
     262      558293 :     psEntry->YMax = ReadInt32();
     263      558293 :     psEntry->nBlockPtr = ReadInt32();
     264             : 
     265      558293 :     if (CPLGetLastErrorType() == CE_Failure)
     266           0 :         return -1;
     267             : 
     268      558293 :     return 0;
     269             : }
     270             : 
     271             : /**********************************************************************
     272             :  *                   TABMAPIndexBlock::ReadAllEntries()
     273             :  *
     274             :  * Init the block by reading all entries from the data block.
     275             :  *
     276             :  * Returns 0 if successful or -1 on error.
     277             :  **********************************************************************/
     278       34375 : int TABMAPIndexBlock::ReadAllEntries()
     279             : {
     280       34375 :     CPLAssert(m_numEntries <= GetMaxEntries());
     281       34375 :     if (m_numEntries == 0)
     282           0 :         return 0;
     283             : 
     284       34375 :     if (GotoByteInBlock(0x004) != 0)
     285           0 :         return -1;
     286             : 
     287      592668 :     for (int i = 0; i < m_numEntries; i++)
     288             :     {
     289      558293 :         if (ReadNextEntry(&(m_asEntries[i])) != 0)
     290           0 :             return -1;
     291             :     }
     292             : 
     293       34375 :     return 0;
     294             : }
     295             : 
     296             : /**********************************************************************
     297             :  *                   TABMAPIndexBlock::WriteNextEntry()
     298             :  *
     299             :  * Write the sEntry index entry at current position in the block.
     300             :  *
     301             :  * Returns 0 if successful or -1 if we reached the end of the block.
     302             :  **********************************************************************/
     303       22795 : int TABMAPIndexBlock::WriteNextEntry(TABMAPIndexEntry *psEntry)
     304             : {
     305       22795 :     if (m_nCurPos < 4)
     306           0 :         GotoByteInBlock(0x004);
     307             : 
     308       22795 :     WriteInt32(psEntry->XMin);
     309       22795 :     WriteInt32(psEntry->YMin);
     310       22795 :     WriteInt32(psEntry->XMax);
     311       22795 :     WriteInt32(psEntry->YMax);
     312       22795 :     WriteInt32(psEntry->nBlockPtr);
     313             : 
     314       22795 :     if (CPLGetLastErrorType() == CE_Failure)
     315           0 :         return -1;
     316             : 
     317       22795 :     return 0;
     318             : }
     319             : 
     320             : /**********************************************************************
     321             :  *                   TABMAPIndexBlock::GetNumFreeEntries()
     322             :  *
     323             :  * Return the number of available entries in this block.
     324             :  *
     325             :  * __TODO__ This function could eventually be improved to search
     326             :  *          children leaves as well.
     327             :  **********************************************************************/
     328        1609 : int TABMAPIndexBlock::GetNumFreeEntries()
     329             : {
     330        1609 :     return (m_nBlockSize - 4) / 20 - m_numEntries;
     331             : }
     332             : 
     333             : /**********************************************************************
     334             :  *                   TABMAPIndexBlock::GetEntry()
     335             :  *
     336             :  * Fetch a reference to the requested entry.
     337             :  *
     338             :  * @param iIndex index of entry, must be from 0 to GetNumEntries()-1.
     339             :  *
     340             :  * @return a reference to the internal copy of the entry, or NULL if out
     341             :  * of range.
     342             :  **********************************************************************/
     343      414607 : TABMAPIndexEntry *TABMAPIndexBlock::GetEntry(int iIndex)
     344             : {
     345      414607 :     if (iIndex < 0 || iIndex >= m_numEntries)
     346           0 :         return nullptr;
     347             : 
     348      414607 :     return m_asEntries + iIndex;
     349             : }
     350             : 
     351             : /**********************************************************************
     352             :  *                   TABMAPIndexBlock::GetCurMaxDepth()
     353             :  *
     354             :  * Return maximum depth in the currently loaded part of the index tree
     355             :  **********************************************************************/
     356        2200 : int TABMAPIndexBlock::GetCurMaxDepth()
     357             : {
     358        2200 :     if (m_poCurChild)
     359         571 :         return m_poCurChild->GetCurMaxDepth() + 1;
     360             : 
     361        1629 :     return 1; /* No current child... this node counts for one. */
     362             : }
     363             : 
     364             : /**********************************************************************
     365             :  *                   TABMAPIndexBlock::GetMBR()
     366             :  *
     367             :  * Return the MBR for the current block.
     368             :  **********************************************************************/
     369       16237 : void TABMAPIndexBlock::GetMBR(GInt32 &nXMin, GInt32 &nYMin, GInt32 &nXMax,
     370             :                               GInt32 &nYMax)
     371             : {
     372       16237 :     nXMin = m_nMinX;
     373       16237 :     nYMin = m_nMinY;
     374       16237 :     nXMax = m_nMaxX;
     375       16237 :     nYMax = m_nMaxY;
     376       16237 : }
     377             : 
     378             : /**********************************************************************
     379             :  *                   TABMAPIndexBlock::SetMBR()
     380             :  *
     381             :  **********************************************************************/
     382        1044 : void TABMAPIndexBlock::SetMBR(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
     383             :                               GInt32 nYMax)
     384             : {
     385        1044 :     m_nMinX = nXMin;
     386        1044 :     m_nMinY = nYMin;
     387        1044 :     m_nMaxX = nXMax;
     388        1044 :     m_nMaxY = nYMax;
     389        1044 : }
     390             : 
     391             : /**********************************************************************
     392             :  *                   TABMAPIndexBlock::InsertEntry()
     393             :  *
     394             :  * Add a new entry to this index block.  It is assumed that there is at
     395             :  * least one free slot available, so if the block has to be split then it
     396             :  * should have been done prior to calling this function.
     397             :  *
     398             :  * Returns 0 on success, -1 on error.
     399             :  **********************************************************************/
     400        1117 : int TABMAPIndexBlock::InsertEntry(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
     401             :                                   GInt32 nYMax, GInt32 nBlockPtr)
     402             : {
     403        1117 :     if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
     404             :     {
     405           0 :         CPLError(
     406             :             CE_Failure, CPLE_AssertionFailed,
     407             :             "Failed adding index entry: File not opened for write access.");
     408           0 :         return -1;
     409             :     }
     410             : 
     411        1117 :     if (GetNumFreeEntries() < 1)
     412             :     {
     413           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     414             :                  "Current Block Index is full, cannot add new entry.");
     415           0 :         return -1;
     416             :     }
     417             : 
     418             :     /*-----------------------------------------------------------------
     419             :      * Update count of entries and store new entry.
     420             :      *----------------------------------------------------------------*/
     421        1117 :     m_numEntries++;
     422        1117 :     CPLAssert(m_numEntries <= GetMaxEntries());
     423             : 
     424        1117 :     m_asEntries[m_numEntries - 1].XMin = nXMin;
     425        1117 :     m_asEntries[m_numEntries - 1].YMin = nYMin;
     426        1117 :     m_asEntries[m_numEntries - 1].XMax = nXMax;
     427        1117 :     m_asEntries[m_numEntries - 1].YMax = nYMax;
     428        1117 :     m_asEntries[m_numEntries - 1].nBlockPtr = nBlockPtr;
     429             : 
     430        1117 :     m_bModified = TRUE;
     431             : 
     432        1117 :     return 0;
     433             : }
     434             : 
     435             : /**********************************************************************
     436             :  *                   TABMAPIndexBlock::ChooseSubEntryForInsert()
     437             :  *
     438             :  * Select the entry in this index block in which the new entry should
     439             :  * be inserted. The criteria used is to select the node whose MBR needs
     440             :  * the least enlargement to include the new entry. We resolve ties by
     441             :  * choosing the entry with the rectangle of smallest area.
     442             :  * (This is the ChooseSubtree part of Guttman's "ChooseLeaf" algorithm.)
     443             :  *
     444             :  * Returns the index of the best candidate or -1 of node is empty.
     445             :  **********************************************************************/
     446       22519 : int TABMAPIndexBlock::ChooseSubEntryForInsert(GInt32 nXMin, GInt32 nYMin,
     447             :                                               GInt32 nXMax, GInt32 nYMax)
     448             : {
     449       22519 :     GInt32 nBestCandidate = -1;
     450             : 
     451       22519 :     double dOptimalAreaDiff = 0.0;
     452             : 
     453       22519 :     const double dNewEntryArea = MITAB_AREA(nXMin, nYMin, nXMax, nYMax);
     454             : 
     455      385639 :     for (GInt32 i = 0; i < m_numEntries; i++)
     456             :     {
     457      363120 :         double dAreaDiff = 0.0;
     458      363120 :         const double dAreaBefore =
     459      363120 :             MITAB_AREA(m_asEntries[i].XMin, m_asEntries[i].YMin,
     460             :                        m_asEntries[i].XMax, m_asEntries[i].YMax);
     461             : 
     462             :         /* Does this entry fully contain the new entry's MBR ?
     463             :          */
     464      363120 :         const GBool bIsContained =
     465      246297 :             nXMin >= m_asEntries[i].XMin && nYMin >= m_asEntries[i].YMin &&
     466      609417 :             nXMax <= m_asEntries[i].XMax && nYMax <= m_asEntries[i].YMax;
     467             : 
     468      363120 :         if (bIsContained)
     469             :         {
     470             :             /* If new entry is fully contained in this entry then
     471             :              * the area difference will be the difference between the area
     472             :              * of the entry to insert and the area of m_asEntries[i]
     473             :              *
     474             :              * The diff value is negative in this case.
     475             :              */
     476       31191 :             dAreaDiff = dNewEntryArea - dAreaBefore;
     477             :         }
     478             :         else
     479             :         {
     480             :             /* Need to calculate the expanded MBR to calculate the area
     481             :              * difference.
     482             :              */
     483      331929 :             GInt32 nXMin2 = std::min(m_asEntries[i].XMin, nXMin);
     484      331929 :             GInt32 nYMin2 = std::min(m_asEntries[i].YMin, nYMin);
     485      331929 :             GInt32 nXMax2 = std::max(m_asEntries[i].XMax, nXMax);
     486      331929 :             GInt32 nYMax2 = std::max(m_asEntries[i].YMax, nYMax);
     487             : 
     488      331929 :             dAreaDiff =
     489      331929 :                 MITAB_AREA(nXMin2, nYMin2, nXMax2, nYMax2) - dAreaBefore;
     490             :         }
     491             : 
     492             :         /* Is this a better candidate?
     493             :          * Note, possible Optimization: In case of tie, we could to pick the
     494             :          * candidate with the smallest area
     495             :          */
     496             : 
     497      363120 :         if (/* No best candidate yet */
     498             :             (nBestCandidate == -1)
     499             :             /* or current candidate is contained and best candidate is not
     500             :                contained */
     501      340601 :             || (dAreaDiff < 0 && dOptimalAreaDiff >= 0)
     502             :             /* or if both are either contained or not contained then use the one
     503             :              * with the smallest area diff, which means maximum coverage in the
     504             :              * case of contained rects, or minimum area increase when not
     505             :              * contained
     506             :              */
     507      856182 :             || (((dOptimalAreaDiff < 0 && dAreaDiff < 0) ||
     508      302583 :                  (dOptimalAreaDiff > 0 && dAreaDiff > 0)) &&
     509      152436 :                 std::abs(dAreaDiff) < std::abs(dOptimalAreaDiff)))
     510             :         {
     511       80089 :             nBestCandidate = i;
     512       80089 :             dOptimalAreaDiff = dAreaDiff;
     513             :         }
     514             :     }
     515             : 
     516       22519 :     return nBestCandidate;
     517             : }
     518             : 
     519             : /**********************************************************************
     520             :  *                   TABMAPIndexBlock::ChooseLeafForInsert()
     521             :  *
     522             :  * Recursively search the tree until we find the best leaf to
     523             :  * contain the specified object MBR.
     524             :  *
     525             :  * Returns the nBlockPtr of the selected leaf node entry (should be a
     526             :  * ref to a TABMAPObjectBlock) or -1 on error.
     527             :  *
     528             :  * After this call, m_poCurChild will be pointing at the selected child
     529             :  * node, for use by later calls to UpdateLeafEntry()
     530             :  **********************************************************************/
     531       21905 : GInt32 TABMAPIndexBlock::ChooseLeafForInsert(GInt32 nXMin, GInt32 nYMin,
     532             :                                              GInt32 nXMax, GInt32 nYMax)
     533             : {
     534       21905 :     GBool bFound = FALSE;
     535             : 
     536       21905 :     if (m_numEntries < 0)
     537           0 :         return -1;
     538             : 
     539             :     /*-----------------------------------------------------------------
     540             :      * Look for the best candidate to contain the new entry
     541             :      *----------------------------------------------------------------*/
     542             : 
     543             :     // Make sure blocks currently in memory are written to disk.
     544             :     // TODO: Could we avoid deleting m_poCurChild if it is already
     545             :     //       the best candidate for insert?
     546       21905 :     if (m_poCurChild)
     547             :     {
     548        9762 :         m_poCurChild->CommitToFile();
     549        9762 :         m_poCurChild.reset();
     550        9762 :         m_nCurChildIndex = -1;
     551             :     }
     552             : 
     553       21905 :     int nBestCandidate = ChooseSubEntryForInsert(nXMin, nYMin, nXMax, nYMax);
     554             : 
     555       21905 :     CPLAssert(nBestCandidate != -1);
     556       21905 :     if (nBestCandidate == -1)
     557           0 :         return -1; /* This should never happen! */
     558             : 
     559             :     // Try to load corresponding child... if it fails then we are
     560             :     // likely in a leaf node, so we'll add the new entry in the current
     561             :     // node.
     562             : 
     563             :     // Prevent error message if referred block not committed yet.
     564       21905 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     565             : 
     566             :     auto poBlock = std::unique_ptr<TABRawBinBlock>(
     567             :         TABCreateMAPBlockFromFile(m_fp, m_asEntries[nBestCandidate].nBlockPtr,
     568       43810 :                                   m_nBlockSize, TRUE, TABReadWrite));
     569       21905 :     if (poBlock != nullptr && poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK)
     570             :     {
     571       10343 :         m_poCurChild.reset(
     572             :             cpl::down_cast<TABMAPIndexBlock *>(poBlock.release()));
     573       10343 :         m_nCurChildIndex = nBestCandidate;
     574       10343 :         m_poCurChild->SetParentRef(this);
     575       10343 :         m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef);
     576       10343 :         bFound = TRUE;
     577             :     }
     578             : 
     579       21905 :     CPLPopErrorHandler();
     580       21905 :     CPLErrorReset();
     581             : 
     582       21905 :     if (bFound)
     583             :     {
     584             :         /*-------------------------------------------------------------
     585             :          * Found a child leaf... pass the call to it.
     586             :          *------------------------------------------------------------*/
     587       10343 :         return m_poCurChild->ChooseLeafForInsert(nXMin, nYMin, nXMax, nYMax);
     588             :     }
     589             : 
     590             :     /*-------------------------------------------------------------
     591             :      * Found no child index node... we must be at the leaf level
     592             :      * (leaf points at map object data blocks) so we return a ref
     593             :      * to the TABMAPObjBlock for insertion
     594             :      *------------------------------------------------------------*/
     595       11562 :     return m_asEntries[nBestCandidate].nBlockPtr;
     596             : }
     597             : 
     598             : /**********************************************************************
     599             :  *                   TABMAPIndexBlock::GetCurLeafEntryMBR()
     600             :  *
     601             :  * Get the MBR for specified nBlockPtr in the leaf at the end of the
     602             :  * chain of m_poCurChild refs.
     603             :  *
     604             :  * This method requires that the chain of m_poCurChild refs already point
     605             :  * to a leaf that contains the specified nBlockPtr, it is usually called
     606             :  * right after ChooseLeafForInsert().
     607             :  *
     608             :  * Returns 0 on success, -1 on error.
     609             :  **********************************************************************/
     610       20891 : int TABMAPIndexBlock::GetCurLeafEntryMBR(GInt32 nBlockPtr, GInt32 &nXMin,
     611             :                                          GInt32 &nYMin, GInt32 &nXMax,
     612             :                                          GInt32 &nYMax)
     613             : {
     614       20891 :     if (m_poCurChild)
     615             :     {
     616             :         /* Pass the call down to current child */
     617       10150 :         return m_poCurChild->GetCurLeafEntryMBR(nBlockPtr, nXMin, nYMin, nXMax,
     618       10150 :                                                 nYMax);
     619             :     }
     620             : 
     621             :     /* We're at the leaf level, look for the entry */
     622       89883 :     for (int i = 0; i < m_numEntries; i++)
     623             :     {
     624       89883 :         if (m_asEntries[i].nBlockPtr == nBlockPtr)
     625             :         {
     626             :             /* Found it. Return its MBR */
     627       10741 :             nXMin = m_asEntries[i].XMin;
     628       10741 :             nYMin = m_asEntries[i].YMin;
     629       10741 :             nXMax = m_asEntries[i].XMax;
     630       10741 :             nYMax = m_asEntries[i].YMax;
     631             : 
     632       10741 :             return 0;
     633             :         }
     634             :     }
     635             : 
     636             :     /* Not found! This should not happen if method is used properly. */
     637           0 :     CPLError(CE_Failure, CPLE_AssertionFailed,
     638             :              "Entry to update not found in GetCurLeafEntryMBR()!");
     639           0 :     return -1;
     640             : }
     641             : 
     642             : /**********************************************************************
     643             :  *                   TABMAPIndexBlock::UpdateLeafEntry()
     644             :  *
     645             :  * Update the MBR for specified nBlockPtr in the leaf at the end of the
     646             :  * chain of m_poCurChild refs and update MBR of parents if required.
     647             :  *
     648             :  * This method requires that the chain of m_poCurChild refs already point
     649             :  * to a leaf that contains the specified nBlockPtr, it is usually called
     650             :  * right after ChooseLeafForInsert().
     651             :  *
     652             :  * Returns 0 on success, -1 on error.
     653             :  **********************************************************************/
     654       21911 : int TABMAPIndexBlock::UpdateLeafEntry(GInt32 nBlockPtr, GInt32 nXMin,
     655             :                                       GInt32 nYMin, GInt32 nXMax, GInt32 nYMax)
     656             : {
     657       21911 :     if (m_poCurChild)
     658             :     {
     659             :         /* Pass the call down to current child */
     660       10343 :         return m_poCurChild->UpdateLeafEntry(nBlockPtr, nXMin, nYMin, nXMax,
     661       10343 :                                              nYMax);
     662             :     }
     663             : 
     664             :     /* We're at the leaf level, look for the entry to update */
     665       91085 :     for (int i = 0; i < m_numEntries; i++)
     666             :     {
     667       91085 :         if (m_asEntries[i].nBlockPtr == nBlockPtr)
     668             :         {
     669             :             /* Found it. */
     670       11568 :             TABMAPIndexEntry *psEntry = &m_asEntries[i];
     671             : 
     672       11568 :             if (psEntry->XMin != nXMin || psEntry->YMin != nYMin ||
     673       11366 :                 psEntry->XMax != nXMax || psEntry->YMax != nYMax)
     674             :             {
     675             :                 /* MBR changed. Update MBR of entry */
     676        1394 :                 psEntry->XMin = nXMin;
     677        1394 :                 psEntry->YMin = nYMin;
     678        1394 :                 psEntry->XMax = nXMax;
     679        1394 :                 psEntry->YMax = nYMax;
     680             : 
     681        1394 :                 m_bModified = TRUE;
     682             : 
     683             :                 /* Update MBR of this node and all parents */
     684        1394 :                 RecomputeMBR();
     685             :             }
     686             : 
     687       11568 :             return 0;
     688             :         }
     689             :     }
     690             : 
     691             :     /* Not found! This should not happen if method is used properly. */
     692           0 :     CPLError(CE_Failure, CPLE_AssertionFailed,
     693             :              "Entry to update not found in UpdateLeafEntry()!");
     694           0 :     return -1;
     695             : }
     696             : 
     697             : /**********************************************************************
     698             :  *                   TABMAPIndexBlock::AddEntry()
     699             :  *
     700             :  * Recursively search the tree until we encounter the best leaf to
     701             :  * contain the specified object MBR and add the new entry to it.
     702             :  *
     703             :  * In the even that the selected leaf node would be full, then it will be
     704             :  * split and this split can propagate up to its parent, etc.
     705             :  *
     706             :  * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and
     707             :  * we do not try to update the child node.  This is used when the parent
     708             :  * of a node that is being split has to be updated.
     709             :  *
     710             :  * Returns 0 on success, -1 on error.
     711             :  **********************************************************************/
     712         732 : int TABMAPIndexBlock::AddEntry(GInt32 nXMin, GInt32 nYMin, GInt32 nXMax,
     713             :                                GInt32 nYMax, GInt32 nBlockPtr,
     714             :                                GBool bAddInThisNodeOnly /*=FALSE*/)
     715             : {
     716         732 :     GBool bFound = FALSE;
     717             : 
     718         732 :     if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
     719             :     {
     720           0 :         CPLError(
     721             :             CE_Failure, CPLE_AssertionFailed,
     722             :             "Failed adding index entry: File not opened for write access.");
     723           0 :         return -1;
     724             :     }
     725             : 
     726             :     /*-----------------------------------------------------------------
     727             :      * Look for the best candidate to contain the new entry
     728             :      *----------------------------------------------------------------*/
     729             : 
     730             :     /*-----------------------------------------------------------------
     731             :      * If bAddInThisNodeOnly=TRUE then we add the entry only locally
     732             :      * and do not need to look for the proper leaf to insert it.
     733             :      *----------------------------------------------------------------*/
     734         732 :     if (bAddInThisNodeOnly)
     735          25 :         bFound = TRUE;
     736             : 
     737         732 :     if (!bFound && m_numEntries > 0)
     738             :     {
     739             :         // Make sure blocks currently in memory are written to disk.
     740         614 :         if (m_poCurChild)
     741             :         {
     742         211 :             m_poCurChild->CommitToFile();
     743         211 :             m_poCurChild.reset();
     744         211 :             m_nCurChildIndex = -1;
     745             :         }
     746             : 
     747             :         int nBestCandidate =
     748         614 :             ChooseSubEntryForInsert(nXMin, nYMin, nXMax, nYMax);
     749             : 
     750         614 :         CPLAssert(nBestCandidate != -1);
     751             : 
     752         614 :         if (nBestCandidate != -1)
     753             :         {
     754             :             // Try to load corresponding child... if it fails then we are
     755             :             // likely in a leaf node, so we'll add the new entry in the current
     756             :             // node.
     757             : 
     758             :             // Prevent error message if referred block not committed yet.
     759         614 :             CPLPushErrorHandler(CPLQuietErrorHandler);
     760             : 
     761             :             auto poBlock =
     762             :                 std::unique_ptr<TABRawBinBlock>(TABCreateMAPBlockFromFile(
     763             :                     m_fp, m_asEntries[nBestCandidate].nBlockPtr, m_nBlockSize,
     764        1228 :                     TRUE, TABReadWrite));
     765        1228 :             if (poBlock != nullptr &&
     766         614 :                 poBlock->GetBlockClass() == TABMAP_INDEX_BLOCK)
     767             :             {
     768         240 :                 m_poCurChild.reset(
     769             :                     cpl::down_cast<TABMAPIndexBlock *>(poBlock.release()));
     770         240 :                 m_nCurChildIndex = nBestCandidate;
     771         240 :                 m_poCurChild->SetParentRef(this);
     772         240 :                 m_poCurChild->SetMAPBlockManagerRef(m_poBlockManagerRef);
     773         240 :                 bFound = TRUE;
     774             :             }
     775             : 
     776         614 :             CPLPopErrorHandler();
     777         614 :             CPLErrorReset();
     778             :         }
     779             :     }
     780             : 
     781         732 :     if (bFound && !bAddInThisNodeOnly)
     782             :     {
     783             :         /*-------------------------------------------------------------
     784             :          * Found a child leaf... pass the call to it.
     785             :          *------------------------------------------------------------*/
     786         240 :         if (m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0)
     787           0 :             return -1;
     788             :     }
     789             :     else
     790             :     {
     791             :         /*-------------------------------------------------------------
     792             :          * Found no child to store new object... we're likely at the leaf
     793             :          * level so we'll store new object in current node
     794             :          *------------------------------------------------------------*/
     795             : 
     796             :         /*-------------------------------------------------------------
     797             :          * First thing to do is make sure that there is room for a new
     798             :          * entry in this node, and to split it if necessary.
     799             :          *------------------------------------------------------------*/
     800         492 :         if (GetNumFreeEntries() < 1)
     801             :         {
     802          19 :             if (m_poParentRef == nullptr)
     803             :             {
     804             :                 /*-----------------------------------------------------
     805             :                  * Splitting the root node adds one level to the tree, so
     806             :                  * after splitting we just redirect the call to the new
     807             :                  * child that's just been created.
     808             :                  *----------------------------------------------------*/
     809           6 :                 if (SplitRootNode(nXMin, nYMin, nXMax, nYMax) != 0)
     810           0 :                     return -1;  // Error happened and has already been reported
     811             : 
     812           6 :                 CPLAssert(m_poCurChild);
     813           6 :                 return m_poCurChild->AddEntry(nXMin, nYMin, nXMax, nYMax,
     814           6 :                                               nBlockPtr, TRUE);
     815             :             }
     816             :             else
     817             :             {
     818             :                 /*-----------------------------------------------------
     819             :                  * Splitting a regular node
     820             :                  *----------------------------------------------------*/
     821          13 :                 if (SplitNode(nXMin, nYMin, nXMax, nYMax) != 0)
     822           0 :                     return -1;
     823             :             }
     824             :         }
     825             : 
     826         486 :         if (InsertEntry(nXMin, nYMin, nXMax, nYMax, nBlockPtr) != 0)
     827           0 :             return -1;
     828             :     }
     829             : 
     830             :     /*-----------------------------------------------------------------
     831             :      * Update current node MBR and the reference to it in our parent.
     832             :      *----------------------------------------------------------------*/
     833         726 :     RecomputeMBR();
     834             : 
     835         726 :     return 0;
     836             : }
     837             : 
     838             : /**********************************************************************
     839             :  *                   TABMAPIndexBlock::ComputeAreaDiff()
     840             :  *
     841             :  * (static method, also used by the TABMAPObjBlock class)
     842             :  *
     843             :  * Compute the area difference between two MBRs. Used in the SplitNode
     844             :  * algorithm to decide to which of the two nodes an entry should be added.
     845             :  *
     846             :  * The returned AreaDiff value is positive if NodeMBR has to be enlarged
     847             :  * and negative if new Entry is fully contained in the NodeMBR.
     848             :  **********************************************************************/
     849       17738 : double TABMAPIndexBlock::ComputeAreaDiff(GInt32 nNodeXMin, GInt32 nNodeYMin,
     850             :                                          GInt32 nNodeXMax, GInt32 nNodeYMax,
     851             :                                          GInt32 nEntryXMin, GInt32 nEntryYMin,
     852             :                                          GInt32 nEntryXMax, GInt32 nEntryYMax)
     853             : {
     854       17738 :     double dAreaDiff = 0.0;
     855             : 
     856       17738 :     const double dNodeAreaBefore =
     857       17738 :         MITAB_AREA(nNodeXMin, nNodeYMin, nNodeXMax, nNodeYMax);
     858             : 
     859             :     // Does the node fully contain the new entry's MBR?
     860       17738 :     const GBool bIsContained =
     861       13779 :         nEntryXMin >= nNodeXMin && nEntryYMin >= nNodeYMin &&
     862       31517 :         nEntryXMax <= nNodeXMax && nEntryYMax <= nNodeYMax;
     863             : 
     864       17738 :     if (bIsContained)
     865             :     {
     866             :         /* If new entry is fully contained in this entry then
     867             :          * the area difference will be the difference between the area
     868             :          * of the entry to insert and the area of the node
     869             :          */
     870        6823 :         dAreaDiff = MITAB_AREA(nEntryXMin, nEntryYMin, nEntryXMax, nEntryYMax) -
     871             :                     dNodeAreaBefore;
     872             :     }
     873             :     else
     874             :     {
     875             :         /* Need to calculate the expanded MBR to calculate the area
     876             :          * difference.
     877             :          */
     878       10915 :         nNodeXMin = std::min(nNodeXMin, nEntryXMin);
     879       10915 :         nNodeYMin = std::min(nNodeYMin, nEntryYMin);
     880       10915 :         nNodeXMax = std::max(nNodeXMax, nEntryXMax);
     881       10915 :         nNodeYMax = std::max(nNodeYMax, nEntryYMax);
     882             : 
     883       10915 :         dAreaDiff = MITAB_AREA(nNodeXMin, nNodeYMin, nNodeXMax, nNodeYMax) -
     884             :                     dNodeAreaBefore;
     885             :     }
     886             : 
     887       17738 :     return dAreaDiff;
     888             : }
     889             : 
     890             : /**********************************************************************
     891             :  *                   TABMAPIndexBlock::PickSeedsForSplit()
     892             :  *
     893             :  * (static method, also used by the TABMAPObjBlock class)
     894             :  *
     895             :  * Pick two seeds to use to start splitting this node.
     896             :  *
     897             :  * Guttman's LinearPickSeed:
     898             :  * - Along each dimension find the entry whose rectangle has the
     899             :  *   highest low side, and the one with the lowest high side
     900             :  * - Calculate the separation for each pair
     901             :  * - Normalize the separation by dividing by the extents of the
     902             :  *   corresponding dimension
     903             :  * - Choose the pair with the greatest normalized separation along
     904             :  *   any dimension
     905             :  **********************************************************************/
     906         272 : int TABMAPIndexBlock::PickSeedsForSplit(
     907             :     TABMAPIndexEntry *pasEntries, int numEntries, int nSrcCurChildIndex,
     908             :     GInt32 nNewEntryXMin, GInt32 nNewEntryYMin, GInt32 nNewEntryXMax,
     909             :     GInt32 nNewEntryYMax, int &nSeed1, int &nSeed2)
     910             : {
     911         272 :     GInt32 nSrcMinX = 0;
     912         272 :     GInt32 nSrcMinY = 0;
     913         272 :     GInt32 nSrcMaxX = 0;
     914         272 :     GInt32 nSrcMaxY = 0;
     915         272 :     int nLowestMaxX = -1;
     916         272 :     int nHighestMinX = -1;
     917         272 :     int nLowestMaxY = -1;
     918         272 :     int nHighestMinY = -1;
     919         272 :     GInt32 nLowestMaxXId = -1;
     920         272 :     GInt32 nHighestMinXId = -1;
     921         272 :     GInt32 nLowestMaxYId = -1;
     922         272 :     GInt32 nHighestMinYId = -1;
     923             : 
     924         272 :     nSeed1 = -1;
     925         272 :     nSeed2 = -1;
     926             : 
     927             :     // Along each dimension find the entry whose rectangle has the
     928             :     // highest low side, and the one with the lowest high side
     929        9414 :     for (int iEntry = 0; iEntry < numEntries; iEntry++)
     930             :     {
     931        9142 :         if (nLowestMaxXId == -1 || pasEntries[iEntry].XMax < nLowestMaxX)
     932             :         {
     933         750 :             nLowestMaxX = pasEntries[iEntry].XMax;
     934         750 :             nLowestMaxXId = iEntry;
     935             :         }
     936             : 
     937        9142 :         if (nHighestMinXId == -1 || pasEntries[iEntry].XMin > nHighestMinX)
     938             :         {
     939        1700 :             nHighestMinX = pasEntries[iEntry].XMin;
     940        1700 :             nHighestMinXId = iEntry;
     941             :         }
     942             : 
     943        9142 :         if (nLowestMaxYId == -1 || pasEntries[iEntry].YMax < nLowestMaxY)
     944             :         {
     945         612 :             nLowestMaxY = pasEntries[iEntry].YMax;
     946         612 :             nLowestMaxYId = iEntry;
     947             :         }
     948             : 
     949        9142 :         if (nHighestMinYId == -1 || pasEntries[iEntry].YMin > nHighestMinY)
     950             :         {
     951        1643 :             nHighestMinY = pasEntries[iEntry].YMin;
     952        1643 :             nHighestMinYId = iEntry;
     953             :         }
     954             : 
     955             :         // Also keep track of MBR of all entries
     956        9142 :         if (iEntry == 0)
     957             :         {
     958         272 :             nSrcMinX = pasEntries[iEntry].XMin;
     959         272 :             nSrcMinY = pasEntries[iEntry].YMin;
     960         272 :             nSrcMaxX = pasEntries[iEntry].XMax;
     961         272 :             nSrcMaxY = pasEntries[iEntry].YMax;
     962             :         }
     963             :         else
     964             :         {
     965        8870 :             nSrcMinX = std::min(nSrcMinX, pasEntries[iEntry].XMin);
     966        8870 :             nSrcMinY = std::min(nSrcMinY, pasEntries[iEntry].YMin);
     967        8870 :             nSrcMaxX = std::max(nSrcMaxX, pasEntries[iEntry].XMax);
     968        8870 :             nSrcMaxY = std::max(nSrcMaxY, pasEntries[iEntry].YMax);
     969             :         }
     970             :     }
     971             : 
     972             :     const double dfSrcWidth =
     973         272 :         std::abs(static_cast<double>(nSrcMaxX) - nSrcMinX);
     974             :     const double dfSrcHeight =
     975         272 :         std::abs(static_cast<double>(nSrcMaxY) - nSrcMinY);
     976             : 
     977             :     // Calculate the separation for each pair (note that it may be negative
     978             :     // in case of overlap)
     979             :     // Normalize the separation by dividing by the extents of the
     980             :     // corresponding dimension
     981         272 :     const double dX =
     982             :         dfSrcWidth == 0.0
     983         272 :             ? 0.0
     984         252 :             : (static_cast<double>(nHighestMinX) - nLowestMaxX) / dfSrcWidth;
     985         272 :     const double dY =
     986             :         dfSrcHeight == 0.0
     987         272 :             ? 0.0
     988         252 :             : (static_cast<double>(nHighestMinY) - nLowestMaxY) / dfSrcHeight;
     989             : 
     990             :     // Choose the pair with the greatest normalized separation along
     991             :     // any dimension
     992         272 :     if (dX > dY)
     993             :     {
     994           7 :         nSeed1 = nHighestMinXId;
     995           7 :         nSeed2 = nLowestMaxXId;
     996             :     }
     997             :     else
     998             :     {
     999         265 :         nSeed1 = nHighestMinYId;
    1000         265 :         nSeed2 = nLowestMaxYId;
    1001             :     }
    1002             : 
    1003             :     // If nSeed1==nSeed2 then just pick any two (giving pref to current child)
    1004         272 :     if (nSeed1 == nSeed2)
    1005             :     {
    1006          20 :         if (nSeed1 != nSrcCurChildIndex && nSrcCurChildIndex != -1)
    1007           0 :             nSeed1 = nSrcCurChildIndex;
    1008          20 :         else if (nSeed1 != 0)
    1009           0 :             nSeed1 = 0;
    1010             :         else
    1011          20 :             nSeed1 = 1;
    1012             :     }
    1013             : 
    1014             :     // Decide which of the two seeds best matches the new entry. That seed and
    1015             :     // the new entry will stay in current node (new entry will be added by the
    1016             :     // caller later). The other seed will go in the 2nd node
    1017         544 :     const double dAreaDiff1 = ComputeAreaDiff(
    1018         272 :         pasEntries[nSeed1].XMin, pasEntries[nSeed1].YMin,
    1019         272 :         pasEntries[nSeed1].XMax, pasEntries[nSeed1].YMax, nNewEntryXMin,
    1020             :         nNewEntryYMin, nNewEntryXMax, nNewEntryYMax);
    1021             : 
    1022         544 :     const double dAreaDiff2 = ComputeAreaDiff(
    1023         272 :         pasEntries[nSeed2].XMin, pasEntries[nSeed2].YMin,
    1024         272 :         pasEntries[nSeed2].XMax, pasEntries[nSeed2].YMax, nNewEntryXMin,
    1025             :         nNewEntryYMin, nNewEntryXMax, nNewEntryYMax);
    1026             : 
    1027             :     /* Note that we want to keep this node's current child in here.
    1028             :      * Since splitting happens only during an addentry() operation and
    1029             :      * then both the current child and the New Entry should fit in the same
    1030             :      * area.
    1031             :      */
    1032         272 :     if (nSeed1 != nSrcCurChildIndex &&
    1033         155 :         (dAreaDiff1 > dAreaDiff2 || nSeed2 == nSrcCurChildIndex))
    1034             :     {
    1035             :         // Seed2 stays in this node, Seed1 moves to new node
    1036             :         // ... swap Seed1 and Seed2 indices
    1037         117 :         int nTmp = nSeed1;
    1038         117 :         nSeed1 = nSeed2;
    1039         117 :         nSeed2 = nTmp;
    1040             :     }
    1041             : 
    1042         272 :     return 0;
    1043             : }
    1044             : 
    1045             : /**********************************************************************
    1046             :  *                   TABMAPIndexBlock::SplitNode()
    1047             :  *
    1048             :  * Split current Node, update the references in the parent node, etc.
    1049             :  * Note that Root Nodes cannot be split using this method... SplitRootNode()
    1050             :  * should be used instead.
    1051             :  *
    1052             :  * nNewEntry* are the coord. of the new entry that
    1053             :  * will be added after the split.  The split is done so that the current
    1054             :  * node will be the one in which the new object should be stored.
    1055             :  *
    1056             :  * Returns 0 on success, -1 on error.
    1057             :  **********************************************************************/
    1058          19 : int TABMAPIndexBlock::SplitNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin,
    1059             :                                 GInt32 nNewEntryXMax, GInt32 nNewEntryYMax)
    1060             : {
    1061          19 :     CPLAssert(m_poBlockManagerRef);
    1062             : 
    1063             :     /*-----------------------------------------------------------------
    1064             :      * Create a 2nd node
    1065             :      *----------------------------------------------------------------*/
    1066          19 :     TABMAPIndexBlock *poNewNode = new TABMAPIndexBlock(m_eAccess);
    1067          19 :     if (poNewNode->InitNewBlock(m_fp, m_nBlockSize,
    1068          38 :                                 m_poBlockManagerRef->AllocNewBlock("INDEX")) !=
    1069             :         0)
    1070             :     {
    1071           0 :         return -1;
    1072             :     }
    1073          19 :     poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef);
    1074             : 
    1075             :     /*-----------------------------------------------------------------
    1076             :      * Make a temporary copy of the entries in current node
    1077             :      *----------------------------------------------------------------*/
    1078          19 :     int nSrcEntries = m_numEntries;
    1079             :     TABMAPIndexEntry *pasSrcEntries = static_cast<TABMAPIndexEntry *>(
    1080          19 :         CPLMalloc(m_numEntries * sizeof(TABMAPIndexEntry)));
    1081          19 :     memcpy(pasSrcEntries, &m_asEntries,
    1082          19 :            m_numEntries * sizeof(TABMAPIndexEntry));
    1083             : 
    1084          19 :     int nSrcCurChildIndex = m_nCurChildIndex;
    1085             : 
    1086             :     /*-----------------------------------------------------------------
    1087             :      * Pick Seeds for each node
    1088             :      *----------------------------------------------------------------*/
    1089             :     int nSeed1, nSeed2;
    1090          19 :     PickSeedsForSplit(pasSrcEntries, nSrcEntries, nSrcCurChildIndex,
    1091             :                       nNewEntryXMin, nNewEntryYMin, nNewEntryXMax,
    1092             :                       nNewEntryYMax, nSeed1, nSeed2);
    1093             : 
    1094             :     /*-----------------------------------------------------------------
    1095             :      * Reset number of entries in this node and start moving new entries
    1096             :      *----------------------------------------------------------------*/
    1097          19 :     m_numEntries = 0;
    1098             : 
    1099             :     // Insert nSeed1 in this node
    1100          19 :     InsertEntry(pasSrcEntries[nSeed1].XMin, pasSrcEntries[nSeed1].YMin,
    1101          19 :                 pasSrcEntries[nSeed1].XMax, pasSrcEntries[nSeed1].YMax,
    1102          19 :                 pasSrcEntries[nSeed1].nBlockPtr);
    1103             : 
    1104             :     // Move nSeed2 to 2nd node
    1105          19 :     poNewNode->InsertEntry(
    1106          19 :         pasSrcEntries[nSeed2].XMin, pasSrcEntries[nSeed2].YMin,
    1107          19 :         pasSrcEntries[nSeed2].XMax, pasSrcEntries[nSeed2].YMax,
    1108          19 :         pasSrcEntries[nSeed2].nBlockPtr);
    1109             : 
    1110             :     // Update cur child index if necessary
    1111          19 :     if (nSeed1 == nSrcCurChildIndex)
    1112           0 :         m_nCurChildIndex = m_numEntries - 1;
    1113             : 
    1114             :     /*-----------------------------------------------------------------
    1115             :      * Go through the rest of the entries and assign them to one
    1116             :      * of the 2 nodes.
    1117             :      *
    1118             :      * Criteria is minimal area difference.
    1119             :      * Resolve ties by adding the entry to the node with smaller total
    1120             :      * area, then to the one with fewer entries, then to either.
    1121             :      *----------------------------------------------------------------*/
    1122         494 :     for (int iEntry = 0; iEntry < nSrcEntries; iEntry++)
    1123             :     {
    1124         475 :         if (iEntry == nSeed1 || iEntry == nSeed2)
    1125          39 :             continue;
    1126             : 
    1127             :         // If one of the two nodes is almost full then all remaining
    1128             :         // entries should go to the other node
    1129             :         // The entry corresponding to the current child also automatically
    1130             :         // stays in this node.
    1131         437 :         if (iEntry == nSrcCurChildIndex)
    1132             :         {
    1133           1 :             InsertEntry(pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
    1134           1 :                         pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
    1135           1 :                         pasSrcEntries[iEntry].nBlockPtr);
    1136             : 
    1137             :             // Update current child index
    1138           1 :             m_nCurChildIndex = m_numEntries - 1;
    1139             : 
    1140           1 :             continue;
    1141             :         }
    1142         436 :         else if (m_numEntries >= GetMaxEntries() - 1)
    1143             :         {
    1144           0 :             poNewNode->InsertEntry(
    1145           0 :                 pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
    1146           0 :                 pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
    1147           0 :                 pasSrcEntries[iEntry].nBlockPtr);
    1148           0 :             continue;
    1149             :         }
    1150         436 :         else if (poNewNode->GetNumEntries() >= GetMaxEntries() - 1)
    1151             :         {
    1152           0 :             InsertEntry(pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
    1153           0 :                         pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
    1154           0 :                         pasSrcEntries[iEntry].nBlockPtr);
    1155           0 :             continue;
    1156             :         }
    1157             : 
    1158             :         // Decide which of the two nodes to put this entry in
    1159         436 :         RecomputeMBR();
    1160         872 :         const double dAreaDiff1 = ComputeAreaDiff(
    1161         436 :             m_nMinX, m_nMinY, m_nMaxX, m_nMaxY, pasSrcEntries[iEntry].XMin,
    1162         436 :             pasSrcEntries[iEntry].YMin, pasSrcEntries[iEntry].XMax,
    1163         436 :             pasSrcEntries[iEntry].YMax);
    1164             : 
    1165         436 :         GInt32 nXMin2 = 0;
    1166         436 :         GInt32 nYMin2 = 0;
    1167         436 :         GInt32 nXMax2 = 0;
    1168         436 :         GInt32 nYMax2 = 0;
    1169         436 :         poNewNode->RecomputeMBR();
    1170         436 :         poNewNode->GetMBR(nXMin2, nYMin2, nXMax2, nYMax2);
    1171         872 :         const double dAreaDiff2 = ComputeAreaDiff(
    1172         436 :             nXMin2, nYMin2, nXMax2, nYMax2, pasSrcEntries[iEntry].XMin,
    1173         436 :             pasSrcEntries[iEntry].YMin, pasSrcEntries[iEntry].XMax,
    1174         436 :             pasSrcEntries[iEntry].YMax);
    1175         436 :         if (dAreaDiff1 < dAreaDiff2)
    1176             :         {
    1177             :             // This entry stays in this node.
    1178         156 :             InsertEntry(pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
    1179         156 :                         pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
    1180         156 :                         pasSrcEntries[iEntry].nBlockPtr);
    1181             :         }
    1182             :         else
    1183             :         {
    1184             :             // This entry goes to new node
    1185         280 :             poNewNode->InsertEntry(
    1186         280 :                 pasSrcEntries[iEntry].XMin, pasSrcEntries[iEntry].YMin,
    1187         280 :                 pasSrcEntries[iEntry].XMax, pasSrcEntries[iEntry].YMax,
    1188         280 :                 pasSrcEntries[iEntry].nBlockPtr);
    1189             :         }
    1190             :     }
    1191             : 
    1192             :     /*-----------------------------------------------------------------
    1193             :      * Recompute MBR and update current node info in parent
    1194             :      *----------------------------------------------------------------*/
    1195          19 :     RecomputeMBR();
    1196          19 :     poNewNode->RecomputeMBR();
    1197             : 
    1198             :     /*-----------------------------------------------------------------
    1199             :      * Add second node info to parent and then flush it to disk.
    1200             :      * This may trigger splitting of parent
    1201             :      *----------------------------------------------------------------*/
    1202          19 :     CPLAssert(m_poParentRef);
    1203             :     int nMinX, nMinY, nMaxX, nMaxY;
    1204          19 :     poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1205          19 :     m_poParentRef->AddEntry(nMinX, nMinY, nMaxX, nMaxY,
    1206             :                             poNewNode->GetNodeBlockPtr(), TRUE);
    1207          19 :     poNewNode->CommitToFile();
    1208          19 :     delete poNewNode;
    1209             : 
    1210          19 :     CPLFree(pasSrcEntries);
    1211             : 
    1212          19 :     return 0;
    1213             : }
    1214             : 
    1215             : /**********************************************************************
    1216             :  *                   TABMAPIndexBlock::SplitRootNode()
    1217             :  *
    1218             :  * (private method)
    1219             :  *
    1220             :  * Split a Root Node.
    1221             :  * First, a level of nodes must be added to the tree, then the contents
    1222             :  * of what used to be the root node is moved 1 level down and then that
    1223             :  * node is split like a regular node.
    1224             :  *
    1225             :  * Returns 0 on success, -1 on error
    1226             :  **********************************************************************/
    1227           6 : int TABMAPIndexBlock::SplitRootNode(GInt32 nNewEntryXMin, GInt32 nNewEntryYMin,
    1228             :                                     GInt32 nNewEntryXMax, GInt32 nNewEntryYMax)
    1229             : {
    1230           6 :     CPLAssert(m_poBlockManagerRef);
    1231           6 :     CPLAssert(m_poParentRef == nullptr);
    1232             : 
    1233             :     /*-----------------------------------------------------------------
    1234             :      * Since a root note cannot be split, we add a level of nodes
    1235             :      * under it and we'll do the split at that level.
    1236             :      *----------------------------------------------------------------*/
    1237          12 :     auto poNewNode = std::make_unique<TABMAPIndexBlock>(m_eAccess);
    1238             : 
    1239          12 :     if (poNewNode->InitNewBlock(m_fp, m_nBlockSize,
    1240          12 :                                 m_poBlockManagerRef->AllocNewBlock("INDEX")) !=
    1241             :         0)
    1242             :     {
    1243           0 :         return -1;
    1244             :     }
    1245           6 :     poNewNode->SetMAPBlockManagerRef(m_poBlockManagerRef);
    1246             : 
    1247             :     // Move all entries to the new child
    1248           6 :     int nSrcEntries = m_numEntries;
    1249           6 :     m_numEntries = 0;
    1250         156 :     for (int iEntry = 0; iEntry < nSrcEntries; iEntry++)
    1251             :     {
    1252         150 :         poNewNode->InsertEntry(
    1253             :             m_asEntries[iEntry].XMin, m_asEntries[iEntry].YMin,
    1254             :             m_asEntries[iEntry].XMax, m_asEntries[iEntry].YMax,
    1255             :             m_asEntries[iEntry].nBlockPtr);
    1256             :     }
    1257             : 
    1258             :     /*-----------------------------------------------------------------
    1259             :      * Transfer current child object to new node.
    1260             :      *----------------------------------------------------------------*/
    1261           6 :     if (m_poCurChild)
    1262             :     {
    1263           1 :         poNewNode->SetCurChild(std::move(m_poCurChild), m_nCurChildIndex);
    1264           1 :         m_nCurChildIndex = -1;
    1265             :     }
    1266             : 
    1267             :     /*-----------------------------------------------------------------
    1268             :      * Place info about new child in current node.
    1269             :      *----------------------------------------------------------------*/
    1270           6 :     poNewNode->RecomputeMBR();
    1271             :     int nMinX, nMinY, nMaxX, nMaxY;
    1272           6 :     poNewNode->GetMBR(nMinX, nMinY, nMaxX, nMaxY);
    1273           6 :     InsertEntry(nMinX, nMinY, nMaxX, nMaxY, poNewNode->GetNodeBlockPtr());
    1274             : 
    1275             :     /*-----------------------------------------------------------------
    1276             :      * Keep a reference to the new child
    1277             :      *----------------------------------------------------------------*/
    1278           6 :     poNewNode->SetParentRef(this);
    1279           6 :     m_poCurChild = std::move(poNewNode);
    1280           6 :     m_nCurChildIndex = m_numEntries - 1;
    1281             : 
    1282             :     /*-----------------------------------------------------------------
    1283             :      * And finally force the child to split itself
    1284             :      *----------------------------------------------------------------*/
    1285           6 :     return m_poCurChild->SplitNode(nNewEntryXMin, nNewEntryYMin, nNewEntryXMax,
    1286           6 :                                    nNewEntryYMax);
    1287             : }
    1288             : 
    1289             : /**********************************************************************
    1290             :  *                   TABMAPIndexBlock::RecomputeMBR()
    1291             :  *
    1292             :  * Recompute current block MBR, and update info in parent.
    1293             :  **********************************************************************/
    1294        3036 : void TABMAPIndexBlock::RecomputeMBR()
    1295             : {
    1296             :     GInt32 nMinX, nMinY, nMaxX, nMaxY;
    1297             : 
    1298        3036 :     nMinX = 1000000000;
    1299        3036 :     nMinY = 1000000000;
    1300        3036 :     nMaxX = -1000000000;
    1301        3036 :     nMaxY = -1000000000;
    1302             : 
    1303       36190 :     for (int i = 0; i < m_numEntries; i++)
    1304             :     {
    1305       33154 :         if (m_asEntries[i].XMin < nMinX)
    1306        8180 :             nMinX = m_asEntries[i].XMin;
    1307       33154 :         if (m_asEntries[i].XMax > nMaxX)
    1308        5259 :             nMaxX = m_asEntries[i].XMax;
    1309             : 
    1310       33154 :         if (m_asEntries[i].YMin < nMinY)
    1311        5686 :             nMinY = m_asEntries[i].YMin;
    1312       33154 :         if (m_asEntries[i].YMax > nMaxY)
    1313        6415 :             nMaxY = m_asEntries[i].YMax;
    1314             :     }
    1315             : 
    1316        3036 :     if (m_nMinX != nMinX || m_nMinY != nMinY || m_nMaxX != nMaxX ||
    1317        1059 :         m_nMaxY != nMaxY)
    1318             :     {
    1319        2002 :         m_nMinX = nMinX;
    1320        2002 :         m_nMinY = nMinY;
    1321        2002 :         m_nMaxX = nMaxX;
    1322        2002 :         m_nMaxY = nMaxY;
    1323             : 
    1324        2002 :         m_bModified = TRUE;
    1325             : 
    1326        2002 :         if (m_poParentRef)
    1327         890 :             m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
    1328             :                                              GetNodeBlockPtr());
    1329             :     }
    1330        3036 : }
    1331             : 
    1332             : /**********************************************************************
    1333             :  *                   TABMAPIndexBlock::UpdateCurChildMBR()
    1334             :  *
    1335             :  * Update current child MBR info, and propagate info in parent.
    1336             :  *
    1337             :  * nBlockPtr is passed only to validate the consistency of the tree.
    1338             :  **********************************************************************/
    1339         913 : void TABMAPIndexBlock::UpdateCurChildMBR(GInt32 nXMin, GInt32 nYMin,
    1340             :                                          GInt32 nXMax, GInt32 nYMax,
    1341             :                                          CPL_UNUSED GInt32 nBlockPtr)
    1342             : {
    1343         913 :     CPLAssert(m_poCurChild);
    1344         913 :     CPLAssert(m_asEntries[m_nCurChildIndex].nBlockPtr == nBlockPtr);
    1345             : 
    1346         913 :     if (m_asEntries[m_nCurChildIndex].XMin == nXMin &&
    1347         874 :         m_asEntries[m_nCurChildIndex].YMin == nYMin &&
    1348         843 :         m_asEntries[m_nCurChildIndex].XMax == nXMax &&
    1349         506 :         m_asEntries[m_nCurChildIndex].YMax == nYMax)
    1350             :     {
    1351         496 :         return; /* Nothing changed... nothing to do */
    1352             :     }
    1353             : 
    1354         417 :     m_bModified = TRUE;
    1355             : 
    1356         417 :     m_asEntries[m_nCurChildIndex].XMin = nXMin;
    1357         417 :     m_asEntries[m_nCurChildIndex].YMin = nYMin;
    1358         417 :     m_asEntries[m_nCurChildIndex].XMax = nXMax;
    1359         417 :     m_asEntries[m_nCurChildIndex].YMax = nYMax;
    1360             : 
    1361         417 :     m_nMinX = 1000000000;
    1362         417 :     m_nMinY = 1000000000;
    1363         417 :     m_nMaxX = -1000000000;
    1364         417 :     m_nMaxY = -1000000000;
    1365             : 
    1366        2558 :     for (int i = 0; i < m_numEntries; i++)
    1367             :     {
    1368        2141 :         if (m_asEntries[i].XMin < m_nMinX)
    1369        1219 :             m_nMinX = m_asEntries[i].XMin;
    1370        2141 :         if (m_asEntries[i].XMax > m_nMaxX)
    1371         451 :             m_nMaxX = m_asEntries[i].XMax;
    1372             : 
    1373        2141 :         if (m_asEntries[i].YMin < m_nMinY)
    1374         767 :             m_nMinY = m_asEntries[i].YMin;
    1375        2141 :         if (m_asEntries[i].YMax > m_nMaxY)
    1376         561 :             m_nMaxY = m_asEntries[i].YMax;
    1377             :     }
    1378             : 
    1379         417 :     if (m_poParentRef)
    1380          23 :         m_poParentRef->UpdateCurChildMBR(m_nMinX, m_nMinY, m_nMaxX, m_nMaxY,
    1381             :                                          GetNodeBlockPtr());
    1382             : }
    1383             : 
    1384             : /**********************************************************************
    1385             :  *                   TABMAPIndexBlock::SetMAPBlockManagerRef()
    1386             :  *
    1387             :  * Pass a reference to the block manager object for the file this
    1388             :  * block belongs to.  The block manager will be used by this object
    1389             :  * when it needs to automatically allocate a new block.
    1390             :  **********************************************************************/
    1391       34493 : void TABMAPIndexBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
    1392             : {
    1393       34493 :     m_poBlockManagerRef = poBlockMgr;
    1394       34493 : }
    1395             : 
    1396             : /**********************************************************************
    1397             :  *                   TABMAPIndexBlock::SetParentRef()
    1398             :  *
    1399             :  * Used to pass a reference to this node's parent.
    1400             :  **********************************************************************/
    1401       33333 : void TABMAPIndexBlock::SetParentRef(TABMAPIndexBlock *poParent)
    1402             : {
    1403       33333 :     m_poParentRef = poParent;
    1404       33333 : }
    1405             : 
    1406             : /**********************************************************************
    1407             :  *                   TABMAPIndexBlock::SetCurChild()
    1408             :  *
    1409             :  * Used to transfer a child object from one node to another
    1410             :  **********************************************************************/
    1411      437351 : void TABMAPIndexBlock::SetCurChild(std::unique_ptr<TABMAPIndexBlock> &&poChild,
    1412             :                                    int nChildIndex)
    1413             : {
    1414      437351 :     if (poChild)
    1415       22744 :         poChild->SetParentRef(this);
    1416      437351 :     m_poCurChild = std::move(poChild);
    1417      437351 :     m_nCurChildIndex = nChildIndex;
    1418      437351 : }
    1419             : 
    1420             : /**********************************************************************
    1421             :  *                   TABMAPIndexBlock::Dump()
    1422             :  *
    1423             :  * Dump block contents... available only in DEBUG mode.
    1424             :  **********************************************************************/
    1425             : #ifdef DEBUG
    1426             : 
    1427           0 : void TABMAPIndexBlock::Dump(FILE *fpOut /*=NULL*/)
    1428             : {
    1429           0 :     if (fpOut == nullptr)
    1430           0 :         fpOut = stdout;
    1431             : 
    1432           0 :     fprintf(fpOut, "----- TABMAPIndexBlock::Dump() -----\n");
    1433           0 :     if (m_pabyBuf == nullptr)
    1434             :     {
    1435           0 :         fprintf(fpOut, "Block has not been initialized yet.");
    1436             :     }
    1437             :     else
    1438             :     {
    1439           0 :         fprintf(fpOut, "Index Block (type %d) at offset %d.\n", m_nBlockType,
    1440             :                 m_nFileOffset);
    1441           0 :         fprintf(fpOut, "  m_numEntries          = %d\n", m_numEntries);
    1442             : 
    1443             :         /*-------------------------------------------------------------
    1444             :          * Loop through all entries, dumping each of them
    1445             :          *------------------------------------------------------------*/
    1446           0 :         if (m_numEntries > 0)
    1447           0 :             ReadAllEntries();
    1448             : 
    1449           0 :         for (int i = 0; i < m_numEntries; i++)
    1450             :         {
    1451           0 :             fprintf(fpOut, "    %6d -> (%d, %d) - (%d, %d)\n",
    1452             :                     m_asEntries[i].nBlockPtr, m_asEntries[i].XMin,
    1453             :                     m_asEntries[i].YMin, m_asEntries[i].XMax,
    1454             :                     m_asEntries[i].YMax);
    1455             :         }
    1456             :     }
    1457             : 
    1458           0 :     fflush(fpOut);
    1459           0 : }
    1460             : #endif  // DEBUG

Generated by: LCOV version 1.14