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 238460 : u() : papoBlocks(nullptr) 51 : { 52 238460 : } 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 238460 : GDALArrayBandBlockCacheCreate(GDALRasterBand *poBand) 78 : { 79 238460 : return new (std::nothrow) GDALArrayBandBlockCache(poBand); 80 : } 81 : 82 : /************************************************************************/ 83 : /* GDALArrayBandBlockCache() */ 84 : /************************************************************************/ 85 : 86 238460 : GDALArrayBandBlockCache::GDALArrayBandBlockCache(GDALRasterBand *poBandIn) 87 238460 : : GDALAbstractBandBlockCache(poBandIn) 88 : { 89 238459 : } 90 : 91 : /************************************************************************/ 92 : /* ~GDALArrayBandBlockCache() */ 93 : /************************************************************************/ 94 : 95 476918 : GDALArrayBandBlockCache::~GDALArrayBandBlockCache() 96 : { 97 238460 : GDALArrayBandBlockCache::FlushCache(); 98 : 99 238460 : if (!bSubBlockingActive) 100 238341 : CPLFree(u.papoBlocks); 101 : else 102 119 : CPLFree(u.papapoBlocks); 103 476919 : } 104 : 105 : /************************************************************************/ 106 : /* Init() */ 107 : /************************************************************************/ 108 : 109 238458 : bool GDALArrayBandBlockCache::Init() 110 : { 111 238458 : if (poBand->nBlocksPerRow < SUBBLOCK_SIZE / 2) 112 : { 113 238338 : bSubBlockingActive = false; 114 : 115 238338 : if (poBand->nBlocksPerRow < INT_MAX / poBand->nBlocksPerColumn) 116 : { 117 238336 : u.papoBlocks = static_cast<GDALRasterBlock **>(VSICalloc( 118 238337 : sizeof(void *), cpl::fits_on<int>(poBand->nBlocksPerRow * 119 238337 : poBand->nBlocksPerColumn))); 120 238337 : 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 1 : poBand->ReportError( 130 : CE_Failure, CPLE_NotSupported, "Too many blocks : %d x %d", 131 1 : poBand->nBlocksPerRow, poBand->nBlocksPerColumn); 132 0 : return false; 133 : } 134 : } 135 : else 136 : { 137 120 : bSubBlockingActive = true; 138 : 139 120 : nSubBlocksPerRow = DIV_ROUND_UP(poBand->nBlocksPerRow, SUBBLOCK_SIZE); 140 120 : nSubBlocksPerColumn = 141 120 : DIV_ROUND_UP(poBand->nBlocksPerColumn, SUBBLOCK_SIZE); 142 : 143 120 : 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 1 : poBand->ReportError(CE_Failure, CPLE_NotSupported, 158 : "Too many subblocks : %d x %d", 159 : nSubBlocksPerRow, nSubBlocksPerColumn); 160 0 : return false; 161 : } 162 : } 163 : 164 238455 : return true; 165 : } 166 : 167 : /************************************************************************/ 168 : /* IsInitOK() */ 169 : /************************************************************************/ 170 : 171 9719140 : bool GDALArrayBandBlockCache::IsInitOK() 172 : { 173 9719140 : return (!bSubBlockingActive) ? u.papoBlocks != nullptr 174 9719140 : : u.papapoBlocks != nullptr; 175 : } 176 : 177 : /************************************************************************/ 178 : /* AdoptBlock() */ 179 : /************************************************************************/ 180 : 181 1352550 : CPLErr GDALArrayBandBlockCache::AdoptBlock(GDALRasterBlock *poBlock) 182 : 183 : { 184 1352550 : const int nXBlockOff = poBlock->GetXOff(); 185 1352530 : const int nYBlockOff = poBlock->GetYOff(); 186 : 187 1352540 : FreeDanglingBlocks(); 188 : 189 : /* -------------------------------------------------------------------- */ 190 : /* Simple case without subblocking. */ 191 : /* -------------------------------------------------------------------- */ 192 : 193 1352530 : if (!bSubBlockingActive) 194 : { 195 1295070 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 196 : 197 1295070 : CPLAssert(u.papoBlocks[nBlockIndex] == nullptr); 198 1295070 : 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 1352530 : return CE_None; 242 : } 243 : 244 : /************************************************************************/ 245 : /* FlushCache() */ 246 : /************************************************************************/ 247 : 248 998997 : CPLErr GDALArrayBandBlockCache::FlushCache() 249 : { 250 998997 : FreeDanglingBlocks(); 251 : 252 998994 : CPLErr eGlobalErr = poBand->eFlushBlockErr; 253 : 254 998994 : StartDirtyBlockFlushingLog(); 255 : 256 : /* -------------------------------------------------------------------- */ 257 : /* Flush all blocks in memory ... this case is without subblocking.*/ 258 : /* -------------------------------------------------------------------- */ 259 998990 : if (!bSubBlockingActive && u.papoBlocks != nullptr) 260 : { 261 998585 : const int nBlocksPerColumn = poBand->nBlocksPerColumn; 262 998585 : const int nBlocksPerRow = poBand->nBlocksPerRow; 263 87459800 : for (int iY = 0; iY < nBlocksPerColumn; iY++) 264 : { 265 173269000 : for (int iX = 0; iX < nBlocksPerRow; iX++) 266 : { 267 86807900 : if (u.papoBlocks[iX + iY * nBlocksPerRow] != nullptr) 268 : { 269 1265570 : CPLErr eErr = FlushBlock(iX, iY, eGlobalErr == CE_None); 270 : 271 1265570 : if (eErr != CE_None) 272 5 : eGlobalErr = eErr; 273 : } 274 : } 275 998586 : } 276 : } 277 : 278 : /* -------------------------------------------------------------------- */ 279 : /* With subblocking. We can short circuit missing subblocks. */ 280 : /* -------------------------------------------------------------------- */ 281 405 : 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 111464 : CPLErr eErr = FlushBlock(iX + iSBX * SUBBLOCK_SIZE, 302 55732 : iY + iSBY * SUBBLOCK_SIZE, 303 : eGlobalErr == CE_None); 304 55732 : 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 998991 : EndDirtyBlockFlushingLog(); 319 : 320 998996 : WaitCompletionPendingTasks(); 321 : 322 998995 : return (eGlobalErr); 323 : } 324 : 325 : /************************************************************************/ 326 : /* UnreferenceBlock() */ 327 : /************************************************************************/ 328 : 329 29642 : CPLErr GDALArrayBandBlockCache::UnreferenceBlock(GDALRasterBlock *poBlock) 330 : { 331 29642 : const int nXBlockOff = poBlock->GetXOff(); 332 29642 : const int nYBlockOff = poBlock->GetYOff(); 333 : 334 29642 : UnreferenceBlockBase(); 335 : 336 : /* -------------------------------------------------------------------- */ 337 : /* Simple case for single level caches. */ 338 : /* -------------------------------------------------------------------- */ 339 29642 : if (!bSubBlockingActive) 340 : { 341 27909 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 342 : 343 27909 : u.papoBlocks[nBlockIndex] = nullptr; 344 : } 345 : 346 : /* -------------------------------------------------------------------- */ 347 : /* Identify our subblock. */ 348 : /* -------------------------------------------------------------------- */ 349 : else 350 : { 351 1733 : const int nSubBlock = TO_SUBBLOCK(nXBlockOff) + 352 1733 : TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; 353 : 354 : /* -------------------------------------------------------------------- 355 : */ 356 : /* Check within subblock. */ 357 : /* -------------------------------------------------------------------- 358 : */ 359 1733 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 360 1733 : if (papoSubBlockGrid == nullptr) 361 0 : return CE_None; 362 : 363 1733 : const int nBlockInSubBlock = 364 1733 : WITHIN_SUBBLOCK(nXBlockOff) + 365 1733 : WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; 366 : 367 1733 : papoSubBlockGrid[nBlockInSubBlock] = nullptr; 368 : } 369 : 370 29642 : return CE_None; 371 : } 372 : 373 : /************************************************************************/ 374 : /* FlushBlock() */ 375 : /************************************************************************/ 376 : 377 1323610 : CPLErr GDALArrayBandBlockCache::FlushBlock(int nXBlockOff, int nYBlockOff, 378 : int bWriteDirtyBlock) 379 : 380 : { 381 1323610 : GDALRasterBlock *poBlock = nullptr; 382 : 383 : /* -------------------------------------------------------------------- */ 384 : /* Simple case for single level caches. */ 385 : /* -------------------------------------------------------------------- */ 386 1323610 : if (!bSubBlockingActive) 387 : { 388 1267870 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 389 : 390 1267870 : assert(u.papoBlocks); 391 1267870 : poBlock = u.papoBlocks[nBlockIndex]; 392 1267870 : u.papoBlocks[nBlockIndex] = nullptr; 393 : } 394 : 395 : /* -------------------------------------------------------------------- */ 396 : /* Identify our subblock. */ 397 : /* -------------------------------------------------------------------- */ 398 : else 399 : { 400 55734 : const int nSubBlock = TO_SUBBLOCK(nXBlockOff) + 401 55734 : TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; 402 : 403 : /* -------------------------------------------------------------------- 404 : */ 405 : /* Check within subblock. */ 406 : /* -------------------------------------------------------------------- 407 : */ 408 55734 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 409 55734 : if (papoSubBlockGrid == nullptr) 410 0 : return CE_None; 411 : 412 55734 : const int nBlockInSubBlock = 413 55734 : WITHIN_SUBBLOCK(nXBlockOff) + 414 55734 : WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; 415 : 416 55734 : poBlock = papoSubBlockGrid[nBlockInSubBlock]; 417 55734 : papoSubBlockGrid[nBlockInSubBlock] = nullptr; 418 : } 419 : 420 1323610 : if (poBlock == nullptr) 421 701 : return CE_None; 422 : 423 1322910 : 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 1322890 : poBlock->Detach(); 430 : 431 1322910 : CPLErr eErr = CE_None; 432 : 433 1322910 : if (!m_nWriteDirtyBlocksDisabled && bWriteDirtyBlock && poBlock->GetDirty()) 434 : { 435 216567 : UpdateDirtyBlockFlushingLog(); 436 : 437 216567 : eErr = poBlock->Write(); 438 : } 439 : 440 : /* -------------------------------------------------------------------- */ 441 : /* Deallocate the block; */ 442 : /* -------------------------------------------------------------------- */ 443 1322910 : delete poBlock; 444 : 445 1322910 : return eErr; 446 : } 447 : 448 : /************************************************************************/ 449 : /* TryGetLockedBlockRef() */ 450 : /************************************************************************/ 451 : 452 7505860 : GDALRasterBlock *GDALArrayBandBlockCache::TryGetLockedBlockRef(int nXBlockOff, 453 : int nYBlockOff) 454 : 455 : { 456 : /* -------------------------------------------------------------------- */ 457 : /* Simple case for single level caches. */ 458 : /* -------------------------------------------------------------------- */ 459 7505860 : if (!bSubBlockingActive) 460 : { 461 6723710 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 462 : 463 6723710 : GDALRasterBlock *poBlock = u.papoBlocks[nBlockIndex]; 464 6723710 : if (poBlock == nullptr || !poBlock->TakeLock()) 465 1247450 : return nullptr; 466 5476710 : return poBlock; 467 : } 468 : else 469 : { 470 : /* -------------------------------------------------------------------- 471 : */ 472 : /* Identify our subblock. */ 473 : /* -------------------------------------------------------------------- 474 : */ 475 782153 : const int nSubBlock = TO_SUBBLOCK(nXBlockOff) + 476 782153 : TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; 477 : 478 : /* -------------------------------------------------------------------- 479 : */ 480 : /* Check within subblock. */ 481 : /* -------------------------------------------------------------------- 482 : */ 483 782153 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 484 782153 : if (papoSubBlockGrid == nullptr) 485 50 : return nullptr; 486 : 487 782103 : const int nBlockInSubBlock = 488 782103 : WITHIN_SUBBLOCK(nXBlockOff) + 489 782103 : WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; 490 : 491 782103 : GDALRasterBlock *poBlock = papoSubBlockGrid[nBlockInSubBlock]; 492 782103 : if (poBlock == nullptr || !poBlock->TakeLock()) 493 57313 : return nullptr; 494 724790 : return poBlock; 495 : } 496 : } 497 : 498 : //! @endcond