LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_indfile.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 336 724 46.4 %
Date: 2024-05-05 20:23:34 Functions: 27 40 67.5 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_indfile.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABINDFile class used to handle
       7             :  *           access to .IND file (table field indexes) attached to a .DAT file
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999-2001, Daniel Morissette
      12             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      13             :  *
      14             :  * Permission is hereby granted, free of charge, to any person obtaining a
      15             :  * copy of this software and associated documentation files (the "Software"),
      16             :  * to deal in the Software without restriction, including without limitation
      17             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      18             :  * and/or sell copies of the Software, and to permit persons to whom the
      19             :  * Software is furnished to do so, subject to the following conditions:
      20             :  *
      21             :  * The above copyright notice and this permission notice shall be included
      22             :  * in all copies or substantial portions of the Software.
      23             :  *
      24             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      25             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      26             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      27             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      28             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      29             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      30             :  * DEALINGS IN THE SOFTWARE.
      31             :  **********************************************************************/
      32             : 
      33             : #include "cpl_port.h"
      34             : #include "mitab.h"
      35             : 
      36             : #include <cctype>
      37             : #include <cstring>
      38             : #include <algorithm>
      39             : 
      40             : #include "cpl_conv.h"
      41             : #include "cpl_error.h"
      42             : #include "cpl_vsi.h"
      43             : #include "mitab_priv.h"
      44             : #include "mitab_utils.h"
      45             : 
      46             : /*=====================================================================
      47             :  *                      class TABINDFile
      48             :  *====================================================================*/
      49             : 
      50             : constexpr GUInt32 IND_MAGIC_COOKIE = 24242424;
      51             : 
      52             : /**********************************************************************
      53             :  *                   TABINDFile::TABINDFile()
      54             :  *
      55             :  * Constructor.
      56             :  **********************************************************************/
      57          35 : TABINDFile::TABINDFile()
      58             :     : m_pszFname(nullptr), m_fp(nullptr), m_eAccessMode(TABRead),
      59          35 :       m_numIndexes(0), m_papoIndexRootNodes(nullptr), m_papbyKeyBuffers(nullptr)
      60             : {
      61          35 :     m_oBlockManager.SetName("IND");
      62          35 :     m_oBlockManager.SetBlockSize(512);
      63          35 : }
      64             : 
      65             : /**********************************************************************
      66             :  *                   TABINDFile::~TABINDFile()
      67             :  *
      68             :  * Destructor.
      69             :  **********************************************************************/
      70          35 : TABINDFile::~TABINDFile()
      71             : {
      72          35 :     Close();
      73          35 : }
      74             : 
      75             : /**********************************************************************
      76             :  *                   TABINDFile::Open()
      77             :  *
      78             :  * Open a .IND file, read the header and the root nodes for all the
      79             :  * field indexes, and be ready to search the indexes.
      80             :  *
      81             :  * If the filename that is passed in contains a .DAT extension then
      82             :  * the extension will be changed to .IND before trying to open the file.
      83             :  *
      84             :  * Note that we pass a pszAccess flag, but only read access is supported
      85             :  * for now (and there are no plans to support write.)
      86             :  *
      87             :  * Set bTestOpenNoError=TRUE to silently return -1 with no error message
      88             :  * if the file cannot be opened because it does not exist.
      89             :  *
      90             :  * Returns 0 on success, -1 on error.
      91             :  **********************************************************************/
      92          48 : int TABINDFile::Open(const char *pszFname, const char *pszAccess,
      93             :                      GBool bTestOpenNoError /*=FALSE*/)
      94             : {
      95          48 :     if (m_fp)
      96             :     {
      97           0 :         CPLError(CE_Failure, CPLE_FileIO,
      98             :                  "Open() failed: object already contains an open file");
      99           0 :         return -1;
     100             :     }
     101             : 
     102             :     /*-----------------------------------------------------------------
     103             :      * Validate access mode and make sure we use binary access.
     104             :      * Note that for write access, we actually need read/write access to
     105             :      * the file.
     106             :      *----------------------------------------------------------------*/
     107          48 :     if (STARTS_WITH_CI(pszAccess, "r") && strchr(pszAccess, '+') != nullptr)
     108             :     {
     109          13 :         m_eAccessMode = TABReadWrite;
     110          13 :         pszAccess = "rb+";
     111             :     }
     112          35 :     else if (STARTS_WITH_CI(pszAccess, "r"))
     113             :     {
     114          19 :         m_eAccessMode = TABRead;
     115          19 :         pszAccess = "rb";
     116             :     }
     117          16 :     else if (STARTS_WITH_CI(pszAccess, "w"))
     118             :     {
     119          16 :         m_eAccessMode = TABWrite;
     120          16 :         pszAccess = "wb+";
     121             :     }
     122             :     else
     123             :     {
     124           0 :         CPLError(CE_Failure, CPLE_FileIO,
     125             :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
     126           0 :         return -1;
     127             :     }
     128             : 
     129             :     /*-----------------------------------------------------------------
     130             :      * Change .DAT (or .TAB) extension to .IND if necessary
     131             :      *----------------------------------------------------------------*/
     132          48 :     m_pszFname = CPLStrdup(pszFname);
     133             : 
     134          48 :     const int nLen = static_cast<int>(strlen(m_pszFname));
     135          48 :     if (nLen > 4 && !EQUAL(m_pszFname + nLen - 4, ".IND"))
     136           3 :         strcpy(m_pszFname + nLen - 4, ".ind");
     137             : 
     138             : #ifndef _WIN32
     139          48 :     TABAdjustFilenameExtension(m_pszFname);
     140             : #endif
     141             : 
     142             :     /*-----------------------------------------------------------------
     143             :      * Open file
     144             :      *----------------------------------------------------------------*/
     145          48 :     m_fp = VSIFOpenL(m_pszFname, pszAccess);
     146             : 
     147          48 :     if (m_fp == nullptr)
     148             :     {
     149           0 :         if (!bTestOpenNoError)
     150           0 :             CPLError(CE_Failure, CPLE_FileIO, "Open() failed for %s (%s)",
     151             :                      m_pszFname, pszAccess);
     152             : 
     153           0 :         CPLFree(m_pszFname);
     154           0 :         m_pszFname = nullptr;
     155           0 :         return -1;
     156             :     }
     157             : 
     158             :     /*-----------------------------------------------------------------
     159             :      * Reset block manager to allocate first block at byte 512, after header.
     160             :      *----------------------------------------------------------------*/
     161          48 :     m_oBlockManager.Reset();
     162          48 :     m_oBlockManager.AllocNewBlock();
     163             : 
     164             :     /*-----------------------------------------------------------------
     165             :      * Read access: Read the header block
     166             :      * This will also alloc and init the array of index root nodes.
     167             :      *----------------------------------------------------------------*/
     168          80 :     if ((m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite) &&
     169          32 :         ReadHeader() != 0)
     170             :     {
     171             :         // Failed reading header... CPLError() has already been called
     172           0 :         Close();
     173           0 :         return -1;
     174             :     }
     175             : 
     176             :     /*-----------------------------------------------------------------
     177             :      * Write access: Init class members and write a dummy header block
     178             :      *----------------------------------------------------------------*/
     179          48 :     if (m_eAccessMode == TABWrite)
     180             :     {
     181          16 :         m_numIndexes = 0;
     182             : 
     183          16 :         if (WriteHeader() != 0)
     184             :         {
     185             :             // Failed writing header... CPLError() has already been called
     186           0 :             Close();
     187           0 :             return -1;
     188             :         }
     189             :     }
     190             : 
     191          48 :     return 0;
     192             : }
     193             : 
     194             : /**********************************************************************
     195             :  *                   TABINDFile::Close()
     196             :  *
     197             :  * Close current file, and release all memory used.
     198             :  *
     199             :  * Returns 0 on success, -1 on error.
     200             :  **********************************************************************/
     201          83 : int TABINDFile::Close()
     202             : {
     203          83 :     if (m_fp == nullptr)
     204          35 :         return 0;
     205             : 
     206             :     /*-----------------------------------------------------------------
     207             :      * In Write Mode, commit all indexes to the file
     208             :      *----------------------------------------------------------------*/
     209          48 :     if (m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite)
     210             :     {
     211          29 :         WriteHeader();
     212             : 
     213          71 :         for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
     214             :         {
     215          42 :             if (m_papoIndexRootNodes && m_papoIndexRootNodes[iIndex])
     216             :             {
     217          42 :                 CPL_IGNORE_RET_VAL(
     218          42 :                     m_papoIndexRootNodes[iIndex]->CommitToFile());
     219             :             }
     220             :         }
     221             :     }
     222             : 
     223             :     /*-----------------------------------------------------------------
     224             :      * Free index nodes in memory
     225             :      *----------------------------------------------------------------*/
     226         120 :     for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
     227             :     {
     228          72 :         if (m_papoIndexRootNodes && m_papoIndexRootNodes[iIndex])
     229          72 :             delete m_papoIndexRootNodes[iIndex];
     230          72 :         if (m_papbyKeyBuffers && m_papbyKeyBuffers[iIndex])
     231          72 :             CPLFree(m_papbyKeyBuffers[iIndex]);
     232             :     }
     233          48 :     CPLFree(m_papoIndexRootNodes);
     234          48 :     m_papoIndexRootNodes = nullptr;
     235          48 :     CPLFree(m_papbyKeyBuffers);
     236          48 :     m_papbyKeyBuffers = nullptr;
     237          48 :     m_numIndexes = 0;
     238             : 
     239             :     /*-----------------------------------------------------------------
     240             :      * Close file
     241             :      *----------------------------------------------------------------*/
     242          48 :     VSIFCloseL(m_fp);
     243          48 :     m_fp = nullptr;
     244             : 
     245          48 :     CPLFree(m_pszFname);
     246          48 :     m_pszFname = nullptr;
     247             : 
     248          48 :     return 0;
     249             : }
     250             : 
     251             : /**********************************************************************
     252             :  *                   TABINDFile::ReadHeader()
     253             :  *
     254             :  * (private method)
     255             :  * Read the header block and init all class members for read access.
     256             :  *
     257             :  * Returns 0 on success, -1 on error.
     258             :  **********************************************************************/
     259          32 : int TABINDFile::ReadHeader()
     260             : {
     261             : 
     262          32 :     CPLAssert(m_fp);
     263          32 :     CPLAssert(m_eAccessMode == TABRead || m_eAccessMode == TABReadWrite);
     264             : 
     265             :     /*-----------------------------------------------------------------
     266             :      * In ReadWrite mode, we need to init BlockManager with file size
     267             :      *----------------------------------------------------------------*/
     268             :     VSIStatBufL sStatBuf;
     269          32 :     if (m_eAccessMode == TABReadWrite && VSIStatL(m_pszFname, &sStatBuf) != -1)
     270             :     {
     271          13 :         m_oBlockManager.SetLastPtr(
     272          13 :             static_cast<int>(((sStatBuf.st_size - 1) / 512) * 512));
     273             :     }
     274             : 
     275             :     /*-----------------------------------------------------------------
     276             :      * Read the header block
     277             :      *----------------------------------------------------------------*/
     278          32 :     TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
     279          32 :     if (poHeaderBlock->ReadFromFile(m_fp, 0, 512) != 0)
     280             :     {
     281             :         // CPLError() has already been called.
     282           0 :         delete poHeaderBlock;
     283           0 :         return -1;
     284             :     }
     285             : 
     286          32 :     poHeaderBlock->GotoByteInBlock(0);
     287          32 :     GUInt32 nMagicCookie = poHeaderBlock->ReadInt32();
     288          32 :     if (nMagicCookie != IND_MAGIC_COOKIE)
     289             :     {
     290           0 :         CPLError(CE_Failure, CPLE_FileIO,
     291             :                  "%s: Invalid Magic Cookie: got %d, expected %d", m_pszFname,
     292             :                  nMagicCookie, IND_MAGIC_COOKIE);
     293           0 :         delete poHeaderBlock;
     294           0 :         return -1;
     295             :     }
     296             : 
     297          32 :     poHeaderBlock->GotoByteInBlock(12);
     298          32 :     m_numIndexes = poHeaderBlock->ReadInt16();
     299          32 :     if (m_numIndexes < 1 || m_numIndexes > 29)
     300             :     {
     301           0 :         CPLError(CE_Failure, CPLE_FileIO,
     302             :                  "Invalid number of indexes (%d) in file %s", m_numIndexes,
     303             :                  m_pszFname);
     304           0 :         delete poHeaderBlock;
     305           0 :         return -1;
     306             :     }
     307             : 
     308             :     /*-----------------------------------------------------------------
     309             :      * Alloc and init the array of index root nodes.
     310             :      *----------------------------------------------------------------*/
     311          32 :     m_papoIndexRootNodes = static_cast<TABINDNode **>(
     312          32 :         CPLCalloc(m_numIndexes, sizeof(TABINDNode *)));
     313             : 
     314          32 :     m_papbyKeyBuffers =
     315          32 :         static_cast<GByte **>(CPLCalloc(m_numIndexes, sizeof(GByte *)));
     316             : 
     317             :     /* First index def. starts at byte 48 */
     318          32 :     poHeaderBlock->GotoByteInBlock(48);
     319             : 
     320          75 :     for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
     321             :     {
     322             :         /*-------------------------------------------------------------
     323             :          * Read next index definition
     324             :          *------------------------------------------------------------*/
     325          43 :         GInt32 nRootNodePtr = poHeaderBlock->ReadInt32();
     326          43 :         poHeaderBlock->ReadInt16();  // skip... max. num of entries per node
     327          43 :         int nTreeDepth = poHeaderBlock->ReadByte();
     328          43 :         int nKeyLength = poHeaderBlock->ReadByte();
     329          43 :         poHeaderBlock->GotoByteRel(8);  // skip next 8 bytes;
     330             : 
     331             :         /*-------------------------------------------------------------
     332             :          * And init root node for this index.
     333             :          * Note that if nRootNodePtr==0 then this means that the
     334             :          * corresponding index does not exist (i.e. has been deleted?)
     335             :          * so we simply do not allocate the root node in this case.
     336             :          * An error will be produced if the user tries to access this index
     337             :          * later during execution.
     338             :          *------------------------------------------------------------*/
     339          43 :         if (nRootNodePtr > 0)
     340             :         {
     341          43 :             m_papoIndexRootNodes[iIndex] = new TABINDNode(m_eAccessMode);
     342          43 :             if (m_papoIndexRootNodes[iIndex]->InitNode(
     343             :                     m_fp, nRootNodePtr, nKeyLength, nTreeDepth, FALSE,
     344          43 :                     &m_oBlockManager) != 0)
     345             :             {
     346             :                 // CPLError has already been called
     347           0 :                 delete poHeaderBlock;
     348           0 :                 return -1;
     349             :             }
     350             : 
     351             :             // Alloc a temporary key buffer for this index.
     352             :             // This buffer will be used by the BuildKey() method
     353          86 :             m_papbyKeyBuffers[iIndex] =
     354          43 :                 static_cast<GByte *>(CPLCalloc(nKeyLength + 1, sizeof(GByte)));
     355             :         }
     356             :         else
     357             :         {
     358           0 :             m_papoIndexRootNodes[iIndex] = nullptr;
     359           0 :             m_papbyKeyBuffers[iIndex] = nullptr;
     360             :         }
     361             :     }
     362             : 
     363             :     /*-----------------------------------------------------------------
     364             :      * OK, we won't need the header block any more... free it.
     365             :      *----------------------------------------------------------------*/
     366          32 :     delete poHeaderBlock;
     367             : 
     368          32 :     return 0;
     369             : }
     370             : 
     371             : /**********************************************************************
     372             :  *                   TABINDFile::WriteHeader()
     373             :  *
     374             :  * (private method)
     375             :  * Write the header block based on current index information.
     376             :  *
     377             :  * Returns 0 on success, -1 on error.
     378             :  **********************************************************************/
     379          45 : int TABINDFile::WriteHeader()
     380             : {
     381          45 :     CPLAssert(m_fp);
     382          45 :     CPLAssert(m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite);
     383             : 
     384             :     /*-----------------------------------------------------------------
     385             :      * Write the 48 bytes of file header
     386             :      *----------------------------------------------------------------*/
     387          45 :     TABRawBinBlock *poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
     388          45 :     poHeaderBlock->InitNewBlock(m_fp, 512, 0);
     389             : 
     390          45 :     poHeaderBlock->WriteInt32(IND_MAGIC_COOKIE);
     391             : 
     392          45 :     poHeaderBlock->WriteInt16(100);  // ???
     393          45 :     poHeaderBlock->WriteInt16(512);  // ???
     394          45 :     poHeaderBlock->WriteInt32(0);    // ???
     395             : 
     396          45 :     poHeaderBlock->WriteInt16(static_cast<GInt16>(m_numIndexes));
     397             : 
     398          45 :     poHeaderBlock->WriteInt16(0x15e7);  // ???
     399             : 
     400          45 :     poHeaderBlock->WriteInt16(10);      // ???
     401          45 :     poHeaderBlock->WriteInt16(0x611d);  // ???
     402             : 
     403          45 :     poHeaderBlock->WriteZeros(28);
     404             : 
     405             :     /*-----------------------------------------------------------------
     406             :      * The first index definition starts at byte 48
     407             :      *----------------------------------------------------------------*/
     408          87 :     for (int iIndex = 0; iIndex < m_numIndexes; iIndex++)
     409             :     {
     410          42 :         TABINDNode *poRootNode = m_papoIndexRootNodes[iIndex];
     411             : 
     412          42 :         if (poRootNode)
     413             :         {
     414             :             /*---------------------------------------------------------
     415             :              * Write next index definition
     416             :              *--------------------------------------------------------*/
     417          42 :             poHeaderBlock->WriteInt32(poRootNode->GetNodeBlockPtr());
     418          42 :             poHeaderBlock->WriteInt16(
     419          42 :                 static_cast<GInt16>(poRootNode->GetMaxNumEntries()));
     420          42 :             poHeaderBlock->WriteByte(
     421          42 :                 static_cast<GByte>(poRootNode->GetSubTreeDepth()));
     422          42 :             poHeaderBlock->WriteByte(
     423          42 :                 static_cast<GByte>(poRootNode->GetKeyLength()));
     424             : 
     425          42 :             poHeaderBlock->WriteZeros(8);
     426             : 
     427             :             /*---------------------------------------------------------
     428             :              * Look for overflow of the SubTreeDepth field (byte)
     429             :              *--------------------------------------------------------*/
     430          42 :             if (poRootNode->GetSubTreeDepth() > 255)
     431             :             {
     432           0 :                 CPLError(CE_Failure, CPLE_AssertionFailed,
     433             :                          "Index no %d is too large and will not be usable. "
     434             :                          "(SubTreeDepth = %d, cannot exceed 255).",
     435             :                          iIndex + 1, poRootNode->GetSubTreeDepth());
     436           0 :                 return -1;
     437             :             }
     438             :         }
     439             :         else
     440             :         {
     441             :             /*---------------------------------------------------------
     442             :              * NULL Root Node: This index has likely been deleted
     443             :              *--------------------------------------------------------*/
     444           0 :             poHeaderBlock->WriteZeros(16);
     445             :         }
     446             :     }
     447             : 
     448             :     /*-----------------------------------------------------------------
     449             :      * OK, we won't need the header block any more... write and free it.
     450             :      *----------------------------------------------------------------*/
     451          45 :     if (poHeaderBlock->CommitToFile() != 0)
     452           0 :         return -1;
     453             : 
     454          45 :     delete poHeaderBlock;
     455             : 
     456          45 :     return 0;
     457             : }
     458             : 
     459             : /**********************************************************************
     460             :  *                   TABINDFile::ValidateIndexNo()
     461             :  *
     462             :  * Private method to validate the index no parameter of some methods...
     463             :  * returns 0 if index no. is OK, or produces an error ands returns -1
     464             :  * if index no is not valid.
     465             :  **********************************************************************/
     466        1088 : int TABINDFile::ValidateIndexNo(int nIndexNumber)
     467             : {
     468        1088 :     if (m_fp == nullptr)
     469             :     {
     470           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     471             :                  "TABINDFile: File has not been opened yet!");
     472           0 :         return -1;
     473             :     }
     474             : 
     475        1088 :     if (nIndexNumber < 1 || nIndexNumber > m_numIndexes ||
     476        1088 :         m_papoIndexRootNodes == nullptr ||
     477        1088 :         m_papoIndexRootNodes[nIndexNumber - 1] == nullptr)
     478             :     {
     479           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     480             :                  "No field index number %d in %s: Valid range is [1..%d].",
     481             :                  nIndexNumber, m_pszFname, m_numIndexes);
     482           0 :         return -1;
     483             :     }
     484             : 
     485        1088 :     return 0;  // Index seems valid
     486             : }
     487             : 
     488             : /**********************************************************************
     489             :  *                   TABINDFile::SetIndexFieldType()
     490             :  *
     491             :  * Sets the field type for the specified index.
     492             :  * This information will then be used in building the key values, etc.
     493             :  *
     494             :  * Returns 0 on success, -1 on error.
     495             :  **********************************************************************/
     496           2 : int TABINDFile::SetIndexFieldType(int nIndexNumber, TABFieldType eType)
     497             : {
     498           2 :     if (ValidateIndexNo(nIndexNumber) != 0)
     499           0 :         return -1;
     500             : 
     501           2 :     return m_papoIndexRootNodes[nIndexNumber - 1]->SetFieldType(eType);
     502             : }
     503             : 
     504             : /**********************************************************************
     505             :  *                   TABINDFile::SetIndexUnique()
     506             :  *
     507             :  * Indicate that an index's keys are unique.  This allows for some
     508             :  * optimization with read access.  By default, an index is treated as if
     509             :  * its keys could have duplicates.
     510             :  *
     511             :  * Returns 0 on success, -1 on error.
     512             :  **********************************************************************/
     513           0 : int TABINDFile::SetIndexUnique(int nIndexNumber, GBool bUnique /*=TRUE*/)
     514             : {
     515           0 :     if (ValidateIndexNo(nIndexNumber) != 0)
     516           0 :         return -1;
     517             : 
     518           0 :     m_papoIndexRootNodes[nIndexNumber - 1]->SetUnique(bUnique);
     519             : 
     520           0 :     return 0;
     521             : }
     522             : 
     523             : /**********************************************************************
     524             :  *                   TABINDFile::BuildKey()
     525             :  *
     526             :  * Encode a field value in the form required to be compared with index
     527             :  * keys in the specified index.
     528             :  *
     529             :  * Note that index numbers are positive values starting at 1.
     530             :  *
     531             :  * Returns a reference to an internal buffer that is valid only until the
     532             :  * next call to BuildKey().  (should not be freed by the caller).
     533             :  * Returns NULL if field index is invalid.
     534             :  *
     535             :  * The first flavor of the function handles integer type of values, this
     536             :  * corresponds to MapInfo types: integer, smallint, logical and date
     537             :  **********************************************************************/
     538         247 : GByte *TABINDFile::BuildKey(int nIndexNumber, GInt32 nValue)
     539             : {
     540         247 :     if (ValidateIndexNo(nIndexNumber) != 0)
     541           0 :         return nullptr;
     542             : 
     543         247 :     int nKeyLength = m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
     544             : 
     545             :     /*-----------------------------------------------------------------
     546             :      * Convert all int values to MSB using the right number of bytes
     547             :      * Note:
     548             :      * The most significant bit has to be unset for negative values,
     549             :      * and to be set for positive ones... that's the reverse of what it
     550             :      * should usually be.  Adding 0x80 to the MSB byte will do the job.
     551             :      *----------------------------------------------------------------*/
     552         247 :     switch (nKeyLength)
     553             :     {
     554           0 :         case 1:
     555           0 :             m_papbyKeyBuffers[nIndexNumber - 1][0] =
     556           0 :                 static_cast<GByte>(nValue & 0xff) + 0x80;
     557           0 :             break;
     558           0 :         case 2:
     559           0 :             m_papbyKeyBuffers[nIndexNumber - 1][0] =
     560           0 :                 static_cast<GByte>(nValue / 0x100 & 0xff) + 0x80;
     561           0 :             m_papbyKeyBuffers[nIndexNumber - 1][1] =
     562             :                 static_cast<GByte>(nValue & 0xff);
     563           0 :             break;
     564         247 :         case 4:
     565         247 :             m_papbyKeyBuffers[nIndexNumber - 1][0] =
     566         247 :                 static_cast<GByte>(nValue / 0x1000000 & 0xff) + 0x80;
     567         247 :             m_papbyKeyBuffers[nIndexNumber - 1][1] =
     568         247 :                 static_cast<GByte>(nValue / 0x10000 & 0xff);
     569         247 :             m_papbyKeyBuffers[nIndexNumber - 1][2] =
     570         247 :                 static_cast<GByte>(nValue / 0x100 & 0xff);
     571         247 :             m_papbyKeyBuffers[nIndexNumber - 1][3] =
     572             :                 static_cast<GByte>(nValue & 0xff);
     573         247 :             break;
     574           0 :         default:
     575           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     576             :                      "BuildKey(): %d bytes integer key length not supported",
     577             :                      nKeyLength);
     578           0 :             break;
     579             :     }
     580             : 
     581         247 :     return m_papbyKeyBuffers[nIndexNumber - 1];
     582             : }
     583             : 
     584             : /**********************************************************************
     585             :  *                   TABINDFile::BuildKey()
     586             :  *
     587             :  * BuildKey() for string fields
     588             :  **********************************************************************/
     589         270 : GByte *TABINDFile::BuildKey(int nIndexNumber, const char *pszStr)
     590             : {
     591         270 :     if (ValidateIndexNo(nIndexNumber) != 0 || pszStr == nullptr)
     592           0 :         return nullptr;
     593             : 
     594         270 :     int nKeyLength = m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
     595             : 
     596             :     /*-----------------------------------------------------------------
     597             :      * Strings keys are all in uppercase, and padded with '\0'
     598             :      *----------------------------------------------------------------*/
     599         270 :     int i = 0;
     600        2259 :     for (i = 0; i < nKeyLength && pszStr[i] != '\0'; i++)
     601             :     {
     602        1989 :         m_papbyKeyBuffers[nIndexNumber - 1][i] = static_cast<GByte>(
     603        1989 :             CPLToupper(static_cast<unsigned char>(pszStr[i])));
     604             :     }
     605             : 
     606             :     /* Pad the end of the buffer with '\0' */
     607        3113 :     for (; i < nKeyLength; i++)
     608             :     {
     609        2843 :         m_papbyKeyBuffers[nIndexNumber - 1][i] = '\0';
     610             :     }
     611             : 
     612         270 :     return m_papbyKeyBuffers[nIndexNumber - 1];
     613             : }
     614             : 
     615             : /**********************************************************************
     616             :  *                   TABINDFile::BuildKey()
     617             :  *
     618             :  * BuildKey() for float and decimal fields
     619             :  **********************************************************************/
     620           8 : GByte *TABINDFile::BuildKey(int nIndexNumber, double dValue)
     621             : {
     622           8 :     if (ValidateIndexNo(nIndexNumber) != 0)
     623           0 :         return nullptr;
     624             : 
     625             :     const int nKeyLength =
     626           8 :         m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
     627           8 :     CPLAssert(nKeyLength == 8 && sizeof(double) == 8);
     628             : 
     629             :     /*-----------------------------------------------------------------
     630             :      * Convert double and decimal values...
     631             :      * Reverse the sign of the value, and convert to MSB
     632             :      *----------------------------------------------------------------*/
     633           8 :     dValue = -dValue;
     634             : 
     635             : #ifndef CPL_MSB
     636           8 :     CPL_SWAPDOUBLE(&dValue);
     637             : #endif
     638             : 
     639           8 :     memcpy(m_papbyKeyBuffers[nIndexNumber - 1],
     640             :            reinterpret_cast<GByte *>(&dValue), nKeyLength);
     641             : 
     642           8 :     return m_papbyKeyBuffers[nIndexNumber - 1];
     643             : }
     644             : 
     645             : /**********************************************************************
     646             :  *                   TABINDFile::BuildKey()
     647             :  *
     648             :  * BuildKey() for LargeInt
     649             :  **********************************************************************/
     650           0 : GByte *TABINDFile::BuildKey(int nIndexNumber, GInt64 nValue)
     651             : {
     652           0 :     if (ValidateIndexNo(nIndexNumber) != 0)
     653           0 :         return nullptr;
     654             : 
     655             :     const int nKeyLength =
     656           0 :         m_papoIndexRootNodes[nIndexNumber - 1]->GetKeyLength();
     657           0 :     CPLAssert(nKeyLength == 8 && sizeof(double) == 8);
     658             : 
     659             : #ifndef CPL_MSB
     660           0 :     CPL_SWAP64PTR(&nValue);
     661             : #endif
     662             : 
     663           0 :     memcpy(m_papbyKeyBuffers[nIndexNumber - 1],
     664             :            reinterpret_cast<GByte *>(&nValue), nKeyLength);
     665             : 
     666           0 :     return m_papbyKeyBuffers[nIndexNumber - 1];
     667             : }
     668             : 
     669             : /**********************************************************************
     670             :  *                   TABINDFile::FindFirst()
     671             :  *
     672             :  * Search one of the indexes for a key value.
     673             :  *
     674             :  * Note that index numbers are positive values starting at 1.
     675             :  *
     676             :  * Return value:
     677             :  *  - the key's corresponding record number in the .DAT file (greater than 0)
     678             :  *  - 0 if the key was not found
     679             :  *  - or -1 if an error happened
     680             :  **********************************************************************/
     681          31 : GInt32 TABINDFile::FindFirst(int nIndexNumber, GByte *pKeyValue)
     682             : {
     683          31 :     if (ValidateIndexNo(nIndexNumber) != 0)
     684           0 :         return -1;
     685             : 
     686          31 :     return m_papoIndexRootNodes[nIndexNumber - 1]->FindFirst(pKeyValue);
     687             : }
     688             : 
     689             : /**********************************************************************
     690             :  *                   TABINDFile::FindNext()
     691             :  *
     692             :  * Continue the Search for pKeyValue previously initiated by FindFirst().
     693             :  * NOTE: FindFirst() MUST have been previously called for this call to
     694             :  *       work...
     695             :  *
     696             :  * Note that index numbers are positive values starting at 1.
     697             :  *
     698             :  * Return value:
     699             :  *  - the key's corresponding record number in the .DAT file (greater than 0)
     700             :  *  - 0 if the key was not found
     701             :  *  - or -1 if an error happened
     702             :  **********************************************************************/
     703          36 : GInt32 TABINDFile::FindNext(int nIndexNumber, GByte *pKeyValue)
     704             : {
     705          36 :     if (ValidateIndexNo(nIndexNumber) != 0)
     706           0 :         return -1;
     707             : 
     708          36 :     return m_papoIndexRootNodes[nIndexNumber - 1]->FindNext(pKeyValue);
     709             : }
     710             : 
     711             : /**********************************************************************
     712             :  *                   TABINDFile::CreateIndex()
     713             :  *
     714             :  * Create a new index with the specified field type and size.
     715             :  * Field size applies only to char field type... the other types have a
     716             :  * predefined key length.
     717             :  *
     718             :  * Key length is limited to 128 chars. char fields longer than 128 chars
     719             :  * will have their key truncated to 128 bytes.
     720             :  *
     721             :  * Note that a .IND file can contain only a maximum of 29 indexes.
     722             :  *
     723             :  * Returns the new field index on success (greater than 0), or -1 on error.
     724             :  **********************************************************************/
     725          29 : int TABINDFile::CreateIndex(TABFieldType eType, int nFieldSize)
     726             : {
     727          29 :     int i, nNewIndexNo = -1;
     728             : 
     729          29 :     if (m_fp == nullptr ||
     730          29 :         (m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite))
     731           0 :         return -1;
     732             : 
     733             :     // __TODO__
     734             :     // We'll need more work in TABDATFile::WriteDateTimeField() before
     735             :     // we can support indexes on fields of type DateTime (see bug #1844)
     736          29 :     if (eType == TABFDateTime)
     737             :     {
     738           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     739             :                  "Index on fields of type DateTime not supported yet.");
     740           0 :         return -1;
     741             :     }
     742             : 
     743             :     /*-----------------------------------------------------------------
     744             :      * Look for an empty slot in the current array, if there is none
     745             :      * then extend the array.
     746             :      *----------------------------------------------------------------*/
     747          42 :     for (i = 0; m_papoIndexRootNodes && i < m_numIndexes; i++)
     748             :     {
     749          13 :         if (m_papoIndexRootNodes[i] == nullptr)
     750             :         {
     751           0 :             nNewIndexNo = i;
     752           0 :             break;
     753             :         }
     754             :     }
     755             : 
     756          29 :     if (nNewIndexNo == -1 && m_numIndexes >= 29)
     757             :     {
     758           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     759             :                  "Cannot add new index to %s.  A dataset can contain only a "
     760             :                  "maximum of 29 indexes.",
     761             :                  m_pszFname);
     762           0 :         return -1;
     763             :     }
     764             : 
     765          29 :     if (nNewIndexNo == -1)
     766             :     {
     767             :         /*-------------------------------------------------------------
     768             :          * Add a slot for new index at the end of the nodes array.
     769             :          *------------------------------------------------------------*/
     770          29 :         m_numIndexes++;
     771          58 :         m_papoIndexRootNodes = static_cast<TABINDNode **>(CPLRealloc(
     772          29 :             m_papoIndexRootNodes, m_numIndexes * sizeof(TABINDNode *)));
     773             : 
     774          29 :         m_papbyKeyBuffers = static_cast<GByte **>(
     775          29 :             CPLRealloc(m_papbyKeyBuffers, m_numIndexes * sizeof(GByte *)));
     776             : 
     777          29 :         nNewIndexNo = m_numIndexes - 1;
     778             :     }
     779             : 
     780             :     /*-----------------------------------------------------------------
     781             :      * Alloc and init new node
     782             :      * The call to InitNode() automatically allocates storage space for
     783             :      * the node in the file.
     784             :      * New nodes are created with a subtree_depth=1 since they start as
     785             :      * leaf nodes, i.e. their entries point directly to .DAT records
     786             :      *----------------------------------------------------------------*/
     787          44 :     int nKeyLength = ((eType == TABFInteger)    ? 4
     788          30 :                       : (eType == TABFSmallInt) ? 2
     789          30 :                       : (eType == TABFLargeInt) ? 8
     790          29 :                       : (eType == TABFFloat)    ? 8
     791          28 :                       : (eType == TABFDecimal)  ? 8
     792          28 :                       : (eType == TABFDate)     ? 4
     793          28 :                       : (eType == TABFTime)     ? 4
     794             :                                                 :
     795             :                                             /*(eType == TABFDateTime) ? 8: */
     796          28 :                           (eType == TABFLogical) ? 4
     797          14 :                                                  : std::min(128, nFieldSize));
     798             : 
     799          29 :     m_papoIndexRootNodes[nNewIndexNo] = new TABINDNode(m_eAccessMode);
     800          29 :     if (m_papoIndexRootNodes[nNewIndexNo]->InitNode(m_fp, 0, nKeyLength,
     801             :                                                     1,      // subtree depth=1
     802             :                                                     FALSE,  // not unique
     803             :                                                     &m_oBlockManager, nullptr,
     804          29 :                                                     0, 0) != 0)
     805             :     {
     806             :         // CPLError has already been called
     807           0 :         return -1;
     808             :     }
     809             : 
     810             :     // Alloc a temporary key buffer for this index.
     811             :     // This buffer will be used by the BuildKey() method
     812          58 :     m_papbyKeyBuffers[nNewIndexNo] =
     813          29 :         static_cast<GByte *>(CPLCalloc(nKeyLength + 1, sizeof(GByte)));
     814             : 
     815             :     // Return 1-based index number
     816          29 :     return nNewIndexNo + 1;
     817             : }
     818             : 
     819             : /**********************************************************************
     820             :  *                   TABINDFile::AddEntry()
     821             :  *
     822             :  * Add an .DAT record entry for pKeyValue in the specified index.
     823             :  *
     824             :  * Note that index numbers are positive values starting at 1.
     825             :  * nRecordNo is the .DAT record number, record numbers start at 1.
     826             :  *
     827             :  * Returns 0 on success, -1 on error
     828             :  **********************************************************************/
     829         494 : int TABINDFile::AddEntry(int nIndexNumber, GByte *pKeyValue, GInt32 nRecordNo)
     830             : {
     831         988 :     if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
     832         494 :         ValidateIndexNo(nIndexNumber) != 0)
     833           0 :         return -1;
     834             : 
     835         494 :     return m_papoIndexRootNodes[nIndexNumber - 1]->AddEntry(pKeyValue,
     836         494 :                                                             nRecordNo);
     837             : }
     838             : 
     839             : /**********************************************************************
     840             :  *                   TABINDFile::Dump()
     841             :  *
     842             :  * Dump block contents... available only in DEBUG mode.
     843             :  **********************************************************************/
     844             : #ifdef DEBUG
     845             : 
     846           0 : void TABINDFile::Dump(FILE *fpOut /*=NULL*/)
     847             : {
     848           0 :     if (fpOut == nullptr)
     849           0 :         fpOut = stdout;
     850             : 
     851           0 :     fprintf(fpOut, "----- TABINDFile::Dump() -----\n");
     852             : 
     853           0 :     if (m_fp == nullptr)
     854             :     {
     855           0 :         fprintf(fpOut, "File is not opened.\n");
     856             :     }
     857             :     else
     858             :     {
     859           0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
     860           0 :         fprintf(fpOut, "   m_numIndexes   = %d\n", m_numIndexes);
     861           0 :         for (int i = 0; i < m_numIndexes && m_papoIndexRootNodes; i++)
     862             :         {
     863           0 :             if (m_papoIndexRootNodes[i])
     864             :             {
     865           0 :                 fprintf(fpOut, "  ----- Index # %d -----\n", i + 1);
     866           0 :                 m_papoIndexRootNodes[i]->Dump(fpOut);
     867             :             }
     868             :         }
     869             :     }
     870             : 
     871           0 :     fflush(fpOut);
     872           0 : }
     873             : 
     874             : #endif  // DEBUG
     875             : 
     876             : /*=====================================================================
     877             :  *                      class TABINDNode
     878             :  *====================================================================*/
     879             : 
     880             : /**********************************************************************
     881             :  *                   TABINDNode::TABINDNode()
     882             :  *
     883             :  * Constructor.
     884             :  **********************************************************************/
     885          72 : TABINDNode::TABINDNode(TABAccess eAccessMode /*=TABRead*/)
     886             :     : m_fp(nullptr), m_eAccessMode(eAccessMode), m_poCurChildNode(nullptr),
     887             :       m_poParentNodeRef(nullptr), m_poBlockManagerRef(nullptr),
     888             :       m_nSubTreeDepth(0), m_nKeyLength(0), m_eFieldType(TABFUnknown),
     889             :       m_bUnique(FALSE), m_nCurDataBlockPtr(0), m_nCurIndexEntry(0),
     890             :       m_poDataBlock(nullptr), m_numEntriesInNode(0), m_nPrevNodePtr(0),
     891          72 :       m_nNextNodePtr(0)
     892             : {
     893          72 : }
     894             : 
     895             : /**********************************************************************
     896             :  *                   TABINDNode::~TABINDNode()
     897             :  *
     898             :  * Destructor.
     899             :  **********************************************************************/
     900         144 : TABINDNode::~TABINDNode()
     901             : {
     902          72 :     if (m_poCurChildNode)
     903           0 :         delete m_poCurChildNode;
     904             : 
     905          72 :     if (m_poDataBlock)
     906          72 :         delete m_poDataBlock;
     907          72 : }
     908             : 
     909             : /**********************************************************************
     910             :  *                   TABINDNode::InitNode()
     911             :  *
     912             :  * Init a node... this function can be used either to initialize a new
     913             :  * node, or to make it point to a new data block in the file.
     914             :  *
     915             :  * By default, this call will read the data from the file at the
     916             :  * specified location if necessary, and leave the object ready to be searched.
     917             :  *
     918             :  * In write access, if the block does not exist (i.e. nBlockPtr=0) then a
     919             :  * new one is created and initialized.
     920             :  *
     921             :  * poParentNode is used in write access in order to update the parent node
     922             :  * when this node becomes full and has to be split.
     923             :  *
     924             :  * Returns 0 on success, -1 on error.
     925             :  **********************************************************************/
     926          72 : int TABINDNode::InitNode(VSILFILE *fp, int nBlockPtr, int nKeyLength,
     927             :                          int nSubTreeDepth, GBool bUnique,
     928             :                          TABBinBlockManager *poBlockMgr /*=NULL*/,
     929             :                          TABINDNode *poParentNode /*=NULL*/,
     930             :                          int nPrevNodePtr /*=0*/, int nNextNodePtr /*=0*/)
     931             : {
     932             :     /*-----------------------------------------------------------------
     933             :      * If the block already points to the right block, then don't do
     934             :      * anything here.
     935             :      *----------------------------------------------------------------*/
     936          72 :     if (m_fp == fp && nBlockPtr > 0 && m_nCurDataBlockPtr == nBlockPtr)
     937           0 :         return 0;
     938             : 
     939             :     // Keep track of some info
     940          72 :     m_fp = fp;
     941          72 :     m_nKeyLength = nKeyLength;
     942          72 :     m_nSubTreeDepth = nSubTreeDepth;
     943          72 :     m_nCurDataBlockPtr = nBlockPtr;
     944          72 :     m_bUnique = bUnique;
     945             : 
     946             :     // Do not overwrite the following values if we receive NULL (the defaults)
     947          72 :     if (poBlockMgr)
     948          72 :         m_poBlockManagerRef = poBlockMgr;
     949          72 :     if (poParentNode)
     950           0 :         m_poParentNodeRef = poParentNode;
     951             : 
     952             :     // Set some defaults
     953          72 :     m_numEntriesInNode = 0;
     954          72 :     m_nPrevNodePtr = nPrevNodePtr;
     955          72 :     m_nNextNodePtr = nNextNodePtr;
     956             : 
     957          72 :     m_nCurIndexEntry = 0;
     958             : 
     959             :     /*-----------------------------------------------------------------
     960             :      * Init RawBinBlock
     961             :      * The node's buffer has to be created with read/write access since
     962             :      * the index is a very dynamic structure!
     963             :      *----------------------------------------------------------------*/
     964          72 :     if (m_poDataBlock == nullptr)
     965          72 :         m_poDataBlock = new TABRawBinBlock(TABReadWrite, TRUE);
     966             : 
     967          72 :     if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) &&
     968          29 :         nBlockPtr == 0 && m_poBlockManagerRef)
     969             :     {
     970             :         /*-------------------------------------------------------------
     971             :          * Write access: Create and init a new block
     972             :          *------------------------------------------------------------*/
     973          29 :         m_nCurDataBlockPtr = m_poBlockManagerRef->AllocNewBlock();
     974          29 :         m_poDataBlock->InitNewBlock(m_fp, 512, m_nCurDataBlockPtr);
     975             : 
     976          29 :         m_poDataBlock->WriteInt32(m_numEntriesInNode);
     977          29 :         m_poDataBlock->WriteInt32(m_nPrevNodePtr);
     978          29 :         m_poDataBlock->WriteInt32(m_nNextNodePtr);
     979             :     }
     980             :     else
     981             :     {
     982          43 :         CPLAssert(m_nCurDataBlockPtr > 0);
     983             :         /*-------------------------------------------------------------
     984             :          * Read the data block from the file, applies to read access, or
     985             :          * to write access (to modify an existing block)
     986             :          *------------------------------------------------------------*/
     987          43 :         if (m_poDataBlock->ReadFromFile(m_fp, m_nCurDataBlockPtr, 512) != 0)
     988             :         {
     989             :             // CPLError() has already been called.
     990           0 :             return -1;
     991             :         }
     992             : 
     993          43 :         m_poDataBlock->GotoByteInBlock(0);
     994          43 :         m_numEntriesInNode = m_poDataBlock->ReadInt32();
     995          43 :         m_nPrevNodePtr = m_poDataBlock->ReadInt32();
     996          43 :         m_nNextNodePtr = m_poDataBlock->ReadInt32();
     997             :     }
     998             : 
     999             :     // m_poDataBlock is now positioned at the beginning of the key entries
    1000             : 
    1001          72 :     return 0;
    1002             : }
    1003             : 
    1004             : /**********************************************************************
    1005             :  *                   TABINDNode::GotoNodePtr()
    1006             :  *
    1007             :  * Move to the specified node ptr, and read the new node data from the file.
    1008             :  *
    1009             :  * This is just a cover function on top of InitNode()
    1010             :  **********************************************************************/
    1011           0 : int TABINDNode::GotoNodePtr(GInt32 nNewNodePtr)
    1012             : {
    1013             :     // First flush current changes if any.
    1014           0 :     if ((m_eAccessMode == TABWrite || m_eAccessMode == TABReadWrite) &&
    1015           0 :         m_poDataBlock && m_poDataBlock->CommitToFile() != 0)
    1016           0 :         return -1;
    1017             : 
    1018           0 :     CPLAssert(nNewNodePtr % 512 == 0);
    1019             : 
    1020             :     // Then move to the requested location.
    1021           0 :     return InitNode(m_fp, nNewNodePtr, m_nKeyLength, m_nSubTreeDepth,
    1022           0 :                     m_bUnique);
    1023             : }
    1024             : 
    1025             : /**********************************************************************
    1026             :  *                   TABINDNode::ReadIndexEntry()
    1027             :  *
    1028             :  * Read the key value and record/node ptr for the specified index entry
    1029             :  * inside the current node data.
    1030             :  *
    1031             :  * nEntryNo is the 0-based index of the index entry that we are interested
    1032             :  * in inside the current node.
    1033             :  *
    1034             :  * Returns the record/node ptr, and copies the key value inside the
    1035             :  * buffer pointed to by *pKeyValue... this assumes that *pKeyValue points
    1036             :  * to a buffer big enough to hold the key value (m_nKeyLength bytes).
    1037             :  * If pKeyValue == NULL, then this parameter is ignored and the key value
    1038             :  * is not copied.
    1039             :  **********************************************************************/
    1040          43 : GInt32 TABINDNode::ReadIndexEntry(int nEntryNo, GByte *pKeyValue)
    1041             : {
    1042          43 :     GInt32 nRecordPtr = 0;
    1043          43 :     if (nEntryNo >= 0 && nEntryNo < m_numEntriesInNode)
    1044             :     {
    1045          43 :         if (pKeyValue)
    1046             :         {
    1047           0 :             m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4));
    1048           0 :             m_poDataBlock->ReadBytes(m_nKeyLength, pKeyValue);
    1049             :         }
    1050             :         else
    1051             :         {
    1052          43 :             m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4) +
    1053          43 :                                            m_nKeyLength);
    1054             :         }
    1055             : 
    1056          43 :         nRecordPtr = m_poDataBlock->ReadInt32();
    1057             :     }
    1058             : 
    1059          43 :     return nRecordPtr;
    1060             : }
    1061             : 
    1062             : /**********************************************************************
    1063             :  *                   TABINDNode::IndexKeyCmp()
    1064             :  *
    1065             :  * Compare the specified index entry with the key value, and
    1066             :  * return 0 if equal, an integer less than 0 if key is smaller than
    1067             :  * index entry, and an integer greater than 0 if key is bigger than
    1068             :  * index entry.
    1069             :  *
    1070             :  * nEntryNo is the 0-based index of the index entry that we are interested
    1071             :  * in inside the current node.
    1072             :  **********************************************************************/
    1073        7429 : int TABINDNode::IndexKeyCmp(const GByte *pKeyValue, int nEntryNo)
    1074             : {
    1075        7429 :     CPLAssert(pKeyValue);
    1076        7429 :     CPLAssert(nEntryNo >= 0 && nEntryNo < m_numEntriesInNode);
    1077             : 
    1078        7429 :     m_poDataBlock->GotoByteInBlock(12 + nEntryNo * (m_nKeyLength + 4));
    1079        7429 :     CPLAssert(m_nKeyLength <= 255);
    1080             :     GByte abyKey[255];
    1081        7429 :     if (m_poDataBlock->ReadBytes(m_nKeyLength, abyKey) != 0)
    1082           0 :         return -1;
    1083        7429 :     return memcmp(pKeyValue, abyKey, m_nKeyLength);
    1084             : }
    1085             : 
    1086             : /**********************************************************************
    1087             :  *                   TABINDNode::SetFieldType()
    1088             :  *
    1089             :  * Sets the field type for the current index and recursively set all
    1090             :  * children as well.
    1091             :  * This information will then be used in building the key values, etc.
    1092             :  *
    1093             :  * Returns 0 on success, -1 on error.
    1094             :  **********************************************************************/
    1095           2 : int TABINDNode::SetFieldType(TABFieldType eType)
    1096             : {
    1097           2 :     if (m_fp == nullptr)
    1098             :     {
    1099           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1100             :                  "TABINDNode::SetFieldType(): File has not been opened yet!");
    1101           0 :         return -1;
    1102             :     }
    1103             : 
    1104             :     /*-----------------------------------------------------------------
    1105             :      * Validate field type with key length
    1106             :      *----------------------------------------------------------------*/
    1107           2 :     if ((eType == TABFInteger && m_nKeyLength != 4) ||
    1108           2 :         (eType == TABFSmallInt && m_nKeyLength != 2) ||
    1109           2 :         (eType == TABFLargeInt && m_nKeyLength != 8) ||
    1110           2 :         (eType == TABFFloat && m_nKeyLength != 8) ||
    1111           2 :         (eType == TABFDecimal && m_nKeyLength != 8) ||
    1112           2 :         (eType == TABFDate && m_nKeyLength != 4) ||
    1113           2 :         (eType == TABFTime && m_nKeyLength != 4) ||
    1114           2 :         (eType == TABFDateTime && m_nKeyLength != 8) ||
    1115           0 :         (eType == TABFLogical && m_nKeyLength != 4))
    1116             :     {
    1117           0 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1118             :                  "Index key length (%d) does not match field type (%s).",
    1119           0 :                  m_nKeyLength, TABFIELDTYPE_2_STRING(eType));
    1120           0 :         return -1;
    1121             :     }
    1122             : 
    1123           2 :     m_eFieldType = eType;
    1124             : 
    1125             :     /*-----------------------------------------------------------------
    1126             :      * Pass the field type info to child nodes
    1127             :      *----------------------------------------------------------------*/
    1128           2 :     if (m_poCurChildNode)
    1129           0 :         return m_poCurChildNode->SetFieldType(eType);
    1130             : 
    1131           2 :     return 0;
    1132             : }
    1133             : 
    1134             : /**********************************************************************
    1135             :  *                   TABINDNode::FindFirst()
    1136             :  *
    1137             :  * Start a new search in this node and its children for a key value.
    1138             :  * If the index is not unique, then FindNext() can be used to return
    1139             :  * the other values that correspond to the key.
    1140             :  *
    1141             :  * Return value:
    1142             :  *  - the key's corresponding record number in the .DAT file (greater than 0)
    1143             :  *  - 0 if the key was not found
    1144             :  *  - or -1 if an error happened
    1145             :  **********************************************************************/
    1146         525 : GInt32 TABINDNode::FindFirst(const GByte *pKeyValue)
    1147             : {
    1148        1050 :     std::set<int> oSetVisitedNodePtr;
    1149        1050 :     return FindFirst(pKeyValue, oSetVisitedNodePtr);
    1150             : }
    1151             : 
    1152         525 : GInt32 TABINDNode::FindFirst(const GByte *pKeyValue,
    1153             :                              std::set<int> &oSetVisitedNodePtr)
    1154             : {
    1155         525 :     if (m_poDataBlock == nullptr)
    1156             :     {
    1157           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1158             :                  "TABINDNode::Search(): Node has not been initialized yet!");
    1159           0 :         return -1;
    1160             :     }
    1161             : 
    1162             :     /*-----------------------------------------------------------------
    1163             :      * Unless something has been broken, this method will be called by our
    1164             :      * parent node after it has established that we are the best candidate
    1165             :      * to contain the first instance of the key value.  So there is no
    1166             :      * need to look in the previous or next nodes in the chain... if the
    1167             :      * value is not found in the current node block then it is not present
    1168             :      * in the index at all.
    1169             :      *
    1170             :      * m_nCurIndexEntry will be used to keep track of the search pointer
    1171             :      * when FindNext() will be used.
    1172             :      *----------------------------------------------------------------*/
    1173         525 :     m_nCurIndexEntry = 0;
    1174             : 
    1175         525 :     if (m_nSubTreeDepth == 1)
    1176             :     {
    1177             :         /*-------------------------------------------------------------
    1178             :          * Leaf node level... we look for an exact match
    1179             :          *------------------------------------------------------------*/
    1180        4102 :         while (m_nCurIndexEntry < m_numEntriesInNode)
    1181             :         {
    1182        3741 :             int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry);
    1183        3741 :             if (nCmpStatus > 0)
    1184             :             {
    1185             :                 /* Not there yet... (pKey > IndexEntry) */
    1186        3577 :                 m_nCurIndexEntry++;
    1187             :             }
    1188         164 :             else if (nCmpStatus == 0)
    1189             :             {
    1190             :                 /* Found it!  Return the record number */
    1191          33 :                 return ReadIndexEntry(m_nCurIndexEntry, nullptr);
    1192             :             }
    1193             :             else
    1194             :             {
    1195             :                 /* Item does not exist... return 0 */
    1196         131 :                 return 0;
    1197             :             }
    1198             :         }
    1199             :     }
    1200             :     else
    1201             :     {
    1202             :         /*-------------------------------------------------------------
    1203             :          * Index Node: Find the child node that is the best candidate to
    1204             :          * contain the value
    1205             :          *
    1206             :          * In the index tree at the node level, for each node entry inside
    1207             :          * the parent node, the key value (in the parent) corresponds to
    1208             :          * the value of the first key that you will find when you access
    1209             :          * the corresponding child node.
    1210             :          *
    1211             :          * This means that to find the child that contains the searched
    1212             :          * key, we look for the first index key >= pKeyValue and the child
    1213             :          * node that we are looking for is the one that precedes it.
    1214             :          *
    1215             :          * If the first key in the list is >= pKeyValue then this means
    1216             :          * that the pKeyValue does not exist in our children and we just
    1217             :          * return 0.  We do not bother searching the previous node at the
    1218             :          * same level since this is the responsibility of our parent.
    1219             :          *
    1220             :          * The same way if the last indexkey in this node is < pKeyValue
    1221             :          * we won't bother searching the next node since this should also
    1222             :          * be taken care of by our parent.
    1223             :          *------------------------------------------------------------*/
    1224           0 :         while (m_nCurIndexEntry < m_numEntriesInNode)
    1225             :         {
    1226           0 :             int nCmpStatus = IndexKeyCmp(pKeyValue, m_nCurIndexEntry);
    1227             : 
    1228           0 :             if (nCmpStatus > 0 && m_nCurIndexEntry + 1 < m_numEntriesInNode)
    1229             :             {
    1230             :                 /* Not there yet... (pKey > IndexEntry) */
    1231           0 :                 m_nCurIndexEntry++;
    1232             :             }
    1233             :             else
    1234             :             {
    1235             :                 /*-----------------------------------------------------
    1236             :                  * We either found an indexkey >= pKeyValue or reached
    1237             :                  * the last entry in this node... still have to decide
    1238             :                  * what we're going to do...
    1239             :                  *----------------------------------------------------*/
    1240           0 :                 if (nCmpStatus < 0 && m_nCurIndexEntry == 0)
    1241             :                 {
    1242             :                     /*-------------------------------------------------
    1243             :                      * First indexkey in block is > pKeyValue...
    1244             :                      * the key definitely does not exist in our children.
    1245             :                      * However, we still want to drill down the rest of the
    1246             :                      * tree because this function is also used when looking
    1247             :                      * for a node to insert a new value.
    1248             :                      *-------------------------------------------------*/
    1249             :                     // Nothing special to do... just continue processing.
    1250             :                 }
    1251             : 
    1252             :                 /*-----------------------------------------------------
    1253             :                  * If we found an node for which pKeyValue < indexkey
    1254             :                  * (or pKeyValue <= indexkey for non-unique indexes) then
    1255             :                  * we access the preceding child node.
    1256             :                  *
    1257             :                  * Note that for indexkey == pKeyValue in non-unique indexes
    1258             :                  * we also check in the preceding node because when keys
    1259             :                  * are not unique then there are chances that the requested
    1260             :                  * key could also be found at the end of the preceding node.
    1261             :                  * In this case, if we don't find the key in the preceding
    1262             :                  * node then we'll do a second search in the current node.
    1263             :                  *----------------------------------------------------*/
    1264           0 :                 int numChildrenToVisit = 1;
    1265           0 :                 if (m_nCurIndexEntry > 0 &&
    1266           0 :                     (nCmpStatus < 0 || (nCmpStatus == 0 && !m_bUnique)))
    1267             :                 {
    1268           0 :                     m_nCurIndexEntry--;
    1269           0 :                     if (nCmpStatus == 0)
    1270           0 :                         numChildrenToVisit = 2;
    1271             :                 }
    1272             : 
    1273             :                 /*-----------------------------------------------------
    1274             :                  * OK, now it is time to load/access the candidate child nodes.
    1275             :                  *----------------------------------------------------*/
    1276           0 :                 int nRetValue = 0;
    1277           0 :                 for (int iChild = 0;
    1278           0 :                      nRetValue == 0 && iChild < numChildrenToVisit; iChild++)
    1279             :                 {
    1280             :                     // If we're doing a second pass then jump to next entry
    1281           0 :                     if (iChild > 0)
    1282           0 :                         m_nCurIndexEntry++;
    1283             : 
    1284             :                     int nChildNodePtr =
    1285           0 :                         ReadIndexEntry(m_nCurIndexEntry, nullptr);
    1286           0 :                     if (nChildNodePtr <= 0)
    1287             :                     {
    1288             :                         /* Invalid child node??? */
    1289           0 :                         nRetValue = 0;
    1290           0 :                         continue;
    1291             :                     }
    1292           0 :                     else if (oSetVisitedNodePtr.find(nChildNodePtr) !=
    1293           0 :                              oSetVisitedNodePtr.end())
    1294             :                     {
    1295           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1296             :                                  "Invalid child node pointer structure");
    1297           0 :                         return -1;
    1298             :                     }
    1299           0 :                     else if ((nChildNodePtr % 512) != 0)
    1300             :                     {
    1301           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
    1302             :                                  "Invalid child node pointer");
    1303           0 :                         return -1;
    1304             :                     }
    1305           0 :                     else if (m_poCurChildNode == nullptr)
    1306             :                     {
    1307             :                         /* Child node has never been initialized...do it now!*/
    1308             : 
    1309           0 :                         m_poCurChildNode = new TABINDNode(m_eAccessMode);
    1310           0 :                         if (m_poCurChildNode->InitNode(
    1311             :                                 m_fp, nChildNodePtr, m_nKeyLength,
    1312           0 :                                 m_nSubTreeDepth - 1, m_bUnique,
    1313           0 :                                 m_poBlockManagerRef, this) != 0 ||
    1314           0 :                             m_poCurChildNode->SetFieldType(m_eFieldType) != 0)
    1315             :                         {
    1316             :                             // An error happened... and was already reported
    1317           0 :                             return -1;
    1318             :                         }
    1319             :                     }
    1320             : 
    1321           0 :                     if (m_poCurChildNode->GotoNodePtr(nChildNodePtr) != 0)
    1322             :                     {
    1323             :                         // An error happened and has already been reported
    1324           0 :                         return -1;
    1325             :                     }
    1326             : 
    1327           0 :                     oSetVisitedNodePtr.insert(nChildNodePtr);
    1328           0 :                     nRetValue = m_poCurChildNode->FindFirst(pKeyValue,
    1329             :                                                             oSetVisitedNodePtr);
    1330             :                 } /*for iChild*/
    1331             : 
    1332           0 :                 return nRetValue;
    1333             :             }  // else
    1334             :         }      // while numEntries
    1335             : 
    1336             :         // No node was found that contains the key value.
    1337             :         // We should never get here... only leaf nodes should return 0
    1338           0 :         CPLAssert(false);
    1339             :         return 0;
    1340             :     }
    1341             : 
    1342         361 :     return 0;  // Not found
    1343             : }
    1344             : 
    1345             : /**********************************************************************
    1346             :  *                   TABINDNode::FindNext()
    1347             :  *
    1348             :  * Continue the search previously started by FindFirst() in this node
    1349             :  * and its children for a key value.
    1350             :  *
    1351             :  * Return value:
    1352             :  *  - the key's corresponding record number in the .DAT file (greater than 0)
    1353             :  *  - 0 if the key was not found
    1354             :  *  - or -1 if an error happened
    1355             :  **********************************************************************/
    1356          36 : GInt32 TABINDNode::FindNext(GByte *pKeyValue)
    1357             : {
    1358          36 :     if (m_poDataBlock == nullptr)
    1359             :     {
    1360           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1361             :                  "TABINDNode::Search(): Node has not been initialized yet!");
    1362           0 :         return -1;
    1363             :     }
    1364             : 
    1365             :     /*-----------------------------------------------------------------
    1366             :      * m_nCurIndexEntry is the index of the last item that has been
    1367             :      * returned by FindFirst()/FindNext().
    1368             :      *----------------------------------------------------------------*/
    1369             : 
    1370          36 :     if (m_nSubTreeDepth == 1)
    1371             :     {
    1372             :         /*-------------------------------------------------------------
    1373             :          * Leaf node level... check if the next entry is an exact match
    1374             :          *------------------------------------------------------------*/
    1375          36 :         m_nCurIndexEntry++;
    1376          36 :         if (m_nCurIndexEntry >= m_numEntriesInNode && m_nNextNodePtr > 0)
    1377             :         {
    1378             :             // We're at the end of a node ... continue with next node
    1379           0 :             GotoNodePtr(m_nNextNodePtr);
    1380           0 :             m_nCurIndexEntry = 0;
    1381             :         }
    1382             : 
    1383          58 :         if (m_nCurIndexEntry < m_numEntriesInNode &&
    1384          22 :             IndexKeyCmp(pKeyValue, m_nCurIndexEntry) == 0)
    1385             :         {
    1386             :             /* Found it!  Return the record number */
    1387          10 :             return ReadIndexEntry(m_nCurIndexEntry, nullptr);
    1388             :         }
    1389             :         else
    1390             :         {
    1391             :             /* No more items with that key... return 0 */
    1392          26 :             return 0;
    1393             :         }
    1394             :     }
    1395             :     else
    1396             :     {
    1397             :         /*-------------------------------------------------------------
    1398             :          * Index Node: just pass the search to this child node.
    1399             :          *------------------------------------------------------------*/
    1400           0 :         while (m_nCurIndexEntry < m_numEntriesInNode)
    1401             :         {
    1402           0 :             if (m_poCurChildNode != nullptr)
    1403           0 :                 return m_poCurChildNode->FindNext(pKeyValue);
    1404             :         }
    1405             :     }
    1406             : 
    1407             :     // No more nodes were found that contain the key value.
    1408           0 :     return 0;
    1409             : }
    1410             : 
    1411             : /**********************************************************************
    1412             :  *                   TABINDNode::CommitToFile()
    1413             :  *
    1414             :  * For write access, write current block and its children to file.
    1415             :  *
    1416             :  * note: TABRawBinBlock::CommitToFile() does nothing unless the block has
    1417             :  *       been modified.  (it has an internal bModified flag)
    1418             :  *
    1419             :  * Returns 0 on success, -1 on error.
    1420             :  **********************************************************************/
    1421          42 : int TABINDNode::CommitToFile()
    1422             : {
    1423          42 :     if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
    1424          42 :         m_poDataBlock == nullptr)
    1425           0 :         return -1;
    1426             : 
    1427          42 :     if (m_poCurChildNode)
    1428             :     {
    1429           0 :         if (m_poCurChildNode->CommitToFile() != 0)
    1430           0 :             return -1;
    1431             : 
    1432           0 :         m_nSubTreeDepth = m_poCurChildNode->GetSubTreeDepth() + 1;
    1433             :     }
    1434             : 
    1435          42 :     return m_poDataBlock->CommitToFile();
    1436             : }
    1437             : 
    1438             : /**********************************************************************
    1439             :  *                   TABINDNode::AddEntry()
    1440             :  *
    1441             :  * Add an .DAT record entry for pKeyValue in this index
    1442             :  *
    1443             :  * nRecordNo is the .DAT record number, record numbers start at 1.
    1444             :  *
    1445             :  * In order to insert a new value, the root node first does a FindFirst()
    1446             :  * that will load the whole tree branch up to the insertion point.
    1447             :  * Then AddEntry() is recursively called up to the leaf node level for
    1448             :  * the insertion of the actual value.
    1449             :  * If the leaf node is full then it will be split and if necessary the
    1450             :  * split will propagate up in the tree through the pointer that each node
    1451             :  * has on its parent.
    1452             :  *
    1453             :  * If bAddInThisNodeOnly=TRUE, then the entry is added only locally and
    1454             :  * we do not try to update the child node.  This is used when the parent
    1455             :  * of a node that is being split has to be updated.
    1456             :  *
    1457             :  * bInsertAfterCurChild forces the insertion to happen immediately after
    1458             :  * the m_nCurIndexEntry.  This works only when bAddInThisNodeOnly=TRUE.
    1459             :  * The default is to search the node for a an insertion point.
    1460             :  *
    1461             :  * Returns 0 on success, -1 on error
    1462             :  **********************************************************************/
    1463         494 : int TABINDNode::AddEntry(GByte *pKeyValue, GInt32 nRecordNo,
    1464             :                          GBool bAddInThisNodeOnly /*=FALSE*/,
    1465             :                          GBool bInsertAfterCurChild /*=FALSE*/,
    1466             :                          GBool bMakeNewEntryCurChild /*=FALSE*/)
    1467             : {
    1468         494 :     if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
    1469         494 :         m_poDataBlock == nullptr)
    1470           0 :         return -1;
    1471             : 
    1472             :     /*-----------------------------------------------------------------
    1473             :      * If I'm the root node, then do a FindFirst() to init all the nodes
    1474             :      * and to make all of them point to the insertion point.
    1475             :      *----------------------------------------------------------------*/
    1476         494 :     if (m_poParentNodeRef == nullptr && !bAddInThisNodeOnly)
    1477             :     {
    1478         494 :         if (FindFirst(pKeyValue) < 0)
    1479           0 :             return -1;  // Error happened and has already been reported.
    1480             :     }
    1481             : 
    1482         494 :     if (m_poCurChildNode && !bAddInThisNodeOnly)
    1483             :     {
    1484           0 :         CPLAssert(m_nSubTreeDepth > 1);
    1485             :         /*-------------------------------------------------------------
    1486             :          * Propagate the call down to our children
    1487             :          * Note: this recursive call could result in new levels of nodes
    1488             :          * being added under our feet by SplitRootnode() so it is very
    1489             :          * important to return right after this call or we might not be
    1490             :          * able to recognize this node at the end of the call!
    1491             :          *------------------------------------------------------------*/
    1492           0 :         return m_poCurChildNode->AddEntry(pKeyValue, nRecordNo);
    1493             :     }
    1494             :     else
    1495             :     {
    1496             :         /*-------------------------------------------------------------
    1497             :          * OK, we're a leaf node... this is where the real work happens!!!
    1498             :          *------------------------------------------------------------*/
    1499         494 :         CPLAssert(m_nSubTreeDepth == 1 || bAddInThisNodeOnly);
    1500             : 
    1501             :         /*-------------------------------------------------------------
    1502             :          * First thing to do is make sure that there is room for a new
    1503             :          * entry in this node, and to split it if necessary.
    1504             :          *------------------------------------------------------------*/
    1505         494 :         if (GetNumEntries() == GetMaxNumEntries())
    1506             :         {
    1507           0 :             if (m_poParentNodeRef == nullptr)
    1508             :             {
    1509             :                 /*-----------------------------------------------------
    1510             :                  * Splitting the root node adds one level to the tree, so
    1511             :                  * after splitting we just redirect the call to our child.
    1512             :                  *----------------------------------------------------*/
    1513           0 :                 if (SplitRootNode() != 0)
    1514           0 :                     return -1;  // Error happened and has already been reported
    1515             : 
    1516           0 :                 CPLAssert(m_poCurChildNode);
    1517           0 :                 CPLAssert(m_nSubTreeDepth > 1);
    1518           0 :                 return m_poCurChildNode->AddEntry(
    1519             :                     pKeyValue, nRecordNo, bAddInThisNodeOnly,
    1520           0 :                     bInsertAfterCurChild, bMakeNewEntryCurChild);
    1521             :             }
    1522             :             else
    1523             :             {
    1524             :                 /*-----------------------------------------------------
    1525             :                  * Splitting a regular node will leave it 50% full.
    1526             :                  *----------------------------------------------------*/
    1527           0 :                 if (SplitNode() != 0)
    1528           0 :                     return -1;
    1529             :             }
    1530             :         }
    1531             : 
    1532             :         /*-------------------------------------------------------------
    1533             :          * Insert new key/value at the right position in node.
    1534             :          *------------------------------------------------------------*/
    1535         494 :         if (InsertEntry(pKeyValue, nRecordNo, bInsertAfterCurChild,
    1536         494 :                         bMakeNewEntryCurChild) != 0)
    1537           0 :             return -1;
    1538             :     }
    1539             : 
    1540         494 :     return 0;
    1541             : }
    1542             : 
    1543             : /**********************************************************************
    1544             :  *                   TABINDNode::InsertEntry()
    1545             :  *
    1546             :  * (private method)
    1547             :  *
    1548             :  * Insert a key/value pair in the current node buffer.
    1549             :  *
    1550             :  * Returns 0 on success, -1 on error
    1551             :  **********************************************************************/
    1552         494 : int TABINDNode::InsertEntry(GByte *pKeyValue, GInt32 nRecordNo,
    1553             :                             GBool bInsertAfterCurChild /*=FALSE*/,
    1554             :                             GBool bMakeNewEntryCurChild /*=FALSE*/)
    1555             : {
    1556         494 :     int iInsertAt = 0;
    1557             : 
    1558         494 :     if (GetNumEntries() >= GetMaxNumEntries())
    1559             :     {
    1560           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1561             :                  "Node is full!  Cannot insert key!");
    1562           0 :         return -1;
    1563             :     }
    1564             : 
    1565             :     /*-----------------------------------------------------------------
    1566             :      * Find the spot where the key belongs
    1567             :      *----------------------------------------------------------------*/
    1568         494 :     if (bInsertAfterCurChild)
    1569             :     {
    1570           0 :         iInsertAt = m_nCurIndexEntry + 1;
    1571             :     }
    1572             :     else
    1573             :     {
    1574        4024 :         while (iInsertAt < m_numEntriesInNode)
    1575             :         {
    1576        3666 :             int nCmpStatus = IndexKeyCmp(pKeyValue, iInsertAt);
    1577        3666 :             if (nCmpStatus <= 0)
    1578             :             {
    1579         136 :                 break;
    1580             :             }
    1581        3530 :             iInsertAt++;
    1582             :         }
    1583             :     }
    1584             : 
    1585         494 :     m_poDataBlock->GotoByteInBlock(12 + iInsertAt * (m_nKeyLength + 4));
    1586             : 
    1587             :     /*-----------------------------------------------------------------
    1588             :      * Shift all entries that follow in the array
    1589             :      *----------------------------------------------------------------*/
    1590         494 :     if (iInsertAt < m_numEntriesInNode)
    1591             :     {
    1592             :         // Since we use memmove() directly, we need to inform
    1593             :         // m_poDataBlock that the upper limit of the buffer will move
    1594         136 :         m_poDataBlock->GotoByteInBlock(12 + (m_numEntriesInNode + 1) *
    1595         136 :                                                 (m_nKeyLength + 4));
    1596         136 :         m_poDataBlock->GotoByteInBlock(12 + iInsertAt * (m_nKeyLength + 4));
    1597             : 
    1598         136 :         memmove(m_poDataBlock->GetCurDataPtr() + (m_nKeyLength + 4),
    1599         136 :                 m_poDataBlock->GetCurDataPtr(),
    1600         136 :                 static_cast<size_t>(m_numEntriesInNode - iInsertAt) *
    1601         136 :                     (m_nKeyLength + 4));
    1602             :     }
    1603             : 
    1604             :     /*-----------------------------------------------------------------
    1605             :      * Write new entry
    1606             :      *----------------------------------------------------------------*/
    1607         494 :     m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue);
    1608         494 :     m_poDataBlock->WriteInt32(nRecordNo);
    1609             : 
    1610         494 :     m_numEntriesInNode++;
    1611         494 :     m_poDataBlock->GotoByteInBlock(0);
    1612         494 :     m_poDataBlock->WriteInt32(m_numEntriesInNode);
    1613             : 
    1614         494 :     if (bMakeNewEntryCurChild)
    1615           0 :         m_nCurIndexEntry = iInsertAt;
    1616         494 :     else if (m_nCurIndexEntry >= iInsertAt)
    1617         494 :         m_nCurIndexEntry++;
    1618             : 
    1619             :     /*-----------------------------------------------------------------
    1620             :      * If we replaced the first entry in the node, then this node's key
    1621             :      * changes and we have to update the reference in the parent node.
    1622             :      *----------------------------------------------------------------*/
    1623         494 :     if (iInsertAt == 0 && m_poParentNodeRef)
    1624             :     {
    1625           0 :         if (m_poParentNodeRef->UpdateCurChildEntry(GetNodeKey(),
    1626           0 :                                                    GetNodeBlockPtr()) != 0)
    1627           0 :             return -1;
    1628             :     }
    1629             : 
    1630         494 :     return 0;
    1631             : }
    1632             : 
    1633             : /**********************************************************************
    1634             :  *                   TABINDNode::UpdateCurChildEntry()
    1635             :  *
    1636             :  * Update the key for the current child node.  This method is called by
    1637             :  * the child when its first entry (defining its node key) is changed.
    1638             :  *
    1639             :  * Returns 0 on success, -1 on error
    1640             :  **********************************************************************/
    1641           0 : int TABINDNode::UpdateCurChildEntry(GByte *pKeyValue, GInt32 nRecordNo)
    1642             : {
    1643             : 
    1644             :     /*-----------------------------------------------------------------
    1645             :      * Update current child entry with the info for the first node.
    1646             :      *
    1647             :      * For some reason, the key for first entry of the first node of each
    1648             :      * level has to be set to 0 except for the leaf level.
    1649             :      *----------------------------------------------------------------*/
    1650           0 :     m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry * (m_nKeyLength + 4));
    1651             : 
    1652             :     int ret;
    1653           0 :     if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0)
    1654             :     {
    1655           0 :         ret = m_poDataBlock->WriteZeros(m_nKeyLength);
    1656             :     }
    1657             :     else
    1658             :     {
    1659           0 :         ret = m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue);
    1660             :     }
    1661           0 :     if (ret == 0)
    1662           0 :         ret = m_poDataBlock->WriteInt32(nRecordNo);
    1663             : 
    1664           0 :     return ret;
    1665             : }
    1666             : 
    1667             : /**********************************************************************
    1668             :  *                   TABINDNode::UpdateSplitChild()
    1669             :  *
    1670             :  * Update the key and/or record ptr information corresponding to the
    1671             :  * current child node.
    1672             :  *
    1673             :  * Returns 0 on success, -1 on error
    1674             :  **********************************************************************/
    1675           0 : int TABINDNode::UpdateSplitChild(GByte *pKeyValue1, GInt32 nRecordNo1,
    1676             :                                  GByte *pKeyValue2, GInt32 nRecordNo2,
    1677             :                                  int nNewCurChildNo /* 1 or 2 */)
    1678             : {
    1679             : 
    1680             :     /*-----------------------------------------------------------------
    1681             :      * Update current child entry with the info for the first node.
    1682             :      *
    1683             :      * For some reason, the key for first entry of the first node of each
    1684             :      * level has to be set to 0 except for the leaf level.
    1685             :      *----------------------------------------------------------------*/
    1686           0 :     m_poDataBlock->GotoByteInBlock(12 + m_nCurIndexEntry * (m_nKeyLength + 4));
    1687             : 
    1688           0 :     if (m_nCurIndexEntry == 0 && m_nSubTreeDepth > 1 && m_nPrevNodePtr == 0)
    1689             :     {
    1690           0 :         m_poDataBlock->WriteZeros(m_nKeyLength);
    1691             :     }
    1692             :     else
    1693             :     {
    1694           0 :         m_poDataBlock->WriteBytes(m_nKeyLength, pKeyValue1);
    1695             :     }
    1696           0 :     m_poDataBlock->WriteInt32(nRecordNo1);
    1697             : 
    1698             :     /*-----------------------------------------------------------------
    1699             :      * Add an entry for the second node after the current one and ask
    1700             :      * AddEntry() to update m_nCurIndexEntry if the new node should
    1701             :      * become the new current child.
    1702             :      *----------------------------------------------------------------*/
    1703           0 :     if (AddEntry(pKeyValue2, nRecordNo2, TRUE, /* bInThisNodeOnly */
    1704             :                  TRUE,                         /* bInsertAfterCurChild */
    1705           0 :                  (nNewCurChildNo == 2)) != 0)
    1706             :     {
    1707           0 :         return -1;
    1708             :     }
    1709             : 
    1710           0 :     return 0;
    1711             : }
    1712             : 
    1713             : /**********************************************************************
    1714             :  *                   TABINDNode::SplitNode()
    1715             :  *
    1716             :  * (private method)
    1717             :  *
    1718             :  * Split a node, update the references in the parent node, etc.
    1719             :  * Note that Root Nodes cannot be split using this method... SplitRootNode()
    1720             :  * should be used instead.
    1721             :  *
    1722             :  * The node is split in a way that the current child stays inside this
    1723             :  * node object, and a new node is created for the other half of the
    1724             :  * entries.  This way, the object references in this node's parent and in its
    1725             :  * current child all remain valid.  The new node is not kept in memory,
    1726             :  * it is written to disk right away.
    1727             :  *
    1728             :  * Returns 0 on success, -1 on error
    1729             :  **********************************************************************/
    1730           0 : int TABINDNode::SplitNode()
    1731             : {
    1732           0 :     CPLAssert(m_numEntriesInNode >= 2);
    1733           0 :     CPLAssert(m_poParentNodeRef);  // This func. does not work for root nodes
    1734             : 
    1735             :     /*-----------------------------------------------------------------
    1736             :      * Prepare new node
    1737             :      *----------------------------------------------------------------*/
    1738           0 :     int numInNode1 = (m_numEntriesInNode + 1) / 2;
    1739           0 :     int numInNode2 = m_numEntriesInNode - numInNode1;
    1740             : 
    1741           0 :     TABINDNode *poNewNode = new TABINDNode(m_eAccessMode);
    1742             : 
    1743           0 :     if (m_nCurIndexEntry < numInNode1)
    1744             :     {
    1745             :         /*-------------------------------------------------------------
    1746             :          * We will move the second half of the array to a new node.
    1747             :          *------------------------------------------------------------*/
    1748           0 :         if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth,
    1749             :                                 m_bUnique, m_poBlockManagerRef,
    1750             :                                 m_poParentNodeRef, GetNodeBlockPtr(),
    1751           0 :                                 m_nNextNodePtr) != 0 ||
    1752           0 :             poNewNode->SetFieldType(m_eFieldType) != 0)
    1753             :         {
    1754           0 :             delete poNewNode;
    1755           0 :             return -1;
    1756             :         }
    1757             : 
    1758             :         // We have to update m_nPrevNodePtr in the node that used to follow
    1759             :         // the current node and will now follow the new node.
    1760           0 :         if (m_nNextNodePtr)
    1761             :         {
    1762           0 :             TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode);
    1763           0 :             if (poTmpNode->InitNode(
    1764             :                     m_fp, m_nNextNodePtr, m_nKeyLength, m_nSubTreeDepth,
    1765           0 :                     m_bUnique, m_poBlockManagerRef, m_poParentNodeRef) != 0 ||
    1766           0 :                 poTmpNode->SetPrevNodePtr(poNewNode->GetNodeBlockPtr()) != 0 ||
    1767           0 :                 poTmpNode->CommitToFile() != 0)
    1768             :             {
    1769           0 :                 delete poTmpNode;
    1770           0 :                 delete poNewNode;
    1771           0 :                 return -1;
    1772             :             }
    1773           0 :             delete poTmpNode;
    1774             :         }
    1775             : 
    1776           0 :         m_nNextNodePtr = poNewNode->GetNodeBlockPtr();
    1777             : 
    1778             :         // Move half the entries to the new block
    1779           0 :         m_poDataBlock->GotoByteInBlock(12 + numInNode1 * (m_nKeyLength + 4));
    1780             : 
    1781           0 :         if (poNewNode->SetNodeBufferDirectly(
    1782           0 :                 numInNode2, m_poDataBlock->GetCurDataPtr()) != 0)
    1783             :         {
    1784           0 :             delete poNewNode;
    1785           0 :             return -1;
    1786             :         }
    1787             : 
    1788             : #ifdef DEBUG
    1789             :         // Just in case, reset space previously used by moved entries
    1790           0 :         memset(m_poDataBlock->GetCurDataPtr(), 0,
    1791           0 :                numInNode2 * (m_nKeyLength + 4));
    1792             : #endif
    1793             :         // And update current node members
    1794           0 :         m_numEntriesInNode = numInNode1;
    1795             : 
    1796             :         // Update parent node with new children info
    1797           0 :         if (m_poParentNodeRef)
    1798             :         {
    1799           0 :             if (m_poParentNodeRef->UpdateSplitChild(
    1800             :                     GetNodeKey(), GetNodeBlockPtr(), poNewNode->GetNodeKey(),
    1801           0 :                     poNewNode->GetNodeBlockPtr(), 1) != 0)
    1802             :             {
    1803           0 :                 delete poNewNode;
    1804           0 :                 return -1;
    1805             :             }
    1806             :         }
    1807             :     }
    1808             :     else
    1809             :     {
    1810             :         /*-------------------------------------------------------------
    1811             :          * We will move the first half of the array to a new node.
    1812             :          *------------------------------------------------------------*/
    1813           0 :         if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth,
    1814             :                                 m_bUnique, m_poBlockManagerRef,
    1815             :                                 m_poParentNodeRef, m_nPrevNodePtr,
    1816           0 :                                 GetNodeBlockPtr()) != 0 ||
    1817           0 :             poNewNode->SetFieldType(m_eFieldType) != 0)
    1818             :         {
    1819           0 :             delete poNewNode;
    1820           0 :             return -1;
    1821             :         }
    1822             : 
    1823             :         // We have to update m_nNextNodePtr in the node that used to precede
    1824             :         // the current node and will now precede the new node.
    1825           0 :         if (m_nPrevNodePtr)
    1826             :         {
    1827           0 :             TABINDNode *poTmpNode = new TABINDNode(m_eAccessMode);
    1828           0 :             if (poTmpNode->InitNode(
    1829             :                     m_fp, m_nPrevNodePtr, m_nKeyLength, m_nSubTreeDepth,
    1830           0 :                     m_bUnique, m_poBlockManagerRef, m_poParentNodeRef) != 0 ||
    1831           0 :                 poTmpNode->SetNextNodePtr(poNewNode->GetNodeBlockPtr()) != 0 ||
    1832           0 :                 poTmpNode->CommitToFile() != 0)
    1833             :             {
    1834           0 :                 delete poTmpNode;
    1835           0 :                 delete poNewNode;
    1836           0 :                 return -1;
    1837             :             }
    1838           0 :             delete poTmpNode;
    1839             :         }
    1840             : 
    1841           0 :         m_nPrevNodePtr = poNewNode->GetNodeBlockPtr();
    1842             : 
    1843             :         // Move half the entries to the new block
    1844           0 :         m_poDataBlock->GotoByteInBlock(12 + 0);
    1845             : 
    1846           0 :         if (poNewNode->SetNodeBufferDirectly(
    1847           0 :                 numInNode1, m_poDataBlock->GetCurDataPtr()) != 0)
    1848             :         {
    1849           0 :             delete poNewNode;
    1850           0 :             return -1;
    1851             :         }
    1852             : 
    1853             :         // Shift the second half of the entries to beginning of buffer
    1854           0 :         memmove(m_poDataBlock->GetCurDataPtr(),
    1855           0 :                 m_poDataBlock->GetCurDataPtr() +
    1856           0 :                     numInNode1 * (m_nKeyLength + 4),
    1857           0 :                 static_cast<size_t>(numInNode2) * (m_nKeyLength + 4));
    1858             : 
    1859             : #ifdef DEBUG
    1860             :         // Just in case, reset space previously used by moved entries
    1861           0 :         memset(m_poDataBlock->GetCurDataPtr() +
    1862           0 :                    static_cast<size_t>(numInNode2) * (m_nKeyLength + 4),
    1863           0 :                0, numInNode1 * (m_nKeyLength + 4));
    1864             : #endif
    1865             : 
    1866             :         // And update current node members
    1867           0 :         m_numEntriesInNode = numInNode2;
    1868           0 :         m_nCurIndexEntry -= numInNode1;
    1869             : 
    1870             :         // Update parent node with new children info
    1871           0 :         if (m_poParentNodeRef)
    1872             :         {
    1873           0 :             if (m_poParentNodeRef->UpdateSplitChild(
    1874             :                     poNewNode->GetNodeKey(), poNewNode->GetNodeBlockPtr(),
    1875           0 :                     GetNodeKey(), GetNodeBlockPtr(), 2) != 0)
    1876             :             {
    1877           0 :                 delete poNewNode;
    1878           0 :                 return -1;
    1879             :             }
    1880             :         }
    1881             :     }
    1882             : 
    1883             :     /*-----------------------------------------------------------------
    1884             :      * Update current node header
    1885             :      *----------------------------------------------------------------*/
    1886           0 :     m_poDataBlock->GotoByteInBlock(0);
    1887           0 :     m_poDataBlock->WriteInt32(m_numEntriesInNode);
    1888           0 :     m_poDataBlock->WriteInt32(m_nPrevNodePtr);
    1889           0 :     m_poDataBlock->WriteInt32(m_nNextNodePtr);
    1890             : 
    1891             :     /*-----------------------------------------------------------------
    1892             :      * Flush and destroy temporary node
    1893             :      *----------------------------------------------------------------*/
    1894           0 :     if (poNewNode->CommitToFile() != 0)
    1895             :     {
    1896           0 :         delete poNewNode;
    1897           0 :         return -1;
    1898             :     }
    1899             : 
    1900           0 :     delete poNewNode;
    1901             : 
    1902           0 :     return 0;
    1903             : }
    1904             : 
    1905             : /**********************************************************************
    1906             :  *                   TABINDNode::SplitRootNode()
    1907             :  *
    1908             :  * (private method)
    1909             :  *
    1910             :  * Split a Root Node.
    1911             :  * First, a level of nodes must be added to the tree, then the contents
    1912             :  * of what used to be the root node is moved 1 level down and then that
    1913             :  * node is split like a regular node.
    1914             :  *
    1915             :  * Returns 0 on success, -1 on error
    1916             :  **********************************************************************/
    1917           0 : int TABINDNode::SplitRootNode()
    1918             : {
    1919             :     /*-----------------------------------------------------------------
    1920             :      * Since a root note cannot be split, we add a level of nodes
    1921             :      * under it and we'll do the split at that level.
    1922             :      *----------------------------------------------------------------*/
    1923           0 :     TABINDNode *poNewNode = new TABINDNode(m_eAccessMode);
    1924             : 
    1925           0 :     if (poNewNode->InitNode(m_fp, 0, m_nKeyLength, m_nSubTreeDepth, m_bUnique,
    1926           0 :                             m_poBlockManagerRef, this, 0, 0) != 0 ||
    1927           0 :         poNewNode->SetFieldType(m_eFieldType) != 0)
    1928             :     {
    1929           0 :         delete poNewNode;
    1930           0 :         return -1;
    1931             :     }
    1932             : 
    1933             :     // Move all entries to the new child
    1934           0 :     m_poDataBlock->GotoByteInBlock(12 + 0);
    1935           0 :     if (poNewNode->SetNodeBufferDirectly(
    1936           0 :             m_numEntriesInNode, m_poDataBlock->GetCurDataPtr(),
    1937           0 :             m_nCurIndexEntry, m_poCurChildNode) != 0)
    1938             :     {
    1939           0 :         delete poNewNode;
    1940           0 :         return -1;
    1941             :     }
    1942             : 
    1943             : #ifdef DEBUG
    1944             :     // Just in case, reset space previously used by moved entries
    1945           0 :     memset(m_poDataBlock->GetCurDataPtr(), 0,
    1946           0 :            m_numEntriesInNode * (m_nKeyLength + 4));
    1947             : #endif
    1948             : 
    1949             :     /*-----------------------------------------------------------------
    1950             :      * Rewrite current node. (the new root node)
    1951             :      *----------------------------------------------------------------*/
    1952           0 :     m_numEntriesInNode = 0;
    1953           0 :     m_nSubTreeDepth++;
    1954             : 
    1955           0 :     m_poDataBlock->GotoByteInBlock(0);
    1956           0 :     m_poDataBlock->WriteInt32(m_numEntriesInNode);
    1957             : 
    1958           0 :     InsertEntry(poNewNode->GetNodeKey(), poNewNode->GetNodeBlockPtr());
    1959             : 
    1960             :     /*-----------------------------------------------------------------
    1961             :      * Keep a reference to the new child
    1962             :      *----------------------------------------------------------------*/
    1963           0 :     m_poCurChildNode = poNewNode;
    1964           0 :     m_nCurIndexEntry = 0;
    1965             : 
    1966             :     /*-----------------------------------------------------------------
    1967             :      * And finally force the child to split itself
    1968             :      *----------------------------------------------------------------*/
    1969           0 :     return m_poCurChildNode->SplitNode();
    1970             : }
    1971             : 
    1972             : /**********************************************************************
    1973             :  *                   TABINDNode::SetNodeBufferDirectly()
    1974             :  *
    1975             :  * (private method)
    1976             :  *
    1977             :  * Set the key/value part of the nodes buffer and the pointers to the
    1978             :  * current child directly.  This is used when copying info to a new node
    1979             :  * in SplitNode() and SplitRootNode()
    1980             :  *
    1981             :  * Returns 0 on success, -1 on error
    1982             :  **********************************************************************/
    1983           0 : int TABINDNode::SetNodeBufferDirectly(int numEntries, GByte *pBuf,
    1984             :                                       int nCurIndexEntry /*=0*/,
    1985             :                                       TABINDNode *poCurChild /*=NULL*/)
    1986             : {
    1987           0 :     m_poDataBlock->GotoByteInBlock(0);
    1988           0 :     m_poDataBlock->WriteInt32(numEntries);
    1989             : 
    1990           0 :     m_numEntriesInNode = numEntries;
    1991             : 
    1992           0 :     m_poDataBlock->GotoByteInBlock(12);
    1993           0 :     if (m_poDataBlock->WriteBytes(numEntries * (m_nKeyLength + 4), pBuf) != 0)
    1994             :     {
    1995           0 :         return -1;  // An error msg should have been reported already
    1996             :     }
    1997             : 
    1998           0 :     m_nCurIndexEntry = nCurIndexEntry;
    1999           0 :     m_poCurChildNode = poCurChild;
    2000           0 :     if (m_poCurChildNode)
    2001           0 :         m_poCurChildNode->m_poParentNodeRef = this;
    2002             : 
    2003           0 :     return 0;
    2004             : }
    2005             : 
    2006             : /**********************************************************************
    2007             :  *                   TABINDNode::GetNodeKey()
    2008             :  *
    2009             :  * Returns a reference to the key for the first entry in the node, which
    2010             :  * is also the key for this node at the level above it in the tree.
    2011             :  *
    2012             :  * Returns NULL if node is empty.
    2013             :  **********************************************************************/
    2014           0 : GByte *TABINDNode::GetNodeKey()
    2015             : {
    2016           0 :     if (m_poDataBlock == nullptr || m_numEntriesInNode == 0)
    2017           0 :         return nullptr;
    2018             : 
    2019           0 :     m_poDataBlock->GotoByteInBlock(12);
    2020             : 
    2021           0 :     return m_poDataBlock->GetCurDataPtr();
    2022             : }
    2023             : 
    2024             : /**********************************************************************
    2025             :  *                   TABINDNode::SetPrevNodePtr()
    2026             :  *
    2027             :  * Update the m_nPrevNodePtr member.
    2028             :  *
    2029             :  * Returns 0 on success, -1 on error.
    2030             :  **********************************************************************/
    2031           0 : int TABINDNode::SetPrevNodePtr(GInt32 nPrevNodePtr)
    2032             : {
    2033           0 :     if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
    2034           0 :         m_poDataBlock == nullptr)
    2035           0 :         return -1;
    2036             : 
    2037           0 :     if (m_nPrevNodePtr == nPrevNodePtr)
    2038           0 :         return 0;  // Nothing to do.
    2039             : 
    2040           0 :     m_poDataBlock->GotoByteInBlock(4);
    2041           0 :     return m_poDataBlock->WriteInt32(nPrevNodePtr);
    2042             : }
    2043             : 
    2044             : /**********************************************************************
    2045             :  *                   TABINDNode::SetNextNodePtr()
    2046             :  *
    2047             :  * Update the m_nNextNodePtr member.
    2048             :  *
    2049             :  * Returns 0 on success, -1 on error.
    2050             :  **********************************************************************/
    2051           0 : int TABINDNode::SetNextNodePtr(GInt32 nNextNodePtr)
    2052             : {
    2053           0 :     if ((m_eAccessMode != TABWrite && m_eAccessMode != TABReadWrite) ||
    2054           0 :         m_poDataBlock == nullptr)
    2055           0 :         return -1;
    2056             : 
    2057           0 :     if (m_nNextNodePtr == nNextNodePtr)
    2058           0 :         return 0;  // Nothing to do.
    2059             : 
    2060           0 :     m_poDataBlock->GotoByteInBlock(8);
    2061           0 :     return m_poDataBlock->WriteInt32(nNextNodePtr);
    2062             : }
    2063             : 
    2064             : /**********************************************************************
    2065             :  *                   TABINDNode::Dump()
    2066             :  *
    2067             :  * Dump block contents... available only in DEBUG mode.
    2068             :  **********************************************************************/
    2069             : #ifdef DEBUG
    2070             : 
    2071           0 : void TABINDNode::Dump(FILE *fpOut /*=NULL*/)
    2072             : {
    2073           0 :     if (fpOut == nullptr)
    2074           0 :         fpOut = stdout;
    2075             : 
    2076           0 :     fprintf(fpOut, "----- TABINDNode::Dump() -----\n");
    2077             : 
    2078           0 :     if (m_fp == nullptr)
    2079             :     {
    2080           0 :         fprintf(fpOut, "Node is not initialized.\n");
    2081             :     }
    2082             :     else
    2083             :     {
    2084           0 :         fprintf(fpOut, "   m_numEntriesInNode   = %d\n", m_numEntriesInNode);
    2085           0 :         fprintf(fpOut, "   m_nCurDataBlockPtr   = %d\n", m_nCurDataBlockPtr);
    2086           0 :         fprintf(fpOut, "   m_nPrevNodePtr       = %d\n", m_nPrevNodePtr);
    2087           0 :         fprintf(fpOut, "   m_nNextNodePtr       = %d\n", m_nNextNodePtr);
    2088           0 :         fprintf(fpOut, "   m_nSubTreeDepth      = %d\n", m_nSubTreeDepth);
    2089           0 :         fprintf(fpOut, "   m_nKeyLength         = %d\n", m_nKeyLength);
    2090           0 :         fprintf(fpOut, "   m_eFieldtype         = %s\n",
    2091           0 :                 TABFIELDTYPE_2_STRING(m_eFieldType));
    2092           0 :         if (m_nSubTreeDepth > 0)
    2093             :         {
    2094             :             GByte aKeyValBuf[255];
    2095             :             GInt32 nRecordPtr, nValue;
    2096           0 :             TABINDNode oChildNode;
    2097             : 
    2098           0 :             if (m_nKeyLength > 254)
    2099             :             {
    2100           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2101             :                          "Dump() cannot handle keys longer than 254 chars.");
    2102           0 :                 return;
    2103             :             }
    2104             : 
    2105           0 :             fprintf(fpOut, "\n");
    2106           0 :             for (int i = 0; i < m_numEntriesInNode; i++)
    2107             :             {
    2108           0 :                 if (m_nSubTreeDepth > 1)
    2109             :                 {
    2110           0 :                     fprintf(fpOut, "   >>>> Child %d of %d <<<<<\n", i,
    2111             :                             m_numEntriesInNode);
    2112             :                 }
    2113             :                 else
    2114             :                 {
    2115           0 :                     fprintf(fpOut, "   >>>> Record (leaf) %d of %d <<<<<\n", i,
    2116             :                             m_numEntriesInNode);
    2117             :                 }
    2118             : 
    2119           0 :                 if (m_eFieldType == TABFChar)
    2120             :                 {
    2121           0 :                     nRecordPtr = ReadIndexEntry(i, aKeyValBuf);
    2122           0 :                     fprintf(fpOut, "   nRecordPtr = %d\n", nRecordPtr);
    2123           0 :                     fprintf(fpOut, "   Char Val= \"%s\"\n",
    2124             :                             reinterpret_cast<char *>(aKeyValBuf));
    2125             :                 }
    2126           0 :                 else if (m_nKeyLength != 4)
    2127             :                 {
    2128           0 :                     nRecordPtr = ReadIndexEntry(i, aKeyValBuf);
    2129           0 :                     GInt32 nInt32 = 0;
    2130           0 :                     memcpy(&nInt32, aKeyValBuf, 4);
    2131           0 :                     GInt16 nInt16 = 0;
    2132           0 :                     memcpy(&nInt16, aKeyValBuf + 2, 2);
    2133           0 :                     GUInt32 nUInt32 = 0;
    2134           0 :                     memcpy(&nUInt32, aKeyValBuf, 4);
    2135           0 :                     fprintf(fpOut, "   nRecordPtr = %d\n", nRecordPtr);
    2136           0 :                     fprintf(fpOut, "   Int Value = %d\n", nInt32);
    2137           0 :                     fprintf(fpOut, "   Int16 Val= %d\n", nInt16);
    2138           0 :                     fprintf(fpOut, "   Hex Val= 0x%8.8x\n", nUInt32);
    2139             :                 }
    2140             :                 else
    2141             :                 {
    2142             :                     nRecordPtr =
    2143           0 :                         ReadIndexEntry(i, reinterpret_cast<GByte *>(&nValue));
    2144           0 :                     fprintf(fpOut, "   nRecordPtr = %d\n", nRecordPtr);
    2145           0 :                     fprintf(fpOut, "   Int Value = %d\n", nValue);
    2146           0 :                     fprintf(fpOut, "   Hex Value = 0x%8.8x\n", nValue);
    2147             :                 }
    2148             : 
    2149           0 :                 if (m_nSubTreeDepth > 1)
    2150             :                 {
    2151           0 :                     CPL_IGNORE_RET_VAL(
    2152           0 :                         oChildNode.InitNode(m_fp, nRecordPtr, m_nKeyLength,
    2153           0 :                                             m_nSubTreeDepth - 1, FALSE));
    2154           0 :                     oChildNode.SetFieldType(m_eFieldType);
    2155           0 :                     oChildNode.Dump(fpOut);
    2156             :                 }
    2157             :             }
    2158             :         }
    2159             :     }
    2160             : 
    2161           0 :     fflush(fpOut);
    2162             : }
    2163             : 
    2164             : #endif  // DEBUG

Generated by: LCOV version 1.14