LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/mitab - mitab_mapcoordblock.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 181 259 69.9 %
Date: 2024-05-05 22:37:24 Functions: 18 20 90.0 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Name:     mitab_mapcoordblock.cpp
       4             :  * Project:  MapInfo TAB Read/Write library
       5             :  * Language: C++
       6             :  * Purpose:  Implementation of the TABMAPCoordBlock class used to handle
       7             :  *           reading/writing of the .MAP files' coordinate blocks
       8             :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
       9             :  *
      10             :  **********************************************************************
      11             :  * Copyright (c) 1999-2001, Daniel Morissette
      12             :  * Copyright (c) 2014, Even Rouault <even.rouault at spatialys.com>
      13             :  *
      14             :  * 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 <climits>
      37             : #include <cstddef>
      38             : #include <algorithm>
      39             : 
      40             : #include "mitab_priv.h"
      41             : #include "mitab_utils.h"
      42             : #include "cpl_conv.h"
      43             : #include "cpl_error.h"
      44             : #include "cpl_vsi.h"
      45             : 
      46             : /*=====================================================================
      47             :  *                      class TABMAPCoordBlock
      48             :  *====================================================================*/
      49             : 
      50             : constexpr int MAP_COORD_HEADER_SIZE = 8;
      51             : 
      52             : /**********************************************************************
      53             :  *                   TABMAPCoordBlock::TABMAPCoordBlock()
      54             :  *
      55             :  * Constructor.
      56             :  **********************************************************************/
      57         234 : TABMAPCoordBlock::TABMAPCoordBlock(TABAccess eAccessMode /*= TABRead*/)
      58             :     : TABRawBinBlock(eAccessMode, TRUE), m_numDataBytes(0),
      59             :       m_nNextCoordBlock(0), m_numBlocksInChain(1),  // Current block counts as 1
      60             :       m_nComprOrgX(0), m_nComprOrgY(0), m_nMinX(1000000000),
      61             :       m_nMinY(1000000000), m_nMaxX(-1000000000), m_nMaxY(-1000000000),
      62             :       m_poBlockManagerRef(nullptr), m_nTotalDataSize(0), m_nFeatureDataSize(0),
      63             :       m_nFeatureXMin(1000000000), m_nFeatureYMin(1000000000),
      64         234 :       m_nFeatureXMax(-1000000000), m_nFeatureYMax(-1000000000)
      65             : {
      66         234 : }
      67             : 
      68             : /**********************************************************************
      69             :  *                   TABMAPCoordBlock::~TABMAPCoordBlock()
      70             :  *
      71             :  * Destructor.
      72             :  **********************************************************************/
      73         468 : TABMAPCoordBlock::~TABMAPCoordBlock()
      74             : {
      75         468 : }
      76             : 
      77             : /**********************************************************************
      78             :  *                   TABMAPCoordBlock::InitBlockFromData()
      79             :  *
      80             :  * Perform some initialization on the block after its binary data has
      81             :  * been set or changed (or loaded from a file).
      82             :  *
      83             :  * Returns 0 if successful or -1 if an error happened, in which case
      84             :  * CPLError() will have been called.
      85             :  **********************************************************************/
      86         559 : int TABMAPCoordBlock::InitBlockFromData(GByte *pabyBuf, int nBlockSize,
      87             :                                         int nSizeUsed,
      88             :                                         GBool bMakeCopy /* = TRUE */,
      89             :                                         VSILFILE *fpSrc /* = NULL */,
      90             :                                         int nOffset /* = 0 */)
      91             : {
      92             : #ifdef DEBUG_VERBOSE
      93             :     CPLDebug("MITAB", "Instantiating COORD block to/from offset %d", nOffset);
      94             : #endif
      95             :     /*-----------------------------------------------------------------
      96             :      * First of all, we must call the base class' InitBlockFromData()
      97             :      *----------------------------------------------------------------*/
      98         559 :     const int nStatus = TABRawBinBlock::InitBlockFromData(
      99             :         pabyBuf, nBlockSize, nSizeUsed, bMakeCopy, fpSrc, nOffset);
     100         559 :     if (nStatus != 0)
     101           0 :         return nStatus;
     102             : 
     103             :     /*-----------------------------------------------------------------
     104             :      * Validate block type
     105             :      *----------------------------------------------------------------*/
     106         559 :     if (m_nBlockType != TABMAP_COORD_BLOCK)
     107             :     {
     108           0 :         CPLError(CE_Failure, CPLE_FileIO,
     109             :                  "InitBlockFromData(): Invalid Block Type: got %d expected %d",
     110             :                  m_nBlockType, TABMAP_COORD_BLOCK);
     111           0 :         CPLFree(m_pabyBuf);
     112           0 :         m_pabyBuf = nullptr;
     113           0 :         return -1;
     114             :     }
     115             : 
     116             :     /*-----------------------------------------------------------------
     117             :      * Init member variables
     118             :      *----------------------------------------------------------------*/
     119         559 :     GotoByteInBlock(0x002);
     120         559 :     m_numDataBytes = ReadInt16(); /* Excluding 8 bytes header */
     121         559 :     if (m_numDataBytes < 0 ||
     122         559 :         m_numDataBytes + MAP_COORD_HEADER_SIZE > nBlockSize)
     123             :     {
     124           0 :         CPLError(CE_Failure, CPLE_FileIO,
     125             :                  "TABMAPCoordBlock::InitBlockFromData(): m_numDataBytes=%d "
     126             :                  "incompatible with block size %d",
     127             :                  m_numDataBytes, nBlockSize);
     128           0 :         CPLFree(m_pabyBuf);
     129           0 :         m_pabyBuf = nullptr;
     130           0 :         return -1;
     131             :     }
     132             : 
     133         559 :     m_nNextCoordBlock = ReadInt32();
     134             : 
     135             :     // Set the real SizeUsed based on numDataBytes
     136         559 :     m_nSizeUsed = m_numDataBytes + MAP_COORD_HEADER_SIZE;
     137             : 
     138             :     /*-----------------------------------------------------------------
     139             :      * The read ptr is now located at the beginning of the data part.
     140             :      *----------------------------------------------------------------*/
     141         559 :     GotoByteInBlock(MAP_COORD_HEADER_SIZE);
     142             : 
     143         559 :     return 0;
     144             : }
     145             : 
     146             : /**********************************************************************
     147             :  *                   TABMAPCoordBlock::CommitToFile()
     148             :  *
     149             :  * Commit the current state of the binary block to the file to which
     150             :  * it has been previously attached.
     151             :  *
     152             :  * This method makes sure all values are properly set in the map object
     153             :  * block header and then calls TABRawBinBlock::CommitToFile() to do
     154             :  * the actual writing to disk.
     155             :  *
     156             :  * Returns 0 if successful or -1 if an error happened, in which case
     157             :  * CPLError() will have been called.
     158             :  **********************************************************************/
     159         472 : int TABMAPCoordBlock::CommitToFile()
     160             : {
     161         472 :     int nStatus = 0;
     162             : 
     163         472 :     CPLErrorReset();
     164             : 
     165         472 :     if (m_pabyBuf == nullptr)
     166             :     {
     167           0 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     168             :                  "CommitToFile(): Block has not been initialized yet!");
     169           0 :         return -1;
     170             :     }
     171             : 
     172             :     /*-----------------------------------------------------------------
     173             :      * Nothing to do here if block has not been modified
     174             :      *----------------------------------------------------------------*/
     175         472 :     if (!m_bModified)
     176         188 :         return 0;
     177             : 
     178             :     /*-----------------------------------------------------------------
     179             :      * Make sure 8 bytes block header is up to date.
     180             :      *----------------------------------------------------------------*/
     181         284 :     GotoByteInBlock(0x000);
     182             : 
     183         284 :     WriteInt16(TABMAP_COORD_BLOCK);  // Block type code
     184         284 :     CPLAssert(m_nSizeUsed >= MAP_COORD_HEADER_SIZE &&
     185             :               m_nSizeUsed < MAP_COORD_HEADER_SIZE + 32768);
     186         284 :     WriteInt16(static_cast<GInt16>(m_nSizeUsed -
     187             :                                    MAP_COORD_HEADER_SIZE));  // num. bytes used
     188         284 :     WriteInt32(m_nNextCoordBlock);
     189             : 
     190         284 :     if (CPLGetLastErrorType() == CE_Failure)
     191           0 :         nStatus = -1;
     192             : 
     193             :     /*-----------------------------------------------------------------
     194             :      * OK, call the base class to write the block to disk.
     195             :      *----------------------------------------------------------------*/
     196         284 :     if (nStatus == 0)
     197             :     {
     198             : #ifdef DEBUG_VERBOSE
     199             :         CPLDebug("MITAB", "Committing COORD block to offset %d", m_nFileOffset);
     200             : #endif
     201         284 :         nStatus = TABRawBinBlock::CommitToFile();
     202             :     }
     203             : 
     204         284 :     return nStatus;
     205             : }
     206             : 
     207             : /**********************************************************************
     208             :  *                   TABMAPCoordBlock::InitNewBlock()
     209             :  *
     210             :  * Initialize a newly created block so that it knows to which file it
     211             :  * is attached, its block size, etc . and then perform any specific
     212             :  * initialization for this block type, including writing a default
     213             :  * block header, etc. and leave the block ready to receive data.
     214             :  *
     215             :  * This is an alternative to calling ReadFromFile() or InitBlockFromData()
     216             :  * that puts the block in a stable state without loading any initial
     217             :  * data in it.
     218             :  *
     219             :  * Returns 0 if successful or -1 if an error happened, in which case
     220             :  * CPLError() will have been called.
     221             :  **********************************************************************/
     222         143 : int TABMAPCoordBlock::InitNewBlock(VSILFILE *fpSrc, int nBlockSize,
     223             :                                    int nFileOffset /* = 0*/)
     224             : {
     225         143 :     CPLErrorReset();
     226             : #ifdef DEBUG_VERBOSE
     227             :     CPLDebug("MITAB", "Instantiating new COORD block at offset %d",
     228             :              nFileOffset);
     229             : #endif
     230             :     /*-----------------------------------------------------------------
     231             :      * Start with the default initialization
     232             :      *----------------------------------------------------------------*/
     233         143 :     if (TABRawBinBlock::InitNewBlock(fpSrc, nBlockSize, nFileOffset) != 0)
     234           0 :         return -1;
     235             : 
     236             :     /*-----------------------------------------------------------------
     237             :      * And then set default values for the block header.
     238             :      *
     239             :      * IMPORTANT: Do not reset m_nComprOrg here because its value needs to be
     240             :      * maintained between blocks in the same chain.
     241             :      *----------------------------------------------------------------*/
     242         143 :     m_nNextCoordBlock = 0;
     243             : 
     244         143 :     m_numDataBytes = 0;
     245             : 
     246             :     // m_nMin/Max are used to keep track of current block MBR
     247             :     // FeatureMin/Max should not be reset here since feature coords can
     248             :     // be split on several blocks
     249         143 :     m_nMinX = 1000000000;
     250         143 :     m_nMinY = 1000000000;
     251         143 :     m_nMaxX = -1000000000;
     252         143 :     m_nMaxY = -1000000000;
     253             : 
     254         143 :     if (m_eAccess != TABRead && nFileOffset != 0)
     255             :     {
     256         121 :         GotoByteInBlock(0x000);
     257             : 
     258         121 :         WriteInt16(TABMAP_COORD_BLOCK);  // Block type code
     259         121 :         WriteInt16(0);                   // num. bytes used, excluding header
     260         121 :         WriteInt32(0);                   // Pointer to next coord block
     261             :     }
     262             : 
     263         143 :     if (CPLGetLastErrorType() == CE_Failure)
     264           0 :         return -1;
     265             : 
     266         143 :     return 0;
     267             : }
     268             : 
     269             : /**********************************************************************
     270             :  *                   TABMAPObjectBlock::SetNextCoordBlock()
     271             :  *
     272             :  * Set the address (offset from beginning of file) of the coord. block
     273             :  * that follows the current one.
     274             :  **********************************************************************/
     275          68 : void TABMAPCoordBlock::SetNextCoordBlock(GInt32 nNextCoordBlockAddress)
     276             : {
     277          68 :     m_nNextCoordBlock = nNextCoordBlockAddress;
     278          68 :     m_bModified = TRUE;
     279          68 : }
     280             : 
     281             : /**********************************************************************
     282             :  *                   TABMAPObjectBlock::SetComprCoordOrigin()
     283             :  *
     284             :  * Set the Compressed integer coordinates space origin to be used when
     285             :  * reading compressed coordinates using ReadIntCoord().
     286             :  **********************************************************************/
     287        2306 : void TABMAPCoordBlock::SetComprCoordOrigin(GInt32 nX, GInt32 nY)
     288             : {
     289        2306 :     m_nComprOrgX = nX;
     290        2306 :     m_nComprOrgY = nY;
     291        2306 : }
     292             : 
     293             : /**********************************************************************
     294             :  *                   TABMAPObjectBlock::ReadIntCoord()
     295             :  *
     296             :  * Read the next pair of integer coordinates value from the block, and
     297             :  * apply the translation relative to the origin of the coord. space
     298             :  * previously set using SetComprCoordOrigin() if bCompressed=TRUE.
     299             :  *
     300             :  * This means that the returned coordinates are always absolute integer
     301             :  * coordinates, even when the source coords are in compressed form.
     302             :  *
     303             :  * Returns 0 if successful or -1 if an error happened, in which case
     304             :  * CPLError() will have been called.
     305             :  **********************************************************************/
     306       10824 : int TABMAPCoordBlock::ReadIntCoord(GBool bCompressed, GInt32 &nX, GInt32 &nY)
     307             : {
     308       10824 :     if (bCompressed)
     309             :     {
     310        5714 :         nX = ReadInt16();
     311        5714 :         nY = ReadInt16();
     312        5714 :         TABSaturatedAdd(nX, m_nComprOrgX);
     313        5714 :         TABSaturatedAdd(nY, m_nComprOrgY);
     314             :     }
     315             :     else
     316             :     {
     317        5110 :         nX = ReadInt32();
     318        5110 :         nY = ReadInt32();
     319             :     }
     320             : 
     321       10824 :     if (CPLGetLastErrorType() == CE_Failure)
     322           0 :         return -1;
     323             : 
     324       10824 :     return 0;
     325             : }
     326             : 
     327             : /**********************************************************************
     328             :  *                   TABMAPObjectBlock::ReadIntCoords()
     329             :  *
     330             :  * Read the specified number of pairs of X,Y integer coordinates values
     331             :  * from the block, and apply the translation relative to the origin of
     332             :  * the coord. space previously set using SetComprCoordOrigin() if
     333             :  * bCompressed=TRUE.
     334             :  *
     335             :  * This means that the returned coordinates are always absolute integer
     336             :  * coordinates, even when the source coords are in compressed form.
     337             :  *
     338             :  * panXY should point to an array big enough to receive the specified
     339             :  * number of coordinates.
     340             :  *
     341             :  * Returns 0 if successful or -1 if an error happened, in which case
     342             :  * CPLError() will have been called.
     343             :  **********************************************************************/
     344         513 : int TABMAPCoordBlock::ReadIntCoords(GBool bCompressed, int numCoordPairs,
     345             :                                     GInt32 *panXY)
     346             : {
     347         513 :     int i, numValues = numCoordPairs * 2;
     348             : 
     349         513 :     if (bCompressed)
     350             :     {
     351        9755 :         for (i = 0; i < numValues; i += 2)
     352             :         {
     353        9297 :             panXY[i] = ReadInt16();
     354        9297 :             panXY[i + 1] = ReadInt16();
     355        9297 :             TABSaturatedAdd(panXY[i], m_nComprOrgX);
     356        9297 :             TABSaturatedAdd(panXY[i + 1], m_nComprOrgY);
     357        9297 :             if (CPLGetLastErrorType() == CE_Failure)
     358           0 :                 return -1;
     359             :         }
     360             :     }
     361             :     else
     362             :     {
     363        4040 :         for (i = 0; i < numValues; i += 2)
     364             :         {
     365        3985 :             panXY[i] = ReadInt32();
     366        3985 :             panXY[i + 1] = ReadInt32();
     367        3985 :             if (CPLGetLastErrorType() == CE_Failure)
     368           0 :                 return -1;
     369             :         }
     370             :     }
     371             : 
     372         513 :     return 0;
     373             : }
     374             : 
     375             : /**********************************************************************
     376             :  *                   TABMAPObjectBlock::ReadCoordSecHdrs()
     377             :  *
     378             :  * Read a set of coordinate section headers for PLINE MULTIPLE or REGIONs
     379             :  * and store the result in the array of structures pasHdrs[].  It is assumed
     380             :  * that pasHdrs points to an allocated array of at least numSections
     381             :  * TABMAPCoordSecHdr structures.
     382             :  *
     383             :  * The function will also set the values of numVerticesTotal to the
     384             :  * total number of coordinates in the object (the sum of all sections
     385             :  * headers read).
     386             :  *
     387             :  * At the end of the call, this TABMAPCoordBlock object will be located
     388             :  * at the beginning of the coordinate data.
     389             :  *
     390             :  * In V450 the numVertices is stored on an int32 instead of an int16
     391             :  *
     392             :  * In V800 the numHoles is stored on an int32 instead of an int16
     393             :  *
     394             :  * IMPORTANT: This function makes the assumption that coordinates for all
     395             :  *            the sections are grouped together immediately after the
     396             :  *            last section header block (i.e. that the coord. data is not
     397             :  *            located all over the place).  If it is not the case then
     398             :  *            an error will be produced and the code to read region and
     399             :  *            multipline objects will have to be updated.
     400             :  *
     401             :  * Returns 0 if successful or -1 if an error happened, in which case
     402             :  * CPLError() will have been called.
     403             :  **********************************************************************/
     404         513 : int TABMAPCoordBlock::ReadCoordSecHdrs(GBool bCompressed, int nVersion,
     405             :                                        int numSections,
     406             :                                        TABMAPCoordSecHdr *pasHdrs,
     407             :                                        GInt32 &numVerticesTotal)
     408             : {
     409         513 :     CPLErrorReset();
     410             : 
     411             :     /*-------------------------------------------------------------
     412             :      * Note about header+vertices size vs compressed coordinates:
     413             :      * The uncompressed header sections are actually 16 bytes, but the
     414             :      * offset calculations are based on prior decompression of the
     415             :      * coordinates.  Our coordinate offset calculations have
     416             :      * to take this fact into account.
     417             :      * Also, V450 header section uses int32 instead of int16 for numVertices
     418             :      * and we add another 2 bytes to align with a 4 bytes boundary.
     419             :      * V800 header section uses int32 for numHoles but there is no need
     420             :      * for the 2 alignment bytes so the size is the same as V450
     421             :      *------------------------------------------------------------*/
     422         513 :     const int nSectionSize = (nVersion >= 450) ? 28 : 24;
     423         513 :     if (numSections > INT_MAX / nSectionSize)
     424             :     {
     425           0 :         CPLError(CE_Failure, CPLE_AssertionFailed, "Invalid numSections");
     426           0 :         return -1;
     427             :     }
     428         513 :     const int nTotalHdrSizeUncompressed = nSectionSize * numSections;
     429             : 
     430         513 :     const int nVertexSize =
     431             :         bCompressed ? 2 * sizeof(GUInt16) : 2 * sizeof(GUInt32);
     432         513 :     numVerticesTotal = 0;
     433             : 
     434        1032 :     for (int i = 0; i < numSections; i++)
     435             :     {
     436             :         /*-------------------------------------------------------------
     437             :          * Read the coord. section header blocks
     438             :          *------------------------------------------------------------*/
     439             : #ifdef TABDUMP
     440             :         int nHdrAddress = GetCurAddress();
     441             : #endif
     442         519 :         if (nVersion >= 450)
     443           8 :             pasHdrs[i].numVertices = ReadInt32();
     444             :         else
     445         511 :             pasHdrs[i].numVertices = ReadInt16();
     446             : 
     447         519 :         if (pasHdrs[i].numVertices < 0 ||
     448         519 :             pasHdrs[i].numVertices > INT_MAX / nVertexSize)
     449             :         {
     450           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     451             :                      "Invalid number of vertices for section %d", i);
     452           0 :             return -1;
     453             :         }
     454         519 :         if (nVersion >= 800)
     455           0 :             pasHdrs[i].numHoles = ReadInt32();
     456             :         else
     457         519 :             pasHdrs[i].numHoles = ReadInt16();
     458         519 :         if (pasHdrs[i].numHoles < 0)
     459             :         {
     460           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     461             :                      "Invalid number of holes for section %d", i);
     462           0 :             return -1;
     463             :         }
     464         519 :         ReadIntCoord(bCompressed, pasHdrs[i].nXMin, pasHdrs[i].nYMin);
     465         519 :         ReadIntCoord(bCompressed, pasHdrs[i].nXMax, pasHdrs[i].nYMax);
     466         519 :         pasHdrs[i].nDataOffset = ReadInt32();
     467         519 :         if (pasHdrs[i].nDataOffset < nTotalHdrSizeUncompressed)
     468             :         {
     469           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     470             :                      "Invalid data offset for section %d", i);
     471           0 :             return -1;
     472             :         }
     473             : 
     474         519 :         if (CPLGetLastErrorType() != 0)
     475           0 :             return -1;
     476             : 
     477         519 :         if (numVerticesTotal > INT_MAX / nVertexSize - pasHdrs[i].numVertices)
     478             :         {
     479           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     480             :                      "Invalid number of vertices for section %d", i);
     481           0 :             return -1;
     482             :         }
     483         519 :         numVerticesTotal += pasHdrs[i].numVertices;
     484             : 
     485         519 :         pasHdrs[i].nVertexOffset =
     486         519 :             (pasHdrs[i].nDataOffset - nTotalHdrSizeUncompressed) / 8;
     487             : #ifdef TABDUMP
     488             :         printf("READING pasHdrs[%d] @ %d = \n" /*ok*/
     489             :                "              { numVertices = %d, numHoles = %d, \n"
     490             :                "                nXMin=%d, nYMin=%d, nXMax=%d, nYMax=%d,\n"
     491             :                "                nDataOffset=%d, nVertexOffset=%d }\n",
     492             :                i, nHdrAddress, pasHdrs[i].numVertices, pasHdrs[i].numHoles,
     493             :                pasHdrs[i].nXMin, pasHdrs[i].nYMin, pasHdrs[i].nXMax,
     494             :                pasHdrs[i].nYMax, pasHdrs[i].nDataOffset,
     495             :                pasHdrs[i].nVertexOffset);
     496             :         printf("                dX = %d, dY = %d  (center = %d , %d)\n", /*ok*/
     497             :                pasHdrs[i].nXMax - pasHdrs[i].nXMin,
     498             :                pasHdrs[i].nYMax - pasHdrs[i].nYMin, m_nComprOrgX, m_nComprOrgY);
     499             : #endif
     500             :     }
     501             : 
     502        1032 :     for (int i = 0; i < numSections; i++)
     503             :     {
     504             :         /*-------------------------------------------------------------
     505             :          * Make sure all coordinates are grouped together
     506             :          * (Well... at least check that all the vertex indices are enclosed
     507             :          * inside the [0..numVerticesTotal] range.)
     508             :          *------------------------------------------------------------*/
     509         519 :         if (pasHdrs[i].nVertexOffset < 0 ||
     510         519 :             pasHdrs[i].nVertexOffset > INT_MAX - pasHdrs[i].numVertices ||
     511         519 :             (pasHdrs[i].nVertexOffset + pasHdrs[i].numVertices) >
     512         519 :                 numVerticesTotal)
     513             :         {
     514           0 :             CPLError(CE_Failure, CPLE_AssertionFailed,
     515             :                      "Unsupported case or corrupt file: MULTIPLINE/REGION "
     516             :                      "object vertices do not appear to be grouped together.");
     517           0 :             return -1;
     518             :         }
     519             :     }
     520             : 
     521         513 :     return 0;
     522             : }
     523             : 
     524             : /**********************************************************************
     525             :  *                   TABMAPObjectBlock::WriteCoordSecHdrs()
     526             :  *
     527             :  * Write a set of coordinate section headers for PLINE MULTIPLE or REGIONs.
     528             :  * pasHdrs should point to an array of numSections TABMAPCoordSecHdr
     529             :  * structures that have been properly initialized.
     530             :  *
     531             :  * In V450 the numVertices is stored on an int32 instead of an int16
     532             :  *
     533             :  * In V800 the numHoles is stored on an int32 instead of an int16
     534             :  *
     535             :  * At the end of the call, this TABMAPCoordBlock object will be ready to
     536             :  * receive the coordinate data.
     537             :  *
     538             :  * Returns 0 if successful or -1 if an error happened, in which case
     539             :  * CPLError() will have been called.
     540             :  **********************************************************************/
     541          96 : int TABMAPCoordBlock::WriteCoordSecHdrs(int nVersion, int numSections,
     542             :                                         TABMAPCoordSecHdr *pasHdrs,
     543             :                                         GBool bCompressed /*=FALSE*/)
     544             : {
     545          96 :     CPLErrorReset();
     546             : 
     547         194 :     for (int i = 0; i < numSections; i++)
     548             :     {
     549             :         /*-------------------------------------------------------------
     550             :          * Write the coord. section header blocks
     551             :          *------------------------------------------------------------*/
     552             : #ifdef TABDUMP
     553             :         printf("WRITING pasHdrs[%d] @ %d = \n" /*ok*/
     554             :                "              { numVertices = %d, numHoles = %d, \n"
     555             :                "                nXMin=%d, nYMin=%d, nXMax=%d, nYMax=%d,\n"
     556             :                "                nDataOffset=%d, nVertexOffset=%d }\n",
     557             :                i, GetCurAddress(), pasHdrs[i].numVertices, pasHdrs[i].numHoles,
     558             :                pasHdrs[i].nXMin, pasHdrs[i].nYMin, pasHdrs[i].nXMax,
     559             :                pasHdrs[i].nYMax, pasHdrs[i].nDataOffset,
     560             :                pasHdrs[i].nVertexOffset);
     561             :         printf("                dX = %d, dY = %d  (center = %d , %d)\n", /*ok*/
     562             :                pasHdrs[i].nXMax - pasHdrs[i].nXMin,
     563             :                pasHdrs[i].nYMax - pasHdrs[i].nYMin, m_nComprOrgX, m_nComprOrgY);
     564             : #endif
     565             : 
     566          98 :         if (nVersion >= 450)
     567           0 :             WriteInt32(pasHdrs[i].numVertices);
     568             :         else
     569          98 :             WriteInt16(static_cast<GInt16>(pasHdrs[i].numVertices));
     570          98 :         if (nVersion >= 800)
     571           0 :             WriteInt32(pasHdrs[i].numHoles);
     572             :         else
     573          98 :             WriteInt16(static_cast<GInt16>(pasHdrs[i].numHoles));
     574          98 :         WriteIntCoord(pasHdrs[i].nXMin, pasHdrs[i].nYMin, bCompressed);
     575          98 :         WriteIntCoord(pasHdrs[i].nXMax, pasHdrs[i].nYMax, bCompressed);
     576          98 :         WriteInt32(pasHdrs[i].nDataOffset);
     577             : 
     578          98 :         if (CPLGetLastErrorType() == CE_Failure)
     579           0 :             return -1;
     580             :     }
     581             : 
     582          96 :     return 0;
     583             : }
     584             : 
     585             : /**********************************************************************
     586             :  *                   TABMAPCoordBlock::WriteIntCoord()
     587             :  *
     588             :  * Write a pair of integer coordinates values to the current position in the
     589             :  * the block.
     590             :  *
     591             :  * Returns 0 if successful or -1 if an error happened, in which case
     592             :  * CPLError() will have been called.
     593             :  **********************************************************************/
     594             : 
     595        7349 : int TABMAPCoordBlock::WriteIntCoord(GInt32 nX, GInt32 nY,
     596             :                                     GBool bCompressed /*=FALSE*/)
     597             : {
     598             : 
     599       11103 :     if ((!bCompressed && (WriteInt32(nX) != 0 || WriteInt32(nY) != 0)) ||
     600        3754 :         (bCompressed && (WriteInt16(TABInt16Diff(nX, m_nComprOrgX)) != 0 ||
     601        3754 :                          WriteInt16(TABInt16Diff(nY, m_nComprOrgY)) != 0)))
     602             :     {
     603           0 :         return -1;
     604             :     }
     605             : 
     606             :     /*-----------------------------------------------------------------
     607             :      * Update block MBR
     608             :      *----------------------------------------------------------------*/
     609             :     //__TODO__ Do we still need to track the block MBR???
     610        7349 :     if (nX < m_nMinX)
     611         607 :         m_nMinX = nX;
     612        7349 :     if (nX > m_nMaxX)
     613        3874 :         m_nMaxX = nX;
     614             : 
     615        7349 :     if (nY < m_nMinY)
     616         660 :         m_nMinY = nY;
     617        7349 :     if (nY > m_nMaxY)
     618        3549 :         m_nMaxY = nY;
     619             : 
     620             :     /*-------------------------------------------------------------
     621             :      * Also keep track of current feature MBR.
     622             :      *------------------------------------------------------------*/
     623        7349 :     if (nX < m_nFeatureXMin)
     624         677 :         m_nFeatureXMin = nX;
     625        7349 :     if (nX > m_nFeatureXMax)
     626        4177 :         m_nFeatureXMax = nX;
     627             : 
     628        7349 :     if (nY < m_nFeatureYMin)
     629         585 :         m_nFeatureYMin = nY;
     630        7349 :     if (nY > m_nFeatureYMax)
     631        2681 :         m_nFeatureYMax = nY;
     632             : 
     633        7349 :     return 0;
     634             : }
     635             : 
     636             : /**********************************************************************
     637             :  *                   TABMAPCoordBlock::SetMAPBlockManagerRef()
     638             :  *
     639             :  * Pass a reference to the block manager object for the file this
     640             :  * block belongs to.  The block manager will be used by this object
     641             :  * when it needs to automatically allocate a new block.
     642             :  **********************************************************************/
     643         234 : void TABMAPCoordBlock::SetMAPBlockManagerRef(TABBinBlockManager *poBlockMgr)
     644             : {
     645         234 :     m_poBlockManagerRef = poBlockMgr;
     646         234 : }
     647             : 
     648             : /**********************************************************************
     649             :  *                   TABMAPCoordBlock::ReadBytes()
     650             :  *
     651             :  * Cover function for TABRawBinBlock::ReadBytes() that will automagically
     652             :  * load the next coordinate block in the chain before reading the
     653             :  * requested bytes if we are at the end of the current block and if
     654             :  * m_nNextCoordBlock is a valid block.
     655             :  *
     656             :  * Then the control is passed to TABRawBinBlock::ReadBytes() to finish the
     657             :  * work:
     658             :  * Copy the number of bytes from the data block's internal buffer to
     659             :  * the user's buffer pointed by pabyDstBuf.
     660             :  *
     661             :  * Passing pabyDstBuf = NULL will only move the read pointer by the
     662             :  * specified number of bytes as if the copy had happened... but it
     663             :  * won't crash.
     664             :  *
     665             :  * Returns 0 if successful or -1 if an error happened, in which case
     666             :  * CPLError() will have been called.
     667             :  **********************************************************************/
     668       50967 : int TABMAPCoordBlock::ReadBytes(int numBytes, GByte *pabyDstBuf)
     669             : {
     670             : 
     671       50967 :     if (m_pabyBuf && m_nCurPos >= (m_numDataBytes + MAP_COORD_HEADER_SIZE) &&
     672         316 :         m_nNextCoordBlock > 0)
     673             :     {
     674             :         // We're at end of current block... advance to next block.
     675         191 :         int nStatus = GotoByteInFile(m_nNextCoordBlock, TRUE);
     676             : 
     677         191 :         if (nStatus != 0)
     678             :         {
     679             :             // Failed.... an error has already been reported.
     680           0 :             return nStatus;
     681             :         }
     682             : 
     683         191 :         GotoByteInBlock(MAP_COORD_HEADER_SIZE);  // Move pointer past header
     684         191 :         m_numBlocksInChain++;
     685             :     }
     686             : 
     687       50967 :     if (m_pabyBuf && m_nCurPos < (m_numDataBytes + MAP_COORD_HEADER_SIZE) &&
     688       50842 :         m_nCurPos + numBytes > (m_numDataBytes + MAP_COORD_HEADER_SIZE) &&
     689           0 :         m_nNextCoordBlock > 0)
     690             :     {
     691             :         // Data overlaps on more than one block
     692             :         // Read until end of this block and then recursively call ReadBytes()
     693             :         // for the rest.
     694           0 :         int numBytesInThisBlock =
     695           0 :             (m_numDataBytes + MAP_COORD_HEADER_SIZE) - m_nCurPos;
     696             :         int nStatus =
     697           0 :             TABRawBinBlock::ReadBytes(numBytesInThisBlock, pabyDstBuf);
     698           0 :         if (nStatus == 0)
     699             :             nStatus =
     700           0 :                 TABMAPCoordBlock::ReadBytes(numBytes - numBytesInThisBlock,
     701           0 :                                             pabyDstBuf + numBytesInThisBlock);
     702           0 :         return nStatus;
     703             :     }
     704             : 
     705       50967 :     return TABRawBinBlock::ReadBytes(numBytes, pabyDstBuf);
     706             : }
     707             : 
     708             : /**********************************************************************
     709             :  *                   TABMAPCoordBlock::WriteBytes()
     710             :  *
     711             :  * Cover function for TABRawBinBlock::WriteBytes() that will automagically
     712             :  * CommitToFile() the current block and create a new one if we are at
     713             :  * the end of the current block.
     714             :  *
     715             :  * Then the control is passed to TABRawBinBlock::WriteBytes() to finish the
     716             :  * work.
     717             :  *
     718             :  * Passing pabySrcBuf = NULL will only move the write pointer by the
     719             :  * specified number of bytes as if the copy had happened... but it
     720             :  * won't crash.
     721             :  *
     722             :  * Returns 0 if successful or -1 if an error happened, in which case
     723             :  * CPLError() will have been called.
     724             :  **********************************************************************/
     725       16228 : int TABMAPCoordBlock::WriteBytes(int nBytesToWrite, const GByte *pabySrcBuf)
     726             : {
     727       16228 :     if (m_eAccess != TABWrite && m_eAccess != TABReadWrite)
     728             :     {
     729           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     730             :                  "WriteBytes(): Block does not support write operations.");
     731           0 :         return -1;
     732             :     }
     733             : 
     734       16228 :     if (m_poBlockManagerRef && (m_nBlockSize - m_nCurPos) < nBytesToWrite)
     735             :     {
     736          68 :         if (nBytesToWrite <= (m_nBlockSize - MAP_COORD_HEADER_SIZE))
     737             :         {
     738             :             // Data won't fit in this block but can fit inside a single
     739             :             // block, so we'll allocate a new block for it.  This will
     740             :             // prevent us from overlapping coordinate values on 2 blocks, but
     741             :             // still allows strings longer than one block (see 'else' below).
     742             :             //
     743             : 
     744          68 :             if (m_nNextCoordBlock != 0)
     745             :             {
     746             :                 // We're in read/write mode and there is already an allocated
     747             :                 // block following this one in the chain ... just reload it
     748             :                 // and continue writing to it
     749             : 
     750           0 :                 CPLAssert(m_eAccess == TABReadWrite);
     751             : 
     752           0 :                 if (CommitToFile() != 0 ||
     753           0 :                     ReadFromFile(m_fp, m_nNextCoordBlock, m_nBlockSize) != 0)
     754             :                 {
     755             :                     // An error message should have already been reported.
     756           0 :                     return -1;
     757             :                 }
     758             :             }
     759             :             else
     760             :             {
     761             :                 // Need to alloc a new block.
     762             : 
     763             :                 int nNewBlockOffset =
     764          68 :                     m_poBlockManagerRef->AllocNewBlock("COORD");
     765          68 :                 SetNextCoordBlock(nNewBlockOffset);
     766             : 
     767         136 :                 if (CommitToFile() != 0 ||
     768          68 :                     InitNewBlock(m_fp, m_nBlockSize, nNewBlockOffset) != 0)
     769             :                 {
     770             :                     // An error message should have already been reported.
     771           0 :                     return -1;
     772             :                 }
     773             : 
     774          68 :                 m_numBlocksInChain++;
     775             :             }
     776             :         }
     777             :         else
     778             :         {
     779             :             // Data to write is longer than one block... so we'll have to
     780             :             // split it over multiple block through multiple calls.
     781             :             //
     782           0 :             int nStatus = 0;
     783           0 :             while (nStatus == 0 && nBytesToWrite > 0)
     784             :             {
     785           0 :                 int nBytes = m_nBlockSize - MAP_COORD_HEADER_SIZE;
     786           0 :                 if ((m_nBlockSize - m_nCurPos) > 0)
     787             :                 {
     788             :                     // Use free room in current block
     789           0 :                     nBytes = (m_nBlockSize - m_nCurPos);
     790             :                 }
     791             : 
     792           0 :                 nBytes = std::min(nBytes, nBytesToWrite);
     793             : 
     794             :                 // The following call will result in a new block being
     795             :                 // allocated in the if() block above.
     796           0 :                 nStatus = TABMAPCoordBlock::WriteBytes(nBytes, pabySrcBuf);
     797             : 
     798           0 :                 nBytesToWrite -= nBytes;
     799           0 :                 pabySrcBuf += nBytes;
     800             :             }
     801           0 :             return nStatus;
     802             :         }
     803             :     }
     804             : 
     805       16228 :     if (m_nCurPos >= MAP_COORD_HEADER_SIZE)
     806             :     {
     807             :         // Keep track of Coordinate data... this means ignore header bytes
     808             :         // that could be written.
     809       14995 :         m_nTotalDataSize += nBytesToWrite;
     810       14995 :         m_nFeatureDataSize += nBytesToWrite;
     811             :     }
     812             : 
     813       16228 :     return TABRawBinBlock::WriteBytes(nBytesToWrite, pabySrcBuf);
     814             : }
     815             : 
     816             : /**********************************************************************
     817             :  *                   TABMAPObjectBlock::SeekEnd()
     818             :  *
     819             :  * Move read/write pointer to end of used part of the block
     820             :  **********************************************************************/
     821         433 : void TABMAPCoordBlock::SeekEnd()
     822             : {
     823         433 :     m_nCurPos = m_nSizeUsed;
     824         433 : }
     825             : 
     826             : /**********************************************************************
     827             :  *                   TABMAPCoordBlock::StartNewFeature()
     828             :  *
     829             :  * Reset all member vars that are used to keep track of data size
     830             :  * and MBR for the current feature.  This is info is not needed by
     831             :  * the coord blocks themselves, but it helps a lot the callers to
     832             :  * have this class take care of that for them.
     833             :  *
     834             :  * See Also: GetFeatureDataSize() and GetFeatureMBR()
     835             :  **********************************************************************/
     836         433 : void TABMAPCoordBlock::StartNewFeature()
     837             : {
     838         433 :     m_nFeatureDataSize = 0;
     839             : 
     840         433 :     m_nFeatureXMin = 1000000000;
     841         433 :     m_nFeatureYMin = 1000000000;
     842         433 :     m_nFeatureXMax = -1000000000;
     843         433 :     m_nFeatureYMax = -1000000000;
     844         433 : }
     845             : 
     846             : /**********************************************************************
     847             :  *                   TABMAPCoordBlock::GetFeatureMBR()
     848             :  *
     849             :  * Return the MBR of all the coords written using WriteIntCoord() since
     850             :  * the last call to StartNewFeature().
     851             :  **********************************************************************/
     852           0 : void TABMAPCoordBlock::GetFeatureMBR(GInt32 &nXMin, GInt32 &nYMin,
     853             :                                      GInt32 &nXMax, GInt32 &nYMax)
     854             : {
     855           0 :     nXMin = m_nFeatureXMin;
     856           0 :     nYMin = m_nFeatureYMin;
     857           0 :     nXMax = m_nFeatureXMax;
     858           0 :     nYMax = m_nFeatureYMax;
     859           0 : }
     860             : 
     861             : /**********************************************************************
     862             :  *                   TABMAPCoordBlock::Dump()
     863             :  *
     864             :  * Dump block contents... available only in DEBUG mode.
     865             :  **********************************************************************/
     866             : #ifdef DEBUG
     867             : 
     868           0 : void TABMAPCoordBlock::Dump(FILE *fpOut /*=NULL*/)
     869             : {
     870           0 :     if (fpOut == nullptr)
     871           0 :         fpOut = stdout;
     872             : 
     873           0 :     fprintf(fpOut, "----- TABMAPCoordBlock::Dump() -----\n");
     874           0 :     if (m_pabyBuf == nullptr)
     875             :     {
     876           0 :         fprintf(fpOut, "Block has not been initialized yet.");
     877             :     }
     878             :     else
     879             :     {
     880           0 :         fprintf(fpOut, "Coordinate Block (type %d) at offset %d.\n",
     881             :                 m_nBlockType, m_nFileOffset);
     882           0 :         fprintf(fpOut, "  m_numDataBytes        = %d\n", m_numDataBytes);
     883           0 :         fprintf(fpOut, "  m_nNextCoordBlock     = %d\n", m_nNextCoordBlock);
     884             :     }
     885             : 
     886           0 :     fflush(fpOut);
     887           0 : }
     888             : 
     889             : #endif  // DEBUG

Generated by: LCOV version 1.14