LCOV - code coverage report
Current view: top level - frmts/pcidsk/sdk/blockdir - blocklayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 119 172 69.2 %
Date: 2025-07-02 23:05:47 Functions: 13 16 81.2 %

          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/blocklayer.h"
      13             : #include "blockdir/blockfile.h"
      14             : #include "pcidsk_exception.h"
      15             : #include "core/pcidsk_utils.h"
      16             : 
      17             : using namespace PCIDSK;
      18             : 
      19             : /************************************************************************/
      20             : /*                               BlockLayer()                           */
      21             : /************************************************************************/
      22             : 
      23             : /**
      24             :  * Constructor.
      25             :  *
      26             :  * @param poBlockDir The associated block directory.
      27             :  * @param nLayer The index of the block layer.
      28             :  */
      29          49 : BlockLayer::BlockLayer(BlockDir * poBlockDir, uint32 nLayer)
      30             :     : mpoBlockDir(poBlockDir),
      31          49 :       mnLayer(nLayer)
      32             : {
      33          49 : }
      34             : 
      35             : /************************************************************************/
      36             : /*                              ~BlockLayer()                           */
      37             : /************************************************************************/
      38             : 
      39             : /**
      40             :  * Destructor.
      41             :  */
      42          49 : BlockLayer::~BlockLayer(void)
      43             : {
      44          49 : }
      45             : 
      46             : /************************************************************************/
      47             : /*                              GetBlockInfo()                          */
      48             : /************************************************************************/
      49             : 
      50             : /**
      51             :  * Gets the layer block at the specified index.
      52             :  *
      53             :  * @param iBlock The index of the layer block.
      54             :  *
      55             :  * @return The layer block at the specified index.
      56             :  */
      57         222 : BlockInfo * BlockLayer::GetBlockInfo(uint32 iBlock)
      58             : {
      59         222 :     if (!IsValid())
      60           0 :         return nullptr;
      61             : 
      62         222 :     uint32 nBlockCount = GetBlockCount();
      63             : 
      64         222 :     if (nBlockCount != moBlockList.size())
      65             :     {
      66          11 :         mpoBlockDir->ReadLayerBlocks(mnLayer);
      67             : 
      68          11 :         if (moBlockList.size() != nBlockCount)
      69           0 :             ThrowPCIDSKExceptionPtr("Corrupted block directory.");
      70             :     }
      71             : 
      72         222 :     if (iBlock >= moBlockList.size())
      73           0 :         return nullptr;
      74             : 
      75         222 :     return &moBlockList[iBlock];
      76             : }
      77             : 
      78             : /************************************************************************/
      79             : /*                             AllocateBlocks()                         */
      80             : /************************************************************************/
      81             : 
      82             : /**
      83             :  * Allocates the blocks of the specified data.
      84             :  *
      85             :  * @param nOffset The offset of the data.
      86             :  * @param nSize The size of the data.
      87             :  */
      88          24 : void BlockLayer::AllocateBlocks(uint64 nOffset, uint64 nSize)
      89             : {
      90          24 :     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
      91             : 
      92          24 :     uint32 iStartBlock = (uint32) (nOffset / nBlockSize);
      93          24 :     uint32 nStartOffset = (uint32) (nOffset % nBlockSize);
      94             : 
      95          24 :     uint32 nNumBlocks = (uint32) DIV_ROUND_UP(nSize + nStartOffset, nBlockSize);
      96             : 
      97          48 :     for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++)
      98             :     {
      99          24 :         BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock);
     100             : 
     101          24 :         if (!psBlock)
     102           0 :             break;
     103             : 
     104          24 :         if (psBlock->nSegment == INVALID_SEGMENT ||
     105           8 :             psBlock->nStartBlock == INVALID_BLOCK)
     106             :         {
     107          16 :             *psBlock = mpoBlockDir->GetFreeBlock();
     108             :         }
     109             :     }
     110          24 : }
     111             : 
     112             : /************************************************************************/
     113             : /*                           AreBlocksAllocated()                       */
     114             : /************************************************************************/
     115             : 
     116             : /**
     117             :  * Checks if the blocks of the specified data are allocated.
     118             :  *
     119             :  * @param nOffset The offset of the data.
     120             :  * @param nSize The size of the data.
     121             :  *
     122             :  * @return If the blocks of the specified data are allocated.
     123             :  */
     124          46 : bool BlockLayer::AreBlocksAllocated(uint64 nOffset, uint64 nSize)
     125             : {
     126          46 :     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
     127             : 
     128          46 :     uint32 iStartBlock = (uint32) (nOffset / nBlockSize);
     129          46 :     uint32 nStartOffset = (uint32) (nOffset % nBlockSize);
     130             : 
     131          46 :     uint32 nNumBlocks = (uint32) DIV_ROUND_UP(nSize + nStartOffset, nBlockSize);
     132             : 
     133          92 :     for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++)
     134             :     {
     135          46 :         BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock);
     136             : 
     137          46 :         if (!psBlock)
     138           0 :             return false;
     139             : 
     140          46 :         if (psBlock->nSegment == INVALID_SEGMENT ||
     141          46 :             psBlock->nStartBlock == INVALID_BLOCK)
     142             :         {
     143           0 :             return false;
     144             :         }
     145             :     }
     146             : 
     147          46 :     return true;
     148             : }
     149             : 
     150             : /************************************************************************/
     151             : /*                           GetContiguousCount()                       */
     152             : /************************************************************************/
     153             : 
     154             : /**
     155             :  * Gets the number of contiguous blocks for the specified data.
     156             :  *
     157             :  * @param nOffset The offset of the data.
     158             :  * @param nSize The size of the data.
     159             :  *
     160             :  * @return The number of contiguous blocks for the specified data.
     161             :  */
     162          70 : uint32 BlockLayer::GetContiguousCount(uint64 nOffset, uint64 nSize)
     163             : {
     164          70 :     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
     165             : 
     166          70 :     uint32 iStartBlock = (uint32) (nOffset / nBlockSize);
     167          70 :     uint32 nStartOffset = (uint32) (nOffset % nBlockSize);
     168             : 
     169          70 :     uint32 nNumBlocks = (uint32) DIV_ROUND_UP(nSize + nStartOffset, nBlockSize);
     170             : 
     171          70 :     BlockInfo * psStartBlock = GetBlockInfo(iStartBlock);
     172             : 
     173          70 :     if (!psStartBlock)
     174           0 :         return 0;
     175             : 
     176          70 :     uint32 nContiguousCount = 1;
     177             : 
     178          70 :     for (uint32 iBlock = 1; iBlock < nNumBlocks; iBlock++)
     179             :     {
     180           0 :         BlockInfo * psNextBlock = GetBlockInfo(iStartBlock + iBlock);
     181             : 
     182           0 :         if (!psNextBlock)
     183           0 :             break;
     184             : 
     185           0 :         if (psNextBlock->nSegment != psStartBlock->nSegment)
     186           0 :             break;
     187             : 
     188           0 :         if (psNextBlock->nStartBlock != psStartBlock->nStartBlock + iBlock)
     189           0 :             break;
     190             : 
     191           0 :         nContiguousCount++;
     192             :     }
     193             : 
     194          70 :     return nContiguousCount;
     195             : }
     196             : 
     197             : /************************************************************************/
     198             : /*                               FreeBlocks()                           */
     199             : /************************************************************************/
     200             : 
     201             : /**
     202             :  * Frees the blocks of the specified data.
     203             :  *
     204             :  * @param nOffset The offset of the data.
     205             :  * @param nSize The size of the data.
     206             :  */
     207           0 : void BlockLayer::FreeBlocks(uint64 nOffset, uint64 nSize)
     208             : {
     209           0 :     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
     210             : 
     211           0 :     uint32 iStartBlock = (uint32) DIV_ROUND_UP(nOffset, nBlockSize);
     212           0 :     uint32 iEndBlock = (uint32) ((nOffset + nSize) / nBlockSize);
     213             : 
     214           0 :     uint32 nNumBlocks = iStartBlock < iEndBlock ? iEndBlock - iStartBlock : 0;
     215             : 
     216           0 :     BlockInfoList oFreeBlocks;
     217             : 
     218           0 :     oFreeBlocks.reserve(nNumBlocks);
     219             : 
     220           0 :     for (uint32 iBlock = 0; iBlock < nNumBlocks; iBlock++)
     221             :     {
     222           0 :         BlockInfo * psBlock = GetBlockInfo(iStartBlock + iBlock);
     223             : 
     224           0 :         if (!psBlock)
     225           0 :             break;
     226             : 
     227           0 :         if (psBlock->nSegment != INVALID_SEGMENT &&
     228           0 :             psBlock->nStartBlock != INVALID_BLOCK)
     229             :         {
     230           0 :             oFreeBlocks.push_back(*psBlock);
     231             : 
     232           0 :             psBlock->nSegment = INVALID_SEGMENT;
     233           0 :             psBlock->nStartBlock = INVALID_BLOCK;
     234             :         }
     235             :     }
     236             : 
     237           0 :     mpoBlockDir->AddFreeBlocks(oFreeBlocks);
     238           0 : }
     239             : 
     240             : /************************************************************************/
     241             : /*                              WriteToLayer()                          */
     242             : /************************************************************************/
     243             : 
     244             : /**
     245             :  * Writes the specified data to the layer.
     246             :  *
     247             :  * @param pData The data buffer to write.
     248             :  * @param nOffset The offset of the data.
     249             :  * @param nSize The size of the data.
     250             :  */
     251          24 : void BlockLayer::WriteToLayer(const void * pData, uint64 nOffset, uint64 nSize)
     252             : {
     253          24 :     if (nOffset + nSize > GetLayerSize())
     254          16 :         Resize(nOffset + nSize);
     255             : 
     256          24 :     AllocateBlocks(nOffset, nSize);
     257             : 
     258          24 :     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
     259             : 
     260          24 :     uint8 * pabyData = (uint8 *) pData;
     261             : 
     262          48 :     for (uint64 iByte = 0; iByte < nSize; )
     263             :     {
     264             :         uint32 nContiguousCount =
     265          24 :             GetContiguousCount(nOffset + iByte, nSize - iByte);
     266             : 
     267          24 :         uint32 iBlock = (uint32) ((nOffset + iByte) / nBlockSize);
     268          24 :         uint32 iWork  = (uint32) ((nOffset + iByte) % nBlockSize);
     269             : 
     270          24 :         uint64 nWorkSize = (uint64)nContiguousCount * nBlockSize - iWork;
     271             : 
     272          24 :         if (nWorkSize > nSize - iByte)
     273          23 :             nWorkSize = nSize - iByte;
     274             : 
     275          24 :         BlockInfo * psBlock = GetBlockInfo(iBlock);
     276             : 
     277          24 :         uint64 nWorkOffset = (uint64) psBlock->nStartBlock * nBlockSize + iWork;
     278             : 
     279          24 :         GetFile()->WriteToSegment(psBlock->nSegment, pabyData + iByte,
     280          24 :                                   nWorkOffset, nWorkSize);
     281             : 
     282          24 :         iByte += nWorkSize;
     283             :     }
     284          24 : }
     285             : 
     286             : /************************************************************************/
     287             : /*                             ReadFromLayer()                          */
     288             : /************************************************************************/
     289             : 
     290             : /**
     291             :  * Reads the specified data from the layer.
     292             :  *
     293             :  * @param pData The data buffer to read.
     294             :  * @param nOffset The offset of the data.
     295             :  * @param nSize The size of the data.
     296             :  */
     297          46 : bool BlockLayer::ReadFromLayer(void * pData, uint64 nOffset, uint64 nSize)
     298             : {
     299          46 :     uint64 nLayerSize = GetLayerSize();
     300             : 
     301          46 :     if (nSize > nLayerSize ||
     302          46 :         nOffset > nLayerSize ||
     303          46 :         nOffset + nSize > nLayerSize)
     304             :     {
     305           0 :         return false;
     306             :     }
     307             : 
     308          46 :     if (!AreBlocksAllocated(nOffset, nSize))
     309           0 :         return false;
     310             : 
     311          46 :     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
     312             : 
     313          46 :     uint8 * pabyData = (uint8 *) pData;
     314             : 
     315          92 :     for (uint64 iByte = 0; iByte < nSize; )
     316             :     {
     317             :         uint32 nContiguousCount =
     318          46 :             GetContiguousCount(nOffset + iByte, nSize - iByte);
     319             : 
     320          46 :         uint32 iBlock = (uint32) ((nOffset + iByte) / nBlockSize);
     321          46 :         uint32 iWork  = (uint32) ((nOffset + iByte) % nBlockSize);
     322             : 
     323          46 :         uint64 nWorkSize = (uint64)nContiguousCount * nBlockSize - iWork;
     324             : 
     325          46 :         if (nWorkSize > nSize - iByte)
     326          43 :             nWorkSize = nSize - iByte;
     327             : 
     328          46 :         BlockInfo * psBlock = GetBlockInfo(iBlock);
     329             : 
     330          46 :         uint64 nWorkOffset = (uint64) psBlock->nStartBlock * nBlockSize + iWork;
     331             : 
     332          46 :         GetFile()->ReadFromSegment(psBlock->nSegment, pabyData + iByte,
     333          46 :                                    nWorkOffset, nWorkSize);
     334             : 
     335          46 :         iByte += nWorkSize;
     336             :     }
     337             : 
     338          46 :     return true;
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /*                                GetFile()                             */
     343             : /************************************************************************/
     344             : 
     345             : /**
     346             :  * Gets the associated file of the block layer.
     347             :  *
     348             :  * @return The associated file of the block layer.
     349             :  */
     350          90 : BlockFile * BlockLayer::GetFile(void) const
     351             : {
     352          90 :     return mpoBlockDir->GetFile();
     353             : }
     354             : 
     355             : /************************************************************************/
     356             : /*                               NeedsSwap()                            */
     357             : /************************************************************************/
     358             : 
     359             : /**
     360             :  * Checks if the block directory on disk needs swapping.
     361             :  *
     362             :  * @return If the block directory on disk needs swapping.
     363             :  */
     364           0 : bool BlockLayer::NeedsSwap(void) const
     365             : {
     366           0 :     return mpoBlockDir->NeedsSwap();
     367             : }
     368             : 
     369             : /************************************************************************/
     370             : /*                                IsValid()                             */
     371             : /************************************************************************/
     372             : 
     373             : /**
     374             :  * Checks if the block layer is valid.
     375             :  *
     376             :  * @return If the block layer is valid.
     377             :  */
     378         382 : bool BlockLayer::IsValid(void) const
     379             : {
     380         382 :     return GetLayerType() != BLTDead;
     381             : }
     382             : 
     383             : /************************************************************************/
     384             : /*                                 Resize()                             */
     385             : /************************************************************************/
     386             : 
     387             : /**
     388             :  * Resizes the block layer to the specified size in bytes.
     389             :  *
     390             :  * @param nLayerSize The new block layer size in bytes.
     391             :  */
     392          24 : void BlockLayer::Resize(uint64 nLayerSize)
     393             : {
     394          24 :     if (!IsValid())
     395           0 :         return;
     396             : 
     397          24 :     if (nLayerSize == GetLayerSize())
     398           0 :         return;
     399             : 
     400          24 :     uint32 nBlockCount = GetBlockCount();
     401             : 
     402          24 :     uint32 nBlockSize = mpoBlockDir->GetBlockSize();
     403             : 
     404             :     // Check how many blocks are needed.
     405          24 :     uint32 nNeededBlocks = (uint32) DIV_ROUND_UP(nLayerSize, nBlockSize);
     406             : 
     407             :     // Create new blocks.
     408          24 :     if (nNeededBlocks > nBlockCount)
     409             :     {
     410          16 :         uint32 nNewBlocks = nNeededBlocks - nBlockCount;
     411             : 
     412          16 :         PushBlocks(mpoBlockDir->CreateNewBlocks(nNewBlocks));
     413             :     }
     414             :     // Free blocks.
     415           8 :     else if (nNeededBlocks < nBlockCount)
     416             :     {
     417           0 :         uint32 nFreeBlocks = nBlockCount - nNeededBlocks;
     418             : 
     419           0 :         mpoBlockDir->AddFreeBlocks(PopBlocks(nFreeBlocks));
     420             :     }
     421             : 
     422          24 :     _SetLayerSize(nLayerSize);
     423             : }
     424             : 
     425             : /************************************************************************/
     426             : /*                               PushBlocks()                           */
     427             : /************************************************************************/
     428             : 
     429             : /**
     430             :  * Pushes the specified block list at the end of the layer's block list.
     431             :  *
     432             :  * @param oBlockList The block list to add.
     433             :  */
     434          24 : void BlockLayer::PushBlocks(const BlockInfoList & oBlockList)
     435             : {
     436          24 :     uint32 nBlockCount = GetBlockCount();
     437             : 
     438          24 :     if (nBlockCount != moBlockList.size())
     439             :     {
     440           0 :         mpoBlockDir->ReadLayerBlocks(mnLayer);
     441             : 
     442           0 :         if (moBlockList.size() != nBlockCount)
     443           0 :             ThrowPCIDSKException("Corrupted block directory.");
     444             :     }
     445             : 
     446             :     try
     447             :     {
     448          24 :         moBlockList.resize(nBlockCount + oBlockList.size());
     449             :     }
     450           0 :     catch (const std::exception & ex)
     451             :     {
     452           0 :         return ThrowPCIDSKException("Out of memory in BlockLayer::PushBlocks(): %s", ex.what());
     453             :     }
     454             : 
     455         168 :     for (size_t iBlock = 0; iBlock < oBlockList.size(); iBlock++)
     456         144 :         moBlockList[nBlockCount + iBlock] = oBlockList[iBlock];
     457             : 
     458          24 :     _SetBlockCount((uint32) moBlockList.size());
     459             : }
     460             : 
     461             : /************************************************************************/
     462             : /*                               PopBlocks()                            */
     463             : /************************************************************************/
     464             : 
     465             : /**
     466             :  * Pops the specified number of blocks from the end of the layer's block list.
     467             :  *
     468             :  * @param nBlockCount The number of blocks to remove.
     469             :  *
     470             :  * @return The removed block list.
     471             :  */
     472          16 : BlockInfoList BlockLayer::PopBlocks(uint32 nBlockCount)
     473             : {
     474          16 :     uint32 nCurrentBlockCount = GetBlockCount();
     475             : 
     476          16 :     if (nCurrentBlockCount != moBlockList.size())
     477             :     {
     478           0 :         mpoBlockDir->ReadLayerBlocks(mnLayer);
     479             : 
     480           0 :         if (moBlockList.size() != nCurrentBlockCount)
     481           0 :             ThrowPCIDSKException("Corrupted block directory.");
     482             :     }
     483             : 
     484             :     uint32 nRemainingBlockCount;
     485             : 
     486          16 :     BlockInfoList oRemovedBlocks;
     487             : 
     488          16 :     if (nBlockCount < nCurrentBlockCount)
     489             :     {
     490          16 :         nRemainingBlockCount = nCurrentBlockCount - nBlockCount;
     491             : 
     492             :         oRemovedBlocks =
     493          64 :             BlockInfoList(moBlockList.begin() + nRemainingBlockCount,
     494          48 :                           moBlockList.begin() + nCurrentBlockCount);
     495             :     }
     496             :     else
     497             :     {
     498           0 :         nRemainingBlockCount = 0;
     499             : 
     500           0 :         oRemovedBlocks = moBlockList;
     501             :     }
     502             : 
     503             :     try
     504             :     {
     505          16 :         moBlockList.resize(nRemainingBlockCount);
     506             :     }
     507           0 :     catch (const std::exception & ex)
     508             :     {
     509           0 :         ThrowPCIDSKException("Out of memory in BlockLayer::PopBlocks(): %s", ex.what());
     510             :     }
     511             : 
     512          16 :     _SetBlockCount(nRemainingBlockCount);
     513             : 
     514          16 :     return oRemovedBlocks;
     515             : }

Generated by: LCOV version 1.14