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 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "cpl_port.h"
31 #include "gdal_priv.h"
32
33 #include <cassert>
34 #include <climits>
35 #include <cstddef>
36 #include <new>
37
38 #include "cpl_conv.h"
39 #include "cpl_error.h"
40 #include "cpl_multiproc.h"
41 #include "cpl_vsi.h"
42
43 //! @cond Doxygen_Suppress
44
45 constexpr int SUBBLOCK_SIZE = 64;
46 #define TO_SUBBLOCK(x) ((x) >> 6)
47 #define WITHIN_SUBBLOCK(x) ((x) & 0x3f)
48
49 CPL_CVSID("$Id: gdalarraybandblockcache.cpp 2cef454ab06723a32379690b46581a40eb2e5af2 2019-11-13 16:36:03 +0100 Even Rouault $")
50
51 /* ******************************************************************** */
52 /* GDALArrayBandBlockCache */
53 /* ******************************************************************** */
54
55 class GDALArrayBandBlockCache final : public GDALAbstractBandBlockCache
56 {
57 bool bSubBlockingActive = false;
58 int nSubBlocksPerRow = 0;
59 int nSubBlocksPerColumn = 0;
60 union u
61 {
62 GDALRasterBlock **papoBlocks;
63 GDALRasterBlock ***papapoBlocks;
64
u()65 u(): papoBlocks(nullptr) {}
66 } u{};
67
68 CPL_DISALLOW_COPY_ASSIGN(GDALArrayBandBlockCache)
69
70 public:
71 explicit GDALArrayBandBlockCache( GDALRasterBand* poBand );
72 ~GDALArrayBandBlockCache() override;
73
74 bool Init() override;
75 bool IsInitOK() override;
76 CPLErr FlushCache() override;
77 CPLErr AdoptBlock( GDALRasterBlock * ) override;
78 GDALRasterBlock *TryGetLockedBlockRef( int nXBlockOff,
79 int nYBlockYOff ) override;
80 CPLErr UnreferenceBlock( GDALRasterBlock* poBlock ) override;
81 CPLErr FlushBlock( int nXBlockOff, int nYBlockOff,
82 int bWriteDirtyBlock ) override;
83 };
84
85 /************************************************************************/
86 /* GDALArrayBandBlockCacheCreate() */
87 /************************************************************************/
88
GDALArrayBandBlockCacheCreate(GDALRasterBand * poBand)89 GDALAbstractBandBlockCache* GDALArrayBandBlockCacheCreate(GDALRasterBand* poBand)
90 {
91 return new (std::nothrow) GDALArrayBandBlockCache(poBand);
92 }
93
94 /************************************************************************/
95 /* GDALArrayBandBlockCache() */
96 /************************************************************************/
97
GDALArrayBandBlockCache(GDALRasterBand * poBandIn)98 GDALArrayBandBlockCache::GDALArrayBandBlockCache(GDALRasterBand* poBandIn) :
99 GDALAbstractBandBlockCache(poBandIn)
100 {
101 }
102
103 /************************************************************************/
104 /* ~GDALArrayBandBlockCache() */
105 /************************************************************************/
106
~GDALArrayBandBlockCache()107 GDALArrayBandBlockCache::~GDALArrayBandBlockCache()
108 {
109 GDALArrayBandBlockCache::FlushCache();
110
111 if( !bSubBlockingActive )
112 CPLFree( u.papoBlocks );
113 else
114 CPLFree( u.papapoBlocks );
115 }
116
117 /************************************************************************/
118 /* Init() */
119 /************************************************************************/
120
Init()121 bool GDALArrayBandBlockCache::Init()
122 {
123 if( poBand->nBlocksPerRow < SUBBLOCK_SIZE/2 )
124 {
125 bSubBlockingActive = false;
126
127 if (poBand->nBlocksPerRow < INT_MAX / poBand->nBlocksPerColumn)
128 {
129 u.papoBlocks = static_cast<GDALRasterBlock **>(
130 VSICalloc( sizeof(void*),
131 poBand->nBlocksPerRow * poBand->nBlocksPerColumn ) );
132 if( u.papoBlocks == nullptr )
133 {
134 poBand->ReportError( CE_Failure, CPLE_OutOfMemory,
135 "Out of memory in InitBlockInfo()." );
136 return false;
137 }
138 }
139 else
140 {
141 poBand->ReportError( CE_Failure, CPLE_NotSupported,
142 "Too many blocks : %d x %d",
143 poBand->nBlocksPerRow, poBand->nBlocksPerColumn );
144 return false;
145 }
146 }
147 else
148 {
149 bSubBlockingActive = true;
150
151 nSubBlocksPerRow = DIV_ROUND_UP(poBand->nBlocksPerRow, SUBBLOCK_SIZE);
152 nSubBlocksPerColumn = DIV_ROUND_UP(poBand->nBlocksPerColumn, SUBBLOCK_SIZE);
153
154 if (nSubBlocksPerRow < INT_MAX / nSubBlocksPerColumn)
155 {
156 u.papapoBlocks = static_cast<GDALRasterBlock ***>(
157 VSICalloc( sizeof(void*),
158 nSubBlocksPerRow * nSubBlocksPerColumn ) );
159 if( u.papapoBlocks == nullptr )
160 {
161 poBand->ReportError( CE_Failure, CPLE_OutOfMemory,
162 "Out of memory in InitBlockInfo()." );
163 return false;
164 }
165 }
166 else
167 {
168 poBand->ReportError( CE_Failure, CPLE_NotSupported,
169 "Too many subblocks : %d x %d",
170 nSubBlocksPerRow, nSubBlocksPerColumn );
171 return false;
172 }
173 }
174
175 return true;
176 }
177
178 /************************************************************************/
179 /* IsInitOK() */
180 /************************************************************************/
181
IsInitOK()182 bool GDALArrayBandBlockCache::IsInitOK()
183 {
184 return (!bSubBlockingActive) ?
185 u.papoBlocks != nullptr : u.papapoBlocks != nullptr;
186 }
187
188 /************************************************************************/
189 /* AdoptBlock() */
190 /************************************************************************/
191
AdoptBlock(GDALRasterBlock * poBlock)192 CPLErr GDALArrayBandBlockCache::AdoptBlock( GDALRasterBlock * poBlock )
193
194 {
195 const int nXBlockOff = poBlock->GetXOff();
196 const int nYBlockOff = poBlock->GetYOff();
197
198 FreeDanglingBlocks();
199
200 /* -------------------------------------------------------------------- */
201 /* Simple case without subblocking. */
202 /* -------------------------------------------------------------------- */
203
204 if( !bSubBlockingActive )
205 {
206 const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
207
208 CPLAssert( u.papoBlocks[nBlockIndex] == nullptr );
209 u.papoBlocks[nBlockIndex] = poBlock;
210 }
211 else
212 {
213 /* -------------------------------------------------------------------- */
214 /* Identify the subblock in which our target occurs, and create */
215 /* it if necessary. */
216 /* -------------------------------------------------------------------- */
217 const int nSubBlock = TO_SUBBLOCK(nXBlockOff)
218 + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
219
220 if( u.papapoBlocks[nSubBlock] == nullptr )
221 {
222 const int nSubGridSize =
223 sizeof(GDALRasterBlock*) * SUBBLOCK_SIZE * SUBBLOCK_SIZE;
224
225 u.papapoBlocks[nSubBlock] = static_cast<GDALRasterBlock **>(
226 VSICalloc(1, nSubGridSize) );
227 if( u.papapoBlocks[nSubBlock] == nullptr )
228 {
229 poBand->ReportError( CE_Failure, CPLE_OutOfMemory,
230 "Out of memory in AdoptBlock()." );
231 return CE_Failure;
232 }
233 }
234
235 /* -------------------------------------------------------------------- */
236 /* Check within subblock. */
237 /* -------------------------------------------------------------------- */
238 GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
239
240 const int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff)
241 + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
242
243 CPLAssert( papoSubBlockGrid[nBlockInSubBlock] == nullptr );
244 papoSubBlockGrid[nBlockInSubBlock] = poBlock;
245 }
246
247 return CE_None;
248 }
249
250 /************************************************************************/
251 /* FlushCache() */
252 /************************************************************************/
253
FlushCache()254 CPLErr GDALArrayBandBlockCache::FlushCache()
255 {
256 FreeDanglingBlocks();
257
258 CPLErr eGlobalErr = poBand->eFlushBlockErr;
259
260 StartDirtyBlockFlushingLog();
261
262 /* -------------------------------------------------------------------- */
263 /* Flush all blocks in memory ... this case is without subblocking.*/
264 /* -------------------------------------------------------------------- */
265 if( !bSubBlockingActive && u.papoBlocks != nullptr )
266 {
267 const int nBlocksPerColumn = poBand->nBlocksPerColumn;
268 const int nBlocksPerRow = poBand->nBlocksPerRow;
269 for( int iY = 0; iY < nBlocksPerColumn; iY++ )
270 {
271 for( int iX = 0; iX < nBlocksPerRow; iX++ )
272 {
273 if( u.papoBlocks[iX + iY*nBlocksPerRow] != nullptr )
274 {
275 CPLErr eErr = FlushBlock( iX, iY, eGlobalErr == CE_None );
276
277 if( eErr != CE_None )
278 eGlobalErr = eErr;
279 }
280 }
281 }
282 }
283
284 /* -------------------------------------------------------------------- */
285 /* With subblocking. We can short circuit missing subblocks. */
286 /* -------------------------------------------------------------------- */
287 else if( u.papapoBlocks != nullptr )
288 {
289 for( int iSBY = 0; iSBY < nSubBlocksPerColumn; iSBY++ )
290 {
291 for( int iSBX = 0; iSBX < nSubBlocksPerRow; iSBX++ )
292 {
293 const int nSubBlock = iSBX + iSBY * nSubBlocksPerRow;
294
295 GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
296
297 if( papoSubBlockGrid == nullptr )
298 continue;
299
300 for( int iY = 0; iY < SUBBLOCK_SIZE; iY++ )
301 {
302 for( int iX = 0; iX < SUBBLOCK_SIZE; iX++ )
303 {
304 if( papoSubBlockGrid[iX + iY * SUBBLOCK_SIZE] != nullptr )
305 {
306 CPLErr eErr = FlushBlock( iX + iSBX * SUBBLOCK_SIZE,
307 iY + iSBY * SUBBLOCK_SIZE,
308 eGlobalErr == CE_None );
309 if( eErr != CE_None )
310 eGlobalErr = eErr;
311 }
312 }
313 }
314
315 // We might as well get rid of this grid chunk since we know
316 // it is now empty.
317 u.papapoBlocks[nSubBlock] = nullptr;
318 CPLFree( papoSubBlockGrid );
319 }
320 }
321 }
322
323 EndDirtyBlockFlushingLog();
324
325 WaitCompletionPendingTasks();
326
327 return( eGlobalErr );
328 }
329
330 /************************************************************************/
331 /* UnreferenceBlock() */
332 /************************************************************************/
333
UnreferenceBlock(GDALRasterBlock * poBlock)334 CPLErr GDALArrayBandBlockCache::UnreferenceBlock( GDALRasterBlock* poBlock )
335 {
336 const int nXBlockOff = poBlock->GetXOff();
337 const int nYBlockOff = poBlock->GetYOff();
338
339 UnreferenceBlockBase();
340
341 /* -------------------------------------------------------------------- */
342 /* Simple case for single level caches. */
343 /* -------------------------------------------------------------------- */
344 if( !bSubBlockingActive )
345 {
346 const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
347
348 u.papoBlocks[nBlockIndex] = nullptr;
349 }
350
351 /* -------------------------------------------------------------------- */
352 /* Identify our subblock. */
353 /* -------------------------------------------------------------------- */
354 else
355 {
356 const int nSubBlock = TO_SUBBLOCK(nXBlockOff)
357 + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
358
359 /* -------------------------------------------------------------------- */
360 /* Check within subblock. */
361 /* -------------------------------------------------------------------- */
362 GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
363 if( papoSubBlockGrid == nullptr )
364 return CE_None;
365
366 const int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff)
367 + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
368
369 papoSubBlockGrid[nBlockInSubBlock] = nullptr;
370 }
371
372 return CE_None;
373 }
374
375 /************************************************************************/
376 /* FlushBlock() */
377 /************************************************************************/
378
FlushBlock(int nXBlockOff,int nYBlockOff,int bWriteDirtyBlock)379 CPLErr GDALArrayBandBlockCache::FlushBlock( int nXBlockOff, int nYBlockOff,
380 int bWriteDirtyBlock )
381
382 {
383 GDALRasterBlock *poBlock = nullptr;
384
385 /* -------------------------------------------------------------------- */
386 /* Simple case for single level caches. */
387 /* -------------------------------------------------------------------- */
388 if( !bSubBlockingActive )
389 {
390 const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
391
392 assert(u.papoBlocks);
393 poBlock = u.papoBlocks[nBlockIndex];
394 u.papoBlocks[nBlockIndex] = nullptr;
395 }
396
397 /* -------------------------------------------------------------------- */
398 /* Identify our subblock. */
399 /* -------------------------------------------------------------------- */
400 else
401 {
402 const int nSubBlock = TO_SUBBLOCK(nXBlockOff)
403 + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
404
405 /* -------------------------------------------------------------------- */
406 /* Check within subblock. */
407 /* -------------------------------------------------------------------- */
408 GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
409 if( papoSubBlockGrid == nullptr )
410 return CE_None;
411
412 const int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff)
413 + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
414
415 poBlock = papoSubBlockGrid[nBlockInSubBlock];
416 papoSubBlockGrid[nBlockInSubBlock] = nullptr;
417 }
418
419 if( poBlock == nullptr )
420 return CE_None;
421
422 if( !poBlock->DropLockForRemovalFromStorage() )
423 return CE_None;
424
425 /* -------------------------------------------------------------------- */
426 /* Is the target block dirty? If so we need to write it. */
427 /* -------------------------------------------------------------------- */
428 poBlock->Detach();
429
430 CPLErr eErr = CE_None;
431
432 if( bWriteDirtyBlock && poBlock->GetDirty() )
433 {
434 UpdateDirtyBlockFlushingLog();
435
436 eErr = poBlock->Write();
437 }
438
439 /* -------------------------------------------------------------------- */
440 /* Deallocate the block; */
441 /* -------------------------------------------------------------------- */
442 delete poBlock;
443
444 return eErr;
445 }
446
447 /************************************************************************/
448 /* TryGetLockedBlockRef() */
449 /************************************************************************/
450
TryGetLockedBlockRef(int nXBlockOff,int nYBlockOff)451 GDALRasterBlock *GDALArrayBandBlockCache::TryGetLockedBlockRef( int nXBlockOff,
452 int nYBlockOff )
453
454 {
455 /* -------------------------------------------------------------------- */
456 /* Simple case for single level caches. */
457 /* -------------------------------------------------------------------- */
458 if( !bSubBlockingActive )
459 {
460 const int nBlockIndex = nXBlockOff + nYBlockOff * poBand->nBlocksPerRow;
461
462 GDALRasterBlock* poBlock = u.papoBlocks[nBlockIndex];
463 if( poBlock == nullptr || !poBlock->TakeLock() )
464 return nullptr;
465 return poBlock;
466 }
467 else
468 {
469 /* -------------------------------------------------------------------- */
470 /* Identify our subblock. */
471 /* -------------------------------------------------------------------- */
472 const int nSubBlock = TO_SUBBLOCK(nXBlockOff)
473 + TO_SUBBLOCK(nYBlockOff) * nSubBlocksPerRow;
474
475 /* -------------------------------------------------------------------- */
476 /* Check within subblock. */
477 /* -------------------------------------------------------------------- */
478 GDALRasterBlock **papoSubBlockGrid = u.papapoBlocks[nSubBlock];
479 if( papoSubBlockGrid == nullptr )
480 return nullptr;
481
482 const int nBlockInSubBlock = WITHIN_SUBBLOCK(nXBlockOff)
483 + WITHIN_SUBBLOCK(nYBlockOff) * SUBBLOCK_SIZE;
484
485 GDALRasterBlock* poBlock = papoSubBlockGrid[nBlockInSubBlock];
486 if( poBlock == nullptr || !poBlock->TakeLock() )
487 return nullptr;
488 return poBlock;
489 }
490 }
491
492 //! @endcond
493