Coverage Report

Created: 2025-06-13 06:29

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