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

Generated by: LCOV version 1.14