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

Generated by: LCOV version 1.14