Coverage Report

Created: 2026-03-30 09:00

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