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

Generated by: LCOV version 1.14