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

Generated by: LCOV version 1.14