LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_rawbinblock.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 276 415 66.5 %
Date: 2024-11-21 22:18:42 Functions: 40 45 88.9 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_rawbinblock.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABRawBinBlock class used to handle
       7             :  *           reading/writing blocks in the .MAP files
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999, 2000, Daniel Morissette
      12             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      13             :  *
      14             :  * SPDX-License-Identifier: MIT
      15             :  **********************************************************************/
      16             : 
      17             : #include "cpl_port.h"
      18             : #include "mitab.h"
      19             : 
      20             : #include <cctype>
      21             : #include <cstddef>
      22             : #include <cstdio>
      23             : #include <cstring>
      24             : #if HAVE_FCNTL_H
      25             : #include <fcntl.h>
      26             : #endif
      27             : #include <algorithm>
      28             : 
      29             : #include "cpl_conv.h"
      30             : #include "cpl_error.h"
      31             : #include "cpl_vsi.h"
      32             : #include "mitab_priv.h"
      33             : 
      34             : /*=====================================================================
      35             :  *                      class TABRawBinBlock
      36             :  *====================================================================*/
      37             : 
      38             : /**********************************************************************
      39             :  *                   TABRawBinBlock::TABRawBinBlock()
      40             :  *
      41             :  * Constructor.
      42             :  **********************************************************************/
      43       83986 : TABRawBinBlock::TABRawBinBlock(TABAccess eAccessMode /*= TABRead*/,
      44       83986 :                                GBool bHardBlockSize /*= TRUE*/)
      45             :     : m_fp(nullptr), m_eAccess(eAccessMode), m_nBlockType(0),
      46             :       m_pabyBuf(nullptr), m_nBlockSize(0), m_nSizeUsed(0),
      47             :       m_bHardBlockSize(bHardBlockSize), m_nFileOffset(0), m_nCurPos(0),
      48       83986 :       m_nFirstBlockPtr(0), m_nFileSize(-1), m_bModified(FALSE)
      49             : {
      50       83986 : }
      51             : 
      52             : /**********************************************************************
      53             :  *                   TABRawBinBlock::~TABRawBinBlock()
      54             :  *
      55             :  * Destructor.
      56             :  **********************************************************************/
      57      172565 : TABRawBinBlock::~TABRawBinBlock()
      58             : {
      59       83986 :     if (m_pabyBuf)
      60       83962 :         CPLFree(m_pabyBuf);
      61       88579 : }
      62             : 
      63             : /**********************************************************************
      64             :  *                   TABRawBinBlock::ReadFromFile()
      65             :  *
      66             :  * Load data from the specified file location and initialize the block.
      67             :  *
      68             :  * Returns 0 if successful or -1 if an error happened, in which case
      69             :  * CPLError() will have been called.
      70             :  **********************************************************************/
      71      283351 : int TABRawBinBlock::ReadFromFile(VSILFILE *fpSrc, int nOffset, int nSize)
      72             : {
      73      283351 :     if (fpSrc == nullptr || nSize == 0)
      74             :     {
      75           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
      76             :                  "TABRawBinBlock::ReadFromFile(): Assertion Failed!");
      77           0 :         return -1;
      78             :     }
      79             : 
      80      283351 :     m_fp = fpSrc;
      81             : 
      82      283351 :     VSIFSeekL(fpSrc, 0, SEEK_END);
      83      283351 :     m_nFileSize = static_cast<int>(VSIFTellL(m_fp));
      84             : 
      85      283351 :     m_nFileOffset = nOffset;
      86      283351 :     m_nCurPos = 0;
      87      283351 :     m_bModified = FALSE;
      88             : 
      89             :     /*----------------------------------------------------------------
      90             :      * Alloc a buffer to contain the data
      91             :      *---------------------------------------------------------------*/
      92      283351 :     GByte *pabyBuf = static_cast<GByte *>(CPLMalloc(nSize * sizeof(GByte)));
      93             : 
      94             :     /*----------------------------------------------------------------
      95             :      * Read from the file
      96             :      *---------------------------------------------------------------*/
      97      283351 :     if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
      98      283351 :         (m_nSizeUsed = static_cast<int>(
      99      566702 :              VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc))) == 0 ||
     100      283351 :         (m_bHardBlockSize && m_nSizeUsed != nSize))
     101             :     {
     102           0 :         CPLError(CE_Failure, CPLE_FileIO,
     103             :                  "ReadFromFile() failed reading %d bytes at offset %d.", nSize,
     104             :                  nOffset);
     105           0 :         CPLFree(pabyBuf);
     106           0 :         return -1;
     107             :     }
     108             : 
     109             :     /*----------------------------------------------------------------
     110             :      * Init block with the data we just read
     111             :      *---------------------------------------------------------------*/
     112      283351 :     return InitBlockFromData(pabyBuf, nSize, m_nSizeUsed, FALSE, fpSrc,
     113      283351 :                              nOffset);
     114             : }
     115             : 
     116             : /**********************************************************************
     117             :  *                   TABRawBinBlock::CommitToFile()
     118             :  *
     119             :  * Commit the current state of the binary block to the file to which
     120             :  * it has been previously attached.
     121             :  *
     122             :  * Derived classes may want to (optionally) reimplement this method if
     123             :  * they need to do special processing before committing the block to disk.
     124             :  *
     125             :  * For files created with bHardBlockSize=TRUE, a complete block of
     126             :  * the specified size is always written, otherwise only the number of
     127             :  * used bytes in the block will be written to disk.
     128             :  *
     129             :  * Returns 0 if successful or -1 if an error happened, in which case
     130             :  * CPLError() will have been called.
     131             :  **********************************************************************/
     132       53564 : int TABRawBinBlock::CommitToFile()
     133             : {
     134       53564 :     int nStatus = 0;
     135             : 
     136       53564 :     if (m_fp == nullptr || m_nBlockSize <= 0 || m_pabyBuf == nullptr ||
     137       53564 :         m_nFileOffset < 0)
     138             :     {
     139           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     140             :                  "TABRawBinBlock::CommitToFile(): Block has not been "
     141             :                  "initialized yet!");
     142           0 :         return -1;
     143             :     }
     144             : 
     145             :     /*----------------------------------------------------------------
     146             :      * If block has not been modified, then just return... nothing to do.
     147             :      *---------------------------------------------------------------*/
     148       53564 :     if (!m_bModified)
     149        7183 :         return 0;
     150             : 
     151             :     /*----------------------------------------------------------------
     152             :      * Move the output file pointer to the right position...
     153             :      *---------------------------------------------------------------*/
     154       46381 :     if (VSIFSeekL(m_fp, m_nFileOffset, SEEK_SET) != 0)
     155             :     {
     156             :         /*------------------------------------------------------------
     157             :          * Moving pointer failed... we may need to pad with zeros if
     158             :          * block destination is beyond current end of file.
     159             :          *-----------------------------------------------------------*/
     160           0 :         int nCurPos = static_cast<int>(VSIFTellL(m_fp));
     161             : 
     162           0 :         if (nCurPos < m_nFileOffset && VSIFSeekL(m_fp, 0L, SEEK_END) == 0 &&
     163           0 :             (nCurPos = static_cast<int>(VSIFTellL(m_fp))) < m_nFileOffset)
     164             :         {
     165           0 :             const GByte cZero = 0;
     166             : 
     167           0 :             while (nCurPos < m_nFileOffset && nStatus == 0)
     168             :             {
     169           0 :                 if (VSIFWriteL(&cZero, 1, 1, m_fp) != 1)
     170             :                 {
     171           0 :                     CPLError(CE_Failure, CPLE_FileIO,
     172             :                              "Failed writing 1 byte at offset %d.", nCurPos);
     173           0 :                     nStatus = -1;
     174           0 :                     break;
     175             :                 }
     176           0 :                 nCurPos++;
     177             :             }
     178             :         }
     179             : 
     180           0 :         if (nCurPos != m_nFileOffset)
     181           0 :             nStatus = -1;  // Error message will follow below
     182             :     }
     183             : 
     184             :     /*----------------------------------------------------------------
     185             :      * At this point we are ready to write to the file.
     186             :      *
     187             :      * If m_bHardBlockSize==FALSE, then we do not write a complete block;
     188             :      * we write only the part of the block that was used.
     189             :      *---------------------------------------------------------------*/
     190       46381 :     int numBytesToWrite = m_bHardBlockSize ? m_nBlockSize : m_nSizeUsed;
     191             : 
     192             :     /*CPLDebug("MITAB", "Committing to offset %d", m_nFileOffset);*/
     193             : 
     194       92762 :     if (nStatus != 0 ||
     195       46381 :         VSIFWriteL(m_pabyBuf, sizeof(GByte), numBytesToWrite, m_fp) !=
     196       46381 :             static_cast<size_t>(numBytesToWrite))
     197             :     {
     198          10 :         CPLError(CE_Failure, CPLE_FileIO,
     199             :                  "Failed writing %d bytes at offset %d.", numBytesToWrite,
     200             :                  m_nFileOffset);
     201          10 :         return -1;
     202             :     }
     203       46371 :     if (m_nFileOffset + numBytesToWrite > m_nFileSize)
     204             :     {
     205       31271 :         m_nFileSize = m_nFileOffset + numBytesToWrite;
     206             :     }
     207             : 
     208       46371 :     VSIFFlushL(m_fp);
     209             : 
     210       46371 :     m_bModified = FALSE;
     211             : 
     212       46371 :     return 0;
     213             : }
     214             : 
     215             : /**********************************************************************
     216             :  *                   TABRawBinBlock::CommitAsDeleted()
     217             :  *
     218             :  * Commit current block to file using block type 4 (garbage block)
     219             :  *
     220             :  * Returns 0 if successful or -1 if an error happened, in which case
     221             :  * CPLError() will have been called.
     222             :  **********************************************************************/
     223          12 : int TABRawBinBlock::CommitAsDeleted(GInt32 nNextBlockPtr)
     224             : {
     225          12 :     CPLErrorReset();
     226             : 
     227          12 :     if (m_pabyBuf == nullptr)
     228             :     {
     229           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     230             :                  "CommitAsDeleted(): Block has not been initialized yet!");
     231           0 :         return -1;
     232             :     }
     233             : 
     234             :     /*-----------------------------------------------------------------
     235             :      * Create deleted block header
     236             :      *----------------------------------------------------------------*/
     237          12 :     GotoByteInBlock(0x000);
     238          12 :     WriteInt16(TABMAP_GARB_BLOCK);  // Block type code
     239          12 :     WriteInt32(nNextBlockPtr);
     240             : 
     241          12 :     int nStatus = CPLGetLastErrorType() == CE_Failure ? -1 : 0;
     242             : 
     243             :     /*-----------------------------------------------------------------
     244             :      * OK, call the base class to write the block to disk.
     245             :      *----------------------------------------------------------------*/
     246          12 :     if (nStatus == 0)
     247             :     {
     248             : #ifdef DEBUG_VERBOSE
     249             :         CPLDebug("MITAB", "Committing GARBAGE block to offset %d",
     250             :                  m_nFileOffset);
     251             : #endif
     252          12 :         nStatus = TABRawBinBlock::CommitToFile();
     253          12 :         m_nSizeUsed = 0;
     254             :     }
     255             : 
     256          12 :     return nStatus;
     257             : }
     258             : 
     259             : /**********************************************************************
     260             :  *                   TABRawBinBlock::InitBlockFromData()
     261             :  *
     262             :  * Set the binary data buffer and initialize the block.
     263             :  *
     264             :  * Calling ReadFromFile() will automatically call InitBlockFromData() to
     265             :  * complete the initialization of the block after the data is read from the
     266             :  * file.  Derived classes should implement their own version of
     267             :  * InitBlockFromData() if they need specific initialization... in this
     268             :  * case the derived InitBlockFromData() should call
     269             :  * TABRawBinBlock::InitBlockFromData() before doing anything else.
     270             :  *
     271             :  * By default, the buffer will be copied, but if bMakeCopy = FALSE then
     272             :  * it won't be copied, and the object will keep a reference to the
     273             :  * user's buffer... and this object will eventually free the user's buffer.
     274             :  *
     275             :  * Returns 0 if successful or -1 if an error happened, in which case
     276             :  * CPLError() will have been called.
     277             :  **********************************************************************/
     278      358409 : int TABRawBinBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
     279             :                                       int nSizeUsed,
     280             :                                       GBool bMakeCopy /* = TRUE */,
     281             :                                       VSILFILE *fpSrc /* = NULL */,
     282             :                                       int nOffset /* = 0 */)
     283             : {
     284      358409 :     m_fp = fpSrc;
     285      358409 :     m_nFileOffset = nOffset;
     286      358409 :     m_nCurPos = 0;
     287      358409 :     m_bModified = FALSE;
     288             : 
     289             :     /*----------------------------------------------------------------
     290             :      * Alloc or realloc the buffer to contain the data if necessary
     291             :      *---------------------------------------------------------------*/
     292      358409 :     if (!bMakeCopy)
     293             :     {
     294      358409 :         if (m_pabyBuf != nullptr)
     295      280630 :             CPLFree(m_pabyBuf);
     296      358409 :         m_pabyBuf = pabyBuf;
     297      358409 :         m_nBlockSize = nBlockSize;
     298      358409 :         m_nSizeUsed = nSizeUsed;
     299             :     }
     300           0 :     else if (m_pabyBuf == nullptr || nBlockSize != m_nBlockSize)
     301             :     {
     302           0 :         m_pabyBuf = static_cast<GByte *>(
     303           0 :             CPLRealloc(m_pabyBuf, nBlockSize * sizeof(GByte)));
     304           0 :         m_nBlockSize = nBlockSize;
     305           0 :         m_nSizeUsed = nSizeUsed;
     306           0 :         memcpy(m_pabyBuf, pabyBuf, m_nSizeUsed);
     307             :     }
     308             : 
     309             :     /*----------------------------------------------------------------
     310             :      * Extract block type... header block (first block in a file) has
     311             :      * no block type, so we assign one by default.
     312             :      *---------------------------------------------------------------*/
     313      358409 :     if (m_nFileOffset == 0)
     314        5609 :         m_nBlockType = TABMAP_HEADER_BLOCK;
     315             :     else
     316             :     {
     317             :         // Block type will be validated only if GetBlockType() is called
     318      352800 :         m_nBlockType = static_cast<int>(m_pabyBuf[0]);
     319             :     }
     320             : 
     321      358409 :     return 0;
     322             : }
     323             : 
     324             : /**********************************************************************
     325             :  *                   TABRawBinBlock::InitNewBlock()
     326             :  *
     327             :  * Initialize the block so that it knows to which file is attached,
     328             :  * its block size, etc.
     329             :  *
     330             :  * This is an alternative to calling ReadFromFile() or InitBlockFromData()
     331             :  * that puts the block in a stable state without loading any initial
     332             :  * data in it.
     333             :  *
     334             :  * Returns 0 if successful or -1 if an error happened, in which case
     335             :  * CPLError() will have been called.
     336             :  **********************************************************************/
     337       23114 : int TABRawBinBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
     338             :                                  int nFileOffset /* = 0*/)
     339             : {
     340       23114 :     m_fp = fpSrc;
     341       23114 :     m_nBlockSize = nBlockSize;
     342       23114 :     m_nSizeUsed = 0;
     343       23114 :     m_nCurPos = 0;
     344       23114 :     m_bModified = FALSE;
     345             : 
     346       23114 :     if (nFileOffset > 0)
     347       17409 :         m_nFileOffset = nFileOffset;
     348             :     else
     349        5705 :         m_nFileOffset = 0;
     350             : 
     351       23114 :     if (m_fp != nullptr && m_nFileSize < 0 && m_eAccess == TABReadWrite)
     352             :     {
     353        4098 :         int nCurPos = static_cast<int>(VSIFTellL(m_fp));
     354        4098 :         VSIFSeekL(fpSrc, 0, SEEK_END);
     355        4098 :         m_nFileSize = static_cast<int>(VSIFTellL(m_fp));
     356        4098 :         VSIFSeekL(fpSrc, nCurPos, SEEK_SET);
     357             :     }
     358             : 
     359       23114 :     m_nBlockType = -1;
     360             : 
     361       23114 :     m_pabyBuf = static_cast<GByte *>(
     362       23114 :         CPLRealloc(m_pabyBuf, m_nBlockSize * sizeof(GByte)));
     363       23114 :     if (m_nBlockSize)
     364       23087 :         memset(m_pabyBuf, 0, m_nBlockSize);
     365             : 
     366       23114 :     return 0;
     367             : }
     368             : 
     369             : /**********************************************************************
     370             :  *                   TABRawBinBlock::GetBlockType()
     371             :  *
     372             :  * Return the block type for the current object.
     373             :  *
     374             :  * Returns a block type >= 0 if successful or -1 if an error happened, in
     375             :  * which case  CPLError() will have been called.
     376             :  **********************************************************************/
     377       93248 : int TABRawBinBlock::GetBlockType()
     378             : {
     379       93248 :     if (m_pabyBuf == nullptr)
     380             :     {
     381           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     382             :                  "GetBlockType(): Block has not been initialized.");
     383           0 :         return -1;
     384             :     }
     385             : 
     386       93248 :     if (m_nBlockType > TABMAP_LAST_VALID_BLOCK_TYPE)
     387             :     {
     388           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     389             :                  "GetBlockType(): Unsupported block type %d.", m_nBlockType);
     390           0 :         return -1;
     391             :     }
     392             : 
     393       93248 :     return m_nBlockType;
     394             : }
     395             : 
     396             : /**********************************************************************
     397             :  *                   TABRawBinBlock::GotoByteInBlock()
     398             :  *
     399             :  * Move the block pointer to the specified position relative to the
     400             :  * beginning of the block.
     401             :  *
     402             :  * Returns 0 if successful or -1 if an error happened, in which case
     403             :  * CPLError() will have been called.
     404             :  **********************************************************************/
     405      606348 : int TABRawBinBlock::GotoByteInBlock(int nOffset)
     406             : {
     407      606348 :     if ((m_eAccess == TABRead && nOffset > m_nSizeUsed) ||
     408      606348 :         (m_eAccess != TABRead && nOffset > m_nBlockSize))
     409             :     {
     410           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     411             :                  "GotoByteInBlock(): Attempt to go past end of data block.");
     412           0 :         return -1;
     413             :     }
     414             : 
     415      606348 :     if (nOffset < 0)
     416             :     {
     417           0 :         CPLError(
     418             :             CE_Failure, CPLE_AppDefined,
     419             :             "GotoByteInBlock(): Attempt to go before start of data block.");
     420           0 :         return -1;
     421             :     }
     422             : 
     423      606348 :     m_nCurPos = nOffset;
     424             : 
     425      606348 :     m_nSizeUsed = std::max(m_nSizeUsed, m_nCurPos);
     426             : 
     427      606348 :     return 0;
     428             : }
     429             : 
     430             : /**********************************************************************
     431             :  *                   TABRawBinBlock::GotoByteRel()
     432             :  *
     433             :  * Move the block pointer by the specified number of bytes relative
     434             :  * to its current position.
     435             :  *
     436             :  * Returns 0 if successful or -1 if an error happened, in which case
     437             :  * CPLError() will have been called.
     438             :  **********************************************************************/
     439          43 : int TABRawBinBlock::GotoByteRel(int nOffset)
     440             : {
     441          43 :     return GotoByteInBlock(m_nCurPos + nOffset);
     442             : }
     443             : 
     444             : /**********************************************************************
     445             :  *                   TABRawBinBlock::GotoByteInFile()
     446             :  *
     447             :  * Move the block pointer to the specified position relative to the
     448             :  * beginning of the file.
     449             :  *
     450             :  * In read access, the current block may be reloaded to contain a right
     451             :  * block of binary data if necessary.
     452             :  *
     453             :  * In write mode, the current block may automagically be committed to
     454             :  * disk and a new block initialized if necessary.
     455             :  *
     456             :  * bForceReadFromFile is used in write mode to read the new block data from
     457             :  * file instead of creating an empty block. (Useful for TABCollection
     458             :  * or other cases that need to do random access in the file in write mode.)
     459             :  *
     460             :  * bOffsetIsEndOfData is set to TRUE to indicate that the nOffset
     461             :  * to which we are attempting to go is the end of the used data in this
     462             :  * block (we are positioning ourselves to append data), so if the nOffset
     463             :  * corresponds to the beginning of a block then we should really
     464             :  * be positioning ourselves at the end of the block that ends at this
     465             :  * address instead of at the beginning of the blocks that starts at this
     466             :  * address. This case can happen when going back and forth to write collection
     467             :  * objects to a Coordblock and is documented in bug 1657.
     468             :  *
     469             :  * Returns 0 if successful or -1 if an error happened, in which case
     470             :  * CPLError() will have been called.
     471             :  **********************************************************************/
     472     1592330 : int TABRawBinBlock::GotoByteInFile(int nOffset,
     473             :                                    GBool bForceReadFromFile /*=FALSE*/,
     474             :                                    GBool bOffsetIsEndOfData /*=FALSE*/)
     475             : {
     476     1592330 :     if (nOffset < 0)
     477             :     {
     478           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     479             :                  "GotoByteInFile(): Attempt to go before start of file.");
     480           0 :         return -1;
     481             :     }
     482             : 
     483     1592330 :     int nNewBlockPtr =
     484     1592330 :         ((nOffset - m_nFirstBlockPtr) / m_nBlockSize) * m_nBlockSize +
     485     1592330 :         m_nFirstBlockPtr;
     486             : 
     487     1592330 :     if (m_eAccess == TABRead)
     488             :     {
     489     4478690 :         if ((nOffset < m_nFileOffset ||
     490     1762720 :              nOffset >= m_nFileOffset + m_nSizeUsed) &&
     491      263302 :             ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)
     492             :         {
     493             :             // Failed reading new block... error has already been reported.
     494           0 :             return -1;
     495             :         }
     496             :     }
     497       92910 :     else if (m_eAccess == TABWrite)
     498             :     {
     499       10359 :         if ((nOffset < m_nFileOffset ||
     500        3453 :              nOffset >= m_nFileOffset + m_nBlockSize) &&
     501           0 :             (CommitToFile() != 0 ||
     502           0 :              InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0))
     503             :         {
     504             :             // Failed reading new block... error has already been reported.
     505           0 :             return -1;
     506             :         }
     507             :     }
     508       89457 :     else if (m_eAccess == TABReadWrite)
     509             :     {
     510             :         // TODO: THIS IS NOT REAL read/write access (it is more extended write)
     511             :         // Currently we try to read from file only if explicitly requested.
     512             :         // If we ever want true read/write mode we should implement
     513             :         // more smarts to detect whether the caller wants an existing block to
     514             :         // be read, or a new one to be created from scratch.
     515             :         // CommitToFile() should only be called only if something changed.
     516             :         //
     517       89457 :         if (bOffsetIsEndOfData && nOffset % m_nBlockSize == 0)
     518             :         {
     519             :             /* We're trying to go byte m_nBlockSize of a block that's full of
     520             :              * data. In this case it is okay to place the m_nCurPos at byte
     521             :              * m_nBlockSize which is past the end of the block.
     522             :              */
     523             : 
     524             :             /* Make sure we request the block that ends with requested
     525             :              * address and not the following block that doesn't exist
     526             :              * yet on disk */
     527           0 :             nNewBlockPtr -= m_nBlockSize;
     528             : 
     529           0 :             if ((nOffset < m_nFileOffset ||
     530           0 :                  nOffset > m_nFileOffset + m_nBlockSize) &&
     531           0 :                 (CommitToFile() != 0 ||
     532           0 :                  (!bForceReadFromFile &&
     533           0 :                   InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
     534           0 :                  (bForceReadFromFile &&
     535           0 :                   ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)))
     536             :             {
     537             :                 // Failed reading new block... error has already been reported.
     538           0 :                 return -1;
     539             :             }
     540             :         }
     541             :         else
     542             :         {
     543       89457 :             if (!bForceReadFromFile && m_nFileSize > 0 && nOffset < m_nFileSize)
     544             :             {
     545       21123 :                 bForceReadFromFile = TRUE;
     546       21123 :                 if (!(nOffset < m_nFileOffset ||
     547       21031 :                       nOffset >= m_nFileOffset + m_nBlockSize))
     548             :                 {
     549       21070 :                     if ((nOffset >= m_nFileOffset + m_nSizeUsed) &&
     550        4870 :                         (CommitToFile() != 0 ||
     551        4870 :                          ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0))
     552             :                     {
     553             :                         // Failed reading new block... error has already been
     554             :                         // reported.
     555           0 :                         return -1;
     556             :                     }
     557             :                 }
     558             :             }
     559             : 
     560      267504 :             if ((nOffset < m_nFileOffset ||
     561      102238 :                  nOffset >= m_nFileOffset + m_nBlockSize) &&
     562       12781 :                 (CommitToFile() != 0 ||
     563         323 :                  (!bForceReadFromFile &&
     564       12781 :                   InitNewBlock(m_fp, m_nBlockSize, nNewBlockPtr) != 0) ||
     565       12458 :                  (bForceReadFromFile &&
     566       12458 :                   ReadFromFile(m_fp, nNewBlockPtr, m_nBlockSize) != 0)))
     567             :             {
     568             :                 // Failed reading new block... error has already been reported.
     569           0 :                 return -1;
     570             :             }
     571             :         }
     572             :     }
     573             :     else
     574             :     {
     575           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     576             :                  "Access mode not supported yet!");
     577           0 :         return -1;
     578             :     }
     579             : 
     580     1592330 :     m_nCurPos = nOffset - m_nFileOffset;
     581             : 
     582     1592330 :     m_nSizeUsed = std::max(m_nSizeUsed, m_nCurPos);
     583             : 
     584     1592330 :     return 0;
     585             : }
     586             : 
     587             : /**********************************************************************
     588             :  *                   TABRawBinBlock::SetFirstBlockPtr()
     589             :  *
     590             :  * Set the position in the file at which the first block starts.
     591             :  * This value will usually be the header size and needs to be specified
     592             :  * only if the header size is different from the other blocks size.
     593             :  *
     594             :  * This value will be used by GotoByteInFile() to properly align the data
     595             :  * blocks that it loads automatically when a requested position is outside
     596             :  * of the block currently in memory.
     597             :  **********************************************************************/
     598        1494 : void TABRawBinBlock::SetFirstBlockPtr(int nOffset)
     599             : {
     600        1494 :     m_nFirstBlockPtr = nOffset;
     601        1494 : }
     602             : 
     603             : /**********************************************************************
     604             :  *                   TABRawBinBlock::GetNumUnusedBytes()
     605             :  *
     606             :  * Return the number of unused bytes in this block.
     607             :  **********************************************************************/
     608       45132 : int TABRawBinBlock::GetNumUnusedBytes()
     609             : {
     610       45132 :     return m_nBlockSize - m_nSizeUsed;
     611             : }
     612             : 
     613             : /**********************************************************************
     614             :  *                   TABRawBinBlock::GetFirstUnusedByteOffset()
     615             :  *
     616             :  * Return the position of the first unused byte in this block relative
     617             :  * to the beginning of the file, or -1 if the block is full.
     618             :  **********************************************************************/
     619       26176 : int TABRawBinBlock::GetFirstUnusedByteOffset()
     620             : {
     621       26176 :     if (m_nSizeUsed < m_nBlockSize)
     622       26176 :         return m_nFileOffset + m_nSizeUsed;
     623             :     else
     624           0 :         return -1;
     625             : }
     626             : 
     627             : /**********************************************************************
     628             :  *                   TABRawBinBlock::GetCurAddress()
     629             :  *
     630             :  * Return the current pointer position, relative to beginning of file.
     631             :  **********************************************************************/
     632         503 : int TABRawBinBlock::GetCurAddress()
     633             : {
     634         503 :     return m_nFileOffset + m_nCurPos;
     635             : }
     636             : 
     637             : /**********************************************************************
     638             :  *                   TABRawBinBlock::ReadBytes()
     639             :  *
     640             :  * Copy the number of bytes from the data block's internal buffer to
     641             :  * the user's buffer pointed by pabyDstBuf.
     642             :  *
     643             :  * Passing pabyDstBuf = NULL will only move the read pointer by the
     644             :  * specified number of bytes as if the copy had happened... but it
     645             :  * won't crash.
     646             :  *
     647             :  * Returns 0 if successful or -1 if an error happened, in which case
     648             :  * CPLError() will have been called.
     649             :  **********************************************************************/
     650     8513100 : int TABRawBinBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
     651             : {
     652             :     /*----------------------------------------------------------------
     653             :      * Make sure block is initialized with Read access and that the
     654             :      * operation won't go beyond the buffer's size.
     655             :      *---------------------------------------------------------------*/
     656     8513100 :     if (m_pabyBuf == nullptr)
     657             :     {
     658           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     659             :                  "ReadBytes(): Block has not been initialized.");
     660           0 :         return -1;
     661             :     }
     662             : 
     663     8513100 :     if (m_nCurPos + numBytes > m_nSizeUsed)
     664             :     {
     665           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     666             :                  "ReadBytes(): Attempt to read past end of data block.");
     667           0 :         return -1;
     668             :     }
     669             : 
     670     8513100 :     if (pabyDstBuf)
     671             :     {
     672     8513100 :         memcpy(pabyDstBuf, m_pabyBuf + m_nCurPos, numBytes);
     673             :     }
     674             : 
     675     8513100 :     m_nCurPos += numBytes;
     676             : 
     677     8513100 :     return 0;
     678             : }
     679             : 
     680             : /**********************************************************************
     681             :  *                   TABRawBinBlock::Read<datatype>()
     682             :  *
     683             :  * MapInfo files are binary files with LSB first (Intel) byte
     684             :  * ordering.  The following functions will read from the input file
     685             :  * and return a value with the bytes ordered properly for the current
     686             :  * platform.
     687             :  **********************************************************************/
     688     2387410 : GByte TABRawBinBlock::ReadByte()
     689             : {
     690     2387410 :     GByte byValue = 0;
     691             : 
     692     2387410 :     ReadBytes(1, &byValue);
     693             : 
     694     2387410 :     return byValue;
     695             : }
     696             : 
     697      171302 : GInt16 TABRawBinBlock::ReadInt16()
     698             : {
     699      171302 :     GInt16 n16Value = 0;
     700             : 
     701      171302 :     ReadBytes(2, reinterpret_cast<GByte *>(&n16Value));
     702             : 
     703             : #ifdef CPL_MSB
     704             :     return static_cast<GInt16>(CPL_SWAP16(n16Value));
     705             : #else
     706      171302 :     return n16Value;
     707             : #endif
     708             : }
     709             : 
     710     5893220 : GInt32 TABRawBinBlock::ReadInt32()
     711             : {
     712     5893220 :     GInt32 n32Value = 0;
     713             : 
     714     5893220 :     ReadBytes(4, reinterpret_cast<GByte *>(&n32Value));
     715             : 
     716             : #ifdef CPL_MSB
     717             :     return static_cast<GInt32>(CPL_SWAP32(n32Value));
     718             : #else
     719     5893220 :     return n32Value;
     720             : #endif
     721             : }
     722             : 
     723           6 : GInt64 TABRawBinBlock::ReadInt64()
     724             : {
     725           6 :     GInt64 n64Value = 0;
     726             : 
     727           6 :     ReadBytes(8, reinterpret_cast<GByte *>(&n64Value));
     728             : 
     729             : #ifdef CPL_MSB
     730             :     CPL_LSBPTR64(&n64Value);
     731             : #endif
     732           6 :     return n64Value;
     733             : }
     734             : 
     735           0 : float TABRawBinBlock::ReadFloat()
     736             : {
     737           0 :     float fValue = 0.0f;
     738             : 
     739           0 :     ReadBytes(4, reinterpret_cast<GByte *>(&fValue));
     740             : 
     741             : #ifdef CPL_MSB
     742             :     CPL_LSBPTR32(&fValue);
     743             : #endif
     744           0 :     return fValue;
     745             : }
     746             : 
     747       50645 : double TABRawBinBlock::ReadDouble()
     748             : {
     749       50645 :     double dValue = 0.0;
     750             : 
     751       50645 :     ReadBytes(8, reinterpret_cast<GByte *>(&dValue));
     752             : 
     753             : #ifdef CPL_MSB
     754             :     CPL_SWAPDOUBLE(&dValue);
     755             : #endif
     756             : 
     757       50645 :     return dValue;
     758             : }
     759             : 
     760             : /**********************************************************************
     761             :  *                   TABRawBinBlock::WriteBytes()
     762             :  *
     763             :  * Copy the number of bytes from the user's buffer pointed by pabySrcBuf
     764             :  * to the data block's internal buffer.
     765             :  * Note that this call only writes to the memory buffer... nothing is
     766             :  * written to the file until WriteToFile() is called.
     767             :  *
     768             :  * Passing pabySrcBuf = NULL will only move the write pointer by the
     769             :  * specified number of bytes as if the copy had happened... but it
     770             :  * won't crash.
     771             :  *
     772             :  * Returns 0 if successful or -1 if an error happened, in which case
     773             :  * CPLError() will have been called.
     774             :  **********************************************************************/
     775      591684 : int TABRawBinBlock::WriteBytes(int nBytesToWrite, const GByte *pabySrcBuf)
     776             : {
     777             :     /*----------------------------------------------------------------
     778             :      * Make sure block is initialized with Write access and that the
     779             :      * operation won't go beyond the buffer's size.
     780             :      *---------------------------------------------------------------*/
     781      591684 :     if (m_pabyBuf == nullptr)
     782             :     {
     783           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     784             :                  "WriteBytes(): Block has not been initialized.");
     785           0 :         return -1;
     786             :     }
     787             : 
     788      591684 :     if (m_eAccess == TABRead)
     789             :     {
     790           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     791             :                  "WriteBytes(): Block does not support write operations.");
     792           0 :         return -1;
     793             :     }
     794             : 
     795      591684 :     if (m_nCurPos + nBytesToWrite > m_nBlockSize)
     796             :     {
     797           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     798             :                  "WriteBytes(): Attempt to write past end of data block.");
     799           0 :         return -1;
     800             :     }
     801             : 
     802             :     /*----------------------------------------------------------------
     803             :      * Everything is OK... copy the data
     804             :      *---------------------------------------------------------------*/
     805      591684 :     if (pabySrcBuf)
     806             :     {
     807      591684 :         memcpy(m_pabyBuf + m_nCurPos, pabySrcBuf, nBytesToWrite);
     808             :     }
     809             : 
     810      591684 :     m_nCurPos += nBytesToWrite;
     811             : 
     812      591684 :     m_nSizeUsed = std::max(m_nSizeUsed, m_nCurPos);
     813             : 
     814      591684 :     m_bModified = TRUE;
     815             : 
     816      591684 :     return 0;
     817             : }
     818             : 
     819             : /**********************************************************************
     820             :  *                    TABRawBinBlock::Write<datatype>()
     821             :  *
     822             :  * Arc/Info files are binary files with MSB first (Motorola) byte
     823             :  * ordering.  The following functions will reorder the byte for the
     824             :  * value properly and write that to the output file.
     825             :  *
     826             :  * If a problem happens, then CPLError() will be called and
     827             :  * CPLGetLastErrNo() can be used to test if a write operation was
     828             :  * successful.
     829             :  **********************************************************************/
     830      119076 : int TABRawBinBlock::WriteByte(GByte byValue)
     831             : {
     832      119076 :     return WriteBytes(1, &byValue);
     833             : }
     834             : 
     835       66193 : int TABRawBinBlock::WriteInt16(GInt16 n16Value)
     836             : {
     837             : #ifdef CPL_MSB
     838             :     n16Value = static_cast<GInt16>(CPL_SWAP16(n16Value));
     839             : #endif
     840             : 
     841       66193 :     return WriteBytes(2, reinterpret_cast<GByte *>(&n16Value));
     842             : }
     843             : 
     844      336014 : int TABRawBinBlock::WriteInt32(GInt32 n32Value)
     845             : {
     846             : #ifdef CPL_MSB
     847             :     n32Value = static_cast<GInt32>(CPL_SWAP32(n32Value));
     848             : #endif
     849             : 
     850      336014 :     return WriteBytes(4, reinterpret_cast<GByte *>(&n32Value));
     851             : }
     852             : 
     853           2 : int TABRawBinBlock::WriteInt64(GInt64 n64Value)
     854             : {
     855             : #ifdef CPL_MSB
     856             :     CPL_SWAP64PTR(&n64Value);
     857             : #endif
     858             : 
     859           2 :     return WriteBytes(8, reinterpret_cast<GByte *>(&n64Value));
     860             : }
     861             : 
     862         160 : int TABRawBinBlock::WriteFloat(float fValue)
     863             : {
     864             : #ifdef CPL_MSB
     865             :     CPL_LSBPTR32(&fValue);
     866             : #endif
     867             : 
     868         160 :     return WriteBytes(4, reinterpret_cast<GByte *>(&fValue));
     869             : }
     870             : 
     871       23121 : int TABRawBinBlock::WriteDouble(double dValue)
     872             : {
     873             : #ifdef CPL_MSB
     874             :     CPL_SWAPDOUBLE(&dValue);
     875             : #endif
     876             : 
     877       23121 :     return WriteBytes(8, reinterpret_cast<GByte *>(&dValue));
     878             : }
     879             : 
     880             : /**********************************************************************
     881             :  *                    TABRawBinBlock::WriteZeros()
     882             :  *
     883             :  * Write a number of zeros (specified in bytes) at the current position
     884             :  * in the file.
     885             :  *
     886             :  * If a problem happens, then CPLError() will be called and
     887             :  * CPLGetLastErrNo() can be used to test if a write operation was
     888             :  * successful.
     889             :  **********************************************************************/
     890        8776 : int TABRawBinBlock::WriteZeros(int nBytesToWrite)
     891             : {
     892        8776 :     const GByte acZeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
     893        8776 :     int nStatus = 0;
     894             : 
     895             :     // Write by 8 bytes chunks.  The last chunk may be less than 8 bytes.
     896       51740 :     for (int i = 0; nStatus == 0 && i < nBytesToWrite; i += 8)
     897             :     {
     898       42964 :         nStatus = WriteBytes(std::min(8, nBytesToWrite - i), acZeros);
     899             :     }
     900             : 
     901        8776 :     return nStatus;
     902             : }
     903             : 
     904             : /**********************************************************************
     905             :  *                   TABRawBinBlock::WritePaddedString()
     906             :  *
     907             :  * Write a string and pad the end of the field (up to nFieldSize) with
     908             :  * spaces number of spaces at the current position in the file.
     909             :  *
     910             :  * If a problem happens, then CPLError() will be called and
     911             :  * CPLGetLastErrNo() can be used to test if a write operation was
     912             :  * successful.
     913             :  **********************************************************************/
     914           0 : int TABRawBinBlock::WritePaddedString(int nFieldSize, const char *pszString)
     915             : {
     916           0 :     char acSpaces[8] = {' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '};
     917             :     int i, nLen, numSpaces;
     918           0 :     int nStatus = 0;
     919             : 
     920           0 :     nLen = static_cast<int>(strlen(pszString));
     921           0 :     nLen = std::min(nLen, nFieldSize);
     922           0 :     numSpaces = nFieldSize - nLen;
     923             : 
     924           0 :     if (nLen > 0)
     925           0 :         nStatus = WriteBytes(nLen, reinterpret_cast<const GByte *>(pszString));
     926             : 
     927             :     /* Write spaces by 8 bytes chunks.  The last chunk may be less than 8 bytes
     928             :      */
     929           0 :     for (i = 0; nStatus == 0 && i < numSpaces; i += 8)
     930             :     {
     931           0 :         nStatus = WriteBytes(std::min(8, numSpaces - i),
     932           0 :                              reinterpret_cast<GByte *>(acSpaces));
     933             :     }
     934             : 
     935           0 :     return nStatus;
     936             : }
     937             : 
     938             : /**********************************************************************
     939             :  *                   TABRawBinBlock::Dump()
     940             :  *
     941             :  * Dump block contents... available only in DEBUG mode.
     942             :  **********************************************************************/
     943             : #ifdef DEBUG
     944             : 
     945           0 : void TABRawBinBlock::Dump(FILE *fpOut /*=NULL*/)
     946             : {
     947           0 :     if (fpOut == nullptr)
     948           0 :         fpOut = stdout;
     949             : 
     950           0 :     fprintf(fpOut, "----- TABRawBinBlock::Dump() -----\n");
     951           0 :     if (m_pabyBuf == nullptr)
     952             :     {
     953           0 :         fprintf(fpOut, "Block has not been initialized yet.");
     954             :     }
     955             :     else
     956             :     {
     957           0 :         if (m_nBlockType == TABMAP_GARB_BLOCK)
     958             :         {
     959           0 :             fprintf(fpOut, "Garbage Block (type %d) at offset %d.\n",
     960             :                     m_nBlockType, m_nFileOffset);
     961           0 :             int nNextGarbageBlock = 0;
     962           0 :             memcpy(&nNextGarbageBlock, m_pabyBuf + 2, 4);
     963           0 :             CPL_LSBPTR32(&nNextGarbageBlock);
     964           0 :             fprintf(fpOut, "  m_nNextGarbageBlock     = %d\n",
     965             :                     nNextGarbageBlock);
     966             :         }
     967             :         else
     968             :         {
     969           0 :             fprintf(fpOut,
     970             :                     "Block (type %d) size=%d bytes at offset %d in file.\n",
     971             :                     m_nBlockType, m_nBlockSize, m_nFileOffset);
     972           0 :             fprintf(fpOut, "Current pointer at byte %d\n", m_nCurPos);
     973             :         }
     974             :     }
     975             : 
     976           0 :     fflush(fpOut);
     977           0 : }
     978             : 
     979             : #endif  // DEBUG
     980             : 
     981             : /**********************************************************************
     982             :  *                          DumpBytes()
     983             :  *
     984             :  * Read and dump the contents of an Binary file.
     985             :  **********************************************************************/
     986           0 : void TABRawBinBlock::DumpBytes(GInt32 nValue, int nOffset /*=0*/,
     987             :                                FILE *fpOut /*=NULL*/)
     988             : {
     989           0 :     float fValue = 0.0f;
     990           0 :     memcpy(&fValue, &nValue, 4);
     991             : 
     992             :     char achValue[4];
     993           0 :     memcpy(achValue, &nValue, 4);
     994             : 
     995           0 :     GInt16 n16Val1 = 0;
     996           0 :     memcpy(&n16Val1, achValue + 2, sizeof(GInt16));
     997           0 :     GInt16 n16Val2 = 0;
     998           0 :     memcpy(&n16Val2, achValue, sizeof(GInt16));
     999             : 
    1000             :     /* For double precision values, we only use the first half
    1001             :      * of the height bytes... and leave the other 4 bytes as zeros!
    1002             :      * It's a bit of a hack, but it seems to be enough for the
    1003             :      * precision of the values we print!
    1004             :      */
    1005             : #ifdef CPL_MSB
    1006             :     const GInt32 anVal[2] = {nValue, 0};
    1007             : #else
    1008           0 :     const GInt32 anVal[2] = {0, nValue};
    1009             : #endif
    1010           0 :     double dValue = 0.0;
    1011           0 :     memcpy(&dValue, anVal, 8);
    1012             : 
    1013           0 :     if (fpOut == nullptr)
    1014           0 :         fpOut = stdout;
    1015             : 
    1016           0 :     fprintf(fpOut, "%d\t0x%8.8x  %-5d\t%-6d %-6d %5.3e  d=%5.3e", nOffset,
    1017             :             nValue, nValue, n16Val1, n16Val2, fValue, dValue);
    1018             : 
    1019           0 :     fprintf(fpOut, "\t[%c%c%c%c]\n", isprint(achValue[0]) ? achValue[0] : '.',
    1020           0 :             isprint(achValue[1]) ? achValue[1] : '.',
    1021           0 :             isprint(achValue[2]) ? achValue[2] : '.',
    1022           0 :             isprint(achValue[3]) ? achValue[3] : '.');
    1023           0 : }
    1024             : 
    1025             : /**********************************************************************
    1026             :  *                   TABCreateMAPBlockFromFile()
    1027             :  *
    1028             :  * Load data from the specified file location and create and initialize
    1029             :  * a TABMAP*Block of the right type to handle it.
    1030             :  *
    1031             :  * Returns the new object if successful or NULL if an error happened, in
    1032             :  * which case CPLError() will have been called.
    1033             :  **********************************************************************/
    1034       36055 : TABRawBinBlock *TABCreateMAPBlockFromFile(VSILFILE *fpSrc, int nOffset,
    1035             :                                           int nSize,
    1036             :                                           GBool bHardBlockSize /*= TRUE */,
    1037             :                                           TABAccess eAccessMode /*= TABRead*/)
    1038             : {
    1039       36055 :     if (fpSrc == nullptr || nSize == 0)
    1040             :     {
    1041           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1042             :                  "TABCreateMAPBlockFromFile(): Assertion Failed!");
    1043           0 :         return nullptr;
    1044             :     }
    1045             : 
    1046             :     /*----------------------------------------------------------------
    1047             :      * Alloc a buffer to contain the data
    1048             :      *---------------------------------------------------------------*/
    1049       36055 :     GByte *pabyBuf = static_cast<GByte *>(CPLMalloc(nSize * sizeof(GByte)));
    1050             : 
    1051             :     /*----------------------------------------------------------------
    1052             :      * Read from the file
    1053             :      *---------------------------------------------------------------*/
    1054       72110 :     if (VSIFSeekL(fpSrc, nOffset, SEEK_SET) != 0 ||
    1055       36055 :         VSIFReadL(pabyBuf, sizeof(GByte), nSize, fpSrc) !=
    1056       36055 :             static_cast<unsigned int>(nSize))
    1057             :     {
    1058          14 :         CPLError(
    1059             :             CE_Failure, CPLE_FileIO,
    1060             :             "TABCreateMAPBlockFromFile() failed reading %d bytes at offset %d.",
    1061             :             nSize, nOffset);
    1062          14 :         CPLFree(pabyBuf);
    1063          14 :         return nullptr;
    1064             :     }
    1065             : 
    1066             :     /*----------------------------------------------------------------
    1067             :      * Create an object of the right type
    1068             :      * Header block is different: it does not start with the object
    1069             :      * type byte but it is always the first block in a file
    1070             :      *---------------------------------------------------------------*/
    1071       36041 :     TABRawBinBlock *poBlock = nullptr;
    1072             : 
    1073       36041 :     if (nOffset == 0)
    1074             :     {
    1075        2634 :         poBlock = new TABMAPHeaderBlock(eAccessMode);
    1076             :     }
    1077             :     else
    1078             :     {
    1079       33407 :         switch (pabyBuf[0])
    1080             :         {
    1081       10583 :             case TABMAP_INDEX_BLOCK:
    1082       10583 :                 poBlock = new TABMAPIndexBlock(eAccessMode);
    1083       10583 :                 break;
    1084       22658 :             case TABMAP_OBJECT_BLOCK:
    1085       22658 :                 poBlock = new TABMAPObjectBlock(eAccessMode);
    1086       22658 :                 break;
    1087         165 :             case TABMAP_COORD_BLOCK:
    1088         165 :                 poBlock = new TABMAPCoordBlock(eAccessMode);
    1089         165 :                 break;
    1090           0 :             case TABMAP_TOOL_BLOCK:
    1091           0 :                 poBlock = new TABMAPToolBlock(eAccessMode);
    1092           0 :                 break;
    1093           1 :             case TABMAP_GARB_BLOCK:
    1094             :             default:
    1095           1 :                 poBlock = new TABRawBinBlock(eAccessMode, bHardBlockSize);
    1096           1 :                 break;
    1097             :         }
    1098             :     }
    1099             : 
    1100             :     /*----------------------------------------------------------------
    1101             :      * Init new object with the data we just read
    1102             :      *---------------------------------------------------------------*/
    1103       72082 :     if (poBlock->InitBlockFromData(pabyBuf, nSize, nSize, FALSE, fpSrc,
    1104       36041 :                                    nOffset) != 0)
    1105             :     {
    1106             :         // Some error happened... and CPLError() has been called
    1107           0 :         delete poBlock;
    1108           0 :         poBlock = nullptr;
    1109             :     }
    1110             : 
    1111       36041 :     return poBlock;
    1112             : }
    1113             : 
    1114             : /*=====================================================================
    1115             :  *                      class TABBinBlockManager
    1116             :  *====================================================================*/
    1117             : 
    1118             : /**********************************************************************
    1119             :  *                   TABBinBlockManager::TABBinBlockManager()
    1120             :  *
    1121             :  * Constructor.
    1122             :  **********************************************************************/
    1123        1451 : TABBinBlockManager::TABBinBlockManager()
    1124             :     : m_nBlockSize(0), m_nLastAllocatedBlock(-1),
    1125        1451 :       m_psGarbageBlocksFirst(nullptr), m_psGarbageBlocksLast(nullptr)
    1126             : {
    1127        1451 :     m_szName[0] = '\0';
    1128        1451 : }
    1129             : 
    1130             : /**********************************************************************
    1131             :  *                   TABBinBlockManager::~TABBinBlockManager()
    1132             :  *
    1133             :  * Destructor.
    1134             :  **********************************************************************/
    1135        2902 : TABBinBlockManager::~TABBinBlockManager()
    1136             : {
    1137        1451 :     Reset();
    1138        1451 : }
    1139             : 
    1140             : /**********************************************************************
    1141             :  *                   TABBinBlockManager::SetBlockSize()
    1142             :  **********************************************************************/
    1143        1474 : void TABBinBlockManager::SetBlockSize(int nBlockSize)
    1144             : {
    1145        1474 :     m_nBlockSize = nBlockSize;
    1146        1474 : }
    1147             : 
    1148             : /**********************************************************************
    1149             :  *                   TABBinBlockManager::SetName()
    1150             :  **********************************************************************/
    1151        1451 : void TABBinBlockManager::SetName(const char *pszName)
    1152             : {
    1153        1451 :     strncpy(m_szName, pszName, sizeof(m_szName));
    1154        1451 :     m_szName[sizeof(m_szName) - 1] = '\0';
    1155        1451 : }
    1156             : 
    1157             : /**********************************************************************
    1158             :  *                   TABBinBlockManager::AllocNewBlock()
    1159             :  *
    1160             :  * Returns and reserves the address of the next available block, either a
    1161             :  * brand new block at end of file, or recycle a garbage block if one is
    1162             :  * available.
    1163             :  **********************************************************************/
    1164         848 : GInt32 TABBinBlockManager::AllocNewBlock(CPL_UNUSED const char *pszReason)
    1165             : {
    1166             :     // Try to reuse garbage blocks first
    1167         848 :     if (GetFirstGarbageBlock() > 0)
    1168             :     {
    1169          12 :         int nRetValue = PopGarbageBlock();
    1170             : #ifdef DEBUG_VERBOSE
    1171             :         CPLDebug("MITAB",
    1172             :                  "AllocNewBlock(%s, %s) = %d (recycling garbage block)",
    1173             :                  m_szName, pszReason, nRetValue);
    1174             : #endif
    1175          12 :         return nRetValue;
    1176             :     }
    1177             : 
    1178             :     // ... or alloc a new block at EOF
    1179         836 :     if (m_nLastAllocatedBlock == -1)
    1180          48 :         m_nLastAllocatedBlock = 0;
    1181             :     else
    1182             :     {
    1183         788 :         CPLAssert(m_nBlockSize);
    1184         788 :         m_nLastAllocatedBlock += m_nBlockSize;
    1185             :     }
    1186             : 
    1187             : #ifdef DEBUG_VERBOSE
    1188             :     CPLDebug("MITAB", "AllocNewBlock(%s, %s) = %d", m_szName, pszReason,
    1189             :              m_nLastAllocatedBlock);
    1190             : #endif
    1191         836 :     return m_nLastAllocatedBlock;
    1192             : }
    1193             : 
    1194             : /**********************************************************************
    1195             :  *                   TABBinBlockManager::Reset()
    1196             :  *
    1197             :  **********************************************************************/
    1198        2941 : void TABBinBlockManager::Reset()
    1199             : {
    1200        2941 :     m_nLastAllocatedBlock = -1;
    1201             : 
    1202             :     // Flush list of garbage blocks
    1203        2941 :     while (m_psGarbageBlocksFirst != nullptr)
    1204             :     {
    1205           0 :         TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
    1206           0 :         CPLFree(m_psGarbageBlocksFirst);
    1207           0 :         m_psGarbageBlocksFirst = psNext;
    1208             :     }
    1209        2941 :     m_psGarbageBlocksLast = nullptr;
    1210        2941 : }
    1211             : 
    1212             : /**********************************************************************
    1213             :  *                   TABBinBlockManager::PushGarbageBlockAsFirst()
    1214             :  *
    1215             :  * Insert a garbage block at the head of the list of garbage blocks.
    1216             :  **********************************************************************/
    1217          12 : void TABBinBlockManager::PushGarbageBlockAsFirst(GInt32 nBlockPtr)
    1218             : {
    1219             :     TABBlockRef *psNewBlockRef =
    1220          12 :         static_cast<TABBlockRef *>(CPLMalloc(sizeof(TABBlockRef)));
    1221             : 
    1222          12 :     psNewBlockRef->nBlockPtr = nBlockPtr;
    1223          12 :     psNewBlockRef->psPrev = nullptr;
    1224          12 :     psNewBlockRef->psNext = m_psGarbageBlocksFirst;
    1225             : 
    1226          12 :     if (m_psGarbageBlocksFirst != nullptr)
    1227           0 :         m_psGarbageBlocksFirst->psPrev = psNewBlockRef;
    1228          12 :     m_psGarbageBlocksFirst = psNewBlockRef;
    1229          12 :     if (m_psGarbageBlocksLast == nullptr)
    1230          12 :         m_psGarbageBlocksLast = m_psGarbageBlocksFirst;
    1231          12 : }
    1232             : 
    1233             : /**********************************************************************
    1234             :  *                   TABBinBlockManager::PushGarbageBlockAsLast()
    1235             :  *
    1236             :  * Insert a garbage block at the tail of the list of garbage blocks.
    1237             :  **********************************************************************/
    1238           0 : void TABBinBlockManager::PushGarbageBlockAsLast(GInt32 nBlockPtr)
    1239             : {
    1240             :     TABBlockRef *psNewBlockRef =
    1241           0 :         static_cast<TABBlockRef *>(CPLMalloc(sizeof(TABBlockRef)));
    1242             : 
    1243           0 :     psNewBlockRef->nBlockPtr = nBlockPtr;
    1244           0 :     psNewBlockRef->psPrev = m_psGarbageBlocksLast;
    1245           0 :     psNewBlockRef->psNext = nullptr;
    1246             : 
    1247           0 :     if (m_psGarbageBlocksLast != nullptr)
    1248           0 :         m_psGarbageBlocksLast->psNext = psNewBlockRef;
    1249           0 :     m_psGarbageBlocksLast = psNewBlockRef;
    1250           0 :     if (m_psGarbageBlocksFirst == nullptr)
    1251           0 :         m_psGarbageBlocksFirst = m_psGarbageBlocksLast;
    1252           0 : }
    1253             : 
    1254             : /**********************************************************************
    1255             :  *                   TABBinBlockManager::GetFirstGarbageBlock()
    1256             :  *
    1257             :  * Return address of the block at the head of the list of garbage blocks
    1258             :  * or 0 if the list is empty.
    1259             :  **********************************************************************/
    1260        2061 : GInt32 TABBinBlockManager::GetFirstGarbageBlock()
    1261             : {
    1262        2061 :     if (m_psGarbageBlocksFirst)
    1263          12 :         return m_psGarbageBlocksFirst->nBlockPtr;
    1264             : 
    1265        2049 :     return 0;
    1266             : }
    1267             : 
    1268             : /**********************************************************************
    1269             :  *                   TABBinBlockManager::PopGarbageBlock()
    1270             :  *
    1271             :  * Return address of the block at the head of the list of garbage blocks
    1272             :  * and remove that block from the list.
    1273             :  * Returns 0 if the list is empty.
    1274             :  **********************************************************************/
    1275          12 : GInt32 TABBinBlockManager::PopGarbageBlock()
    1276             : {
    1277          12 :     GInt32 nBlockPtr = 0;
    1278             : 
    1279          12 :     if (m_psGarbageBlocksFirst)
    1280             :     {
    1281          12 :         nBlockPtr = m_psGarbageBlocksFirst->nBlockPtr;
    1282          12 :         TABBlockRef *psNext = m_psGarbageBlocksFirst->psNext;
    1283          12 :         CPLFree(m_psGarbageBlocksFirst);
    1284          12 :         if (psNext != nullptr)
    1285           0 :             psNext->psPrev = nullptr;
    1286             :         else
    1287          12 :             m_psGarbageBlocksLast = nullptr;
    1288          12 :         m_psGarbageBlocksFirst = psNext;
    1289             :     }
    1290             : 
    1291          12 :     return nBlockPtr;
    1292             : }

Generated by: LCOV version 1.14