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

Generated by: LCOV version 1.14