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