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