Coverage Report

Created: 2025-11-16 06:25

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