LCOV - code coverage report
Current view: top level - frmts/pcidsk/sdk/blockdir - asciitiledir.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 313 385 81.3 %
Date: 2024-11-21 22:18:42 Functions: 19 21 90.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Purpose:  Block directory API.
       4             :  *
       5             :  ******************************************************************************
       6             :  * Copyright (c) 2011
       7             :  * PCI Geomatics, 90 Allstate Parkway, Markham, Ontario, Canada.
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "blockdir/asciitiledir.h"
      13             : #include "blockdir/asciitilelayer.h"
      14             : #include "blockdir/blockfile.h"
      15             : #include "core/pcidsk_utils.h"
      16             : #include "core/pcidsk_scanint.h"
      17             : #include "pcidsk_exception.h"
      18             : #include "pcidsk_buffer.h"
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : #include <cstdio>
      22             : #include <algorithm>
      23             : #include <limits>
      24             : #include <set>
      25             : 
      26             : using namespace PCIDSK;
      27             : 
      28             : #define ASCII_TILEDIR_VERSION 1
      29             : 
      30             : #define SYS_BLOCK_SIZE                  8192
      31             : #define SYS_BLOCK_INFO_SIZE             28
      32             : #define SYS_BLOCK_LAYER_INFO_SIZE       24
      33             : 
      34             : struct SysBlockInfo
      35             : {
      36             :     uint16 nSegment;
      37             :     uint32 nStartBlock;
      38             :     uint32 nNextBlock;
      39             : };
      40             : 
      41             : typedef std::vector<SysBlockInfo> SysBlockInfoList;
      42             : 
      43             : /************************************************************************/
      44             : /*                              GetBlockList()                          */
      45             : /************************************************************************/
      46          10 : static BlockInfoList GetBlockList(const SysBlockInfoList & oBlockInfoList,
      47             :                                   uint32 iStartBlock)
      48             : {
      49          10 :     uint32 iBlock = iStartBlock;
      50             : 
      51          10 :     BlockInfoList oBlockList;
      52          10 :     oBlockList.reserve(oBlockInfoList.size());
      53             : 
      54         170 :     while (iBlock < oBlockInfoList.size() &&
      55          80 :            oBlockList.size() <= oBlockInfoList.size())
      56             :     {
      57          80 :         const SysBlockInfo * psBlockInfo = &oBlockInfoList[iBlock];
      58             : 
      59             :         BlockInfo sBlock;
      60          80 :         sBlock.nSegment = psBlockInfo->nSegment;
      61          80 :         sBlock.nStartBlock = psBlockInfo->nStartBlock;
      62             : 
      63          80 :         oBlockList.push_back(sBlock);
      64             : 
      65          80 :         iBlock = psBlockInfo->nNextBlock;
      66             :     }
      67             : 
      68             :     // If the block list is larger than the block info list, it means that the
      69             :     // file is corrupted so look for a loop in the block list.
      70          10 :     if (oBlockList.size() > oBlockInfoList.size())
      71             :     {
      72           0 :         iBlock = iStartBlock;
      73             : 
      74           0 :         std::set<uint32> oBlockSet;
      75             : 
      76           0 :         oBlockList.clear();
      77             : 
      78           0 :         while (iBlock < oBlockInfoList.size())
      79             :         {
      80           0 :             const SysBlockInfo * psBlockInfo = &oBlockInfoList[iBlock];
      81             : 
      82             :             BlockInfo sBlock;
      83           0 :             sBlock.nSegment = psBlockInfo->nSegment;
      84           0 :             sBlock.nStartBlock = psBlockInfo->nStartBlock;
      85             : 
      86           0 :             oBlockList.push_back(sBlock);
      87             : 
      88           0 :             oBlockSet.insert(iBlock);
      89             : 
      90           0 :             iBlock = psBlockInfo->nNextBlock;
      91             : 
      92           0 :             if (oBlockSet.find(iBlock) != oBlockSet.end())
      93           0 :                 break;
      94             :         }
      95             :     }
      96             : 
      97          20 :     return oBlockList;
      98             : }
      99             : 
     100             : /************************************************************************/
     101             : /*                          GetOptimizedDirSize()                       */
     102             : /************************************************************************/
     103           2 : size_t AsciiTileDir::GetOptimizedDirSize(BlockFile * poFile)
     104             : {
     105           2 :     std::string oFileOptions = poFile->GetFileOptions();
     106             : 
     107          34 :     for (char & chIter : oFileOptions)
     108          32 :         chIter = (char) toupper((uchar) chIter);
     109             : 
     110             :     // Compute the ratio.
     111           2 :     double dfRatio = 0.0;
     112             : 
     113             :     // The 35% is for the overviews.
     114           2 :     if (oFileOptions.find("TILED") != std::string::npos)
     115           2 :         dfRatio = 1.35;
     116             :     else
     117           0 :         dfRatio = 0.35;
     118             : 
     119             :     // The 5% is for the new blocks.
     120           2 :     dfRatio += 0.05;
     121             : 
     122           2 :     double dfFileSize = poFile->GetImageFileSize() * dfRatio;
     123             : 
     124           2 :     uint32 nBlockSize = SYS_BLOCK_SIZE;
     125             : 
     126           2 :     uint64 nBlockCount = (uint64) (dfFileSize / nBlockSize);
     127             : 
     128           2 :     uint64 nLayerCount = poFile->GetChannels();
     129             : 
     130             :     // The 12 is for the overviews.
     131           2 :     nLayerCount *= 12;
     132             : 
     133           2 :     uint64 nDirSize = 512 +
     134           2 :         (nBlockCount * SYS_BLOCK_INFO_SIZE +
     135           2 :          nLayerCount * SYS_BLOCK_LAYER_INFO_SIZE +
     136           2 :          nLayerCount * sizeof(TileLayerInfo));
     137             : 
     138             : #if SIZEOF_VOIDP < 8
     139             :     if (nDirSize > std::numeric_limits<size_t>::max())
     140             :         return ThrowPCIDSKException(0, "Unable to create extremely large file on 32-bit system.");
     141             : #endif
     142             : 
     143           4 :     return static_cast<size_t>(nDirSize);
     144             : }
     145             : 
     146             : /************************************************************************/
     147             : /*                              AsciiTileDir()                          */
     148             : /************************************************************************/
     149             : 
     150             : /**
     151             :  * Constructor.
     152             :  *
     153             :  * @param poFile The associated file object.
     154             :  * @param nSegment The segment of the block directory.
     155             :  */
     156          11 : AsciiTileDir::AsciiTileDir(BlockFile * poFile, uint16 nSegment)
     157          11 :     : BlockTileDir(poFile, nSegment)
     158             : {
     159             :     // Read the block directory header from disk.
     160             :     uint8 abyHeader[512];
     161             : 
     162          11 :     mpoFile->ReadFromSegment(mnSegment, abyHeader, 0, 512);
     163             : 
     164             :     // Get the version of the block directory.
     165          11 :     mnVersion = ScanInt3(abyHeader + 7);
     166             : 
     167             :     // Read the block directory info from the header.
     168          11 :     msBlockDir.nLayerCount     = ScanInt8(abyHeader + 10);
     169          11 :     msBlockDir.nBlockCount     = ScanInt8(abyHeader + 18);
     170          11 :     msBlockDir.nFirstFreeBlock = ScanInt8(abyHeader + 26);
     171             : 
     172             :     // The third last byte is for the endianness.
     173          11 :     mchEndianness = abyHeader[512 - 3];
     174          11 :     mbNeedsSwap = (mchEndianness == 'B' ?
     175          11 :                    !BigEndianSystem() : BigEndianSystem());
     176             : 
     177             :     // The last 2 bytes of the header are for the validity info.
     178          11 :     memcpy(&mnValidInfo, abyHeader + 512 - 2, 2);
     179             : 
     180          11 :     SwapValue(&mnValidInfo);
     181             : 
     182             :     // Check that we support the tile directory version.
     183          11 :     if (mnVersion > ASCII_TILEDIR_VERSION)
     184             :     {
     185           0 :         ThrowPCIDSKException("The tile directory version %d is not supported.", mnVersion);
     186           0 :         return;
     187             :     }
     188             : 
     189             :     // The size of the block layers.
     190          11 :     uint64 nReadSize = (static_cast<uint64>(msBlockDir.nBlockCount) * SYS_BLOCK_INFO_SIZE +
     191          11 :                         static_cast<uint64>(msBlockDir.nLayerCount) * SYS_BLOCK_LAYER_INFO_SIZE);
     192             : 
     193          11 :     if (mpoFile->IsCorruptedSegment(mnSegment, 512, nReadSize))
     194             :     {
     195           0 :         ThrowPCIDSKException("The tile directory is corrupted.");
     196           0 :         return;
     197             :     }
     198             : 
     199             : #if SIZEOF_VOIDP < 8
     200             :     if (nReadSize > std::numeric_limits<size_t>::max())
     201             :     {
     202             :         ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
     203             :         return;
     204             :     }
     205             : #endif
     206             : 
     207             :     // Initialize the block layers.
     208             :     try
     209             :     {
     210          11 :         moLayerInfoList.resize(msBlockDir.nLayerCount);
     211          11 :         moTileLayerInfoList.resize(msBlockDir.nLayerCount);
     212             : 
     213          11 :         moLayerList.resize(msBlockDir.nLayerCount);
     214             :     }
     215           0 :     catch (const std::exception & ex)
     216             :     {
     217           0 :         ThrowPCIDSKException("Out of memory in AsciiTileDir(): %s", ex.what());
     218           0 :         return;
     219             :     }
     220             : 
     221          24 :     for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
     222             :     {
     223          13 :         moLayerInfoList[iLayer] = new BlockLayerInfo;
     224          13 :         moTileLayerInfoList[iLayer] = new TileLayerInfo;
     225             : 
     226          13 :         moLayerList[iLayer] = new AsciiTileLayer(this, iLayer,
     227          13 :                                                  moLayerInfoList[iLayer],
     228          13 :                                                  moTileLayerInfoList[iLayer]);
     229             :     }
     230             : 
     231             :     // Read the block directory from disk.
     232          11 :     if (memcmp(abyHeader + 128, "SUBVERSION 1", 12) != 0)
     233             :     {
     234           5 :         ReadFullDir();
     235             : 
     236          10 :         for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
     237           5 :             GetTileLayer(iLayer)->ReadHeader();
     238             :     }
     239             :     else
     240             :     {
     241           6 :         ReadPartialDir();
     242             :     }
     243             : 
     244             :     // Check if any of the tile layers are corrupted.
     245          24 :     for (BlockLayer * poLayer : moLayerList)
     246             :     {
     247          13 :         BlockTileLayer * poTileLayer = dynamic_cast<BlockTileLayer *>(poLayer);
     248             : 
     249          13 :         if (poTileLayer == nullptr || poTileLayer->IsCorrupted())
     250             :         {
     251           0 :             ThrowPCIDSKException("The tile directory is corrupted.");
     252           0 :             return;
     253             :         }
     254             :     }
     255             : }
     256             : 
     257             : /************************************************************************/
     258             : /*                              AsciiTileDir()                          */
     259             : /************************************************************************/
     260             : 
     261             : /**
     262             :  * Constructor.
     263             :  *
     264             :  * @param poFile The associated file object.
     265             :  * @param nSegment The segment of the block directory.
     266             :  * @param nBlockSize The size of the blocks.
     267             :  */
     268           2 : AsciiTileDir::AsciiTileDir(BlockFile * poFile, uint16 nSegment,
     269           2 :                            CPL_UNUSED uint32 nBlockSize)
     270           2 :     : BlockTileDir(poFile, nSegment, ASCII_TILEDIR_VERSION)
     271             : {
     272             :     // Initialize the directory info.
     273           2 :     msBlockDir.nLayerCount = 0;
     274           2 :     msBlockDir.nBlockCount = 0;
     275           2 :     msBlockDir.nFirstFreeBlock = 0;
     276             : 
     277             :     // Create an empty free block layer.
     278           2 :     msFreeBlockLayer.nLayerType = BLTFree;
     279           2 :     msFreeBlockLayer.nStartBlock = INVALID_BLOCK;
     280           2 :     msFreeBlockLayer.nBlockCount = 0;
     281           2 :     msFreeBlockLayer.nLayerSize = 0;
     282             : 
     283           2 :     mpoFreeBlockLayer = new AsciiTileLayer(this, INVALID_LAYER,
     284           2 :                                            &msFreeBlockLayer, nullptr);
     285           2 : }
     286             : 
     287             : /************************************************************************/
     288             : /*                              GetTileLayer()                          */
     289             : /************************************************************************/
     290             : 
     291             : /**
     292             :  * Gets the block layer at the specified index.
     293             :  *
     294             :  * @param iLayer The index of the block layer.
     295             :  *
     296             :  * @return The block layer at the specified index.
     297             :  */
     298          12 : AsciiTileLayer * AsciiTileDir::GetTileLayer(uint32 iLayer)
     299             : {
     300          12 :     return (AsciiTileLayer *) BlockDir::GetLayer(iLayer);
     301             : }
     302             : 
     303             : /************************************************************************/
     304             : /*                              GetBlockSize()                          */
     305             : /************************************************************************/
     306             : 
     307             : /**
     308             :  * Gets the block size of the block directory.
     309             :  *
     310             :  * @return The block size of the block directory.
     311             :  */
     312          99 : uint32 AsciiTileDir::GetBlockSize(void) const
     313             : {
     314          99 :     return SYS_BLOCK_SIZE;
     315             : }
     316             : 
     317             : /************************************************************************/
     318             : /*                              ReadFullDir()                           */
     319             : /************************************************************************/
     320           5 : void AsciiTileDir::ReadFullDir(void)
     321             : {
     322             :     // The size of the block layers.
     323           5 :     uint64 nReadSize = (static_cast<uint64>(msBlockDir.nBlockCount) * SYS_BLOCK_INFO_SIZE +
     324           5 :                         static_cast<uint64>(msBlockDir.nLayerCount) * SYS_BLOCK_LAYER_INFO_SIZE);
     325             : 
     326           5 :     if (mpoFile->IsCorruptedSegment(mnSegment, 512, nReadSize))
     327           0 :         return ThrowPCIDSKException("The tile directory is corrupted.");
     328             : 
     329             : #if SIZEOF_VOIDP < 8
     330             :     if (nReadSize > std::numeric_limits<size_t>::max())
     331             :         return ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
     332             : #endif
     333             : 
     334             :     // Read the block layers from disk.
     335           5 :     uint8 * pabyBlockDir = (uint8 *) malloc(static_cast<size_t>(nReadSize));
     336             : 
     337           5 :     if (pabyBlockDir == nullptr)
     338           0 :         return ThrowPCIDSKException("Out of memory in AsciiTileDir::ReadFullDir().");
     339             : 
     340          10 :     PCIDSKBuffer oBlockDirAutoPtr;
     341           5 :     oBlockDirAutoPtr.buffer = (char *) pabyBlockDir;
     342             : 
     343           5 :     uint8 * pabyBlockDirIter = pabyBlockDir;
     344             : 
     345           5 :     mpoFile->ReadFromSegment(mnSegment, pabyBlockDir, 512, nReadSize);
     346             : 
     347             :     // Read the block list.
     348           5 :     SysBlockInfoList oBlockInfoList(msBlockDir.nBlockCount);
     349             : 
     350          85 :     for (uint32 iBlock = 0; iBlock < msBlockDir.nBlockCount; iBlock++)
     351             :     {
     352          80 :         SysBlockInfo * psBlock = &oBlockInfoList[iBlock];
     353             : 
     354          80 :         psBlock->nSegment    = ScanInt4(pabyBlockDirIter);
     355          80 :         pabyBlockDirIter += 4;
     356             : 
     357          80 :         psBlock->nStartBlock = ScanInt8(pabyBlockDirIter);
     358          80 :         pabyBlockDirIter += 8;
     359             : 
     360             :         //psBlock->nLayer      = ScanInt8(pabyBlockDirIter);
     361          80 :         pabyBlockDirIter += 8;
     362             : 
     363          80 :         psBlock->nNextBlock  = ScanInt8(pabyBlockDirIter);
     364          80 :         pabyBlockDirIter += 8;
     365             :     }
     366             : 
     367             :     // Read the block layers.
     368          10 :     for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
     369             :     {
     370           5 :         BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
     371             : 
     372           5 :         psLayer->nLayerType  = ScanInt4(pabyBlockDirIter);
     373           5 :         pabyBlockDirIter += 4;
     374             : 
     375           5 :         psLayer->nStartBlock = ScanInt8(pabyBlockDirIter);
     376           5 :         pabyBlockDirIter += 8;
     377             : 
     378           5 :         psLayer->nLayerSize  = ScanInt12(pabyBlockDirIter);
     379           5 :         pabyBlockDirIter += 12;
     380             :     }
     381             : 
     382             :     // Create all the block layers.
     383          10 :     for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
     384             :     {
     385           5 :         BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
     386             : 
     387           5 :         AsciiTileLayer * poLayer = GetTileLayer((uint32) iLayer);
     388             : 
     389             :         poLayer->moBlockList =
     390           5 :             GetBlockList(oBlockInfoList, psLayer->nStartBlock);
     391             : 
     392             :         // We need to validate the block count field.
     393           5 :         psLayer->nBlockCount = (uint32) poLayer->moBlockList.size();
     394             :     }
     395             : 
     396             :     // Create the free block layer.
     397           5 :     msFreeBlockLayer.nLayerType = BLTFree;
     398           5 :     msFreeBlockLayer.nStartBlock = msBlockDir.nFirstFreeBlock;
     399           5 :     msFreeBlockLayer.nBlockCount = 0;
     400           5 :     msFreeBlockLayer.nLayerSize = 0;
     401             : 
     402           5 :     mpoFreeBlockLayer = new AsciiTileLayer(this, INVALID_LAYER,
     403           5 :                                            &msFreeBlockLayer, nullptr);
     404             : 
     405           5 :     ((AsciiTileLayer *) mpoFreeBlockLayer)->moBlockList =
     406           5 :         GetBlockList(oBlockInfoList, msFreeBlockLayer.nStartBlock);
     407             : 
     408             :     // We need to validate the block count field.
     409           5 :     msFreeBlockLayer.nBlockCount = (uint32)
     410           5 :         ((AsciiTileLayer *) mpoFreeBlockLayer)->moBlockList.size();
     411             : }
     412             : 
     413             : /************************************************************************/
     414             : /*                             ReadPartialDir()                         */
     415             : /************************************************************************/
     416           6 : void AsciiTileDir::ReadPartialDir(void)
     417             : {
     418             :     // The offset of the block layers.
     419           6 :     uint64 nOffset = static_cast<uint64>(msBlockDir.nBlockCount) * SYS_BLOCK_INFO_SIZE;
     420             : 
     421             :     // The size of the block layers.
     422           6 :     uint64 nReadSize = (static_cast<uint64>(msBlockDir.nLayerCount) * SYS_BLOCK_LAYER_INFO_SIZE +
     423             :                         static_cast<uint64>(msBlockDir.nLayerCount) * sizeof(TileLayerInfo));
     424             : 
     425           6 :     if (mpoFile->IsCorruptedSegment(mnSegment, 512 + nOffset, nReadSize))
     426           0 :         return ThrowPCIDSKException("The tile directory is corrupted.");
     427             : 
     428             : #if SIZEOF_VOIDP < 8
     429             :     if (nReadSize > std::numeric_limits<size_t>::max())
     430             :         return ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
     431             : #endif
     432             : 
     433             :     // Read the block layers from disk.
     434           6 :     uint8 * pabyBlockDir = (uint8 *) malloc(static_cast<size_t>(nReadSize));
     435             : 
     436           6 :     if (pabyBlockDir == nullptr)
     437           0 :         return ThrowPCIDSKException("Out of memory in AsciiTileDir::ReadPartialDir().");
     438             : 
     439           6 :     PCIDSKBuffer oBlockDirAutoPtr;
     440           6 :     oBlockDirAutoPtr.buffer = (char *) pabyBlockDir;
     441             : 
     442           6 :     uint8 * pabyBlockDirIter = pabyBlockDir;
     443             : 
     444           6 :     mpoFile->ReadFromSegment(mnSegment, pabyBlockDir, 512 + nOffset, nReadSize);
     445             : 
     446             :     // Read the block layers.
     447           6 :     BlockLayerInfo * psPreviousLayer = nullptr;
     448             : 
     449          14 :     for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
     450             :     {
     451           8 :         BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
     452             : 
     453           8 :         psLayer->nLayerType  = ScanInt4(pabyBlockDirIter);
     454           8 :         pabyBlockDirIter += 4;
     455             : 
     456           8 :         psLayer->nStartBlock = ScanInt8(pabyBlockDirIter);
     457           8 :         pabyBlockDirIter += 8;
     458             : 
     459           8 :         psLayer->nLayerSize  = ScanInt12(pabyBlockDirIter);
     460           8 :         pabyBlockDirIter += 12;
     461             : 
     462           8 :         if (psLayer->nStartBlock != INVALID_BLOCK)
     463             :         {
     464           8 :             if (psPreviousLayer)
     465             :             {
     466           2 :                 if (psLayer->nStartBlock < psPreviousLayer->nStartBlock)
     467           0 :                     return ThrowPCIDSKException("The tile directory is corrupted.");
     468             : 
     469           2 :                 psPreviousLayer->nBlockCount =
     470           2 :                     psLayer->nStartBlock - psPreviousLayer->nStartBlock;
     471             :             }
     472             : 
     473           8 :             psPreviousLayer = psLayer;
     474             :         }
     475             :         else
     476             :         {
     477           0 :             psLayer->nBlockCount = 0;
     478             :         }
     479             :     }
     480             : 
     481             :     // Read the tile layers.
     482          14 :     for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
     483             :     {
     484           8 :         size_t nSize = sizeof(TileLayerInfo);
     485           8 :         SwapTileLayer((TileLayerInfo *) pabyBlockDirIter);
     486           8 :         memcpy(moTileLayerInfoList[iLayer], pabyBlockDirIter, nSize);
     487           8 :         pabyBlockDirIter += nSize;
     488             :     }
     489             : 
     490             :     // Read the free block layer.
     491           6 :     msFreeBlockLayer.nLayerType = BLTFree;
     492           6 :     msFreeBlockLayer.nStartBlock = msBlockDir.nFirstFreeBlock;
     493           6 :     msFreeBlockLayer.nBlockCount = 0;
     494           6 :     msFreeBlockLayer.nLayerSize = 0;
     495             : 
     496           6 :     if (msFreeBlockLayer.nStartBlock != INVALID_BLOCK)
     497             :     {
     498           6 :         if (psPreviousLayer)
     499             :         {
     500           6 :             if (msFreeBlockLayer.nStartBlock < psPreviousLayer->nStartBlock)
     501           0 :                 return ThrowPCIDSKException("The tile directory is corrupted.");
     502             : 
     503           6 :             psPreviousLayer->nBlockCount =
     504           6 :                 msFreeBlockLayer.nStartBlock - psPreviousLayer->nStartBlock;
     505             :         }
     506             : 
     507           6 :         if (msBlockDir.nBlockCount < msFreeBlockLayer.nStartBlock)
     508           0 :             return ThrowPCIDSKException("The tile directory is corrupted.");
     509             : 
     510           6 :         msFreeBlockLayer.nBlockCount =
     511           6 :             msBlockDir.nBlockCount - msFreeBlockLayer.nStartBlock;
     512             :     }
     513             :     else
     514             :     {
     515           0 :         if (psPreviousLayer)
     516             :         {
     517           0 :             if (msBlockDir.nBlockCount < psPreviousLayer->nStartBlock)
     518           0 :                 return ThrowPCIDSKException("The tile directory is corrupted.");
     519             : 
     520           0 :             psPreviousLayer->nBlockCount =
     521           0 :                 msBlockDir.nBlockCount - psPreviousLayer->nStartBlock;
     522             :         }
     523             : 
     524           0 :         msFreeBlockLayer.nBlockCount = 0;
     525             :     }
     526             : }
     527             : 
     528             : /************************************************************************/
     529             : /*                               GetDirSize()                           */
     530             : /************************************************************************/
     531             : 
     532             : /**
     533             :  * Gets the size in bytes of the block tile directory.
     534             :  *
     535             :  * @return The size in bytes of the block tile directory.
     536             :  */
     537           2 : size_t AsciiTileDir::GetDirSize(void) const
     538             : {
     539           2 :     uint64 nDirSize = 0;
     540             : 
     541             :     // Add the size of the header.
     542           2 :     nDirSize += 512;
     543             : 
     544             :     // Add the size of the blocks.
     545           4 :     for (size_t iLayer = 0; iLayer < moLayerInfoList.size(); iLayer++)
     546             :     {
     547           2 :         const BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
     548             : 
     549           2 :         nDirSize += static_cast<uint64>(psLayer->nBlockCount) * SYS_BLOCK_INFO_SIZE;
     550             :     }
     551             : 
     552             :     // Add the size of the free blocks.
     553           2 :     nDirSize += static_cast<uint64>(msFreeBlockLayer.nBlockCount) * SYS_BLOCK_INFO_SIZE;
     554             : 
     555             :     // Add the size of the block layers.
     556           2 :     nDirSize += static_cast<uint64>(moLayerInfoList.size()) * SYS_BLOCK_LAYER_INFO_SIZE;
     557             : 
     558             :     // Add the size of the tile layers.
     559           2 :     nDirSize += static_cast<uint64>(moTileLayerInfoList.size()) * sizeof(TileLayerInfo);
     560             : 
     561             : #if SIZEOF_VOIDP < 8
     562             :     if (nDirSize > std::numeric_limits<size_t>::max())
     563             :         return ThrowPCIDSKException(0, "Unable to open extremely large file on 32-bit system or the tile directory is corrupted.");
     564             : #endif
     565             : 
     566           2 :     return static_cast<size_t>(nDirSize);
     567             : }
     568             : 
     569             : /************************************************************************/
     570             : /*                           GetLayerBlockCount()                       */
     571             : /************************************************************************/
     572           8 : uint32 AsciiTileDir::GetLayerBlockCount(void) const
     573             : {
     574           8 :     uint32 nLayerBlockCount = 0;
     575             : 
     576          16 :     for (size_t iLayer = 0; iLayer < moLayerInfoList.size(); iLayer++)
     577             :     {
     578           8 :         const BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
     579             : 
     580           8 :         nLayerBlockCount += psLayer->nBlockCount;
     581             :     }
     582             : 
     583           8 :     return nLayerBlockCount;
     584             : }
     585             : 
     586             : /************************************************************************/
     587             : /*                           GetFreeBlockCount()                        */
     588             : /************************************************************************/
     589           8 : uint32 AsciiTileDir::GetFreeBlockCount(void) const
     590             : {
     591           8 :     return msFreeBlockLayer.nBlockCount;
     592             : }
     593             : 
     594             : /************************************************************************/
     595             : /*                           UpdateBlockDirInfo()                       */
     596             : /************************************************************************/
     597           2 : void AsciiTileDir::UpdateBlockDirInfo(void)
     598             : {
     599           2 :     uint32 nLayerBlockCount = GetLayerBlockCount();
     600           2 :     uint32 nFreeBlockCount = GetFreeBlockCount();
     601             : 
     602             :     // Update the block directory info.
     603           2 :     msBlockDir.nLayerCount = (uint32) moLayerInfoList.size();
     604           2 :     msBlockDir.nBlockCount = nLayerBlockCount + nFreeBlockCount;
     605           2 :     msBlockDir.nFirstFreeBlock = nLayerBlockCount;
     606           2 : }
     607             : 
     608             : /************************************************************************/
     609             : /*                             InitBlockList()                          */
     610             : /************************************************************************/
     611           4 : void AsciiTileDir::InitBlockList(AsciiTileLayer * poLayer)
     612             : {
     613           4 :     if (!poLayer)
     614           0 :         return;
     615           4 :     if( poLayer->mpsBlockLayer->nBlockCount == 0)
     616             :     {
     617           0 :         poLayer->moBlockList = BlockInfoList();
     618           0 :         return;
     619             :     }
     620             : 
     621           4 :     BlockLayerInfo * psLayer = poLayer->mpsBlockLayer;
     622             : 
     623             :     // The offset of the blocks.
     624           4 :     uint64 nOffset = static_cast<uint64>(psLayer->nStartBlock) * SYS_BLOCK_INFO_SIZE;
     625             : 
     626             :     // The size of the blocks.
     627           4 :     uint64 nReadSize = static_cast<uint64>(psLayer->nBlockCount) * SYS_BLOCK_INFO_SIZE;
     628             : 
     629           4 :     if (mpoFile->IsCorruptedSegment(mnSegment, 512 + nOffset, nReadSize))
     630           0 :         return ThrowPCIDSKException("The tile directory is corrupted.");
     631             : 
     632             : #if SIZEOF_VOIDP < 8
     633             :     if (nReadSize > std::numeric_limits<size_t>::max())
     634             :         return ThrowPCIDSKException("Unable to open extremely large file on 32-bit system.");
     635             : #endif
     636             : 
     637             :     // Read the blocks from disk.
     638           4 :     uint8 * pabyBlockDir = (uint8 *) malloc(static_cast<size_t>(nReadSize));
     639             : 
     640           4 :     if (pabyBlockDir == nullptr)
     641           0 :         return ThrowPCIDSKException("Out of memory in AsciiTileDir::InitBlockList().");
     642             : 
     643           4 :     PCIDSKBuffer oBlockDirAutoPtr;
     644           4 :     oBlockDirAutoPtr.buffer = (char *) pabyBlockDir;
     645             : 
     646           4 :     uint8 * pabyBlockDirIter = pabyBlockDir;
     647             : 
     648           4 :     mpoFile->ReadFromSegment(mnSegment, pabyBlockDir, 512 + nOffset, nReadSize);
     649             : 
     650             :     // Setup the block list.
     651             :     try
     652             :     {
     653           4 :         poLayer->moBlockList.resize(psLayer->nBlockCount);
     654             :     }
     655           0 :     catch (const std::exception & ex)
     656             :     {
     657           0 :         return ThrowPCIDSKException("Out of memory in AsciiTileDir::InitBlockList(): %s", ex.what());
     658             :     }
     659             : 
     660          13 :     for (uint32 iBlock = 0; iBlock < psLayer->nBlockCount; iBlock++)
     661             :     {
     662           9 :         BlockInfo * psBlock = &poLayer->moBlockList[iBlock];
     663             : 
     664           9 :         psBlock->nSegment    = ScanInt4(pabyBlockDirIter);
     665           9 :         pabyBlockDirIter += 4;
     666             : 
     667           9 :         psBlock->nStartBlock = ScanInt8(pabyBlockDirIter);
     668           9 :         pabyBlockDirIter += 8;
     669             : 
     670             :         //psBlock->nLayer      = ScanInt8(pabyBlockDirIter);
     671           9 :         pabyBlockDirIter += 8;
     672             : 
     673             :         //psBlock->nNextBlock  = ScanInt8(pabyBlockDirIter);
     674           9 :         pabyBlockDirIter += 8;
     675             :     }
     676             : }
     677             : 
     678             : /************************************************************************/
     679             : /*                            ReadLayerBlocks()                         */
     680             : /************************************************************************/
     681           4 : void AsciiTileDir::ReadLayerBlocks(uint32 iLayer)
     682             : {
     683           4 :     InitBlockList((AsciiTileLayer *) moLayerList[iLayer]);
     684           4 : }
     685             : 
     686             : /************************************************************************/
     687             : /*                           ReadFreeBlockLayer()                       */
     688             : /************************************************************************/
     689           0 : void AsciiTileDir::ReadFreeBlockLayer(void)
     690             : {
     691           0 :     mpoFreeBlockLayer = new AsciiTileLayer(this, INVALID_LAYER,
     692           0 :                                            &msFreeBlockLayer, nullptr);
     693             : 
     694           0 :     InitBlockList((AsciiTileLayer *) mpoFreeBlockLayer);
     695           0 : }
     696             : 
     697             : /************************************************************************/
     698             : /*                                WriteDir()                            */
     699             : /************************************************************************/
     700           2 : void AsciiTileDir::WriteDir(void)
     701             : {
     702           2 :     UpdateBlockDirInfo();
     703             : 
     704             :     // Make sure all the layer's block list are valid.
     705           2 :     if (mbOnDisk)
     706             :     {
     707           0 :         for (size_t iLayer = 0; iLayer < moLayerList.size(); iLayer++)
     708             :         {
     709           0 :             AsciiTileLayer * poLayer = GetTileLayer((uint32) iLayer);
     710             : 
     711           0 :             if (poLayer->moBlockList.size() != poLayer->GetBlockCount())
     712           0 :                 InitBlockList(poLayer);
     713             :         }
     714             :     }
     715             : 
     716             :     // What is the size of the block directory.
     717           2 :     size_t nDirSize = GetDirSize();
     718             : 
     719             :     // If we are resizing the segment, resize it to the optimized size.
     720           2 :     if (nDirSize > mpoFile->GetSegmentSize(mnSegment))
     721           0 :         nDirSize = std::max(nDirSize, GetOptimizedDirSize(mpoFile));
     722             : 
     723             :     // Write the block directory to disk.
     724           2 :     char * pabyBlockDir = (char *) malloc(nDirSize + 1); // +1 for '\0'.
     725             : 
     726           2 :     if (pabyBlockDir == nullptr)
     727           0 :         return ThrowPCIDSKException("Out of memory in AsciiTileDir::WriteDir().");
     728             : 
     729           4 :     PCIDSKBuffer oBlockDirAutoPtr;
     730           2 :     oBlockDirAutoPtr.buffer = pabyBlockDir;
     731             : 
     732           2 :     char * pabyBlockDirIter = pabyBlockDir;
     733             : 
     734             :     // Initialize the header.
     735           2 :     memset(pabyBlockDir, ' ', 512);
     736             : 
     737             :     // The first 10 bytes are for the version.
     738           2 :     memcpy(pabyBlockDirIter, "VERSION", 7);
     739           2 :     snprintf(pabyBlockDirIter + 7, 9, "%3d", mnVersion);
     740           2 :     pabyBlockDirIter += 10;
     741             : 
     742             :     // Write the block directory info.
     743           2 :     snprintf(pabyBlockDirIter, 9, "%8d", msBlockDir.nLayerCount);
     744           2 :     pabyBlockDirIter += 8;
     745             : 
     746           2 :     snprintf(pabyBlockDirIter, 9, "%8d", msBlockDir.nBlockCount);
     747           2 :     pabyBlockDirIter += 8;
     748             : 
     749           2 :     snprintf(pabyBlockDirIter, 9, "%8d", msBlockDir.nFirstFreeBlock);
     750             : 
     751             :     // The bytes from 128 to 140 are for the subversion.
     752           2 :     memcpy(pabyBlockDir + 128, "SUBVERSION 1", 12);
     753             : 
     754             :     // The third last byte is for the endianness.
     755           2 :     pabyBlockDir[512 - 3] = mchEndianness;
     756             : 
     757             :     // The last 2 bytes of the header are for the validity info.
     758           2 :     uint16 nValidInfo = ++mnValidInfo;
     759           2 :     SwapValue(&nValidInfo);
     760           2 :     memcpy(pabyBlockDir + 512 - 2, &nValidInfo, 2);
     761             : 
     762             :     // The header is 512 bytes.
     763           2 :     pabyBlockDirIter = pabyBlockDir + 512;
     764             : 
     765             :     // Write the block info list.
     766           2 :     uint32 nNextBlock = 1;
     767             : 
     768           4 :     for (size_t iLayer = 0; iLayer < moLayerInfoList.size(); iLayer++)
     769             :     {
     770           2 :         BlockLayerInfo * psLayer = moLayerInfoList[iLayer];
     771             : 
     772           2 :         AsciiTileLayer * poLayer = GetTileLayer((uint32) iLayer);
     773             : 
     774           6 :         for (size_t iBlock = 0; iBlock < psLayer->nBlockCount; iBlock++)
     775             :         {
     776           4 :             BlockInfo * psBlock = &poLayer->moBlockList[iBlock];
     777             : 
     778           4 :             snprintf(pabyBlockDirIter, 9, "%4d", psBlock->nSegment);
     779           4 :             pabyBlockDirIter += 4;
     780             : 
     781           4 :             snprintf(pabyBlockDirIter, 9, "%8d", psBlock->nStartBlock);
     782           4 :             pabyBlockDirIter += 8;
     783             : 
     784           4 :             snprintf(pabyBlockDirIter, 9, "%8d", (uint32) iLayer);
     785           4 :             pabyBlockDirIter += 8;
     786             : 
     787           4 :             if (iBlock != psLayer->nBlockCount - 1)
     788           2 :                 snprintf(pabyBlockDirIter, 9, "%8d", nNextBlock);
     789             :             else
     790           2 :                 snprintf(pabyBlockDirIter, 9, "%8d", -1);
     791           4 :             pabyBlockDirIter += 8;
     792             : 
     793           4 :             nNextBlock++;
     794             :         }
     795             :     }
     796             : 
     797             :     // Write the free block info list.
     798           2 :     AsciiTileLayer * poLayer = (AsciiTileLayer *) mpoFreeBlockLayer;
     799             : 
     800          30 :     for (size_t iBlock = 0; iBlock < msFreeBlockLayer.nBlockCount; iBlock++)
     801             :     {
     802          28 :         BlockInfo * psBlock = &poLayer->moBlockList[iBlock];
     803             : 
     804          28 :         snprintf(pabyBlockDirIter, 9, "%4d", psBlock->nSegment);
     805          28 :         pabyBlockDirIter += 4;
     806             : 
     807          28 :         snprintf(pabyBlockDirIter, 9, "%8d", psBlock->nStartBlock);
     808          28 :         pabyBlockDirIter += 8;
     809             : 
     810          28 :         snprintf(pabyBlockDirIter, 9, "%8d", -1);
     811          28 :         pabyBlockDirIter += 8;
     812             : 
     813          28 :         if (iBlock != msFreeBlockLayer.nBlockCount - 1)
     814          26 :             snprintf(pabyBlockDirIter, 9, "%8d", nNextBlock);
     815             :         else
     816           2 :             snprintf(pabyBlockDirIter, 9, "%8d", -1);
     817          28 :         pabyBlockDirIter += 8;
     818             : 
     819          28 :         nNextBlock++;
     820             :     }
     821             : 
     822             :     // Write the block layers.
     823           2 :     uint32 nStartBlock = 0;
     824             : 
     825           4 :     for (BlockLayerInfo * psLayer : moLayerInfoList)
     826             :     {
     827           2 :         snprintf(pabyBlockDirIter, 9, "%4d", psLayer->nLayerType);
     828           2 :         pabyBlockDirIter += 4;
     829             : 
     830           2 :         if (psLayer->nBlockCount != 0)
     831           2 :             snprintf(pabyBlockDirIter, 9, "%8d", nStartBlock);
     832             :         else
     833           0 :             snprintf(pabyBlockDirIter, 9, "%8d", -1);
     834           2 :         pabyBlockDirIter += 8;
     835             : 
     836           2 :         snprintf(pabyBlockDirIter, 13, "%12" PCIDSK_FRMT_64_WITHOUT_PREFIX "d", psLayer->nLayerSize);
     837           2 :         pabyBlockDirIter += 12;
     838             : 
     839           2 :         nStartBlock += psLayer->nBlockCount;
     840             :     }
     841             : 
     842             :     // Write the tile layers.
     843           4 :     for (uint32 iLayer = 0; iLayer < msBlockDir.nLayerCount; iLayer++)
     844             :     {
     845           2 :         size_t nSize = sizeof(TileLayerInfo);
     846           2 :         memcpy(pabyBlockDirIter, moTileLayerInfoList[iLayer], nSize);
     847           2 :         SwapTileLayer((TileLayerInfo *) pabyBlockDirIter);
     848           2 :         pabyBlockDirIter += nSize;
     849             :     }
     850             : 
     851             :     // Initialize the remaining bytes so that Valgrind doesn't complain.
     852           2 :     size_t nRemainingBytes = pabyBlockDir + nDirSize - pabyBlockDirIter;
     853             : 
     854           2 :     if (nRemainingBytes)
     855           0 :         memset(pabyBlockDirIter, 0, nRemainingBytes);
     856             : 
     857             :     // Write the block directory to disk.
     858           2 :     mpoFile->WriteToSegment(mnSegment, pabyBlockDir, 0, nDirSize);
     859             : }
     860             : 
     861             : /************************************************************************/
     862             : /*                              _CreateLayer()                          */
     863             : /************************************************************************/
     864             : 
     865             : /**
     866             :  * Creates a block layer of the specified type at the specified index.
     867             :  *
     868             :  * @param nLayerType The type of the block layer to create.
     869             :  * @param iLayer The index of the block layer to create.
     870             :  *
     871             :  * @return The new block layer.
     872             :  */
     873           2 : BlockLayer * AsciiTileDir::_CreateLayer(uint16 nLayerType, uint32 iLayer)
     874             : {
     875           2 :     if (iLayer == moLayerInfoList.size())
     876             :     {
     877             :         try
     878             :         {
     879           2 :             moLayerInfoList.resize(moLayerInfoList.size() + 1);
     880           2 :             moTileLayerInfoList.resize(moLayerInfoList.size());
     881             :         }
     882           0 :         catch (const std::exception & ex)
     883             :         {
     884           0 :             return (BlockLayer *) ThrowPCIDSKExceptionPtr("Out of memory in AsciiTileDir::_CreateLayer(): %s", ex.what());
     885             :         }
     886             : 
     887           2 :         moLayerInfoList[iLayer] = new BlockLayerInfo;
     888           2 :         moTileLayerInfoList[iLayer] = new TileLayerInfo;
     889             :     }
     890             : 
     891             :     // Setup the block layer info.
     892           2 :     BlockLayerInfo * psBlockLayer = moLayerInfoList[iLayer];
     893             : 
     894           2 :     psBlockLayer->nLayerType = nLayerType;
     895           2 :     psBlockLayer->nBlockCount = 0;
     896           2 :     psBlockLayer->nLayerSize = 0;
     897             : 
     898             :     // Setup the tile layer info.
     899           2 :     TileLayerInfo * psTileLayer = moTileLayerInfoList[iLayer];
     900             : 
     901           2 :     memset(psTileLayer, 0, sizeof(TileLayerInfo));
     902             : 
     903           2 :     return new AsciiTileLayer(this, iLayer, psBlockLayer, psTileLayer);
     904             : }
     905             : 
     906             : /************************************************************************/
     907             : /*                              _DeleteLayer()                          */
     908             : /************************************************************************/
     909             : 
     910             : /**
     911             :  * Deletes the block layer with the specified index.
     912             :  *
     913             :  * @param iLayer The index of the block layer to delete.
     914             :  */
     915           0 : void AsciiTileDir::_DeleteLayer(uint32 iLayer)
     916             : {
     917             :     // Invalidate the block layer info.
     918           0 :     BlockLayerInfo * psBlockLayer = moLayerInfoList[iLayer];
     919             : 
     920           0 :     psBlockLayer->nLayerType = BLTDead;
     921           0 :     psBlockLayer->nBlockCount = 0;
     922           0 :     psBlockLayer->nLayerSize = 0;
     923             : 
     924             :     // Invalidate the tile layer info.
     925           0 :     TileLayerInfo * psTileLayer = moTileLayerInfoList[iLayer];
     926             : 
     927           0 :     memset(psTileLayer, 0, sizeof(TileLayerInfo));
     928           0 : }
     929             : 
     930             : /************************************************************************/
     931             : /*                           GetDataSegmentName()                       */
     932             : /************************************************************************/
     933           2 : std::string AsciiTileDir::GetDataSegmentName(void) const
     934             : {
     935           2 :     return "SysBData";
     936             : }
     937             : 
     938             : /************************************************************************/
     939             : /*                           GetDataSegmentDesc()                       */
     940             : /************************************************************************/
     941           2 : std::string AsciiTileDir::GetDataSegmentDesc(void) const
     942             : {
     943           2 :     return "Block Tile Data - Do not modify.";
     944             : }
     945             : 
     946             : /************************************************************************/
     947             : /*                           ValidateNewBlocks()                        */
     948             : /************************************************************************/
     949           6 : void AsciiTileDir::ValidateNewBlocks(uint32 & nNewBlockCount, bool bFreeBlocks)
     950             : {
     951           6 :     uint32 nLimitBlockCount = 99999999;
     952           6 :     uint32 nTotalBlockCount = GetLayerBlockCount() + GetFreeBlockCount();
     953             : 
     954           6 :     if (nTotalBlockCount >= nLimitBlockCount)
     955             :     {
     956           0 :         Sync(); // Make sure the directory is synchronized to disk.
     957             : 
     958           0 :         ThrowPCIDSKException("The file size limit has been reached.");
     959             :     }
     960             : 
     961           6 :     if (nTotalBlockCount + nNewBlockCount > nLimitBlockCount)
     962             :     {
     963           0 :         if (!bFreeBlocks)
     964             :         {
     965           0 :             Sync(); // Make sure the directory is synchronized to disk.
     966             : 
     967           0 :             ThrowPCIDSKException("The file size limit has been reached.");
     968             :         }
     969             : 
     970           0 :         nNewBlockCount = nLimitBlockCount - nTotalBlockCount;
     971             :     }
     972           6 : }

Generated by: LCOV version 1.14