LCOV - code coverage report
Current view: top level - gcore - gdalarraybandblockcache.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 150 163 92.0 %
Date: 2025-10-21 22:35:35 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Store cached blocks in a array or a two-level array
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys dot org>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1998, Frank Warmerdam
       9             :  * Copyright (c) 2007-2015, Even Rouault <even dot rouault at spatialys dot org>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gdal_priv.h"
      16             : 
      17             : #include <cassert>
      18             : #include <climits>
      19             : #include <cstddef>
      20             : #include <new>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_error.h"
      24             : #include "cpl_multiproc.h"
      25             : #include "cpl_vsi.h"
      26             : 
      27             : #include "gdal_abstractbandblockcache.h"
      28             : 
      29             : //! @cond Doxygen_Suppress
      30             : 
      31             : constexpr int SUBBLOCK_SIZE = 64;
      32             : #define TO_SUBBLOCK(x) ((x) >> 6)
      33             : #define WITHIN_SUBBLOCK(x) ((x)&0x3f)
      34             : 
      35             : /* ******************************************************************** */
      36             : /*                        GDALArrayBandBlockCache                       */
      37             : /* ******************************************************************** */
      38             : 
      39             : class GDALArrayBandBlockCache final : public GDALAbstractBandBlockCache
      40             : {
      41             :     bool bSubBlockingActive = false;
      42             :     int nSubBlocksPerRow = 0;
      43             :     int nSubBlocksPerColumn = 0;
      44             : 
      45             :     union u
      46             :     {
      47             :         GDALRasterBlock **papoBlocks;
      48             :         GDALRasterBlock ***papapoBlocks;
      49             : 
      50      238457 :         u() : papoBlocks(nullptr)
      51             :         {
      52      238457 :         }
      53             :     } u{};
      54             : 
      55             :     CPL_DISALLOW_COPY_ASSIGN(GDALArrayBandBlockCache)
      56             : 
      57             :   public:
      58             :     explicit GDALArrayBandBlockCache(GDALRasterBand *poBand);
      59             :     ~GDALArrayBandBlockCache() override;
      60             : 
      61             :     bool Init() override;
      62             :     bool IsInitOK() override;
      63             :     CPLErr FlushCache() override;
      64             :     CPLErr AdoptBlock(GDALRasterBlock *) override;
      65             :     GDALRasterBlock *TryGetLockedBlockRef(int nXBlockOff,
      66             :                                           int nYBlockYOff) override;
      67             :     CPLErr UnreferenceBlock(GDALRasterBlock *poBlock) override;
      68             :     CPLErr FlushBlock(int nXBlockOff, int nYBlockOff,
      69             :                       int bWriteDirtyBlock) override;
      70             : };
      71             : 
      72             : /************************************************************************/
      73             : /*                     GDALArrayBandBlockCacheCreate()                 */
      74             : /************************************************************************/
      75             : 
      76             : GDALAbstractBandBlockCache *
      77      238458 : GDALArrayBandBlockCacheCreate(GDALRasterBand *poBand)
      78             : {
      79      238458 :     return new (std::nothrow) GDALArrayBandBlockCache(poBand);
      80             : }
      81             : 
      82             : /************************************************************************/
      83             : /*                       GDALArrayBandBlockCache()                      */
      84             : /************************************************************************/
      85             : 
      86      238458 : GDALArrayBandBlockCache::GDALArrayBandBlockCache(GDALRasterBand *poBandIn)
      87      238458 :     : GDALAbstractBandBlockCache(poBandIn)
      88             : {
      89      238456 : }
      90             : 
      91             : /************************************************************************/
      92             : /*                      ~GDALArrayBandBlockCache()                     */
      93             : /************************************************************************/
      94             : 
      95      476916 : GDALArrayBandBlockCache::~GDALArrayBandBlockCache()
      96             : {
      97      238458 :     GDALArrayBandBlockCache::FlushCache();
      98             : 
      99      238458 :     if (!bSubBlockingActive)
     100      238339 :         CPLFree(u.papoBlocks);
     101             :     else
     102         119 :         CPLFree(u.papapoBlocks);
     103      476916 : }
     104             : 
     105             : /************************************************************************/
     106             : /*                                  Init()                              */
     107             : /************************************************************************/
     108             : 
     109      238456 : bool GDALArrayBandBlockCache::Init()
     110             : {
     111      238456 :     if (poBand->nBlocksPerRow < SUBBLOCK_SIZE / 2)
     112             :     {
     113      238337 :         bSubBlockingActive = false;
     114             : 
     115      238337 :         if (poBand->nBlocksPerRow < INT_MAX / poBand->nBlocksPerColumn)
     116             :         {
     117      238337 :             u.papoBlocks = static_cast<GDALRasterBlock **>(VSICalloc(
     118      238338 :                 sizeof(void *), cpl::fits_on<int>(poBand->nBlocksPerRow *
     119      238338 :                                                   poBand->nBlocksPerColumn)));
     120      238338 :             if (u.papoBlocks == nullptr)
     121             :             {
     122           0 :                 poBand->ReportError(CE_Failure, CPLE_OutOfMemory,
     123             :                                     "Out of memory in InitBlockInfo().");
     124           0 :                 return false;
     125             :             }
     126             :         }
     127             :         else
     128             :         {
     129           0 :             poBand->ReportError(
     130             :                 CE_Failure, CPLE_NotSupported, "Too many blocks : %d x %d",
     131           0 :                 poBand->nBlocksPerRow, poBand->nBlocksPerColumn);
     132           0 :             return false;
     133             :         }
     134             :     }
     135             :     else
     136             :     {
     137         119 :         bSubBlockingActive = true;
     138             : 
     139         119 :         nSubBlocksPerRow = DIV_ROUND_UP(poBand->nBlocksPerRow, SUBBLOCK_SIZE);
     140         119 :         nSubBlocksPerColumn =
     141         119 :             DIV_ROUND_UP(poBand->nBlocksPerColumn, SUBBLOCK_SIZE);
     142             : 
     143         119 :         if (nSubBlocksPerRow < INT_MAX / nSubBlocksPerColumn)
     144             :         {
     145         119 :             u.papapoBlocks = static_cast<GDALRasterBlock ***>(VSICalloc(
     146             :                 sizeof(void *),
     147         119 :                 cpl::fits_on<int>(nSubBlocksPerRow * nSubBlocksPerColumn)));
     148         118 :             if (u.papapoBlocks == nullptr)
     149             :             {
     150           0 :                 poBand->ReportError(CE_Failure, CPLE_OutOfMemory,
     151             :                                     "Out of memory in InitBlockInfo().");
     152           0 :                 return false;
     153             :             }
     154             :         }
     155             :         else
     156             :         {
     157           0 :             poBand->ReportError(CE_Failure, CPLE_NotSupported,
     158             :                                 "Too many subblocks : %d x %d",
     159             :                                 nSubBlocksPerRow, nSubBlocksPerColumn);
     160           0 :             return false;
     161             :         }
     162             :     }
     163             : 
     164      238456 :     return true;
     165             : }
     166             : 
     167             : /************************************************************************/
     168             : /*                             IsInitOK()                               */
     169             : /************************************************************************/
     170             : 
     171     9719460 : bool GDALArrayBandBlockCache::IsInitOK()
     172             : {
     173     9719460 :     return (!bSubBlockingActive) ? u.papoBlocks != nullptr
     174     9719460 :                                  : u.papapoBlocks != nullptr;
     175             : }
     176             : 
     177             : /************************************************************************/
     178             : /*                            AdoptBlock()                              */
     179             : /************************************************************************/
     180             : 
     181     1352570 : CPLErr GDALArrayBandBlockCache::AdoptBlock(GDALRasterBlock *poBlock)
     182             : 
     183             : {
     184     1352570 :     const int nXBlockOff = poBlock->GetXOff();
     185     1352560 :     const int nYBlockOff = poBlock->GetYOff();
     186             : 
     187     1352570 :     FreeDanglingBlocks();
     188             : 
     189             :     /* -------------------------------------------------------------------- */
     190             :     /*      Simple case without subblocking.                                */
     191             :     /* -------------------------------------------------------------------- */
     192             : 
     193     1352560 :     if (!bSubBlockingActive)
     194             :     {
     195     1295090 :         const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
     196             : 
     197     1295090 :         CPLAssert(u.papoBlocks[nBlockIndex] == nullptr);
     198     1295090 :         u.papoBlocks[nBlockIndex] = poBlock;
     199             :     }
     200             :     else
     201             :     {
     202             :         /* --------------------------------------------------------------------
     203             :          */
     204             :         /*      Identify the subblock in which our target occurs, and create */
     205             :         /*      it if necessary. */
     206             :         /* --------------------------------------------------------------------
     207             :          */
     208       57467 :         const int nSubBlock = TO_SUBBLOCK(nXBlockOff) +
     209       57467 :                               TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
     210             : 
     211       57467 :         if (u.papapoBlocks[nSubBlock] == nullptr)
     212             :         {
     213         161 :             const int nSubGridSize =
     214             :                 sizeof(GDALRasterBlock *) * SUBBLOCK_SIZE * SUBBLOCK_SIZE;
     215             : 
     216         322 :             u.papapoBlocks[nSubBlock] =
     217         161 :                 static_cast<GDALRasterBlock **>(VSICalloc(1, nSubGridSize));
     218         161 :             if (u.papapoBlocks[nSubBlock] == nullptr)
     219             :             {
     220           0 :                 poBand->ReportError(CE_Failure, CPLE_OutOfMemory,
     221             :                                     "Out of memory in AdoptBlock().");
     222           0 :                 return CE_Failure;
     223             :             }
     224             :         }
     225             : 
     226             :         /* --------------------------------------------------------------------
     227             :          */
     228             :         /*      Check within subblock. */
     229             :         /* --------------------------------------------------------------------
     230             :          */
     231       57467 :         GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
     232             : 
     233       57467 :         const int nBlockInSubBlock =
     234       57467 :             WITHIN_SUBBLOCK(nXBlockOff) +
     235       57467 :             WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
     236             : 
     237       57467 :         CPLAssert(papoSubBlockGrid[nBlockInSubBlock] == nullptr);
     238       57467 :         papoSubBlockGrid[nBlockInSubBlock] = poBlock;
     239             :     }
     240             : 
     241     1352560 :     return CE_None;
     242             : }
     243             : 
     244             : /************************************************************************/
     245             : /*                            FlushCache()                              */
     246             : /************************************************************************/
     247             : 
     248      998988 : CPLErr GDALArrayBandBlockCache::FlushCache()
     249             : {
     250      998988 :     FreeDanglingBlocks();
     251             : 
     252      998986 :     CPLErr eGlobalErr = poBand->eFlushBlockErr;
     253             : 
     254      998986 :     StartDirtyBlockFlushingLog();
     255             : 
     256             :     /* -------------------------------------------------------------------- */
     257             :     /*      Flush all blocks in memory ... this case is without subblocking.*/
     258             :     /* -------------------------------------------------------------------- */
     259      998984 :     if (!bSubBlockingActive && u.papoBlocks != nullptr)
     260             :     {
     261      998577 :         const int nBlocksPerColumn = poBand->nBlocksPerColumn;
     262      998577 :         const int nBlocksPerRow = poBand->nBlocksPerRow;
     263    87459700 :         for (int iY = 0; iY < nBlocksPerColumn; iY++)
     264             :         {
     265   173269000 :             for (int iX = 0; iX < nBlocksPerRow; iX++)
     266             :             {
     267    86807700 :                 if (u.papoBlocks[iX + iY * nBlocksPerRow] != nullptr)
     268             :                 {
     269     1265600 :                     CPLErr eErr = FlushBlock(iX, iY, eGlobalErr == CE_None);
     270             : 
     271     1265600 :                     if (eErr != CE_None)
     272           5 :                         eGlobalErr = eErr;
     273             :                 }
     274             :             }
     275      998574 :         }
     276             :     }
     277             : 
     278             :     /* -------------------------------------------------------------------- */
     279             :     /*      With subblocking.  We can short circuit missing subblocks.      */
     280             :     /* -------------------------------------------------------------------- */
     281         407 :     else if (u.papapoBlocks != nullptr)
     282             :     {
     283         876 :         for (int iSBY = 0; iSBY < nSubBlocksPerColumn; iSBY++)
     284             :         {
     285        1483 :             for (int iSBX = 0; iSBX < nSubBlocksPerRow; iSBX++)
     286             :             {
     287        1015 :                 const int nSubBlock = iSBX + iSBY * nSubBlocksPerRow;
     288             : 
     289        1015 :                 GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
     290             : 
     291        1015 :                 if (papoSubBlockGrid == nullptr)
     292         854 :                     continue;
     293             : 
     294       10465 :                 for (int iY = 0; iY < SUBBLOCK_SIZE; iY++)
     295             :                 {
     296      669760 :                     for (int iX = 0; iX < SUBBLOCK_SIZE; iX++)
     297             :                     {
     298      659456 :                         if (papoSubBlockGrid[iX + iY * SUBBLOCK_SIZE] !=
     299             :                             nullptr)
     300             :                         {
     301      111508 :                             CPLErr eErr = FlushBlock(iX + iSBX * SUBBLOCK_SIZE,
     302       55754 :                                                      iY + iSBY * SUBBLOCK_SIZE,
     303             :                                                      eGlobalErr == CE_None);
     304       55754 :                             if (eErr != CE_None)
     305           2 :                                 eGlobalErr = eErr;
     306             :                         }
     307             :                     }
     308             :                 }
     309             : 
     310             :                 // We might as well get rid of this grid chunk since we know
     311             :                 // it is now empty.
     312         161 :                 u.papapoBlocks[nSubBlock] = nullptr;
     313         161 :                 CPLFree(papoSubBlockGrid);
     314             :             }
     315             :         }
     316             :     }
     317             : 
     318      998981 :     EndDirtyBlockFlushingLog();
     319             : 
     320      998985 :     WaitCompletionPendingTasks();
     321             : 
     322      998987 :     return (eGlobalErr);
     323             : }
     324             : 
     325             : /************************************************************************/
     326             : /*                        UnreferenceBlock()                            */
     327             : /************************************************************************/
     328             : 
     329       29626 : CPLErr GDALArrayBandBlockCache::UnreferenceBlock(GDALRasterBlock *poBlock)
     330             : {
     331       29626 :     const int nXBlockOff = poBlock->GetXOff();
     332       29626 :     const int nYBlockOff = poBlock->GetYOff();
     333             : 
     334       29626 :     UnreferenceBlockBase();
     335             : 
     336             :     /* -------------------------------------------------------------------- */
     337             :     /*      Simple case for single level caches.                            */
     338             :     /* -------------------------------------------------------------------- */
     339       29626 :     if (!bSubBlockingActive)
     340             :     {
     341       27915 :         const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
     342             : 
     343       27915 :         u.papoBlocks[nBlockIndex] = nullptr;
     344             :     }
     345             : 
     346             :     /* -------------------------------------------------------------------- */
     347             :     /*      Identify our subblock.                                          */
     348             :     /* -------------------------------------------------------------------- */
     349             :     else
     350             :     {
     351        1711 :         const int nSubBlock = TO_SUBBLOCK(nXBlockOff) +
     352        1711 :                               TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
     353             : 
     354             :         /* --------------------------------------------------------------------
     355             :          */
     356             :         /*      Check within subblock. */
     357             :         /* --------------------------------------------------------------------
     358             :          */
     359        1711 :         GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
     360        1711 :         if (papoSubBlockGrid == nullptr)
     361           0 :             return CE_None;
     362             : 
     363        1711 :         const int nBlockInSubBlock =
     364        1711 :             WITHIN_SUBBLOCK(nXBlockOff) +
     365        1711 :             WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
     366             : 
     367        1711 :         papoSubBlockGrid[nBlockInSubBlock] = nullptr;
     368             :     }
     369             : 
     370       29626 :     return CE_None;
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                            FlushBlock()                              */
     375             : /************************************************************************/
     376             : 
     377     1323660 : CPLErr GDALArrayBandBlockCache::FlushBlock(int nXBlockOff, int nYBlockOff,
     378             :                                            int bWriteDirtyBlock)
     379             : 
     380             : {
     381     1323660 :     GDALRasterBlock *poBlock = nullptr;
     382             : 
     383             :     /* -------------------------------------------------------------------- */
     384             :     /*      Simple case for single level caches.                            */
     385             :     /* -------------------------------------------------------------------- */
     386     1323660 :     if (!bSubBlockingActive)
     387             :     {
     388     1267900 :         const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
     389             : 
     390     1267900 :         assert(u.papoBlocks);
     391     1267900 :         poBlock = u.papoBlocks[nBlockIndex];
     392     1267900 :         u.papoBlocks[nBlockIndex] = nullptr;
     393             :     }
     394             : 
     395             :     /* -------------------------------------------------------------------- */
     396             :     /*      Identify our subblock.                                          */
     397             :     /* -------------------------------------------------------------------- */
     398             :     else
     399             :     {
     400       55756 :         const int nSubBlock = TO_SUBBLOCK(nXBlockOff) +
     401       55756 :                               TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
     402             : 
     403             :         /* --------------------------------------------------------------------
     404             :          */
     405             :         /*      Check within subblock. */
     406             :         /* --------------------------------------------------------------------
     407             :          */
     408       55756 :         GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
     409       55756 :         if (papoSubBlockGrid == nullptr)
     410           0 :             return CE_None;
     411             : 
     412       55756 :         const int nBlockInSubBlock =
     413       55756 :             WITHIN_SUBBLOCK(nXBlockOff) +
     414       55756 :             WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
     415             : 
     416       55756 :         poBlock = papoSubBlockGrid[nBlockInSubBlock];
     417       55756 :         papoSubBlockGrid[nBlockInSubBlock] = nullptr;
     418             :     }
     419             : 
     420     1323660 :     if (poBlock == nullptr)
     421         701 :         return CE_None;
     422             : 
     423     1322950 :     if (!poBlock->DropLockForRemovalFromStorage())
     424           2 :         return CE_None;
     425             : 
     426             :     /* -------------------------------------------------------------------- */
     427             :     /*      Is the target block dirty?  If so we need to write it.          */
     428             :     /* -------------------------------------------------------------------- */
     429     1322960 :     poBlock->Detach();
     430             : 
     431     1322960 :     CPLErr eErr = CE_None;
     432             : 
     433     1322960 :     if (!m_nWriteDirtyBlocksDisabled && bWriteDirtyBlock && poBlock->GetDirty())
     434             :     {
     435      216563 :         UpdateDirtyBlockFlushingLog();
     436             : 
     437      216563 :         eErr = poBlock->Write();
     438             :     }
     439             : 
     440             :     /* -------------------------------------------------------------------- */
     441             :     /*      Deallocate the block;                                           */
     442             :     /* -------------------------------------------------------------------- */
     443     1322960 :     delete poBlock;
     444             : 
     445     1322960 :     return eErr;
     446             : }
     447             : 
     448             : /************************************************************************/
     449             : /*                        TryGetLockedBlockRef()                        */
     450             : /************************************************************************/
     451             : 
     452     7506390 : GDALRasterBlock *GDALArrayBandBlockCache::TryGetLockedBlockRef(int nXBlockOff,
     453             :                                                                int nYBlockOff)
     454             : 
     455             : {
     456             :     /* -------------------------------------------------------------------- */
     457             :     /*      Simple case for single level caches.                            */
     458             :     /* -------------------------------------------------------------------- */
     459     7506390 :     if (!bSubBlockingActive)
     460             :     {
     461     6724090 :         const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
     462             : 
     463     6724090 :         GDALRasterBlock *poBlock = u.papoBlocks[nBlockIndex];
     464     6724090 :         if (poBlock == nullptr || !poBlock->TakeLock())
     465     1247450 :             return nullptr;
     466     5476760 :         return poBlock;
     467             :     }
     468             :     else
     469             :     {
     470             :         /* --------------------------------------------------------------------
     471             :          */
     472             :         /*      Identify our subblock. */
     473             :         /* --------------------------------------------------------------------
     474             :          */
     475      782305 :         const int nSubBlock = TO_SUBBLOCK(nXBlockOff) +
     476      782305 :                               TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
     477             : 
     478             :         /* --------------------------------------------------------------------
     479             :          */
     480             :         /*      Check within subblock. */
     481             :         /* --------------------------------------------------------------------
     482             :          */
     483      782305 :         GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
     484      782305 :         if (papoSubBlockGrid == nullptr)
     485          50 :             return nullptr;
     486             : 
     487      782255 :         const int nBlockInSubBlock =
     488      782255 :             WITHIN_SUBBLOCK(nXBlockOff) +
     489      782255 :             WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
     490             : 
     491      782255 :         GDALRasterBlock *poBlock = papoSubBlockGrid[nBlockInSubBlock];
     492      782255 :         if (poBlock == nullptr || !poBlock->TakeLock())
     493       57313 :             return nullptr;
     494      724942 :         return poBlock;
     495             :     }
     496             : }
     497             : 
     498             : //! @endcond

Generated by: LCOV version 1.14