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 : //! @cond Doxygen_Suppress 28 : 29 : constexpr int SUBBLOCK_SIZE = 64; 30 : #define TO_SUBBLOCK(x) ((x) >> 6) 31 : #define WITHIN_SUBBLOCK(x) ((x)&0x3f) 32 : 33 : /* ******************************************************************** */ 34 : /* GDALArrayBandBlockCache */ 35 : /* ******************************************************************** */ 36 : 37 : class GDALArrayBandBlockCache final : public GDALAbstractBandBlockCache 38 : { 39 : bool bSubBlockingActive = false; 40 : int nSubBlocksPerRow = 0; 41 : int nSubBlocksPerColumn = 0; 42 : 43 : union u 44 : { 45 : GDALRasterBlock **papoBlocks; 46 : GDALRasterBlock ***papapoBlocks; 47 : 48 32953 : u() : papoBlocks(nullptr) 49 : { 50 32953 : } 51 : } u{}; 52 : 53 : CPL_DISALLOW_COPY_ASSIGN(GDALArrayBandBlockCache) 54 : 55 : public: 56 : explicit GDALArrayBandBlockCache(GDALRasterBand *poBand); 57 : ~GDALArrayBandBlockCache() override; 58 : 59 : bool Init() override; 60 : bool IsInitOK() override; 61 : CPLErr FlushCache() override; 62 : CPLErr AdoptBlock(GDALRasterBlock *) override; 63 : GDALRasterBlock *TryGetLockedBlockRef(int nXBlockOff, 64 : int nYBlockYOff) override; 65 : CPLErr UnreferenceBlock(GDALRasterBlock *poBlock) override; 66 : CPLErr FlushBlock(int nXBlockOff, int nYBlockOff, 67 : int bWriteDirtyBlock) override; 68 : }; 69 : 70 : /************************************************************************/ 71 : /* GDALArrayBandBlockCacheCreate() */ 72 : /************************************************************************/ 73 : 74 : GDALAbstractBandBlockCache * 75 32953 : GDALArrayBandBlockCacheCreate(GDALRasterBand *poBand) 76 : { 77 32953 : return new (std::nothrow) GDALArrayBandBlockCache(poBand); 78 : } 79 : 80 : /************************************************************************/ 81 : /* GDALArrayBandBlockCache() */ 82 : /************************************************************************/ 83 : 84 32953 : GDALArrayBandBlockCache::GDALArrayBandBlockCache(GDALRasterBand *poBandIn) 85 32953 : : GDALAbstractBandBlockCache(poBandIn) 86 : { 87 32953 : } 88 : 89 : /************************************************************************/ 90 : /* ~GDALArrayBandBlockCache() */ 91 : /************************************************************************/ 92 : 93 65905 : GDALArrayBandBlockCache::~GDALArrayBandBlockCache() 94 : { 95 32952 : GDALArrayBandBlockCache::FlushCache(); 96 : 97 32950 : if (!bSubBlockingActive) 98 32825 : CPLFree(u.papoBlocks); 99 : else 100 125 : CPLFree(u.papapoBlocks); 101 65905 : } 102 : 103 : /************************************************************************/ 104 : /* Init() */ 105 : /************************************************************************/ 106 : 107 32953 : bool GDALArrayBandBlockCache::Init() 108 : { 109 32953 : if (poBand->nBlocksPerRow < SUBBLOCK_SIZE / 2) 110 : { 111 32828 : bSubBlockingActive = false; 112 : 113 32828 : if (poBand->nBlocksPerRow < INT_MAX / poBand->nBlocksPerColumn) 114 : { 115 32828 : u.papoBlocks = static_cast<GDALRasterBlock **>(VSICalloc( 116 32828 : sizeof(void *), cpl::fits_on<int>(poBand->nBlocksPerRow * 117 32828 : poBand->nBlocksPerColumn))); 118 32828 : if (u.papoBlocks == nullptr) 119 : { 120 0 : poBand->ReportError(CE_Failure, CPLE_OutOfMemory, 121 : "Out of memory in InitBlockInfo()."); 122 0 : return false; 123 : } 124 : } 125 : else 126 : { 127 0 : poBand->ReportError( 128 : CE_Failure, CPLE_NotSupported, "Too many blocks : %d x %d", 129 0 : poBand->nBlocksPerRow, poBand->nBlocksPerColumn); 130 0 : return false; 131 : } 132 : } 133 : else 134 : { 135 125 : bSubBlockingActive = true; 136 : 137 125 : nSubBlocksPerRow = DIV_ROUND_UP(poBand->nBlocksPerRow, SUBBLOCK_SIZE); 138 125 : nSubBlocksPerColumn = 139 125 : DIV_ROUND_UP(poBand->nBlocksPerColumn, SUBBLOCK_SIZE); 140 : 141 125 : if (nSubBlocksPerRow < INT_MAX / nSubBlocksPerColumn) 142 : { 143 125 : u.papapoBlocks = static_cast<GDALRasterBlock ***>(VSICalloc( 144 : sizeof(void *), 145 125 : cpl::fits_on<int>(nSubBlocksPerRow * nSubBlocksPerColumn))); 146 125 : if (u.papapoBlocks == nullptr) 147 : { 148 0 : poBand->ReportError(CE_Failure, CPLE_OutOfMemory, 149 : "Out of memory in InitBlockInfo()."); 150 0 : return false; 151 : } 152 : } 153 : else 154 : { 155 0 : poBand->ReportError(CE_Failure, CPLE_NotSupported, 156 : "Too many subblocks : %d x %d", 157 : nSubBlocksPerRow, nSubBlocksPerColumn); 158 0 : return false; 159 : } 160 : } 161 : 162 32953 : return true; 163 : } 164 : 165 : /************************************************************************/ 166 : /* IsInitOK() */ 167 : /************************************************************************/ 168 : 169 8368720 : bool GDALArrayBandBlockCache::IsInitOK() 170 : { 171 8368720 : return (!bSubBlockingActive) ? u.papoBlocks != nullptr 172 8368720 : : u.papapoBlocks != nullptr; 173 : } 174 : 175 : /************************************************************************/ 176 : /* AdoptBlock() */ 177 : /************************************************************************/ 178 : 179 1113160 : CPLErr GDALArrayBandBlockCache::AdoptBlock(GDALRasterBlock *poBlock) 180 : 181 : { 182 1113160 : const int nXBlockOff = poBlock->GetXOff(); 183 1113150 : const int nYBlockOff = poBlock->GetYOff(); 184 : 185 1113150 : FreeDanglingBlocks(); 186 : 187 : /* -------------------------------------------------------------------- */ 188 : /* Simple case without subblocking. */ 189 : /* -------------------------------------------------------------------- */ 190 : 191 1113120 : if (!bSubBlockingActive) 192 : { 193 1055370 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 194 : 195 1055370 : CPLAssert(u.papoBlocks[nBlockIndex] == nullptr); 196 1055370 : u.papoBlocks[nBlockIndex] = poBlock; 197 : } 198 : else 199 : { 200 : /* -------------------------------------------------------------------- 201 : */ 202 : /* Identify the subblock in which our target occurs, and create */ 203 : /* it if necessary. */ 204 : /* -------------------------------------------------------------------- 205 : */ 206 57743 : const int nSubBlock = TO_SUBBLOCK(nXBlockOff) + 207 57743 : TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; 208 : 209 57743 : if (u.papapoBlocks[nSubBlock] == nullptr) 210 : { 211 169 : const int nSubGridSize = 212 : sizeof(GDALRasterBlock *) * SUBBLOCK_SIZE * SUBBLOCK_SIZE; 213 : 214 338 : u.papapoBlocks[nSubBlock] = 215 169 : static_cast<GDALRasterBlock **>(VSICalloc(1, nSubGridSize)); 216 169 : if (u.papapoBlocks[nSubBlock] == nullptr) 217 : { 218 0 : poBand->ReportError(CE_Failure, CPLE_OutOfMemory, 219 : "Out of memory in AdoptBlock()."); 220 0 : return CE_Failure; 221 : } 222 : } 223 : 224 : /* -------------------------------------------------------------------- 225 : */ 226 : /* Check within subblock. */ 227 : /* -------------------------------------------------------------------- 228 : */ 229 57743 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 230 : 231 57743 : const int nBlockInSubBlock = 232 57743 : WITHIN_SUBBLOCK(nXBlockOff) + 233 57743 : WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; 234 : 235 57743 : CPLAssert(papoSubBlockGrid[nBlockInSubBlock] == nullptr); 236 57743 : papoSubBlockGrid[nBlockInSubBlock] = poBlock; 237 : } 238 : 239 1113120 : return CE_None; 240 : } 241 : 242 : /************************************************************************/ 243 : /* FlushCache() */ 244 : /************************************************************************/ 245 : 246 179677 : CPLErr GDALArrayBandBlockCache::FlushCache() 247 : { 248 179677 : FreeDanglingBlocks(); 249 : 250 179674 : CPLErr eGlobalErr = poBand->eFlushBlockErr; 251 : 252 179674 : StartDirtyBlockFlushingLog(); 253 : 254 : /* -------------------------------------------------------------------- */ 255 : /* Flush all blocks in memory ... this case is without subblocking.*/ 256 : /* -------------------------------------------------------------------- */ 257 179671 : if (!bSubBlockingActive && u.papoBlocks != nullptr) 258 : { 259 179247 : const int nBlocksPerColumn = poBand->nBlocksPerColumn; 260 179247 : const int nBlocksPerRow = poBand->nBlocksPerRow; 261 87883100 : for (int iY = 0; iY < nBlocksPerColumn; iY++) 262 : { 263 175729000 : for (int iX = 0; iX < nBlocksPerRow; iX++) 264 : { 265 88025200 : if (u.papoBlocks[iX + iY * nBlocksPerRow] != nullptr) 266 : { 267 1025960 : CPLErr eErr = FlushBlock(iX, iY, eGlobalErr == CE_None); 268 : 269 1025960 : if (eErr != CE_None) 270 5 : eGlobalErr = eErr; 271 : } 272 : } 273 179248 : } 274 : } 275 : 276 : /* -------------------------------------------------------------------- */ 277 : /* With subblocking. We can short circuit missing subblocks. */ 278 : /* -------------------------------------------------------------------- */ 279 424 : else if (u.papapoBlocks != nullptr) 280 : { 281 906 : for (int iSBY = 0; iSBY < nSubBlocksPerColumn; iSBY++) 282 : { 283 1522 : for (int iSBX = 0; iSBX < nSubBlocksPerRow; iSBX++) 284 : { 285 1039 : const int nSubBlock = iSBX + iSBY * nSubBlocksPerRow; 286 : 287 1039 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 288 : 289 1039 : if (papoSubBlockGrid == nullptr) 290 870 : continue; 291 : 292 10985 : for (int iY = 0; iY < SUBBLOCK_SIZE; iY++) 293 : { 294 703040 : for (int iX = 0; iX < SUBBLOCK_SIZE; iX++) 295 : { 296 692224 : if (papoSubBlockGrid[iX + iY * SUBBLOCK_SIZE] != 297 : nullptr) 298 : { 299 112128 : CPLErr eErr = FlushBlock(iX + iSBX * SUBBLOCK_SIZE, 300 56064 : iY + iSBY * SUBBLOCK_SIZE, 301 : eGlobalErr == CE_None); 302 56064 : if (eErr != CE_None) 303 2 : eGlobalErr = eErr; 304 : } 305 : } 306 : } 307 : 308 : // We might as well get rid of this grid chunk since we know 309 : // it is now empty. 310 169 : u.papapoBlocks[nSubBlock] = nullptr; 311 169 : CPLFree(papoSubBlockGrid); 312 : } 313 : } 314 : } 315 : 316 179672 : EndDirtyBlockFlushingLog(); 317 : 318 179673 : WaitCompletionPendingTasks(); 319 : 320 179672 : return (eGlobalErr); 321 : } 322 : 323 : /************************************************************************/ 324 : /* UnreferenceBlock() */ 325 : /************************************************************************/ 326 : 327 29590 : CPLErr GDALArrayBandBlockCache::UnreferenceBlock(GDALRasterBlock *poBlock) 328 : { 329 29590 : const int nXBlockOff = poBlock->GetXOff(); 330 29590 : const int nYBlockOff = poBlock->GetYOff(); 331 : 332 29590 : UnreferenceBlockBase(); 333 : 334 : /* -------------------------------------------------------------------- */ 335 : /* Simple case for single level caches. */ 336 : /* -------------------------------------------------------------------- */ 337 29590 : if (!bSubBlockingActive) 338 : { 339 27913 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 340 : 341 27913 : u.papoBlocks[nBlockIndex] = nullptr; 342 : } 343 : 344 : /* -------------------------------------------------------------------- */ 345 : /* Identify our subblock. */ 346 : /* -------------------------------------------------------------------- */ 347 : else 348 : { 349 1677 : const int nSubBlock = TO_SUBBLOCK(nXBlockOff) + 350 1677 : TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; 351 : 352 : /* -------------------------------------------------------------------- 353 : */ 354 : /* Check within subblock. */ 355 : /* -------------------------------------------------------------------- 356 : */ 357 1677 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 358 1677 : if (papoSubBlockGrid == nullptr) 359 0 : return CE_None; 360 : 361 1677 : const int nBlockInSubBlock = 362 1677 : WITHIN_SUBBLOCK(nXBlockOff) + 363 1677 : WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; 364 : 365 1677 : papoSubBlockGrid[nBlockInSubBlock] = nullptr; 366 : } 367 : 368 29590 : return CE_None; 369 : } 370 : 371 : /************************************************************************/ 372 : /* FlushBlock() */ 373 : /************************************************************************/ 374 : 375 1084320 : CPLErr GDALArrayBandBlockCache::FlushBlock(int nXBlockOff, int nYBlockOff, 376 : int bWriteDirtyBlock) 377 : 378 : { 379 1084320 : GDALRasterBlock *poBlock = nullptr; 380 : 381 : /* -------------------------------------------------------------------- */ 382 : /* Simple case for single level caches. */ 383 : /* -------------------------------------------------------------------- */ 384 1084320 : if (!bSubBlockingActive) 385 : { 386 1028250 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 387 : 388 1028250 : assert(u.papoBlocks); 389 1028250 : poBlock = u.papoBlocks[nBlockIndex]; 390 1028250 : u.papoBlocks[nBlockIndex] = nullptr; 391 : } 392 : 393 : /* -------------------------------------------------------------------- */ 394 : /* Identify our subblock. */ 395 : /* -------------------------------------------------------------------- */ 396 : else 397 : { 398 56066 : const int nSubBlock = TO_SUBBLOCK(nXBlockOff) + 399 56066 : TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; 400 : 401 : /* -------------------------------------------------------------------- 402 : */ 403 : /* Check within subblock. */ 404 : /* -------------------------------------------------------------------- 405 : */ 406 56066 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 407 56066 : if (papoSubBlockGrid == nullptr) 408 0 : return CE_None; 409 : 410 56066 : const int nBlockInSubBlock = 411 56066 : WITHIN_SUBBLOCK(nXBlockOff) + 412 56066 : WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; 413 : 414 56066 : poBlock = papoSubBlockGrid[nBlockInSubBlock]; 415 56066 : papoSubBlockGrid[nBlockInSubBlock] = nullptr; 416 : } 417 : 418 1084320 : if (poBlock == nullptr) 419 701 : return CE_None; 420 : 421 1083620 : if (!poBlock->DropLockForRemovalFromStorage()) 422 3 : return CE_None; 423 : 424 : /* -------------------------------------------------------------------- */ 425 : /* Is the target block dirty? If so we need to write it. */ 426 : /* -------------------------------------------------------------------- */ 427 1083610 : poBlock->Detach(); 428 : 429 1083620 : CPLErr eErr = CE_None; 430 : 431 1083620 : if (!m_nWriteDirtyBlocksDisabled && bWriteDirtyBlock && poBlock->GetDirty()) 432 : { 433 165033 : UpdateDirtyBlockFlushingLog(); 434 : 435 165032 : eErr = poBlock->Write(); 436 : } 437 : 438 : /* -------------------------------------------------------------------- */ 439 : /* Deallocate the block; */ 440 : /* -------------------------------------------------------------------- */ 441 1083620 : delete poBlock; 442 : 443 1083620 : return eErr; 444 : } 445 : 446 : /************************************************************************/ 447 : /* TryGetLockedBlockRef() */ 448 : /************************************************************************/ 449 : 450 6904360 : GDALRasterBlock *GDALArrayBandBlockCache::TryGetLockedBlockRef(int nXBlockOff, 451 : int nYBlockOff) 452 : 453 : { 454 : /* -------------------------------------------------------------------- */ 455 : /* Simple case for single level caches. */ 456 : /* -------------------------------------------------------------------- */ 457 6904360 : if (!bSubBlockingActive) 458 : { 459 6121070 : const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow; 460 : 461 6121070 : GDALRasterBlock *poBlock = u.papoBlocks[nBlockIndex]; 462 6121070 : if (poBlock == nullptr || !poBlock->TakeLock()) 463 1107050 : return nullptr; 464 5015690 : return poBlock; 465 : } 466 : else 467 : { 468 : /* -------------------------------------------------------------------- 469 : */ 470 : /* Identify our subblock. */ 471 : /* -------------------------------------------------------------------- 472 : */ 473 783291 : const int nSubBlock = TO_SUBBLOCK(nXBlockOff) + 474 783291 : TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow; 475 : 476 : /* -------------------------------------------------------------------- 477 : */ 478 : /* Check within subblock. */ 479 : /* -------------------------------------------------------------------- 480 : */ 481 783291 : GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock]; 482 783291 : if (papoSubBlockGrid == nullptr) 483 52 : return nullptr; 484 : 485 783239 : const int nBlockInSubBlock = 486 783239 : WITHIN_SUBBLOCK(nXBlockOff) + 487 783239 : WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE; 488 : 489 783239 : GDALRasterBlock *poBlock = papoSubBlockGrid[nBlockInSubBlock]; 490 783239 : if (poBlock == nullptr || !poBlock->TakeLock()) 491 57581 : return nullptr; 492 725658 : return poBlock; 493 : } 494 : } 495 : 496 : //! @endcond